From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:30
Hi,
The following patchset contains Netfilter/IPVS fixes for net:
1) ipset limits the max allocatable memory via kvmalloc() to MAX_INT,
from Jozsef Kadlecsik.
2) Check ip_vs_conn_tab_bits value to be in the range specified
in Kconfig, from Andrea Claudi.
3) Initialize fragment offset in ip6tables, from Jeremy Sowden.
4) Make conntrack hash chain length random, from Florian Westphal.
5) Add zone ID to conntrack and NAT hashtuple again, also from Florian.
6) Add selftests for bidirectional zone support and colliding tuples,
from Florian Westphal.
7) Unlink table before synchronize_rcu when cleaning tables with
owner, from Florian.
8) ipset limits the max allocatable memory via kvmalloc() to MAX_INT.
9) Release conntrack entries via workqueue in masquerade, from Florian.
10) Fix bogus net_init in iptables raw table definition, also from Florian.
11) Work around missing softdep in log extensions, from Florian Westphal.
12) Serialize hash resizes and cleanups with mutex, from Eric Dumazet.
Please, pull these changes from:
git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git
Thanks.
----------------------------------------------------------------
The following changes since commit 276aae377206d60b9b7b7df4586cd9f2a813f5d0:
net: stmmac: fix system hang caused by eee_ctrl_timer during suspend/resume (2021-09-08 12:28:26 +0100)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git HEAD
for you to fetch changes up to e9edc188fc76499b0b9bd60364084037f6d03773:
netfilter: conntrack: serialize hash resizes and cleanups (2021-09-21 03:46:56 +0200)
----------------------------------------------------------------
Andrea Claudi (1):
ipvs: check that ip_vs_conn_tab_bits is between 8 and 20
Eric Dumazet (1):
netfilter: conntrack: serialize hash resizes and cleanups
Florian Westphal (10):
netfilter: conntrack: make max chain length random
netfilter: conntrack: include zone id in tuple hash again
netfilter: nat: include zone id in nat table hash again
selftests: netfilter: add selftest for directional zone support
selftests: netfilter: add zone stress test with colliding tuples
netfilter: nf_tables: unlink table before deleting it
netfilter: nf_nat_masquerade: make async masq_inet6_event handling generic
netfilter: nf_nat_masquerade: defer conntrack walk to work queue
netfilter: iptable_raw: drop bogus net_init annotation
netfilter: log: work around missing softdep backend module
Jeremy Sowden (1):
netfilter: ip6_tables: zero-initialize fragment offset
Jozsef Kadlecsik (1):
netfilter: ipset: Fix oversized kvmalloc() calls
Pablo Neira Ayuso (1):
netfilter: nf_tables: Fix oversized kvmalloc() calls
net/ipv4/netfilter/iptable_raw.c | 2 +-
net/ipv6/netfilter/ip6_tables.c | 1 +
net/netfilter/ipset/ip_set_hash_gen.h | 4 +-
net/netfilter/ipvs/ip_vs_conn.c | 4 +
net/netfilter/nf_conntrack_core.c | 154 ++++++----
net/netfilter/nf_nat_core.c | 17 +-
net/netfilter/nf_nat_masquerade.c | 168 ++++++-----
net/netfilter/nf_tables_api.c | 30 +-
net/netfilter/nft_compat.c | 17 +-
net/netfilter/xt_LOG.c | 10 +-
net/netfilter/xt_NFLOG.c | 10 +-
tools/testing/selftests/netfilter/nft_nat_zones.sh | 309 +++++++++++++++++++++
.../testing/selftests/netfilter/nft_zones_many.sh | 156 +++++++++++
13 files changed, 735 insertions(+), 147 deletions(-)
create mode 100755 tools/testing/selftests/netfilter/nft_nat_zones.sh
create mode 100755 tools/testing/selftests/netfilter/nft_zones_many.sh
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:31
From: Andrea Claudi <redacted>
ip_vs_conn_tab_bits may be provided by the user through the
conn_tab_bits module parameter. If this value is greater than 31, or
less than 0, the shift operator used to derive tab_size causes undefined
behaviour.
Fix this checking ip_vs_conn_tab_bits value to be in the range specified
in ipvs Kconfig. If not, simply use default value.
Fixes: 6f7edb4881bf ("IPVS: Allow boot time change of hash size")
Reported-by: Yi Chen <redacted>
Signed-off-by: Andrea Claudi <redacted>
Acked-by: Julian Anastasov <ja@ssi.bg>
Acked-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/ipvs/ip_vs_conn.c | 4 ++++
1 file changed, 4 insertions(+)
@@ -1468,6 +1468,10 @@ int __init ip_vs_conn_init(void)intidx;/* Compute size and mask */+if(ip_vs_conn_tab_bits<8||ip_vs_conn_tab_bits>20){+pr_info("conn_tab_bits not in [8, 20]. Using default value\n");+ip_vs_conn_tab_bits=CONFIG_IP_VS_TAB_BITS;+}ip_vs_conn_tab_size=1<<ip_vs_conn_tab_bits;ip_vs_conn_tab_mask=ip_vs_conn_tab_size-1;
@@ -130,11 +130,11 @@ htable_size(u8 hbits){size_thsize;-/* We must fit both into u32 in jhash and size_t */+/* We must fit both into u32 in jhash and INT_MAX in kvmalloc_node() */if(hbits>31)return0;hsize=jhash_size(hbits);-if((((size_t)-1)-sizeof(structhtable))/sizeof(structhbucket*)+if((INT_MAX-sizeof(structhtable))/sizeof(structhbucket*)<hsize)return0;
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:34
From: Jeremy Sowden <redacted>
ip6tables only sets the `IP6T_F_PROTO` flag on a rule if a protocol is
specified (`-p tcp`, for example). However, if the flag is not set,
`ip6_packet_match` doesn't call `ipv6_find_hdr` for the skb, in which
case the fragment offset is left uninitialized and a garbage value is
passed to each matcher.
Signed-off-by: Jeremy Sowden <redacted>
Reviewed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/ipv6/netfilter/ip6_tables.c | 1 +
1 file changed, 1 insertion(+)
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:36
From: Florian Westphal <fw@strlen.de>
commit deedb59039f111 ("netfilter: nf_conntrack: add direction support for zones")
removed the zone id from the hash value.
This has implications on hash chain lengths with overlapping tuples, which
can hit 64k entries on released kernels, before upper droplimit was added
in d7e7747ac5c ("netfilter: refuse insertion if chain has grown too large").
With that change reverted, test script coming with this series shows
linear insertion time growth:
10000 entries in 3737 ms (now 10000 total, loop 1)
10000 entries in 16994 ms (now 20000 total, loop 2)
10000 entries in 47787 ms (now 30000 total, loop 3)
10000 entries in 72731 ms (now 40000 total, loop 4)
10000 entries in 95761 ms (now 50000 total, loop 5)
10000 entries in 96809 ms (now 60000 total, loop 6)
inserted 60000 entries from packet path in 333825 ms
With d7e7747ac5c in place, the test fails.
There are three supported zone use cases:
1. Connection is in the default zone (zone 0).
This means to special config (the default).
2. Connection is in a different zone (1 to 2**16).
This means rules are in place to put packets in
the desired zone, e.g. derived from vlan id or interface.
3. Original direction is in zone X and Reply is in zone 0.
3) allows to use of the existing NAT port collision avoidance to provide
connectivity to internet/wan even when the various zones have overlapping
source networks separated via policy routing.
In case the original zone is 0 all three cases are identical.
There is no way to place original direction in zone x and reply in
zone y (with y != 0).
Zones need to be assigned manually via the iptables/nftables ruleset,
before conntrack lookup occurs (raw table in iptables) using the
"CT" target conntrack template support
(-j CT --{zone,zone-orig,zone-reply} X).
Normally zone assignment happens based on incoming interface, but could
also be derived from packet mark, vlan id and so on.
This means that when case 3 is used, the ruleset will typically not even
assign a connection tracking template to the "reply" packets, so lookup
happens in zone 0.
However, it is possible that reply packets also match a ct zone
assignment rule which sets up a template for zone X (X > 0) in original
direction only.
Therefore, after making the zone id part of the hash, we need to do a
second lookup using the reply zone id if we did not find an entry on
the first lookup.
In practice, most deployments will either not use zones at all or the
origin and reply zones are the same, no second lookup is required in
either case.
After this change, packet path insertion test passes with constant
insertion times:
10000 entries in 1064 ms (now 10000 total, loop 1)
10000 entries in 1074 ms (now 20000 total, loop 2)
10000 entries in 1066 ms (now 30000 total, loop 3)
10000 entries in 1079 ms (now 40000 total, loop 4)
10000 entries in 1081 ms (now 50000 total, loop 5)
10000 entries in 1082 ms (now 60000 total, loop 6)
inserted 60000 entries from packet path in 6452 ms
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_conntrack_core.c | 67 ++++++++++++++++++++++++-------
1 file changed, 52 insertions(+), 15 deletions(-)
@@ -206,6 +208,7 @@ static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple,/* The direction must be ignored, so handle usable members manually. */combined.src=tuple->src;combined.dst_addr=tuple->dst.u3;+combined.zone=zoneid;combined.net_mix=net_hash_mix(net);combined.dport=(__force__u16)tuple->dst.u.all;combined.proto=tuple->dst.protonum;
@@ -1137,8 +1158,8 @@ __nf_conntrack_confirm(struct sk_buff *skb)hash=*(unsignedlong*)&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev;hash=scale_hash(hash);reply_hash=hash_conntrack(net,-&ct->tuplehash[IP_CT_DIR_REPLY].tuple);-+&ct->tuplehash[IP_CT_DIR_REPLY].tuple,+nf_ct_zone_id(nf_ct_zone(ct),IP_CT_DIR_REPLY));}while(nf_conntrack_double_lock(net,hash,reply_hash,sequence));/* We're not in hash table, and we refuse to set up related
@@ -1704,8 +1725,20 @@ resolve_normal_ct(struct nf_conn *tmpl,/* look for tuple match */zone=nf_ct_zone_tmpl(tmpl,skb,&tmp);-hash=hash_conntrack_raw(&tuple,state->net);++zone_id=nf_ct_zone_id(zone,IP_CT_DIR_ORIGINAL);+hash=hash_conntrack_raw(&tuple,zone_id,state->net);h=__nf_conntrack_find_get(state->net,zone,&tuple,hash);++if(!h){+rid=nf_ct_zone_id(zone,IP_CT_DIR_REPLY);+if(zone_id!=rid){+u32tmp=hash_conntrack_raw(&tuple,rid,state->net);++h=__nf_conntrack_find_get(state->net,zone,&tuple,tmp);+}+}+if(!h){h=init_conntrack(state->net,tmpl,&tuple,skb,dataoff,hash);
@@ -2542,12 +2575,16 @@ int nf_conntrack_hash_resize(unsigned int hashsize)for(i=0;i<nf_conntrack_htable_size;i++){while(!hlist_nulls_empty(&nf_conntrack_hash[i])){+unsignedintzone_id;+h=hlist_nulls_entry(nf_conntrack_hash[i].first,structnf_conntrack_tuple_hash,hnnode);ct=nf_ct_tuplehash_to_ctrack(h);hlist_nulls_del_rcu(&h->hnnode);++zone_id=nf_ct_zone_id(nf_ct_zone(ct),NF_CT_DIRECTION(h));bucket=__hash_conntrack(nf_ct_net(ct),-&h->tuple,hashsize);+&h->tuple,zone_id,hashsize);hlist_nulls_add_head_rcu(&h->hnnode,&hash[bucket]);}}
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:38
From: Florian Westphal <fw@strlen.de>
Similar to the conntrack change, also use the zone id for the nat source
lists if the zone id is valid in both directions.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_nat_core.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
@@ -150,13 +150,16 @@ static void __nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl)/* We keep an extra hash for each conntrack, for fast searching. */staticunsignedint-hash_by_src(conststructnet*n,conststructnf_conntrack_tuple*tuple)+hash_by_src(conststructnet*net,+conststructnf_conntrack_zone*zone,+conststructnf_conntrack_tuple*tuple){unsignedinthash;struct{structnf_conntrack_mansrc;u32net_mix;u32protonum;+u32zone;}__aligned(SIPHASH_ALIGNMENT)combined;get_random_once(&nf_nat_hash_rnd,sizeof(nf_nat_hash_rnd));
@@ -165,9 +168,13 @@ hash_by_src(const struct net *n, const struct nf_conntrack_tuple *tuple)/* Original src, to ensure we map it consistently if poss. */combined.src=tuple->src;-combined.net_mix=net_hash_mix(n);+combined.net_mix=net_hash_mix(net);combined.protonum=tuple->dst.protonum;+/* Zone ID can be used provided its valid for both directions */+if(zone->dir==NF_CT_DEFAULT_ZONE_DIR)+combined.zone=zone->id;+hash=siphash(&combined,sizeof(combined),&nf_nat_hash_rnd);returnreciprocal_scale(hash,nf_nat_htable_size);
@@ -272,7 +279,7 @@ find_appropriate_src(struct net *net,structnf_conntrack_tuple*result,conststructnf_nat_range2*range){-unsignedinth=hash_by_src(net,tuple);+unsignedinth=hash_by_src(net,zone,tuple);conststructnf_conn*ct;hlist_for_each_entry_rcu(ct,&nf_nat_bysource[h],nat_bysource){
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:40
From: Florian Westphal <fw@strlen.de>
Similar to commit 67d6d681e15b
("ipv4: make exception cache less predictible"):
Use a random drop length to make it harder to detect when entries were
hashed to same bucket list.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_conntrack_core.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
@@ -857,13 +859,15 @@ nf_conntrack_hash_check_insert(struct nf_conn *ct)&ct->tuplehash[IP_CT_DIR_REPLY].tuple);}while(nf_conntrack_double_lock(net,hash,reply_hash,sequence));+max_chainlen=MIN_CHAINLEN+prandom_u32_max(MAX_CHAINLEN);+/* See if there's one in the list already, including reverse */hlist_nulls_for_each_entry(h,n,&nf_conntrack_hash[hash],hnnode){if(nf_ct_key_equal(h,&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,zone,net))gotoout;-if(chainlen++>MAX_CHAINLEN)+if(chainlen++>max_chainlen)gotochaintoolong;}
@@ -1168,6 +1172,7 @@ __nf_conntrack_confirm(struct sk_buff *skb)gotodying;}+max_chainlen=MIN_CHAINLEN+prandom_u32_max(MAX_CHAINLEN);/* See if there's one in the list already, including reverse:NATcouldhavegrabbeditwithoutrealizing,sincewe'renotinthehash.Ifthereis,welostrace.*/
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:43
From: Florian Westphal <fw@strlen.de>
Add 20k entries to the connection tracking table, once from the
data plane, once via ctnetlink.
In both cases, each entry lives in a different conntrack zone
and addresses/ports are identical.
Expectation is that insertions work and occurs in constant time:
PASS: added 10000 entries in 1215 ms (now 10000 total, loop 1)
PASS: added 10000 entries in 1214 ms (now 20000 total, loop 2)
PASS: inserted 20000 entries from packet path in 2434 ms total
PASS: added 10000 entries in 57631 ms (now 10000 total)
PASS: added 10000 entries in 58572 ms (now 20000 total)
PASS: inserted 20000 entries via ctnetlink in 116205 ms
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
.../selftests/netfilter/nft_zones_many.sh | 156 ++++++++++++++++++
1 file changed, 156 insertions(+)
create mode 100755 tools/testing/selftests/netfilter/nft_zones_many.sh
@@ -0,0 +1,156 @@+#!/bin/bash++# Test insertion speed for packets with identical addresses/ports+# that are all placed in distinct conntrack zones.++sfx=$(mktemp-u"XXXXXXXX")+ns="ns-$sfx"++# Kselftest framework requirement - SKIP code is 4.+ksft_skip=4++zones=20000+have_ct_tool=0+ret=0++cleanup()+{+ipnetnsdel$ns+}++ipnetnsadd$ns+if[$?-ne0];then+echo"SKIP: Could not create net namespace $gw"+exit$ksft_skip+fi++trapcleanupEXIT++conntrack-V>/dev/null2>&1+if[$?-eq0];then+have_ct_tool=1+fi++ip-net"$ns"linksetloup++test_zones(){+localmax_zones=$1++ipnetnsexec$nssysctl-qnet.netfilter.nf_conntrack_udp_timeout=3600+ipnetnsexec$nsnft-f/dev/stdin<<EOF+flushruleset+tableinetraw{+maprndzone{+typeofnumgenincmod$max_zones:ctzone+}++chainoutput{+typefilterhookoutputpriority-64000;policyaccept;+udpdport12345ctzonesetnumgenincmod65536map@rndzone+}+}+EOF+(+echo"add element inet raw rndzone {"+foriin$(seq1$max_zones);do+echo-n"$i : $i"+if[$i-lt$max_zones];then+echo","+else+echo"}"+fi+done+)|ipnetnsexec$nsnft-f/dev/stdin++locali=0+localj=0+localouterstart=$(date+%s%3N)+localstop=$outerstart++while[$i-lt$max_zones];do+localstart=$(date+%s%3N)+i=$((i+10000))+j=$((j+1))+ddif=/dev/zeroof=/dev/stdoutbs=8kcount=100002>/dev/null|ipnetnsexec"$ns"nc-w1-q1-u-p12345127.0.0.112345>/dev/null+if[$?-ne0];then+ret=1+break+fi++stop=$(date+%s%3N)+localduration=$((stop-start))+echo"PASS: added 10000 entries in $duration ms (now $i total, loop $j)"+done++if[$have_ct_tool-eq1];then+localcount=$(ipnetnsexec"$ns"conntrack-C)+localduration=$((stop-outerstart))++if[$count-eq$max_zones];then+echo"PASS: inserted $count entries from packet path in $duration ms total"+else+ipnetnsexec$nsconntrack-S1>&2+echo"FAIL: inserted $count entries from packet path in $duration ms total, expected $max_zones entries"+ret=1+fi+fi++if[$ret-ne0];then+echo"FAIL: insert $max_zones entries from packet path"1>&2+fi+}++test_conntrack_tool(){+localmax_zones=$1++ipnetnsexec$nsconntrack-F>/dev/null2>/dev/null++localouterstart=$(date+%s%3N)+localstart=$(date+%s%3N)+localstop=$start+locali=0+while[$i-lt$max_zones];do+i=$((i+1))+ipnetnsexec"$ns"conntrack-I-s1.1.1.1-d2.2.2.2--protonum6\+--timeout3600--stateESTABLISHED--sport12345--dport1000--zone$i>/dev/null2>&1+if[$?-ne0];then+ipnetnsexec"$ns"conntrack-I-s1.1.1.1-d2.2.2.2--protonum6\+--timeout3600--stateESTABLISHED--sport12345--dport1000--zone$i>/dev/null+echo"FAIL: conntrack -I returned an error"+ret=1+break+fi++if[$((i%10000))-eq0];then+stop=$(date+%s%3N)++localduration=$((stop-start))+echo"PASS: added 10000 entries in $duration ms (now $i total)"+start=$stop+fi+done++localcount=$(ipnetnsexec"$ns"conntrack-C)+localduration=$((stop-outerstart))++if[$count-eq$max_zones];then+echo"PASS: inserted $count entries via ctnetlink in $duration ms"+else+ipnetnsexec$nsconntrack-S1>&2+echo"FAIL: inserted $count entries via ctnetlink in $duration ms, expected $max_zones entries ($duration ms)"+ret=1+fi+}++test_zones$zones++if[$have_ct_tool-eq1];then+test_conntrack_tool$zones+else+echo"SKIP: Could not run ctnetlink insertion test without conntrack tool"+if[$ret-eq0];then+exit$ksft_skip+fi+fi++exit$ret
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:45
From: Florian Westphal <fw@strlen.de>
Add a script to exercise NAT port clash resolution with directional zones.
Add net namespaces that use the same IP address and connect them to a
gateway.
Gateway uses policy routing based on iif/mark and conntrack zones to
isolate the client namespaces. In server direction, same zone with NAT
to single address is used.
Then, connect to a server from each client netns, using identical
connection id, i.e. saddr:sport -> daddr:dport.
Expectation is for all connections to succeeed: NAT gatway is
supposed to do port reallocation for each of the (clashing) connections.
This is based on the description/use case provided in the commit message of
deedb59039f111 ("netfilter: nf_conntrack: add direction support for zones").
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
.../selftests/netfilter/nft_nat_zones.sh | 309 ++++++++++++++++++
1 file changed, 309 insertions(+)
create mode 100755 tools/testing/selftests/netfilter/nft_nat_zones.sh
@@ -0,0 +1,309 @@+#!/bin/bash+#+# Test connection tracking zone and NAT source port reallocation support.+#++# Kselftest framework requirement - SKIP code is 4.+ksft_skip=4++# Don't increase too much, 2000 clients should work+# just fine but script can then take several minutes with+# KASAN/debug builds.+maxclients=100++have_iperf=1+ret=0++# client1---.+# veth1-.+# |+# NAT Gateway --veth0--> Server+# | |+# veth2-' |+# client2---' |+# .... |+# clientX----vethX---'++# All clients share identical IP address.+# NAT Gateway uses policy routing and conntrack zones to isolate client+# namespaces. Each client connects to Server, each with colliding tuples:+# clientsaddr:10000 -> serveraddr:dport+# NAT Gateway is supposed to do port reallocation for each of the+# connections.++sfx=$(mktemp-u"XXXXXXXX")+gw="ns-gw-$sfx"+cl1="ns-cl1-$sfx"+cl2="ns-cl2-$sfx"+srv="ns-srv-$sfx"++v4gc1=$(sysctl-nnet.ipv4.neigh.default.gc_thresh12>/dev/null)+v4gc2=$(sysctl-nnet.ipv4.neigh.default.gc_thresh22>/dev/null)+v4gc3=$(sysctl-nnet.ipv4.neigh.default.gc_thresh32>/dev/null)+v6gc1=$(sysctl-nnet.ipv6.neigh.default.gc_thresh12>/dev/null)+v6gc2=$(sysctl-nnet.ipv6.neigh.default.gc_thresh22>/dev/null)+v6gc3=$(sysctl-nnet.ipv6.neigh.default.gc_thresh32>/dev/null)++cleanup()+{+ipnetnsdel$gw+ipnetnsdel$srv+foriin$(seq1$maxclients);do+ipnetnsdelns-cl$i-$sfx2>/dev/null+done++sysctl-qnet.ipv4.neigh.default.gc_thresh1=$v4gc12>/dev/null+sysctl-qnet.ipv4.neigh.default.gc_thresh2=$v4gc22>/dev/null+sysctl-qnet.ipv4.neigh.default.gc_thresh3=$v4gc32>/dev/null+sysctl-qnet.ipv6.neigh.default.gc_thresh1=$v6gc12>/dev/null+sysctl-qnet.ipv6.neigh.default.gc_thresh2=$v6gc22>/dev/null+sysctl-qnet.ipv6.neigh.default.gc_thresh3=$v6gc32>/dev/null+}++nft--version>/dev/null2>&1+if[$?-ne0];then+echo"SKIP: Could not run test without nft tool"+exit$ksft_skip+fi++ip-Version>/dev/null2>&1+if[$?-ne0];then+echo"SKIP: Could not run test without ip tool"+exit$ksft_skip+fi++conntrack-V>/dev/null2>&1+if[$?-ne0];then+echo"SKIP: Could not run test without conntrack tool"+exit$ksft_skip+fi++iperf3-v>/dev/null2>&1+if[$?-ne0];then+have_iperf=0+fi++ipnetnsadd"$gw"+if[$?-ne0];then+echo"SKIP: Could not create net namespace $gw"+exit$ksft_skip+fi+ip-net"$gw"linksetloup++trapcleanupEXIT++ipnetnsadd"$srv"+if[$?-ne0];then+echo"SKIP: Could not create server netns $srv"+exit$ksft_skip+fi++iplinkaddveth0netns"$gw"typevethpeernameeth0netns"$srv"+ip-net"$gw"linksetveth0up+ip-net"$srv"linksetloup+ip-net"$srv"linkseteth0up++sysctl-qnet.ipv6.neigh.default.gc_thresh1=5122>/dev/null+sysctl-qnet.ipv6.neigh.default.gc_thresh2=10242>/dev/null+sysctl-qnet.ipv6.neigh.default.gc_thresh3=40962>/dev/null+sysctl-qnet.ipv4.neigh.default.gc_thresh1=5122>/dev/null+sysctl-qnet.ipv4.neigh.default.gc_thresh2=10242>/dev/null+sysctl-qnet.ipv4.neigh.default.gc_thresh3=40962>/dev/null++foriin$(seq1$maxclients);do+cl="ns-cl$i-$sfx"++ipnetnsadd"$cl"+if[$?-ne0];then+echo"SKIP: Could not create client netns $cl"+exit$ksft_skip+fi+iplinkaddveth$inetns"$gw"typevethpeernameeth0netns"$cl">/dev/null2>&1+if[$?-ne0];then+echo"SKIP: No virtual ethernet pair device support in kernel"+exit$ksft_skip+fi+done++foriin$(seq1$maxclients);do+cl="ns-cl$i-$sfx"+echonetnsexec"$cl"iplinksetloup+echonetnsexec"$cl"iplinkseteth0up+echonetnsexec"$cl"sysctl-qnet.ipv4.tcp_syn_retries=2+echonetnsexec"$gw"iplinksetveth$iup+echonetnsexec"$gw"sysctl-qnet.ipv4.conf.veth$i.arp_ignore=2+echonetnsexec"$gw"sysctl-qnet.ipv4.conf.veth$i.rp_filter=0++# clients have same IP addresses.+echonetnsexec"$cl"ipaddradd10.1.0.3/24deveth0+echonetnsexec"$cl"ipaddradddead:1::3/64deveth0+echonetnsexec"$cl"iprouteadddefaultvia10.1.0.2deveth0+echonetnsexec"$cl"iprouteadddefaultviadead:1::2deveth0++# NB: same addresses on client-facing interfaces.+echonetnsexec"$gw"ipaddradd10.1.0.2/24devveth$i+echonetnsexec"$gw"ipaddradddead:1::2/64devveth$i++# gw: policy routing+echonetnsexec"$gw"iprouteadd10.1.0.0/24devveth$itable$((1000+i))+echonetnsexec"$gw"iprouteadddead:1::0/64devveth$itable$((1000+i))+echonetnsexec"$gw"iprouteadd10.3.0.0/24devveth0table$((1000+i))+echonetnsexec"$gw"iprouteadddead:3::0/64devveth0table$((1000+i))+echonetnsexec"$gw"ipruleaddfwmark$ilookup$((1000+i))+done|ip-batch/dev/stdin++ip-net"$gw"addradd10.3.0.1/24devveth0+ip-net"$gw"addradddead:3::1/64devveth0++ip-net"$srv"addradd10.3.0.99/24deveth0+ip-net"$srv"addradddead:3::99/64deveth0++ipnetnsexec$gwnft-f/dev/stdin<<EOF+tableinetraw{+mapiiftomark{+typeifname:mark+}++mapiiftozone{+typeofiifname:ctzone+}++setinicmp{+flagsdynamic+typeipv4_addr.ifname.ipv4_addr+}+setinflows{+flagsdynamic+typeipv4_addr.inet_service.ifname.ipv4_addr.inet_service+}++setinflows6{+flagsdynamic+typeipv6_addr.inet_service.ifname.ipv6_addr.inet_service+}++chainprerouting{+typefilterhookpreroutingpriority-64000;policyaccept;+ctoriginalzonesetmetaiifnamemap@iiftozone+metamarksetmetaiifnamemap@iiftomark++tcpflags&(syn|ack)==ackadd@inflows{ipsaddr.tcpsport.metaiifname.ipdaddr.tcpdportcounter}+add@inflows6{ip6saddr.tcpsport.metaiifname.ip6daddr.tcpdportcounter}+ipprotocolicmpadd@inicmp{ipsaddr.metaiifname.ipdaddrcounter}+}++chainnat_postrouting{+typenathookpostroutingpriority0;policyaccept;+ctmarksetmetamarkmetaoifnameveth0masquerade+}++chainmangle_prerouting{+typefilterhookpreroutingpriority-100;policyaccept;+ctdirectionreplymetamarksetctmark+}+}+EOF++(echoaddelementinetrawiiftomark\{+foriin$(seq1$((maxclients-1)));do+echo\"veth$i\":$i,+done+echo\"veth$maxclients\":$maxclients\}+echoaddelementinetrawiiftozone\{+foriin$(seq1$((maxclients-1)));do+echo\"veth$i\":$i,+done+echo\"veth$maxclients\":$maxclients\}+)|ipnetnsexec$gwnft-f/dev/stdin++ipnetnsexec"$gw"sysctl-qnet.ipv4.conf.all.forwarding=1>/dev/null+ipnetnsexec"$gw"sysctl-qnet.ipv6.conf.all.forwarding=1>/dev/null+ipnetnsexec"$gw"sysctl-qnet.ipv4.conf.all.rp_filter=0>/dev/null++# useful for debugging: allows to use 'ping' from clients to gateway.+ipnetnsexec"$gw"sysctl-qnet.ipv4.fwmark_reflect=1>/dev/null+ipnetnsexec"$gw"sysctl-qnet.ipv6.fwmark_reflect=1>/dev/null++foriin$(seq1$maxclients);do+cl="ns-cl$i-$sfx"+ipnetnsexec$clping-i0.5-q-c310.3.0.99>/dev/null2>&1&+if[$?-ne0];then+echoFAIL:Pingfailurefrom$cl1>&2+ret=1+break+fi+done++wait++foriin$(seq1$maxclients);do+ipnetnsexec$gwnftgetelementinetrawinicmp"{ 10.1.0.3 . \"veth$i\" . 10.3.0.99 }"|grep-q"{ 10.1.0.3 . \"veth$i\" . 10.3.0.99 counter packets 3 bytes 252 }"+if[$?-ne0];then+ret=1+echo"FAIL: counter icmp mismatch for veth$i"1>&2+ipnetnsexec$gwnftgetelementinetrawinicmp"{ 10.1.0.3 . \"veth$i\" . 10.3.0.99 }"1>&2+break+fi+done++ipnetnsexec$gwnftgetelementinetrawinicmp"{ 10.3.0.99 . \"veth0\" . 10.3.0.1 }"|grep-q"{ 10.3.0.99 . \"veth0\" . 10.3.0.1 counter packets $((3*$maxclients)) bytes $((252*$maxclients)) }"+if[$?-ne0];then+ret=1+echo"FAIL: counter icmp mismatch for veth0: { 10.3.0.99 . \"veth0\" . 10.3.0.1 counter packets $((3*$maxclients)) bytes $((252*$maxclients)) }"+ipnetnsexec$gwnftgetelementinetrawinicmp"{ 10.3.99 . \"veth0\" . 10.3.0.1 }"1>&2+fi++if[$ret-eq0];then+echo"PASS: ping test from all $maxclients namespaces"+fi++if[$have_iperf-eq0];then+echo"SKIP: iperf3 not installed"+if[$ret-ne0];then+exit$ret+fi+exit$ksft_skip+fi++ipnetnsexec$srviperf3-s>/dev/null2>&1&+iperfpid=$!+sleep1++foriin$(seq1$maxclients);do+if[$ret-ne0];then+break+fi+cl="ns-cl$i-$sfx"+ipnetnsexec$cliperf3-c10.3.0.99--cport10000-n1>/dev/null+if[$?-ne0];then+echoFAIL:Failuretoconnectfor$cl1>&2+ipnetnsexec$gwconntrack-S1>&2+ret=1+fi+done+if[$ret-eq0];then+echo"PASS: iperf3 connections for all $maxclients net namespaces"+fi++kill$iperfpid+wait++foriin$(seq1$maxclients);do+ipnetnsexec$gwnftgetelementinetrawinflows"{ 10.1.0.3 . 10000 . \"veth$i\" . 10.3.0.99 . 5201 }">/dev/null+if[$?-ne0];then+ret=1+echo"FAIL: can't find expected tcp entry for veth$i"1>&2+break+fi+done+if[$ret-eq0];then+echo"PASS: Found client connection for all $maxclients net namespaces"+fi++ipnetnsexec$gwnftgetelementinetrawinflows"{ 10.3.0.99 . 5201 . \"veth0\" . 10.3.0.1 . 10000 }">/dev/null+if[$?-ne0];then+ret=1+echo"FAIL: cannot find return entry on veth0"1>&2+fi++exit$ret
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:47
From: Florian Westphal <fw@strlen.de>
syzbot reports following UAF:
BUG: KASAN: use-after-free in memcmp+0x18f/0x1c0 lib/string.c:955
nla_strcmp+0xf2/0x130 lib/nlattr.c:836
nft_table_lookup.part.0+0x1a2/0x460 net/netfilter/nf_tables_api.c:570
nft_table_lookup net/netfilter/nf_tables_api.c:4064 [inline]
nf_tables_getset+0x1b3/0x860 net/netfilter/nf_tables_api.c:4064
nfnetlink_rcv_msg+0x659/0x13f0 net/netfilter/nfnetlink.c:285
netlink_rcv_skb+0x153/0x420 net/netlink/af_netlink.c:2504
Problem is that all get operations are lockless, so the commit_mutex
held by nft_rcv_nl_event() isn't enough to stop a parallel GET request
from doing read-accesses to the table object even after synchronize_rcu().
To avoid this, unlink the table first and store the table objects in
on-stack scratch space.
Fixes: 6001a930ce03 ("netfilter: nftables: introduce table ownership")
Reported-and-tested-by: syzbot+f31660cf279b0557160c@syzkaller.appspotmail.com
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_tables_api.c | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:48
From: Florian Westphal <fw@strlen.de>
masq_inet6_event is called asynchronously from system work queue,
because the inet6 notifier is atomic and nf_iterate_cleanup can sleep.
The ipv4 and device notifiers call nf_iterate_cleanup directly.
This is legal, but these notifiers are called with RTNL mutex held.
A large conntrack table with many devices coming and going will have severe
impact on the system usability, with 'ip a' blocking for several seconds.
This change places the defer code into a helper and makes it more
generic so ipv4 and ifdown notifiers can be converted to defer the
cleanup walk as well in a follow patch.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_nat_masquerade.c | 122 ++++++++++++++++++------------
1 file changed, 75 insertions(+), 47 deletions(-)
@@ -63,6 +74,63 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum,}EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4);+staticvoiditerate_cleanup_work(structwork_struct*work)+{+structmasq_dev_work*w;++w=container_of(work,structmasq_dev_work,work);++nf_ct_iterate_cleanup_net(w->net,w->iter,(void*)w,0,0);++put_net(w->net);+kfree(w);+atomic_dec(&masq_worker_count);+module_put(THIS_MODULE);+}++/* Iterate conntrack table in the background and remove conntrack entries+*thatusethedevice/addressbeingremoved.+*+*Incasetoomanyworkitemshavebeenqueuedalreadyormemoryallocation+*failsiterationisskipped,conntrackentrieswilltimeouteventually.+*/+staticvoidnf_nat_masq_schedule(structnet*net,unionnf_inet_addr*addr,+intifindex,+int(*iter)(structnf_conn*i,void*data),+gfp_tgfp_flags)+{+structmasq_dev_work*w;++if(atomic_read(&masq_worker_count)>MAX_MASQ_WORKER_COUNT)+return;++net=maybe_get_net(net);+if(!net)+return;++if(!try_module_get(THIS_MODULE))+gotoerr_module;++w=kzalloc(sizeof(*w),gfp_flags);+if(w){+/* We can overshoot MAX_MASQ_WORKER_COUNT, no big deal */+atomic_inc(&masq_worker_count);++INIT_WORK(&w->work,iterate_cleanup_work);+w->ifindex=ifindex;+w->net=net;+w->iter=iter;+if(addr)+w->addr=*addr;+schedule_work(&w->work);+return;+}++module_put(THIS_MODULE);+err_module:+put_net(net);+}+staticintdevice_cmp(structnf_conn*i,void*ifindex){conststructnf_conn_nat*nat=nfct_nat(i);
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:55
From: Florian Westphal <fw@strlen.de>
The ipv4 and device notifiers are called with RTNL mutex held.
The table walk can take some time, better not block other RTNL users.
'ip a' has been reported to block for up to 20 seconds when conntrack table
has many entries and device down events are frequent (e.g., PPP).
Reported-and-tested-by: Martin Zaharinov <redacted>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_nat_masquerade.c | 50 +++++++++++++++----------------
1 file changed, 24 insertions(+), 26 deletions(-)
@@ -131,13 +131,14 @@ static void nf_nat_masq_schedule(struct net *net, union nf_inet_addr *addr,put_net(net);}-staticintdevice_cmp(structnf_conn*i,void*ifindex)+staticintdevice_cmp(structnf_conn*i,void*arg){conststructnf_conn_nat*nat=nfct_nat(i);+conststructmasq_dev_work*w=arg;if(!nat)return0;-returnnat->masq_index==(int)(long)ifindex;+returnnat->masq_index==w->ifindex;}staticintmasq_device_event(structnotifier_block*this,
@@ -153,8 +154,8 @@ static int masq_device_event(struct notifier_block *this,*andforgetthem.*/-nf_ct_iterate_cleanup_net(net,device_cmp,-(void*)(long)dev->ifindex,0,0);+nf_nat_masq_schedule(net,NULL,dev->ifindex,+device_cmp,GFP_KERNEL);}returnNOTIFY_DONE;
@@ -162,35 +163,45 @@ static int masq_device_event(struct notifier_block *this,staticintinet_cmp(structnf_conn*ct,void*ptr){-structin_ifaddr*ifa=(structin_ifaddr*)ptr;-structnet_device*dev=ifa->ifa_dev->dev;structnf_conntrack_tuple*tuple;+structmasq_dev_work*w=ptr;-if(!device_cmp(ct,(void*)(long)dev->ifindex))+if(!device_cmp(ct,ptr))return0;tuple=&ct->tuplehash[IP_CT_DIR_REPLY].tuple;-returnifa->ifa_address==tuple->dst.u3.ip;+returnnf_inet_addr_cmp(&w->addr,&tuple->dst.u3);}staticintmasq_inet_event(structnotifier_block*this,unsignedlongevent,void*ptr){-structin_device*idev=((structin_ifaddr*)ptr)->ifa_dev;-structnet*net=dev_net(idev->dev);+conststructin_ifaddr*ifa=ptr;+conststructin_device*idev;+conststructnet_device*dev;+unionnf_inet_addraddr;++if(event!=NETDEV_DOWN)+returnNOTIFY_DONE;/* The masq_dev_notifier will catch the case of the device going*down.Soiftheinetdevisdeadandbeingdestroyedwehave*noworktodo.Otherwisethisisanindividualaddressremoval*andwehavetoperformtheflush.*/+idev=ifa->ifa_dev;if(idev->dead)returnNOTIFY_DONE;-if(event==NETDEV_DOWN)-nf_ct_iterate_cleanup_net(net,inet_cmp,ptr,0,0);+memset(&addr,0,sizeof(addr));++addr.ip=ifa->ifa_address;++dev=idev->dev;+nf_nat_masq_schedule(dev_net(idev->dev),&addr,dev->ifindex,+inet_cmp,GFP_KERNEL);returnNOTIFY_DONE;}
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:57
From: Eric Dumazet <edumazet@google.com>
Syzbot was able to trigger the following warning [1]
No repro found by syzbot yet but I was able to trigger similar issue
by having 2 scripts running in parallel, changing conntrack hash sizes,
and:
for j in `seq 1 1000` ; do unshare -n /bin/true >/dev/null ; done
It would take more than 5 minutes for net_namespace structures
to be cleaned up.
This is because nf_ct_iterate_cleanup() has to restart everytime
a resize happened.
By adding a mutex, we can serialize hash resizes and cleanups
and also make get_next_corpse() faster by skipping over empty
buckets.
Even without resizes in the picture, this patch considerably
speeds up network namespace dismantles.
[1]
INFO: task syz-executor.0:8312 can't die for more than 144 seconds.
task:syz-executor.0 state:R running task stack:25672 pid: 8312 ppid: 6573 flags:0x00004006
Call Trace:
context_switch kernel/sched/core.c:4955 [inline]
__schedule+0x940/0x26f0 kernel/sched/core.c:6236
preempt_schedule_common+0x45/0xc0 kernel/sched/core.c:6408
preempt_schedule_thunk+0x16/0x18 arch/x86/entry/thunk_64.S:35
__local_bh_enable_ip+0x109/0x120 kernel/softirq.c:390
local_bh_enable include/linux/bottom_half.h:32 [inline]
get_next_corpse net/netfilter/nf_conntrack_core.c:2252 [inline]
nf_ct_iterate_cleanup+0x15a/0x450 net/netfilter/nf_conntrack_core.c:2275
nf_conntrack_cleanup_net_list+0x14c/0x4f0 net/netfilter/nf_conntrack_core.c:2469
ops_exit_list+0x10d/0x160 net/core/net_namespace.c:171
setup_net+0x639/0xa30 net/core/net_namespace.c:349
copy_net_ns+0x319/0x760 net/core/net_namespace.c:470
create_new_namespaces+0x3f6/0xb20 kernel/nsproxy.c:110
unshare_nsproxy_namespaces+0xc1/0x1f0 kernel/nsproxy.c:226
ksys_unshare+0x445/0x920 kernel/fork.c:3128
__do_sys_unshare kernel/fork.c:3202 [inline]
__se_sys_unshare kernel/fork.c:3200 [inline]
__x64_sys_unshare+0x2d/0x40 kernel/fork.c:3200
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x44/0xae
RIP: 0033:0x7f63da68e739
RSP: 002b:00007f63d7c05188 EFLAGS: 00000246 ORIG_RAX: 0000000000000110
RAX: ffffffffffffffda RBX: 00007f63da792f80 RCX: 00007f63da68e739
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000040000000
RBP: 00007f63da6e8cc4 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f63da792f80
R13: 00007fff50b75d3f R14: 00007f63d7c05300 R15: 0000000000022000
Showing all locks held in the system:
1 lock held by khungtaskd/27:
#0: ffffffff8b980020 (rcu_read_lock){....}-{1:2}, at: debug_show_all_locks+0x53/0x260 kernel/locking/lockdep.c:6446
2 locks held by kworker/u4:2/153:
#0: ffff888010c69138 ((wq_completion)events_unbound){+.+.}-{0:0}, at: arch_atomic64_set arch/x86/include/asm/atomic64_64.h:34 [inline]
#0: ffff888010c69138 ((wq_completion)events_unbound){+.+.}-{0:0}, at: arch_atomic_long_set include/linux/atomic/atomic-long.h:41 [inline]
#0: ffff888010c69138 ((wq_completion)events_unbound){+.+.}-{0:0}, at: atomic_long_set include/linux/atomic/atomic-instrumented.h:1198 [inline]
#0: ffff888010c69138 ((wq_completion)events_unbound){+.+.}-{0:0}, at: set_work_data kernel/workqueue.c:634 [inline]
#0: ffff888010c69138 ((wq_completion)events_unbound){+.+.}-{0:0}, at: set_work_pool_and_clear_pending kernel/workqueue.c:661 [inline]
#0: ffff888010c69138 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_one_work+0x896/0x1690 kernel/workqueue.c:2268
#1: ffffc9000140fdb0 ((kfence_timer).work){+.+.}-{0:0}, at: process_one_work+0x8ca/0x1690 kernel/workqueue.c:2272
1 lock held by systemd-udevd/2970:
1 lock held by in:imklog/6258:
#0: ffff88807f970ff0 (&f->f_pos_lock){+.+.}-{3:3}, at: __fdget_pos+0xe9/0x100 fs/file.c:990
3 locks held by kworker/1:6/8158:
1 lock held by syz-executor.0/8312:
2 locks held by kworker/u4:13/9320:
1 lock held by syz-executor.5/10178:
1 lock held by syz-executor.4/10217:
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: syzbot <redacted>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_conntrack_core.c | 70 ++++++++++++++++---------------
1 file changed, 37 insertions(+), 33 deletions(-)
@@ -2263,28 +2266,31 @@ get_next_corpse(int (*iter)(struct nf_conn *i, void *data),spinlock_t*lockp;for(;*bucket<nf_conntrack_htable_size;(*bucket)++){+structhlist_nulls_head*hslot=&nf_conntrack_hash[*bucket];++if(hlist_nulls_empty(hslot))+continue;+lockp=&nf_conntrack_locks[*bucket%CONNTRACK_LOCKS];local_bh_disable();nf_conntrack_lock(lockp);-if(*bucket<nf_conntrack_htable_size){-hlist_nulls_for_each_entry(h,n,&nf_conntrack_hash[*bucket],hnnode){-if(NF_CT_DIRECTION(h)!=IP_CT_DIR_REPLY)-continue;-/* All nf_conn objects are added to hash table twice, one-*fororiginaldirectiontuple,onceforthereplytuple.-*-*Exception:IntheIPS_NAT_CLASHcase,onlythereply-*tupleisadded(theoriginaltuplealreadyexistedfor-*adifferentobject).-*-*Weonlyneedtocalltheiteratoronceforeach-*conntrack,sowejustusethe'reply'direction-*tuplewhileiterating.-*/-ct=nf_ct_tuplehash_to_ctrack(h);-if(iter(ct,data))-gotofound;-}+hlist_nulls_for_each_entry(h,n,hslot,hnnode){+if(NF_CT_DIRECTION(h)!=IP_CT_DIR_REPLY)+continue;+/* All nf_conn objects are added to hash table twice, one+*fororiginaldirectiontuple,onceforthereplytuple.+*+*Exception:IntheIPS_NAT_CLASHcase,onlythereply+*tupleisadded(theoriginaltuplealreadyexistedfor+*adifferentobject).+*+*Weonlyneedtocalltheiteratoronceforeach+*conntrack,sowejustusethe'reply'direction+*tuplewhileiterating.+*/+ct=nf_ct_tuplehash_to_ctrack(h);+if(iter(ct,data))+gotofound;}spin_unlock(lockp);local_bh_enable();
@@ -2302,26 +2308,20 @@ get_next_corpse(int (*iter)(struct nf_conn *i, void *data),staticvoidnf_ct_iterate_cleanup(int(*iter)(structnf_conn*i,void*data),void*data,u32portid,intreport){-unsignedintbucket=0,sequence;+unsignedintbucket=0;structnf_conn*ct;might_sleep();-for(;;){-sequence=read_seqcount_begin(&nf_conntrack_generation);--while((ct=get_next_corpse(iter,data,&bucket))!=NULL){-/* Time to push up daises... */+mutex_lock(&nf_conntrack_mutex);+while((ct=get_next_corpse(iter,data,&bucket))!=NULL){+/* Time to push up daises... */-nf_ct_delete(ct,portid,report);-nf_ct_put(ct);-cond_resched();-}--if(!read_seqcount_retry(&nf_conntrack_generation,sequence))-break;-bucket=0;+nf_ct_delete(ct,portid,report);+nf_ct_put(ct);+cond_resched();}+mutex_unlock(&nf_conntrack_mutex);}structiter_data{
@@ -2557,8 +2557,10 @@ int nf_conntrack_hash_resize(unsigned int hashsize)if(!hash)return-ENOMEM;+mutex_lock(&nf_conntrack_mutex);old_size=nf_conntrack_htable_size;if(old_size==hashsize){+mutex_unlock(&nf_conntrack_mutex);kvfree(hash);return0;}
@@ -2598,6 +2600,8 @@ int nf_conntrack_hash_resize(unsigned int hashsize)nf_conntrack_all_unlock();local_bh_enable();+mutex_unlock(&nf_conntrack_mutex);+synchronize_net();kvfree(old_hash);return0;
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:11:58
From: Florian Westphal <fw@strlen.de>
This is a leftover from the times when this function was wired up via
pernet_operations. Now its called when userspace asks for the table.
With CONFIG_NET_NS=n, iptable_raw_table_init memory has been discarded
already and we get a kernel crash.
Other tables are fine, __net_init annotation was removed already.
Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default")
Reported-by: youling 257 <redacted>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/ipv4/netfilter/iptable_raw.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Pablo Neira Ayuso <pablo@netfilter.org> Date: 2021-09-24 22:12:00
From: Florian Westphal <fw@strlen.de>
iptables/nftables has two types of log modules:
1. backend, e.g. nf_log_syslog, which implement the functionality
2. frontend, e.g. xt_LOG or nft_log, which call the functionality
provided by backend based on nf_tables or xtables rule set.
Problem is that the request_module() call to load the backed in
nf_logger_find_get() might happen with nftables transaction mutex held
in case the call path is via nf_tables/nft_compat.
This can cause deadlocks (see 'Fixes' tags for details).
The chosen solution as to let modprobe deal with this by adding 'pre: '
soft dep tag to xt_LOG (to load the syslog backend) and xt_NFLOG (to
load nflog backend).
Eric reports that this breaks on systems with older modprobe that
doesn't support softdeps.
Another, similar issue occurs when someone either insmods xt_(NF)LOG
directly or unloads the backend module (possible if no log frontend
is in use): because the frontend module is already loaded, modprobe is
not invoked again so the softdep isn't evaluated.
Add a workaround: If nf_logger_find_get() returns -ENOENT and call
is not via nft_compat, load the backend explicitly and try again.
Else, let nft_compat ask for deferred request_module via nf_tables
infra.
Softdeps are kept in-place, so with newer modprobe the dependencies
are resolved from userspace.
Fixes: cefa31a9d461 ("netfilter: nft_log: perform module load from nf_tables")
Fixes: a38b5b56d6f4 ("netfilter: nf_log: add module softdeps")
Reported-and-tested-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nft_compat.c | 17 ++++++++++++++++-
net/netfilter/xt_LOG.c | 10 +++++++++-
net/netfilter/xt_NFLOG.c | 10 +++++++++-
3 files changed, 34 insertions(+), 3 deletions(-)
@@ -19,6 +19,7 @@#include<linux/netfilter_bridge/ebtables.h>#include<linux/netfilter_arp/arp_tables.h>#include<net/netfilter/nf_tables.h>+#include<net/netfilter/nf_log.h>/* Used for matches where *info is larger than X byte */#define NFT_MATCH_LARGE_THRESH 192
@@ -257,8 +258,22 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,nft_compat_wait_for_destructors();ret=xt_check_target(&par,size,proto,inv);-if(ret<0)+if(ret<0){+if(ret==-ENOENT){+constchar*modname=NULL;++if(strcmp(target->name,"LOG")==0)+modname="nf_log_syslog";+elseif(strcmp(target->name,"NFLOG")==0)+modname="nfnetlink_log";++if(modname&&+nft_request_module(ctx->net,"%s",modname)==-EAGAIN)+return-EAGAIN;+}+returnret;+}/* The standard target cannot be used */if(!target->target)