* [PATCH net 01/12] netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 15:30 ` patchwork-bot+netdevbpf
2026-03-26 12:51 ` [PATCH net 02/12] selftests: netfilter: nft_concat_range.sh: add check for flush+reload bug Pablo Neira Ayuso
` (10 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
From: Florian Westphal <fw@strlen.de>
New test case fails unexpectedly when avx2 matching functions are used.
The test first loads a ranomly generated pipapo set
with 'ipv4 . port' key, i.e. nft -f foo.
This works. Then, it reloads the set after a flush:
(echo flush set t s; cat foo) | nft -f -
This is expected to work, because its the same set after all and it was
already loaded once.
But with avx2, this fails: nft reports a clashing element.
The reported clash is of following form:
We successfully re-inserted
a . b
c . d
Then we try to insert a . d
avx2 finds the already existing a . d, which (due to 'flush set') is marked
as invalid in the new generation. It skips the element and moves to next.
Due to incorrect masking, the skip-step finds the next matching
element *only considering the first field*,
i.e. we return the already reinserted "a . b", even though the
last field is different and the entry should not have been matched.
No such error is reported for the generic c implementation (no avx2) or when
the last field has to use the 'nft_pipapo_avx2_lookup_slow' fallback.
Bisection points to
7711f4bb4b36 ("netfilter: nft_set_pipapo: fix range overlap detection")
but that fix merely uncovers this bug.
Before this commit, the wrong element is returned, but erronously
reported as a full, identical duplicate.
The root-cause is too early return in the avx2 match functions.
When we process the last field, we should continue to process data
until the entire input size has been consumed to make sure no stale
bits remain in the map.
Link: https://lore.kernel.org/netfilter-devel/20260321152506.037f68c0@elisabeth/
Signed-off-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nft_set_pipapo_avx2.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/net/netfilter/nft_set_pipapo_avx2.c b/net/netfilter/nft_set_pipapo_avx2.c
index 7ff90325c97f..6395982e4d95 100644
--- a/net/netfilter/nft_set_pipapo_avx2.c
+++ b/net/netfilter/nft_set_pipapo_avx2.c
@@ -242,7 +242,7 @@ static int nft_pipapo_avx2_lookup_4b_2(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -319,7 +319,7 @@ static int nft_pipapo_avx2_lookup_4b_4(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -414,7 +414,7 @@ static int nft_pipapo_avx2_lookup_4b_8(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -505,7 +505,7 @@ static int nft_pipapo_avx2_lookup_4b_12(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -641,7 +641,7 @@ static int nft_pipapo_avx2_lookup_4b_32(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -699,7 +699,7 @@ static int nft_pipapo_avx2_lookup_8b_1(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -764,7 +764,7 @@ static int nft_pipapo_avx2_lookup_8b_2(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -839,7 +839,7 @@ static int nft_pipapo_avx2_lookup_8b_4(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -925,7 +925,7 @@ static int nft_pipapo_avx2_lookup_8b_6(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -1019,7 +1019,7 @@ static int nft_pipapo_avx2_lookup_8b_16(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH net 01/12] netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry
2026-03-26 12:51 ` [PATCH net 01/12] netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry Pablo Neira Ayuso
@ 2026-03-26 15:30 ` patchwork-bot+netdevbpf
0 siblings, 0 replies; 22+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-03-26 15:30 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, fw, horms
Hello:
This series was applied to netdev/net.git (main)
by Pablo Neira Ayuso <pablo@netfilter.org>:
On Thu, 26 Mar 2026 13:51:42 +0100 you wrote:
> From: Florian Westphal <fw@strlen.de>
>
> New test case fails unexpectedly when avx2 matching functions are used.
>
> The test first loads a ranomly generated pipapo set
> with 'ipv4 . port' key, i.e. nft -f foo.
>
> [...]
Here is the summary with links:
- [net,01/12] netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry
https://git.kernel.org/netdev/net/c/d3c0037ffe12
- [net,02/12] selftests: netfilter: nft_concat_range.sh: add check for flush+reload bug
https://git.kernel.org/netdev/net/c/6caefcd9491c
- [net,03/12] netfilter: nfnetlink_log: fix uninitialized padding leak in NFULA_PAYLOAD
https://git.kernel.org/netdev/net/c/52025ebaa29f
- [net,04/12] netfilter: ip6t_rt: reject oversized addrnr in rt_mt6_check()
https://git.kernel.org/netdev/net/c/9d3f027327c2
- [net,05/12] netfilter: nft_set_rbtree: revisit array resize logic
https://git.kernel.org/netdev/net/c/fafdd92b9e30
- [net,06/12] netfilter: nf_conntrack_expect: honor expectation helper field
https://git.kernel.org/netdev/net/c/9c42bc9db90a
- [net,07/12] netfilter: nf_conntrack_expect: use expect->helper
https://git.kernel.org/netdev/net/c/f01794106042
- [net,08/12] netfilter: ctnetlink: ensure safe access to master conntrack
https://git.kernel.org/netdev/net/c/bffcaad9afdf
- [net,09/12] netfilter: nf_conntrack_expect: store netns and zone in expectation
https://git.kernel.org/netdev/net/c/02a3231b6d82
- [net,10/12] netfilter: nf_conntrack_expect: skip expectations in other netns via proc
https://git.kernel.org/netdev/net/c/3db5647984de
- [net,11/12] netfilter: nf_conntrack_sip: fix use of uninitialized rtp_addr in process_sdp
https://git.kernel.org/netdev/net/c/6a2b724460cb
- [net,12/12] netfilter: ctnetlink: use netlink policy range checks
https://git.kernel.org/netdev/net/c/8f15b5071b45
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net 02/12] selftests: netfilter: nft_concat_range.sh: add check for flush+reload bug
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 01/12] netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 03/12] netfilter: nfnetlink_log: fix uninitialized padding leak in NFULA_PAYLOAD Pablo Neira Ayuso
` (9 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
From: Florian Westphal <fw@strlen.de>
This test will fail without
the preceding commit ("netfilter: nft_set_pipapo_avx2: fix match retart if found element is expired"):
reject overlapping range on add 0s [ OK ]
reload with flush /dev/stdin:59:32-52: Error: Could not process rule: File exists
add element inet filter test { 10.0.0.29 . 10.0.2.29 }
Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
.../net/netfilter/nft_concat_range.sh | 70 ++++++++++++++++++-
1 file changed, 69 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/netfilter/nft_concat_range.sh b/tools/testing/selftests/net/netfilter/nft_concat_range.sh
index 394166f224a4..ffdc6ccc6511 100755
--- a/tools/testing/selftests/net/netfilter/nft_concat_range.sh
+++ b/tools/testing/selftests/net/netfilter/nft_concat_range.sh
@@ -29,7 +29,8 @@ TYPES="net_port port_net net6_port port_proto net6_port_mac net6_port_mac_proto
net6_port_net6_port net_port_mac_proto_net"
# Reported bugs, also described by TYPE_ variables below
-BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate insert_overlap"
+BUGS="flush_remove_add reload net_port_proto_match avx2_mismatch doublecreate
+ insert_overlap load_flush_load4 load_flush_load8"
# List of possible paths to pktgen script from kernel tree for performance tests
PKTGEN_SCRIPT_PATHS="
@@ -432,6 +433,30 @@ race_repeat 0
perf_duration 0
"
+TYPE_load_flush_load4="
+display reload with flush, 4bit groups
+type_spec ipv4_addr . ipv4_addr
+chain_spec ip saddr . ip daddr
+dst addr4
+proto icmp
+
+race_repeat 0
+
+perf_duration 0
+"
+
+TYPE_load_flush_load8="
+display reload with flush, 8bit groups
+type_spec ipv4_addr . ipv4_addr
+chain_spec ip saddr . ip daddr
+dst addr4
+proto icmp
+
+race_repeat 0
+
+perf_duration 0
+"
+
# Set template for all tests, types and rules are filled in depending on test
set_template='
flush ruleset
@@ -1997,6 +2022,49 @@ test_bug_insert_overlap()
return 0
}
+test_bug_load_flush_load4()
+{
+ local i
+
+ setup veth send_"${proto}" set || return ${ksft_skip}
+
+ for i in $(seq 0 255); do
+ local addelem="add element inet filter test"
+ local j
+
+ for j in $(seq 0 20); do
+ echo "$addelem { 10.$j.0.$i . 10.$j.1.$i }"
+ echo "$addelem { 10.$j.0.$i . 10.$j.2.$i }"
+ done
+ done > "$tmp"
+
+ nft -f "$tmp" || return 1
+
+ ( echo "flush set inet filter test";cat "$tmp") | nft -f -
+ [ $? -eq 0 ] || return 1
+
+ return 0
+}
+
+test_bug_load_flush_load8()
+{
+ local i
+
+ setup veth send_"${proto}" set || return ${ksft_skip}
+
+ for i in $(seq 1 100); do
+ echo "add element inet filter test { 10.0.0.$i . 10.0.1.$i }"
+ echo "add element inet filter test { 10.0.0.$i . 10.0.2.$i }"
+ done > "$tmp"
+
+ nft -f "$tmp" || return 1
+
+ ( echo "flush set inet filter test";cat "$tmp") | nft -f -
+ [ $? -eq 0 ] || return 1
+
+ return 0
+}
+
test_reported_issues() {
eval test_bug_"${subtest}"
}
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH net 03/12] netfilter: nfnetlink_log: fix uninitialized padding leak in NFULA_PAYLOAD
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 01/12] netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 02/12] selftests: netfilter: nft_concat_range.sh: add check for flush+reload bug Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 04/12] netfilter: ip6t_rt: reject oversized addrnr in rt_mt6_check() Pablo Neira Ayuso
` (8 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
From: Weiming Shi <bestswngs@gmail.com>
__build_packet_message() manually constructs the NFULA_PAYLOAD netlink
attribute using skb_put() and skb_copy_bits(), bypassing the standard
nla_reserve()/nla_put() helpers. While nla_total_size(data_len) bytes
are allocated (including NLA alignment padding), only data_len bytes
of actual packet data are copied. The trailing nla_padlen(data_len)
bytes (1-3 when data_len is not 4-byte aligned) are never initialized,
leaking stale heap contents to userspace via the NFLOG netlink socket.
Replace the manual attribute construction with nla_reserve(), which
handles the tailroom check, header setup, and padding zeroing via
__nla_reserve(). The subsequent skb_copy_bits() fills in the payload
data on top of the properly initialized attribute.
Fixes: df6fb868d611 ("[NETFILTER]: nfnetlink: convert to generic netlink attribute functions")
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nfnetlink_log.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index b35a90955e2e..fcbe54940b2e 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -647,15 +647,11 @@ __build_packet_message(struct nfnl_log_net *log,
if (data_len) {
struct nlattr *nla;
- int size = nla_attr_size(data_len);
- if (skb_tailroom(inst->skb) < nla_total_size(data_len))
+ nla = nla_reserve(inst->skb, NFULA_PAYLOAD, data_len);
+ if (!nla)
goto nla_put_failure;
- nla = skb_put(inst->skb, nla_total_size(data_len));
- nla->nla_type = NFULA_PAYLOAD;
- nla->nla_len = size;
-
if (skb_copy_bits(skb, 0, nla_data(nla), data_len))
BUG();
}
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH net 04/12] netfilter: ip6t_rt: reject oversized addrnr in rt_mt6_check()
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
` (2 preceding siblings ...)
2026-03-26 12:51 ` [PATCH net 03/12] netfilter: nfnetlink_log: fix uninitialized padding leak in NFULA_PAYLOAD Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 05/12] netfilter: nft_set_rbtree: revisit array resize logic Pablo Neira Ayuso
` (7 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
From: Ren Wei <n05ec@lzu.edu.cn>
Reject rt match rules whose addrnr exceeds IP6T_RT_HOPS.
rt_mt6() expects addrnr to stay within the bounds of rtinfo->addrs[].
Validate addrnr during rule installation so malformed rules are rejected
before the match logic can use an out-of-range value.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Co-developed-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Yuan Tan <yuantan098@gmail.com>
Suggested-by: Xin Liu <bird@lzu.edu.cn>
Tested-by: Yuhang Zheng <z1652074432@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/ipv6/netfilter/ip6t_rt.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c
index 4ad8b2032f1f..5561bd9cea81 100644
--- a/net/ipv6/netfilter/ip6t_rt.c
+++ b/net/ipv6/netfilter/ip6t_rt.c
@@ -157,6 +157,10 @@ static int rt_mt6_check(const struct xt_mtchk_param *par)
pr_debug("unknown flags %X\n", rtinfo->invflags);
return -EINVAL;
}
+ if (rtinfo->addrnr > IP6T_RT_HOPS) {
+ pr_debug("too many addresses specified\n");
+ return -EINVAL;
+ }
if ((rtinfo->flags & (IP6T_RT_RES | IP6T_RT_FST_MASK)) &&
(!(rtinfo->flags & IP6T_RT_TYP) ||
(rtinfo->rt_type != 0) ||
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH net 05/12] netfilter: nft_set_rbtree: revisit array resize logic
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
` (3 preceding siblings ...)
2026-03-26 12:51 ` [PATCH net 04/12] netfilter: ip6t_rt: reject oversized addrnr in rt_mt6_check() Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field Pablo Neira Ayuso
` (6 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
Chris Arges reports high memory consumption with thousands of
containers, this patch revisits the array allocation logic.
For anonymous sets, start by 16 slots (which takes 256 bytes on x86_64).
Expand it by x2 until threshold of 512 slots is reached, over that
threshold, expand it by x1.5.
For non-anonymous set, start by 1024 slots in the array (which takes 16
Kbytes initially on x86_64). Expand it by x1.5.
Use set->ndeact to subtract deactivated elements when calculating the
number of the slots in the array, otherwise the array size array gets
increased artifically. Add special case shrink logic to deal with flush
set too.
The shrink logic is skipped by anonymous sets.
Use check_add_overflow() to calculate the new array size.
Add a WARN_ON_ONCE check to make sure elements fit into the new array
size.
Reported-by: Chris Arges <carges@cloudflare.com>
Fixes: 7e43e0a1141d ("netfilter: nft_set_rbtree: translate rbtree to array for binary search")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nft_set_rbtree.c | 92 +++++++++++++++++++++++++++-------
1 file changed, 75 insertions(+), 17 deletions(-)
diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
index fe8bd497d74a..737c339decd0 100644
--- a/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -572,14 +572,12 @@ static struct nft_array *nft_array_alloc(u32 max_intervals)
return array;
}
-#define NFT_ARRAY_EXTRA_SIZE 10240
-
/* Similar to nft_rbtree_{u,k}size to hide details to userspace, but consider
* packed representation coming from userspace for anonymous sets too.
*/
static u32 nft_array_elems(const struct nft_set *set)
{
- u32 nelems = atomic_read(&set->nelems);
+ u32 nelems = atomic_read(&set->nelems) - set->ndeact;
/* Adjacent intervals are represented with a single start element in
* anonymous sets, use the current element counter as is.
@@ -595,27 +593,87 @@ static u32 nft_array_elems(const struct nft_set *set)
return (nelems / 2) + 2;
}
-static int nft_array_may_resize(const struct nft_set *set)
+#define NFT_ARRAY_INITIAL_SIZE 1024
+#define NFT_ARRAY_INITIAL_ANON_SIZE 16
+#define NFT_ARRAY_INITIAL_ANON_THRESH (8192U / sizeof(struct nft_array_interval))
+
+static int nft_array_may_resize(const struct nft_set *set, bool flush)
{
- u32 nelems = nft_array_elems(set), new_max_intervals;
+ u32 initial_intervals, max_intervals, new_max_intervals, delta;
+ u32 shrinked_max_intervals, nelems = nft_array_elems(set);
struct nft_rbtree *priv = nft_set_priv(set);
struct nft_array *array;
- if (!priv->array_next) {
- array = nft_array_alloc(nelems + NFT_ARRAY_EXTRA_SIZE);
- if (!array)
- return -ENOMEM;
+ if (nft_set_is_anonymous(set))
+ initial_intervals = NFT_ARRAY_INITIAL_ANON_SIZE;
+ else
+ initial_intervals = NFT_ARRAY_INITIAL_SIZE;
+
+ if (priv->array_next) {
+ max_intervals = priv->array_next->max_intervals;
+ new_max_intervals = priv->array_next->max_intervals;
+ } else {
+ if (priv->array) {
+ max_intervals = priv->array->max_intervals;
+ new_max_intervals = priv->array->max_intervals;
+ } else {
+ max_intervals = 0;
+ new_max_intervals = initial_intervals;
+ }
+ }
- priv->array_next = array;
+ if (nft_set_is_anonymous(set))
+ goto maybe_grow;
+
+ if (flush) {
+ /* Set flush just started, nelems still report elements.*/
+ nelems = 0;
+ new_max_intervals = NFT_ARRAY_INITIAL_SIZE;
+ goto realloc_array;
}
- if (nelems < priv->array_next->max_intervals)
- return 0;
+ if (check_add_overflow(new_max_intervals, new_max_intervals,
+ &shrinked_max_intervals))
+ return -EOVERFLOW;
+
+ shrinked_max_intervals = DIV_ROUND_UP(shrinked_max_intervals, 3);
- new_max_intervals = priv->array_next->max_intervals + NFT_ARRAY_EXTRA_SIZE;
- if (nft_array_intervals_alloc(priv->array_next, new_max_intervals) < 0)
+ if (shrinked_max_intervals > NFT_ARRAY_INITIAL_SIZE &&
+ nelems < shrinked_max_intervals) {
+ new_max_intervals = shrinked_max_intervals;
+ goto realloc_array;
+ }
+maybe_grow:
+ if (nelems > new_max_intervals) {
+ if (nft_set_is_anonymous(set) &&
+ new_max_intervals < NFT_ARRAY_INITIAL_ANON_THRESH) {
+ new_max_intervals <<= 1;
+ } else {
+ delta = new_max_intervals >> 1;
+ if (check_add_overflow(new_max_intervals, delta,
+ &new_max_intervals))
+ return -EOVERFLOW;
+ }
+ }
+
+realloc_array:
+ if (WARN_ON_ONCE(nelems > new_max_intervals))
return -ENOMEM;
+ if (priv->array_next) {
+ if (max_intervals == new_max_intervals)
+ return 0;
+
+ if (nft_array_intervals_alloc(priv->array_next, new_max_intervals) < 0)
+ return -ENOMEM;
+ } else {
+ array = nft_array_alloc(new_max_intervals);
+ if (!array)
+ return -ENOMEM;
+
+ priv->array_next = array;
+ }
+
return 0;
}
@@ -630,7 +688,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,
nft_rbtree_maybe_reset_start_cookie(priv, tstamp);
- if (nft_array_may_resize(set) < 0)
+ if (nft_array_may_resize(set, false) < 0)
return -ENOMEM;
do {
@@ -741,7 +799,7 @@ nft_rbtree_deactivate(const struct net *net, const struct nft_set *set,
nft_rbtree_interval_null(set, this))
priv->start_rbe_cookie = 0;
- if (nft_array_may_resize(set) < 0)
+ if (nft_array_may_resize(set, false) < 0)
return NULL;
while (parent != NULL) {
@@ -811,7 +869,7 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
switch (iter->type) {
case NFT_ITER_UPDATE_CLONE:
- if (nft_array_may_resize(set) < 0) {
+ if (nft_array_may_resize(set, true) < 0) {
iter->err = -ENOMEM;
break;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
` (4 preceding siblings ...)
2026-03-26 12:51 ` [PATCH net 05/12] netfilter: nft_set_rbtree: revisit array resize logic Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-04-30 20:58 ` Ilya Maximets
2026-03-26 12:51 ` [PATCH net 07/12] netfilter: nf_conntrack_expect: use expect->helper Pablo Neira Ayuso
` (5 subsequent siblings)
11 siblings, 1 reply; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
The expectation helper field is mostly unused. As a result, the
netfilter codebase relies on accessing the helper through exp->master.
Always set on the expectation helper field so it can be used to reach
the helper.
nf_ct_expect_init() is called from packet path where the skb owns
the ct object, therefore accessing exp->master for the newly created
expectation is safe. This saves a lot of updates in all callsites
to pass the ct object as parameter to nf_ct_expect_init().
This is a preparation patches for follow up fixes.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
include/net/netfilter/nf_conntrack_expect.h | 2 +-
net/netfilter/nf_conntrack_broadcast.c | 2 +-
net/netfilter/nf_conntrack_expect.c | 14 +++++++++++++-
net/netfilter/nf_conntrack_h323_main.c | 12 ++++++------
net/netfilter/nf_conntrack_helper.c | 7 ++++++-
net/netfilter/nf_conntrack_netlink.c | 2 +-
net/netfilter/nf_conntrack_sip.c | 2 +-
7 files changed, 29 insertions(+), 12 deletions(-)
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index 165e7a03b8e9..1b01400b10bd 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -40,7 +40,7 @@ struct nf_conntrack_expect {
struct nf_conntrack_expect *this);
/* Helper to assign to new connection */
- struct nf_conntrack_helper *helper;
+ struct nf_conntrack_helper __rcu *helper;
/* The conntrack of the master connection */
struct nf_conn *master;
diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c
index a7552a46d6ac..1964c596c646 100644
--- a/net/netfilter/nf_conntrack_broadcast.c
+++ b/net/netfilter/nf_conntrack_broadcast.c
@@ -70,7 +70,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
exp->expectfn = NULL;
exp->flags = NF_CT_EXPECT_PERMANENT;
exp->class = NF_CT_EXPECT_CLASS_DEFAULT;
- exp->helper = NULL;
+ rcu_assign_pointer(exp->helper, helper);
nf_ct_expect_related(exp, 0);
nf_ct_expect_put(exp);
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index cfc2daa3fc7f..841e316240da 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -309,12 +309,19 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
}
EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
+/* This function can only be used from packet path, where accessing
+ * master's helper is safe, because the packet holds a reference on
+ * the conntrack object. Never use it from control plane.
+ */
void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
u_int8_t family,
const union nf_inet_addr *saddr,
const union nf_inet_addr *daddr,
u_int8_t proto, const __be16 *src, const __be16 *dst)
{
+ struct nf_conntrack_helper *helper = NULL;
+ struct nf_conn *ct = exp->master;
+ struct nf_conn_help *help;
int len;
if (family == AF_INET)
@@ -325,7 +332,12 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
exp->flags = 0;
exp->class = class;
exp->expectfn = NULL;
- exp->helper = NULL;
+
+ help = nfct_help(ct);
+ if (help)
+ helper = rcu_dereference(help->helper);
+
+ rcu_assign_pointer(exp->helper, helper);
exp->tuple.src.l3num = family;
exp->tuple.dst.protonum = proto;
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index a2a0e22ccee1..3f5c50455b71 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -643,7 +643,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
- exp->helper = &nf_conntrack_helper_h245;
+ rcu_assign_pointer(exp->helper, &nf_conntrack_helper_h245);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -767,7 +767,7 @@ static int expect_callforwarding(struct sk_buff *skb,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
- exp->helper = nf_conntrack_helper_q931;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -1234,7 +1234,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3 : NULL,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
- exp->helper = nf_conntrack_helper_q931;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */
nathook = rcu_dereference(nfct_h323_nat_hook);
@@ -1306,7 +1306,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_UDP, NULL, &port);
- exp->helper = nf_conntrack_helper_ras;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_ras);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect RAS ");
@@ -1523,7 +1523,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
- exp->helper = nf_conntrack_helper_q931;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");
@@ -1577,7 +1577,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
- exp->helper = nf_conntrack_helper_q931;
+ rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index ceb48c3ca0a4..294a6ffcbccd 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -399,7 +399,7 @@ static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
const struct nf_conntrack_helper *me = data;
const struct nf_conntrack_helper *this;
- if (exp->helper == me)
+ if (rcu_access_pointer(exp->helper) == me)
return true;
this = rcu_dereference_protected(help->helper,
@@ -421,6 +421,11 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
nf_ct_expect_iterate_destroy(expect_iter_me, NULL);
nf_ct_iterate_destroy(unhelp, me);
+
+ /* nf_ct_iterate_destroy() does an unconditional synchronize_rcu() as
+ * last step, this ensures rcu readers of exp->helper are done.
+ * No need for another synchronize_rcu() here.
+ */
}
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index c156574e1273..a42d14290786 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3573,7 +3573,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
exp->class = class;
exp->master = ct;
- exp->helper = helper;
+ rcu_assign_pointer(exp->helper, helper);
exp->tuple = *tuple;
exp->mask.src.u3 = mask->src.u3;
exp->mask.src.u.all = mask->src.u.all;
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 4ab5ef71d96d..106b2f419e19 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -1297,7 +1297,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct),
saddr, &daddr, proto, NULL, &port);
exp->timeout.expires = sip_timeout * HZ;
- exp->helper = helper;
+ rcu_assign_pointer(exp->helper, helper);
exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
hooks = rcu_dereference(nf_nat_sip_hooks);
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
2026-03-26 12:51 ` [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field Pablo Neira Ayuso
@ 2026-04-30 20:58 ` Ilya Maximets
2026-05-01 10:37 ` Pablo Neira Ayuso
0 siblings, 1 reply; 22+ messages in thread
From: Ilya Maximets @ 2026-04-30 20:58 UTC (permalink / raw)
To: Pablo Neira Ayuso, netfilter-devel, fw
Cc: davem, netdev, kuba, pabeni, edumazet, horms, i.maximets,
Eelco Chaudron, Aaron Conole
On 3/26/26 1:51 PM, Pablo Neira Ayuso wrote:
> The expectation helper field is mostly unused. As a result, the
> netfilter codebase relies on accessing the helper through exp->master.
>
> Always set on the expectation helper field so it can be used to reach
> the helper.
>
> nf_ct_expect_init() is called from packet path where the skb owns
> the ct object, therefore accessing exp->master for the newly created
> expectation is safe. This saves a lot of updates in all callsites
> to pass the ct object as parameter to nf_ct_expect_init().
>
> This is a preparation patches for follow up fixes.
>
> Signed-off-by: Florian Westphal <fw@strlen.de>
> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> ---
Hi, Pablo and Florian.
I was investigating FTP test failures in OVS with 7.0 kernel and bisected
the issue down to this commit. AFAIU, with this change all the related
connections over time gain their parents' helpers,. This is causing a change
visible to the userspace, because FTP data connections are now reported to
have helpers in the conntrack dump:
# conntrack -L
tcp 6 119 TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=59534 dport=21 \
src=10.1.1.2 dst=10.1.1.1 sport=21 dport=59534 \
[ASSURED] mark=0 helper=ftp use=2
tcp 6 119 TIME_WAIT src=10.1.1.2 dst=10.1.1.1 sport=52709 dport=52381 \
src=10.1.1.1 dst=10.1.1.2 sport=52381 dport=52709 \
[ASSURED] mark=0 helper=ftp use=1
Before this commit only the control connection had helper=ftp reported in
the dump. The traffic seems to work fine, but our tests fail because we
do not expect the helper attached.
AFAIU, it's generally not something that should be happening, as helpers
on data connections do not really make much sense. But I'm just trying to
figure out if you would consider this as a regression and fix in the kernel
or if we should adjust our userspace components for this new dump content,
which would not be very straightforward to do if we want to be able to run
tests on both old and the new versions.
What do you think?
Best regards, Ilya Maximets.
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
2026-04-30 20:58 ` Ilya Maximets
@ 2026-05-01 10:37 ` Pablo Neira Ayuso
2026-05-04 12:19 ` Ilya Maximets
0 siblings, 1 reply; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-05-01 10:37 UTC (permalink / raw)
To: Ilya Maximets
Cc: netfilter-devel, fw, davem, netdev, kuba, pabeni, edumazet, horms,
Eelco Chaudron, Aaron Conole
Hi Ilya,
On Thu, Apr 30, 2026 at 10:58:38PM +0200, Ilya Maximets wrote:
> On 3/26/26 1:51 PM, Pablo Neira Ayuso wrote:
> > The expectation helper field is mostly unused. As a result, the
> > netfilter codebase relies on accessing the helper through exp->master.
> >
> > Always set on the expectation helper field so it can be used to reach
> > the helper.
> >
> > nf_ct_expect_init() is called from packet path where the skb owns
> > the ct object, therefore accessing exp->master for the newly created
> > expectation is safe. This saves a lot of updates in all callsites
> > to pass the ct object as parameter to nf_ct_expect_init().
> >
> > This is a preparation patches for follow up fixes.
> >
> > Signed-off-by: Florian Westphal <fw@strlen.de>
> > Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> > ---
>
> Hi, Pablo and Florian.
>
> I was investigating FTP test failures in OVS with 7.0 kernel and bisected
> the issue down to this commit. AFAIU, with this change all the related
> connections over time gain their parents' helpers,. This is causing a change
> visible to the userspace, because FTP data connections are now reported to
> have helpers in the conntrack dump:
>
> # conntrack -L
> tcp 6 119 TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=59534 dport=21 \
> src=10.1.1.2 dst=10.1.1.1 sport=21 dport=59534 \
> [ASSURED] mark=0 helper=ftp use=2
> tcp 6 119 TIME_WAIT src=10.1.1.2 dst=10.1.1.1 sport=52709 dport=52381 \
> src=10.1.1.1 dst=10.1.1.2 sport=52381 dport=52709 \
> [ASSURED] mark=0 helper=ftp use=1
>
> Before this commit only the control connection had helper=ftp reported in
> the dump. The traffic seems to work fine, but our tests fail because we
> do not expect the helper attached.
>
> AFAIU, it's generally not something that should be happening, as helpers
> on data connections do not really make much sense. But I'm just trying to
> figure out if you would consider this as a regression and fix in the kernel
> or if we should adjust our userspace components for this new dump content,
> which would not be very straightforward to do if we want to be able to run
> tests on both old and the new versions.
>
> What do you think?
It seems previous behaviour to 9c42bc9db90a was inconsistent, ie. only
the h323 helper sets on exp->helper, then it shows helper= in expected
connections via ctnetlink. I guess this is for debugging given that
h323 is actually a family of helpers.
To consistently skip dumping this for expected connections, probably
this is the way to do:
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conn
index eda5fe4a75c8..9491ae9e080e 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -226,7 +226,7 @@ static int ctnetlink_dump_helpinfo(struct sk_buff *sk
const struct nf_conn_help *help = nfct_help(ct);
struct nf_conntrack_helper *helper;
- if (!help)
+ if (!help || ct->status & IPS_EXPECTED)
return 0;
rcu_read_lock();
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
2026-05-01 10:37 ` Pablo Neira Ayuso
@ 2026-05-04 12:19 ` Ilya Maximets
2026-05-04 23:16 ` Pablo Neira Ayuso
0 siblings, 1 reply; 22+ messages in thread
From: Ilya Maximets @ 2026-05-04 12:19 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: i.maximets, netfilter-devel, fw, davem, netdev, kuba, pabeni,
edumazet, horms, Eelco Chaudron, Aaron Conole
On 5/1/26 12:37 PM, Pablo Neira Ayuso wrote:
> Hi Ilya,
>
> On Thu, Apr 30, 2026 at 10:58:38PM +0200, Ilya Maximets wrote:
>> On 3/26/26 1:51 PM, Pablo Neira Ayuso wrote:
>>> The expectation helper field is mostly unused. As a result, the
>>> netfilter codebase relies on accessing the helper through exp->master.
>>>
>>> Always set on the expectation helper field so it can be used to reach
>>> the helper.
>>>
>>> nf_ct_expect_init() is called from packet path where the skb owns
>>> the ct object, therefore accessing exp->master for the newly created
>>> expectation is safe. This saves a lot of updates in all callsites
>>> to pass the ct object as parameter to nf_ct_expect_init().
>>>
>>> This is a preparation patches for follow up fixes.
>>>
>>> Signed-off-by: Florian Westphal <fw@strlen.de>
>>> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
>>> ---
>>
>> Hi, Pablo and Florian.
>>
>> I was investigating FTP test failures in OVS with 7.0 kernel and bisected
>> the issue down to this commit. AFAIU, with this change all the related
>> connections over time gain their parents' helpers,. This is causing a change
>> visible to the userspace, because FTP data connections are now reported to
>> have helpers in the conntrack dump:
>>
>> # conntrack -L
>> tcp 6 119 TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=59534 dport=21 \
>> src=10.1.1.2 dst=10.1.1.1 sport=21 dport=59534 \
>> [ASSURED] mark=0 helper=ftp use=2
>> tcp 6 119 TIME_WAIT src=10.1.1.2 dst=10.1.1.1 sport=52709 dport=52381 \
>> src=10.1.1.1 dst=10.1.1.2 sport=52381 dport=52709 \
>> [ASSURED] mark=0 helper=ftp use=1
>>
>> Before this commit only the control connection had helper=ftp reported in
>> the dump. The traffic seems to work fine, but our tests fail because we
>> do not expect the helper attached.
>>
>> AFAIU, it's generally not something that should be happening, as helpers
>> on data connections do not really make much sense. But I'm just trying to
>> figure out if you would consider this as a regression and fix in the kernel
>> or if we should adjust our userspace components for this new dump content,
>> which would not be very straightforward to do if we want to be able to run
>> tests on both old and the new versions.
>>
>> What do you think?
>
> It seems previous behaviour to 9c42bc9db90a was inconsistent, ie. only
> the h323 helper sets on exp->helper, then it shows helper= in expected
> connections via ctnetlink. I guess this is for debugging given that
> h323 is actually a family of helpers.
>
> To consistently skip dumping this for expected connections, probably
> this is the way to do:
>
> diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conn
> index eda5fe4a75c8..9491ae9e080e 100644
> --- a/net/netfilter/nf_conntrack_netlink.c
> +++ b/net/netfilter/nf_conntrack_netlink.c
> @@ -226,7 +226,7 @@ static int ctnetlink_dump_helpinfo(struct sk_buff *sk
> const struct nf_conn_help *help = nfct_help(ct);
> struct nf_conntrack_helper *helper;
>
> - if (!help)
> + if (!help || ct->status & IPS_EXPECTED)
> return 0;
>
> rcu_read_lock();
I'm not sure. I tried this change and it fixed one case but broke another.
Looking at what we're testing, the old behavior (at least for FTP) was:
"if helper was committed - report it, if not - don't". i.e. it's not really
about the connection being expected it's about if the user committed the
helper for the connection or not.
Let me explain a few scenarios that we have in the OVS system tests and what
I see with the old kernel (6.19), the new (7.0) and the patch above.
A) The first scenario has the following OpenFlow rules (simplified):
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=1
This set of rule blindly commits every packet coming from port 1 with the
helper and sends to port 2. Packets from port 2 are passed through ct and
only related or established traffic is passed to port 1. This is a very
rudimentary setup that users can make to allow ftp from port 1 towards port 2,
but not in the opposite direction.
For this scenario regardless of the kernel version or the patch above I see
that both the data and the control connections have a helper reported in the
ctnetlink dump.
B) The second scenario:
table=0,in_port=1,tcp,action=ct(table=1)
table=1,in_port=1,tcp,ct_state=+trk+new,action=ct(commit,alg=ftp),2
table=1,in_port=1,tcp,ct_state=+trk+est,action=2
table=0,in_port=2,tcp,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+new+rel,action=ct(commit),1
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
This is a more reasonable setup where new connections are committed on the
way from 1 to 2 and new related connections are committed on the way from
2 to 1. This allows port 1 to initiate the control connection and the port
2 to initiate the related data connection.
In case of active FTP (port 1 initiates control, port 2 initiates the data):
- old: control has the helper, data does not.
- new: both have the helper.
- patch: control has the helper, data does not.
In case of passive FTP (port 1 initiates both the control and data):
- old: both have the helper (because both are +new traffic from port 1).
- new: both have the helper.
- patch: control has the helper, data does not.
C) We can modify the scenario B to avoid committing the helper on related:
table=0,in_port=1,tcp,action=ct(table=1)
table=1,in_port=1,tcp,ct_state=+trk+new-rel,action=ct(commit,alg=ftp),2
table=1,in_port=1,tcp,ct_state=+trk+new+rel,action=ct(commit),2
table=1,in_port=1,tcp,ct_state=+trk+est,action=2
table=0,in_port=2,tcp,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+new+rel,action=ct(commit),1
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
Here we have (same for active and passive):
- old: control has the helper, data does not.
- new: both have the helper.
- patch: control has the helper, data does not.
I hope that clarifies the situation a little bit.
So, if we want to restore the old behavior, the we'd probably need to track
how connection gained the helper, i.e. was it via commit or was it inherited.
I'm also not sure why we see the helper with the patch above in scenario A that
commits established traffic, but not in B or C that only commits new traffic.
Best regards, Ilya Maximets.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
2026-05-04 12:19 ` Ilya Maximets
@ 2026-05-04 23:16 ` Pablo Neira Ayuso
2026-05-04 23:40 ` Pablo Neira Ayuso
2026-05-05 11:01 ` Ilya Maximets
0 siblings, 2 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-05-04 23:16 UTC (permalink / raw)
To: Ilya Maximets
Cc: netfilter-devel, fw, davem, netdev, kuba, pabeni, edumazet, horms,
Eelco Chaudron, Aaron Conole
[-- Attachment #1: Type: text/plain, Size: 7383 bytes --]
Hi Ilya,
On Mon, May 04, 2026 at 02:19:20PM +0200, Ilya Maximets wrote:
> On 5/1/26 12:37 PM, Pablo Neira Ayuso wrote:
> > Hi Ilya,
> >
> > On Thu, Apr 30, 2026 at 10:58:38PM +0200, Ilya Maximets wrote:
> >> On 3/26/26 1:51 PM, Pablo Neira Ayuso wrote:
> >>> The expectation helper field is mostly unused. As a result, the
> >>> netfilter codebase relies on accessing the helper through exp->master.
> >>>
> >>> Always set on the expectation helper field so it can be used to reach
> >>> the helper.
> >>>
> >>> nf_ct_expect_init() is called from packet path where the skb owns
> >>> the ct object, therefore accessing exp->master for the newly created
> >>> expectation is safe. This saves a lot of updates in all callsites
> >>> to pass the ct object as parameter to nf_ct_expect_init().
> >>>
> >>> This is a preparation patches for follow up fixes.
> >>>
> >>> Signed-off-by: Florian Westphal <fw@strlen.de>
> >>> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> >>> ---
> >>
> >> Hi, Pablo and Florian.
> >>
> >> I was investigating FTP test failures in OVS with 7.0 kernel and bisected
> >> the issue down to this commit. AFAIU, with this change all the related
> >> connections over time gain their parents' helpers,. This is causing a change
> >> visible to the userspace, because FTP data connections are now reported to
> >> have helpers in the conntrack dump:
> >>
> >> # conntrack -L
> >> tcp 6 119 TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=59534 dport=21 \
> >> src=10.1.1.2 dst=10.1.1.1 sport=21 dport=59534 \
> >> [ASSURED] mark=0 helper=ftp use=2
> >> tcp 6 119 TIME_WAIT src=10.1.1.2 dst=10.1.1.1 sport=52709 dport=52381 \
> >> src=10.1.1.1 dst=10.1.1.2 sport=52381 dport=52709 \
> >> [ASSURED] mark=0 helper=ftp use=1
> >>
> >> Before this commit only the control connection had helper=ftp reported in
> >> the dump. The traffic seems to work fine, but our tests fail because we
> >> do not expect the helper attached.
> >>
> >> AFAIU, it's generally not something that should be happening, as helpers
> >> on data connections do not really make much sense. But I'm just trying to
> >> figure out if you would consider this as a regression and fix in the kernel
> >> or if we should adjust our userspace components for this new dump content,
> >> which would not be very straightforward to do if we want to be able to run
> >> tests on both old and the new versions.
> >>
> >> What do you think?
> >
> > It seems previous behaviour to 9c42bc9db90a was inconsistent, ie. only
> > the h323 helper sets on exp->helper, then it shows helper= in expected
> > connections via ctnetlink. I guess this is for debugging given that
> > h323 is actually a family of helpers.
> >
> > To consistently skip dumping this for expected connections, probably
> > this is the way to do:
> >
> > diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conn
> > index eda5fe4a75c8..9491ae9e080e 100644
> > --- a/net/netfilter/nf_conntrack_netlink.c
> > +++ b/net/netfilter/nf_conntrack_netlink.c
> > @@ -226,7 +226,7 @@ static int ctnetlink_dump_helpinfo(struct sk_buff *sk
> > const struct nf_conn_help *help = nfct_help(ct);
> > struct nf_conntrack_helper *helper;
> >
> > - if (!help)
> > + if (!help || ct->status & IPS_EXPECTED)
> > return 0;
> >
> > rcu_read_lock();
>
> I'm not sure. I tried this change and it fixed one case but broke another.
> Looking at what we're testing, the old behavior (at least for FTP) was:
> "if helper was committed - report it, if not - don't". i.e. it's not really
> about the connection being expected it's about if the user committed the
> helper for the connection or not.
>
> Let me explain a few scenarios that we have in the OVS system tests and what
> I see with the old kernel (6.19), the new (7.0) and the patch above.
>
> A) The first scenario has the following OpenFlow rules (simplified):
>
> table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
> table=0,in_port=2,tcp,action=ct(table=1)
> table=1,in_port=2,tcp,ct_state=+trk+est,action=1
> table=1,in_port=2,tcp,ct_state=+trk+rel,action=1
>
> This set of rule blindly commits every packet coming from port 1 with the
> helper and sends to port 2. Packets from port 2 are passed through ct and
> only related or established traffic is passed to port 1. This is a very
> rudimentary setup that users can make to allow ftp from port 1 towards port 2,
> but not in the opposite direction.
>
> For this scenario regardless of the kernel version or the patch above I see
> that both the data and the control connections have a helper reported in the
> ctnetlink dump.
This ruleset then is attached the conntrack helper to data connection,
that is, ALG is inspecting the FTP data connection but it will just
find no patterns because it is only the FTP control connection that
creates expectations?
> B) The second scenario:
>
> table=0,in_port=1,tcp,action=ct(table=1)
> table=1,in_port=1,tcp,ct_state=+trk+new,action=ct(commit,alg=ftp),2
> table=1,in_port=1,tcp,ct_state=+trk+est,action=2
>
> table=0,in_port=2,tcp,action=ct(table=1)
> table=1,in_port=2,tcp,ct_state=+trk+new+rel,action=ct(commit),1
> table=1,in_port=2,tcp,ct_state=+trk+est,action=1
>
> This is a more reasonable setup where new connections are committed on the
> way from 1 to 2 and new related connections are committed on the way from
> 2 to 1. This allows port 1 to initiate the control connection and the port
> 2 to initiate the related data connection.
>
> In case of active FTP (port 1 initiates control, port 2 initiates the data):
> - old: control has the helper, data does not.
> - new: both have the helper.
> - patch: control has the helper, data does not.
>
> In case of passive FTP (port 1 initiates both the control and data):
> - old: both have the helper (because both are +new traffic from port 1).
> - new: both have the helper.
> - patch: control has the helper, data does not.
>
> C) We can modify the scenario B to avoid committing the helper on related:
>
> table=0,in_port=1,tcp,action=ct(table=1)
> table=1,in_port=1,tcp,ct_state=+trk+new-rel,action=ct(commit,alg=ftp),2
> table=1,in_port=1,tcp,ct_state=+trk+new+rel,action=ct(commit),2
> table=1,in_port=1,tcp,ct_state=+trk+est,action=2
>
> table=0,in_port=2,tcp,action=ct(table=1)
> table=1,in_port=2,tcp,ct_state=+trk+new+rel,action=ct(commit),1
> table=1,in_port=2,tcp,ct_state=+trk+est,action=1
>
> Here we have (same for active and passive):
> - old: control has the helper, data does not.
> - new: both have the helper.
> - patch: control has the helper, data does not.
>
> I hope that clarifies the situation a little bit.
>
> So, if we want to restore the old behavior, the we'd probably need to track
> how connection gained the helper, i.e. was it via commit or was it inherited.
>
> I'm also not sure why we see the helper with the patch above in scenario A that
> commits established traffic, but not in B or C that only commits new traffic.
Thanks for the detailed report. It seems I changed the semantics of
exp->helper, this used to be use to set a new helper for an expected
connection, which is the case for sip and h323.
Would this patch help address the issue you are observing?
Thanks.
[-- Attachment #2: fix-exp-helper.patch --]
[-- Type: text/x-diff, Size: 5017 bytes --]
commit 39b6894dd511e14450e2a640eeaa83379e0f8eb1
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue May 5 01:15:19 2026 +0200
x
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index e9a8350e7ccf..80f50fd0f7ad 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -45,9 +45,12 @@ struct nf_conntrack_expect {
void (*expectfn)(struct nf_conn *new,
struct nf_conntrack_expect *this);
- /* Helper to assign to new connection */
+ /* Helper that created this expectation */
struct nf_conntrack_helper __rcu *helper;
+ /* Helper to assign to new connection */
+ struct nf_conntrack_helper __rcu *assign_helper;
+
/* The conntrack of the master connection */
struct nf_conn *master;
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index b08189226320..656287d16d86 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -1815,10 +1815,10 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
__set_bit(IPS_EXPECTED_BIT, &ct->status);
/* exp->master safe, refcnt bumped in nf_ct_find_expectation */
ct->master = exp->master;
- if (exp->helper) {
+ if (exp->assign_helper) {
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help)
- rcu_assign_pointer(help->helper, exp->helper);
+ rcu_assign_pointer(help->helper, exp->assign_helper);
}
#ifdef CONFIG_NF_CONNTRACK_MARK
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index 3f5c50455b71..b2fe6554b9cf 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -643,7 +643,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
- rcu_assign_pointer(exp->helper, &nf_conntrack_helper_h245);
+ rcu_assign_pointer(exp->assign_helper, &nf_conntrack_helper_h245);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -767,7 +767,7 @@ static int expect_callforwarding(struct sk_buff *skb,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -1234,7 +1234,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3 : NULL,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */
nathook = rcu_dereference(nfct_h323_nat_hook);
@@ -1306,7 +1306,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_UDP, NULL, &port);
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_ras);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect RAS ");
@@ -1523,7 +1523,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");
@@ -1577,7 +1577,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 1eb55907d470..d24bfa9e8234 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -1383,7 +1383,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct),
saddr, &daddr, proto, NULL, &port);
exp->timeout.expires = sip_timeout * HZ;
- rcu_assign_pointer(exp->helper, helper);
+ rcu_assign_pointer(exp->assign_helper, helper);
exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
hooks = rcu_dereference(nf_nat_sip_hooks);
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
2026-05-04 23:16 ` Pablo Neira Ayuso
@ 2026-05-04 23:40 ` Pablo Neira Ayuso
2026-05-05 11:01 ` Ilya Maximets
2026-05-05 11:01 ` Ilya Maximets
1 sibling, 1 reply; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-05-04 23:40 UTC (permalink / raw)
To: Ilya Maximets
Cc: netfilter-devel, fw, davem, netdev, kuba, pabeni, edumazet, horms,
Eelco Chaudron, Aaron Conole
[-- Attachment #1: Type: text/plain, Size: 418 bytes --]
On Tue, May 05, 2026 at 01:16:05AM +0200, Pablo Neira Ayuso wrote:
> Thanks for the detailed report. It seems I changed the semantics of
> exp->helper, this used to be use to set a new helper for an expected
> connection, which is the case for sip and h323.
>
> Would this patch help address the issue you are observing?
Actually, this needs to set to NULL the new exp->assign_helper field,
see new patch, untested.
[-- Attachment #2: fix-exp-helper.patch --]
[-- Type: text/x-diff, Size: 5296 bytes --]
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index e9a8350e7ccf..80f50fd0f7ad 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -45,9 +45,12 @@ struct nf_conntrack_expect {
void (*expectfn)(struct nf_conn *new,
struct nf_conntrack_expect *this);
- /* Helper to assign to new connection */
+ /* Helper that created this expectation */
struct nf_conntrack_helper __rcu *helper;
+ /* Helper to assign to new connection */
+ struct nf_conntrack_helper __rcu *assign_helper;
+
/* The conntrack of the master connection */
struct nf_conn *master;
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index b08189226320..656287d16d86 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -1815,10 +1815,10 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
__set_bit(IPS_EXPECTED_BIT, &ct->status);
/* exp->master safe, refcnt bumped in nf_ct_find_expectation */
ct->master = exp->master;
- if (exp->helper) {
+ if (exp->assign_helper) {
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help)
- rcu_assign_pointer(help->helper, exp->helper);
+ rcu_assign_pointer(help->helper, exp->assign_helper);
}
#ifdef CONFIG_NF_CONNTRACK_MARK
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 24d0576d84b7..b01d48ab351e 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -308,6 +308,7 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
if (!new)
return NULL;
+ new->assign_helper = NULL;
new->master = me;
refcount_set(&new->use, 1);
return new;
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index 3f5c50455b71..b2fe6554b9cf 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -643,7 +643,7 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
- rcu_assign_pointer(exp->helper, &nf_conntrack_helper_h245);
+ rcu_assign_pointer(exp->assign_helper, &nf_conntrack_helper_h245);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -767,7 +767,7 @@ static int expect_callforwarding(struct sk_buff *skb,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
nathook = rcu_dereference(nfct_h323_nat_hook);
if (memcmp(&ct->tuplehash[dir].tuple.src.u3,
@@ -1234,7 +1234,7 @@ static int expect_q931(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3 : NULL,
&ct->tuplehash[!dir].tuple.dst.u3,
IPPROTO_TCP, NULL, &port);
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
exp->flags = NF_CT_EXPECT_PERMANENT; /* Accept multiple calls */
nathook = rcu_dereference(nfct_h323_nat_hook);
@@ -1306,7 +1306,7 @@ static int process_gcf(struct sk_buff *skb, struct nf_conn *ct,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, nf_ct_l3num(ct),
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_UDP, NULL, &port);
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_ras);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_ras);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect RAS ");
@@ -1523,7 +1523,7 @@ static int process_acf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");
@@ -1577,7 +1577,7 @@ static int process_lcf(struct sk_buff *skb, struct nf_conn *ct,
&ct->tuplehash[!dir].tuple.src.u3, &addr,
IPPROTO_TCP, NULL, &port);
exp->flags = NF_CT_EXPECT_PERMANENT;
- rcu_assign_pointer(exp->helper, nf_conntrack_helper_q931);
+ rcu_assign_pointer(exp->assign_helper, nf_conntrack_helper_q931);
if (nf_ct_expect_related(exp, 0) == 0) {
pr_debug("nf_ct_ras: expect Q.931 ");
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 1eb55907d470..d24bfa9e8234 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -1383,7 +1383,7 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
nf_ct_expect_init(exp, SIP_EXPECT_SIGNALLING, nf_ct_l3num(ct),
saddr, &daddr, proto, NULL, &port);
exp->timeout.expires = sip_timeout * HZ;
- rcu_assign_pointer(exp->helper, helper);
+ rcu_assign_pointer(exp->assign_helper, helper);
exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE;
hooks = rcu_dereference(nf_nat_sip_hooks);
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
2026-05-04 23:40 ` Pablo Neira Ayuso
@ 2026-05-05 11:01 ` Ilya Maximets
2026-05-05 11:26 ` Pablo Neira Ayuso
0 siblings, 1 reply; 22+ messages in thread
From: Ilya Maximets @ 2026-05-05 11:01 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: i.maximets, netfilter-devel, fw, davem, netdev, kuba, pabeni,
edumazet, horms, Eelco Chaudron, Aaron Conole
On 5/5/26 1:40 AM, Pablo Neira Ayuso wrote:
> On Tue, May 05, 2026 at 01:16:05AM +0200, Pablo Neira Ayuso wrote:
>> Thanks for the detailed report. It seems I changed the semantics of
>> exp->helper, this used to be use to set a new helper for an expected
>> connection, which is the case for sip and h323.
>>
>> Would this patch help address the issue you are observing?
>
> Actually, this needs to set to NULL the new exp->assign_helper field,
> see new patch, untested.
I ran this through OVS system tests and all passed. So, this restores
the old behavior, at least for FTP (we do not support sip/h323). For
that part:
Tested-by: Ilya Maximets <i.maximets@ovn.org>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
2026-05-05 11:01 ` Ilya Maximets
@ 2026-05-05 11:26 ` Pablo Neira Ayuso
0 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-05-05 11:26 UTC (permalink / raw)
To: Ilya Maximets
Cc: netfilter-devel, fw, davem, netdev, kuba, pabeni, edumazet, horms,
Eelco Chaudron, Aaron Conole
On Tue, May 05, 2026 at 01:01:22PM +0200, Ilya Maximets wrote:
> On 5/5/26 1:40 AM, Pablo Neira Ayuso wrote:
> > On Tue, May 05, 2026 at 01:16:05AM +0200, Pablo Neira Ayuso wrote:
> >> Thanks for the detailed report. It seems I changed the semantics of
> >> exp->helper, this used to be use to set a new helper for an expected
> >> connection, which is the case for sip and h323.
> >>
> >> Would this patch help address the issue you are observing?
> >
> > Actually, this needs to set to NULL the new exp->assign_helper field,
> > see new patch, untested.
>
> I ran this through OVS system tests and all passed. So, this restores
> the old behavior, at least for FTP (we do not support sip/h323). For
> that part:
>
> Tested-by: Ilya Maximets <i.maximets@ovn.org>
Thanks! I will be posting a format patch asap, I will keep you on Cc.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field
2026-05-04 23:16 ` Pablo Neira Ayuso
2026-05-04 23:40 ` Pablo Neira Ayuso
@ 2026-05-05 11:01 ` Ilya Maximets
1 sibling, 0 replies; 22+ messages in thread
From: Ilya Maximets @ 2026-05-05 11:01 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: i.maximets, netfilter-devel, fw, davem, netdev, kuba, pabeni,
edumazet, horms, Eelco Chaudron, Aaron Conole
On 5/5/26 1:16 AM, Pablo Neira Ayuso wrote:
> Hi Ilya,
>
> On Mon, May 04, 2026 at 02:19:20PM +0200, Ilya Maximets wrote:
>> On 5/1/26 12:37 PM, Pablo Neira Ayuso wrote:
>>> Hi Ilya,
>>>
>>> On Thu, Apr 30, 2026 at 10:58:38PM +0200, Ilya Maximets wrote:
>>>> On 3/26/26 1:51 PM, Pablo Neira Ayuso wrote:
>>>>> The expectation helper field is mostly unused. As a result, the
>>>>> netfilter codebase relies on accessing the helper through exp->master.
>>>>>
>>>>> Always set on the expectation helper field so it can be used to reach
>>>>> the helper.
>>>>>
>>>>> nf_ct_expect_init() is called from packet path where the skb owns
>>>>> the ct object, therefore accessing exp->master for the newly created
>>>>> expectation is safe. This saves a lot of updates in all callsites
>>>>> to pass the ct object as parameter to nf_ct_expect_init().
>>>>>
>>>>> This is a preparation patches for follow up fixes.
>>>>>
>>>>> Signed-off-by: Florian Westphal <fw@strlen.de>
>>>>> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
>>>>> ---
>>>>
>>>> Hi, Pablo and Florian.
>>>>
>>>> I was investigating FTP test failures in OVS with 7.0 kernel and bisected
>>>> the issue down to this commit. AFAIU, with this change all the related
>>>> connections over time gain their parents' helpers,. This is causing a change
>>>> visible to the userspace, because FTP data connections are now reported to
>>>> have helpers in the conntrack dump:
>>>>
>>>> # conntrack -L
>>>> tcp 6 119 TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=59534 dport=21 \
>>>> src=10.1.1.2 dst=10.1.1.1 sport=21 dport=59534 \
>>>> [ASSURED] mark=0 helper=ftp use=2
>>>> tcp 6 119 TIME_WAIT src=10.1.1.2 dst=10.1.1.1 sport=52709 dport=52381 \
>>>> src=10.1.1.1 dst=10.1.1.2 sport=52381 dport=52709 \
>>>> [ASSURED] mark=0 helper=ftp use=1
>>>>
>>>> Before this commit only the control connection had helper=ftp reported in
>>>> the dump. The traffic seems to work fine, but our tests fail because we
>>>> do not expect the helper attached.
>>>>
>>>> AFAIU, it's generally not something that should be happening, as helpers
>>>> on data connections do not really make much sense. But I'm just trying to
>>>> figure out if you would consider this as a regression and fix in the kernel
>>>> or if we should adjust our userspace components for this new dump content,
>>>> which would not be very straightforward to do if we want to be able to run
>>>> tests on both old and the new versions.
>>>>
>>>> What do you think?
>>>
>>> It seems previous behaviour to 9c42bc9db90a was inconsistent, ie. only
>>> the h323 helper sets on exp->helper, then it shows helper= in expected
>>> connections via ctnetlink. I guess this is for debugging given that
>>> h323 is actually a family of helpers.
>>>
>>> To consistently skip dumping this for expected connections, probably
>>> this is the way to do:
>>>
>>> diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conn
>>> index eda5fe4a75c8..9491ae9e080e 100644
>>> --- a/net/netfilter/nf_conntrack_netlink.c
>>> +++ b/net/netfilter/nf_conntrack_netlink.c
>>> @@ -226,7 +226,7 @@ static int ctnetlink_dump_helpinfo(struct sk_buff *sk
>>> const struct nf_conn_help *help = nfct_help(ct);
>>> struct nf_conntrack_helper *helper;
>>>
>>> - if (!help)
>>> + if (!help || ct->status & IPS_EXPECTED)
>>> return 0;
>>>
>>> rcu_read_lock();
>>
>> I'm not sure. I tried this change and it fixed one case but broke another.
>> Looking at what we're testing, the old behavior (at least for FTP) was:
>> "if helper was committed - report it, if not - don't". i.e. it's not really
>> about the connection being expected it's about if the user committed the
>> helper for the connection or not.
>>
>> Let me explain a few scenarios that we have in the OVS system tests and what
>> I see with the old kernel (6.19), the new (7.0) and the patch above.
>>
>> A) The first scenario has the following OpenFlow rules (simplified):
>>
>> table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
>> table=0,in_port=2,tcp,action=ct(table=1)
>> table=1,in_port=2,tcp,ct_state=+trk+est,action=1
>> table=1,in_port=2,tcp,ct_state=+trk+rel,action=1
>>
>> This set of rule blindly commits every packet coming from port 1 with the
>> helper and sends to port 2. Packets from port 2 are passed through ct and
>> only related or established traffic is passed to port 1. This is a very
>> rudimentary setup that users can make to allow ftp from port 1 towards port 2,
>> but not in the opposite direction.
>>
>> For this scenario regardless of the kernel version or the patch above I see
>> that both the data and the control connections have a helper reported in the
>> ctnetlink dump.
>
> This ruleset then is attached the conntrack helper to data connection,
> that is, ALG is inspecting the FTP data connection but it will just
> find no patterns because it is only the FTP control connection that
> creates expectations?
Yes. It's just a "lazy" way to make the traffic work, we do not expect
the helper on the data connection to do anything useful in this scenario.
Best regards, Ilya Maximets.
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH net 07/12] netfilter: nf_conntrack_expect: use expect->helper
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
` (5 preceding siblings ...)
2026-03-26 12:51 ` [PATCH net 06/12] netfilter: nf_conntrack_expect: honor expectation helper field Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 08/12] netfilter: ctnetlink: ensure safe access to master conntrack Pablo Neira Ayuso
` (4 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
Use expect->helper in ctnetlink and /proc to dump the helper name.
Using nfct_help() without holding a reference to the master conntrack
is unsafe.
Use exp->master->helper in ctnetlink path if userspace does not provide
an explicit helper when creating an expectation to retain the existing
behaviour. The ctnetlink expectation path holds the reference on the
master conntrack and nf_conntrack_expect lock and the nfnetlink glue
path refers to the master ct that is attached to the skb.
Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_conntrack_expect.c | 2 +-
net/netfilter/nf_conntrack_helper.c | 6 +-----
net/netfilter/nf_conntrack_netlink.c | 24 ++++++++++--------------
net/netfilter/nf_conntrack_sip.c | 2 +-
4 files changed, 13 insertions(+), 21 deletions(-)
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 841e316240da..64977db12b1d 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -666,7 +666,7 @@ static int exp_seq_show(struct seq_file *s, void *v)
if (expect->flags & NF_CT_EXPECT_USERSPACE)
seq_printf(s, "%sUSERSPACE", delim);
- helper = rcu_dereference(nfct_help(expect->master)->helper);
+ helper = rcu_dereference(expect->helper);
if (helper) {
seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
if (helper->expect_policy[expect->class].name[0])
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index 294a6ffcbccd..1b330ba6613b 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -395,14 +395,10 @@ EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data)
{
- struct nf_conn_help *help = nfct_help(exp->master);
const struct nf_conntrack_helper *me = data;
const struct nf_conntrack_helper *this;
- if (rcu_access_pointer(exp->helper) == me)
- return true;
-
- this = rcu_dereference_protected(help->helper,
+ this = rcu_dereference_protected(exp->helper,
lockdep_is_held(&nf_conntrack_expect_lock));
return this == me;
}
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index a42d14290786..8477c3736432 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3012,7 +3012,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
{
struct nf_conn *master = exp->master;
long timeout = ((long)exp->timeout.expires - (long)jiffies) / HZ;
- struct nf_conn_help *help;
+ struct nf_conntrack_helper *helper;
#if IS_ENABLED(CONFIG_NF_NAT)
struct nlattr *nest_parms;
struct nf_conntrack_tuple nat_tuple = {};
@@ -3057,15 +3057,12 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
nla_put_be32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags)) ||
nla_put_be32(skb, CTA_EXPECT_CLASS, htonl(exp->class)))
goto nla_put_failure;
- help = nfct_help(master);
- if (help) {
- struct nf_conntrack_helper *helper;
- helper = rcu_dereference(help->helper);
- if (helper &&
- nla_put_string(skb, CTA_EXPECT_HELP_NAME, helper->name))
- goto nla_put_failure;
- }
+ helper = rcu_dereference(exp->helper);
+ if (helper &&
+ nla_put_string(skb, CTA_EXPECT_HELP_NAME, helper->name))
+ goto nla_put_failure;
+
expfn = nf_ct_helper_expectfn_find_by_symbol(exp->expectfn);
if (expfn != NULL &&
nla_put_string(skb, CTA_EXPECT_FN, expfn->name))
@@ -3394,12 +3391,9 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
static bool expect_iter_name(struct nf_conntrack_expect *exp, void *data)
{
struct nf_conntrack_helper *helper;
- const struct nf_conn_help *m_help;
const char *name = data;
- m_help = nfct_help(exp->master);
-
- helper = rcu_dereference(m_help->helper);
+ helper = rcu_dereference(exp->helper);
if (!helper)
return false;
@@ -3534,9 +3528,9 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
struct nf_conntrack_tuple *tuple,
struct nf_conntrack_tuple *mask)
{
- u_int32_t class = 0;
struct nf_conntrack_expect *exp;
struct nf_conn_help *help;
+ u32 class = 0;
int err;
help = nfct_help(ct);
@@ -3573,6 +3567,8 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
exp->class = class;
exp->master = ct;
+ if (!helper)
+ helper = rcu_dereference(help->helper);
rcu_assign_pointer(exp->helper, helper);
exp->tuple = *tuple;
exp->mask.src.u3 = mask->src.u3;
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 106b2f419e19..20e57cf5c83a 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -924,7 +924,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple);
if (!exp || exp->master == ct ||
- nfct_help(exp->master)->helper != nfct_help(ct)->helper ||
+ exp->helper != nfct_help(ct)->helper ||
exp->class != class)
break;
#if IS_ENABLED(CONFIG_NF_NAT)
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH net 08/12] netfilter: ctnetlink: ensure safe access to master conntrack
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
` (6 preceding siblings ...)
2026-03-26 12:51 ` [PATCH net 07/12] netfilter: nf_conntrack_expect: use expect->helper Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 09/12] netfilter: nf_conntrack_expect: store netns and zone in expectation Pablo Neira Ayuso
` (3 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
Holding reference on the expectation is not sufficient, the master
conntrack object can just go away, making exp->master invalid.
To access exp->master safely:
- Grab the nf_conntrack_expect_lock, this gets serialized with
clean_from_lists() which also holds this lock when the master
conntrack goes away.
- Hold reference on master conntrack via nf_conntrack_find_get().
Not so easy since the master tuple to look up for the master conntrack
is not available in the existing problematic paths.
This patch goes for extending the nf_conntrack_expect_lock section
to address this issue for simplicity, in the cases that are described
below this is just slightly extending the lock section.
The add expectation command already holds a reference to the master
conntrack from ctnetlink_create_expect().
However, the delete expectation command needs to grab the spinlock
before looking up for the expectation. Expand the existing spinlock
section to address this to cover the expectation lookup. Note that,
the nf_ct_expect_iterate_net() calls already grabs the spinlock while
iterating over the expectation table, which is correct.
The get expectation command needs to grab the spinlock to ensure master
conntrack does not go away. This also expands the existing spinlock
section to cover the expectation lookup too. I needed to move the
netlink skb allocation out of the spinlock to keep it GFP_KERNEL.
For the expectation events, the IPEXP_DESTROY event is already delivered
under the spinlock, just move the delivery of IPEXP_NEW under the
spinlock too because the master conntrack event cache is reached through
exp->master.
While at it, add lockdep notations to help identify what codepaths need
to grab the spinlock.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
include/net/netfilter/nf_conntrack_core.h | 5 ++++
net/netfilter/nf_conntrack_ecache.c | 2 ++
net/netfilter/nf_conntrack_expect.c | 10 +++++++-
net/netfilter/nf_conntrack_netlink.c | 28 +++++++++++++++--------
4 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index 3384859a8921..8883575adcc1 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -83,6 +83,11 @@ void nf_conntrack_lock(spinlock_t *lock);
extern spinlock_t nf_conntrack_expect_lock;
+static inline void lockdep_nfct_expect_lock_held(void)
+{
+ lockdep_assert_held(&nf_conntrack_expect_lock);
+}
+
/* ctnetlink code shared by both ctnetlink and nf_conntrack_bpf */
static inline void __nf_ct_set_timeout(struct nf_conn *ct, u64 timeout)
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index 81baf2082604..9df159448b89 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -247,6 +247,8 @@ void nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
struct nf_ct_event_notifier *notify;
struct nf_conntrack_ecache *e;
+ lockdep_nfct_expect_lock_held();
+
rcu_read_lock();
notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
if (!notify)
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 64977db12b1d..1cbe5f1108c2 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -51,6 +51,7 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
struct net *net = nf_ct_exp_net(exp);
struct nf_conntrack_net *cnet;
+ lockdep_nfct_expect_lock_held();
WARN_ON(!master_help);
WARN_ON(timer_pending(&exp->timeout));
@@ -118,6 +119,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
{
+ lockdep_nfct_expect_lock_held();
+
if (timer_delete(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_ct_expect_put(exp);
@@ -177,6 +180,8 @@ nf_ct_find_expectation(struct net *net,
struct nf_conntrack_expect *i, *exp = NULL;
unsigned int h;
+ lockdep_nfct_expect_lock_held();
+
if (!cnet->expect_count)
return NULL;
@@ -454,6 +459,8 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect,
unsigned int h;
int ret = 0;
+ lockdep_nfct_expect_lock_held();
+
if (!master_help) {
ret = -ESHUTDOWN;
goto out;
@@ -510,8 +517,9 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
nf_ct_expect_insert(expect);
- spin_unlock_bh(&nf_conntrack_expect_lock);
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+
return 0;
out:
spin_unlock_bh(&nf_conntrack_expect_lock);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 8477c3736432..89540112d165 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3355,31 +3355,37 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
if (err < 0)
return err;
+ skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb2)
+ return -ENOMEM;
+
+ spin_lock_bh(&nf_conntrack_expect_lock);
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
- if (!exp)
+ if (!exp) {
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+ kfree_skb(skb2);
return -ENOENT;
+ }
if (cda[CTA_EXPECT_ID]) {
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
if (id != nf_expect_get_id(exp)) {
nf_ct_expect_put(exp);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+ kfree_skb(skb2);
return -ENOENT;
}
}
- skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!skb2) {
- nf_ct_expect_put(exp);
- return -ENOMEM;
- }
-
rcu_read_lock();
err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).portid,
info->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
exp);
rcu_read_unlock();
nf_ct_expect_put(exp);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+
if (err <= 0) {
kfree_skb(skb2);
return -ENOMEM;
@@ -3426,22 +3432,26 @@ static int ctnetlink_del_expect(struct sk_buff *skb,
if (err < 0)
return err;
+ spin_lock_bh(&nf_conntrack_expect_lock);
+
/* bump usage count to 2 */
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
- if (!exp)
+ if (!exp) {
+ spin_unlock_bh(&nf_conntrack_expect_lock);
return -ENOENT;
+ }
if (cda[CTA_EXPECT_ID]) {
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
if (id != nf_expect_get_id(exp)) {
nf_ct_expect_put(exp);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
return -ENOENT;
}
}
/* after list removal, usage count == 1 */
- spin_lock_bh(&nf_conntrack_expect_lock);
if (timer_delete(&exp->timeout)) {
nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid,
nlmsg_report(info->nlh));
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH net 09/12] netfilter: nf_conntrack_expect: store netns and zone in expectation
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
` (7 preceding siblings ...)
2026-03-26 12:51 ` [PATCH net 08/12] netfilter: ctnetlink: ensure safe access to master conntrack Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 10/12] netfilter: nf_conntrack_expect: skip expectations in other netns via proc Pablo Neira Ayuso
` (2 subsequent siblings)
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
__nf_ct_expect_find() and nf_ct_expect_find_get() are called under
rcu_read_lock() but they dereference the master conntrack via
exp->master.
Since the expectation does not hold a reference on the master conntrack,
this could be dying conntrack or different recycled conntrack than the
real master due to SLAB_TYPESAFE_RCU.
Store the netns, the master_tuple and the zone in struct
nf_conntrack_expect as a safety measure.
This patch is required by the follow up fix not to dump expectations
that do not belong to this netns.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
include/net/netfilter/nf_conntrack_expect.h | 18 +++++++++++++++++-
net/netfilter/nf_conntrack_broadcast.c | 6 +++++-
net/netfilter/nf_conntrack_expect.c | 9 +++++++--
net/netfilter/nf_conntrack_netlink.c | 5 +++++
4 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index 1b01400b10bd..e9a8350e7ccf 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -22,10 +22,16 @@ struct nf_conntrack_expect {
/* Hash member */
struct hlist_node hnode;
+ /* Network namespace */
+ possible_net_t net;
+
/* We expect this tuple, with the following mask */
struct nf_conntrack_tuple tuple;
struct nf_conntrack_tuple_mask mask;
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ struct nf_conntrack_zone zone;
+#endif
/* Usage count. */
refcount_t use;
@@ -62,7 +68,17 @@ struct nf_conntrack_expect {
static inline struct net *nf_ct_exp_net(struct nf_conntrack_expect *exp)
{
- return nf_ct_net(exp->master);
+ return read_pnet(&exp->net);
+}
+
+static inline bool nf_ct_exp_zone_equal_any(const struct nf_conntrack_expect *a,
+ const struct nf_conntrack_zone *b)
+{
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ return a->zone.id == b->id;
+#else
+ return true;
+#endif
}
#define NF_CT_EXP_POLICY_NAME_LEN 16
diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c
index 1964c596c646..4f39bf7c843f 100644
--- a/net/netfilter/nf_conntrack_broadcast.c
+++ b/net/netfilter/nf_conntrack_broadcast.c
@@ -21,6 +21,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
unsigned int timeout)
{
const struct nf_conntrack_helper *helper;
+ struct net *net = read_pnet(&ct->ct_net);
struct nf_conntrack_expect *exp;
struct iphdr *iph = ip_hdr(skb);
struct rtable *rt = skb_rtable(skb);
@@ -71,7 +72,10 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb,
exp->flags = NF_CT_EXPECT_PERMANENT;
exp->class = NF_CT_EXPECT_CLASS_DEFAULT;
rcu_assign_pointer(exp->helper, helper);
-
+ write_pnet(&exp->net, net);
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ exp->zone = ct->zone;
+#endif
nf_ct_expect_related(exp, 0);
nf_ct_expect_put(exp);
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 1cbe5f1108c2..db28801b1688 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -113,8 +113,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
const struct net *net)
{
return nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
- net_eq(net, nf_ct_net(i->master)) &&
- nf_ct_zone_equal_any(i->master, zone);
+ net_eq(net, read_pnet(&i->net)) &&
+ nf_ct_exp_zone_equal_any(i, zone);
}
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
@@ -326,6 +326,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
{
struct nf_conntrack_helper *helper = NULL;
struct nf_conn *ct = exp->master;
+ struct net *net = read_pnet(&ct->ct_net);
struct nf_conn_help *help;
int len;
@@ -343,6 +344,10 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
helper = rcu_dereference(help->helper);
rcu_assign_pointer(exp->helper, helper);
+ write_pnet(&exp->net, net);
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ exp->zone = ct->zone;
+#endif
exp->tuple.src.l3num = family;
exp->tuple.dst.protonum = proto;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 89540112d165..6e6aeb0ab0a1 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3538,6 +3538,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
struct nf_conntrack_tuple *tuple,
struct nf_conntrack_tuple *mask)
{
+ struct net *net = read_pnet(&ct->ct_net);
struct nf_conntrack_expect *exp;
struct nf_conn_help *help;
u32 class = 0;
@@ -3577,6 +3578,10 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
exp->class = class;
exp->master = ct;
+ write_pnet(&exp->net, net);
+#ifdef CONFIG_NF_CONNTRACK_ZONES
+ exp->zone = ct->zone;
+#endif
if (!helper)
helper = rcu_dereference(help->helper);
rcu_assign_pointer(exp->helper, helper);
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH net 10/12] netfilter: nf_conntrack_expect: skip expectations in other netns via proc
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
` (8 preceding siblings ...)
2026-03-26 12:51 ` [PATCH net 09/12] netfilter: nf_conntrack_expect: store netns and zone in expectation Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 11/12] netfilter: nf_conntrack_sip: fix use of uninitialized rtp_addr in process_sdp Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 12/12] netfilter: ctnetlink: use netlink policy range checks Pablo Neira Ayuso
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
Skip expectations that do not reside in this netns.
Similar to e77e6ff502ea ("netfilter: conntrack: do not dump other netns's
conntrack entries via proc").
Fixes: 9b03f38d0487 ("netfilter: netns nf_conntrack: per-netns expectations")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_conntrack_expect.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index db28801b1688..24d0576d84b7 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -652,11 +652,15 @@ static int exp_seq_show(struct seq_file *s, void *v)
{
struct nf_conntrack_expect *expect;
struct nf_conntrack_helper *helper;
+ struct net *net = seq_file_net(s);
struct hlist_node *n = v;
char *delim = "";
expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
+ if (!net_eq(nf_ct_exp_net(expect), net))
+ return 0;
+
if (expect->timeout.function)
seq_printf(s, "%ld ", timer_pending(&expect->timeout)
? (long)(expect->timeout.expires - jiffies)/HZ : 0);
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH net 11/12] netfilter: nf_conntrack_sip: fix use of uninitialized rtp_addr in process_sdp
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
` (9 preceding siblings ...)
2026-03-26 12:51 ` [PATCH net 10/12] netfilter: nf_conntrack_expect: skip expectations in other netns via proc Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
2026-03-26 12:51 ` [PATCH net 12/12] netfilter: ctnetlink: use netlink policy range checks Pablo Neira Ayuso
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
From: Weiming Shi <bestswngs@gmail.com>
process_sdp() declares union nf_inet_addr rtp_addr on the stack and
passes it to the nf_nat_sip sdp_session hook after walking the SDP
media descriptions. However rtp_addr is only initialized inside the
media loop when a recognized media type with a non-zero port is found.
If the SDP body contains no m= lines, only inactive media sections
(m=audio 0 ...) or only unrecognized media types, rtp_addr is never
assigned. Despite that, the function still calls hooks->sdp_session()
with &rtp_addr, causing nf_nat_sdp_session() to format the stale stack
value as an IP address and rewrite the SDP session owner and connection
lines with it.
With CONFIG_INIT_STACK_ALL_ZERO (default on most distributions) this
results in the session-level o= and c= addresses being rewritten to
0.0.0.0 for inactive SDP sessions. Without stack auto-init the
rewritten address is whatever happened to be on the stack.
Fix this by pre-initializing rtp_addr from the session-level connection
address (caddr) when available, and tracking via a have_rtp_addr flag
whether any valid address was established. Skip the sdp_session hook
entirely when no valid address exists.
Fixes: 4ab9e64e5e3c ("[NETFILTER]: nf_nat_sip: split up SDP mangling")
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_conntrack_sip.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 20e57cf5c83a..939502ff7c87 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -1040,6 +1040,7 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
unsigned int port;
const struct sdp_media_type *t;
int ret = NF_ACCEPT;
+ bool have_rtp_addr = false;
hooks = rcu_dereference(nf_nat_sip_hooks);
@@ -1056,8 +1057,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
caddr_len = 0;
if (ct_sip_parse_sdp_addr(ct, *dptr, sdpoff, *datalen,
SDP_HDR_CONNECTION, SDP_HDR_MEDIA,
- &matchoff, &matchlen, &caddr) > 0)
+ &matchoff, &matchlen, &caddr) > 0) {
caddr_len = matchlen;
+ memcpy(&rtp_addr, &caddr, sizeof(rtp_addr));
+ have_rtp_addr = true;
+ }
mediaoff = sdpoff;
for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) {
@@ -1091,9 +1095,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
&matchoff, &matchlen, &maddr) > 0) {
maddr_len = matchlen;
memcpy(&rtp_addr, &maddr, sizeof(rtp_addr));
- } else if (caddr_len)
+ have_rtp_addr = true;
+ } else if (caddr_len) {
memcpy(&rtp_addr, &caddr, sizeof(rtp_addr));
- else {
+ have_rtp_addr = true;
+ } else {
nf_ct_helper_log(skb, ct, "cannot parse SDP message");
return NF_DROP;
}
@@ -1125,7 +1131,7 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
/* Update session connection and owner addresses */
hooks = rcu_dereference(nf_nat_sip_hooks);
- if (hooks && ct->status & IPS_NAT_MASK)
+ if (hooks && ct->status & IPS_NAT_MASK && have_rtp_addr)
ret = hooks->sdp_session(skb, protoff, dataoff,
dptr, datalen, sdpoff,
&rtp_addr);
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH net 12/12] netfilter: ctnetlink: use netlink policy range checks
2026-03-26 12:51 [PATCH net,v3 00/12] Netfilter for net Pablo Neira Ayuso
` (10 preceding siblings ...)
2026-03-26 12:51 ` [PATCH net 11/12] netfilter: nf_conntrack_sip: fix use of uninitialized rtp_addr in process_sdp Pablo Neira Ayuso
@ 2026-03-26 12:51 ` Pablo Neira Ayuso
11 siblings, 0 replies; 22+ messages in thread
From: Pablo Neira Ayuso @ 2026-03-26 12:51 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, fw, horms
From: David Carlier <devnexen@gmail.com>
Replace manual range and mask validations with netlink policy
annotations in ctnetlink code paths, so that the netlink core rejects
invalid values early and can generate extack errors.
- CTA_PROTOINFO_TCP_STATE: reject values > TCP_CONNTRACK_SYN_SENT2 at
policy level, removing the manual >= TCP_CONNTRACK_MAX check.
- CTA_PROTOINFO_TCP_WSCALE_ORIGINAL/REPLY: reject values > TCP_MAX_WSCALE
(14). The normal TCP option parsing path already clamps to this value,
but the ctnetlink path accepted 0-255, causing undefined behavior when
used as a u32 shift count.
- CTA_FILTER_ORIG_FLAGS/REPLY_FLAGS: use NLA_POLICY_MASK with
CTA_FILTER_F_ALL, removing the manual mask checks.
- CTA_EXPECT_FLAGS: use NLA_POLICY_MASK with NF_CT_EXPECT_MASK, adding
a new mask define grouping all valid expect flags.
Extracted from a broader nf-next patch by Florian Westphal, scoped to
ctnetlink for the fixes tree.
Fixes: c8e2078cfe41 ("[NETFILTER]: ctnetlink: add support for internal tcp connection tracking flags handling")
Signed-off-by: David Carlier <devnexen@gmail.com>
Co-developed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
.../uapi/linux/netfilter/nf_conntrack_common.h | 4 ++++
net/netfilter/nf_conntrack_netlink.c | 16 +++++-----------
net/netfilter/nf_conntrack_proto_tcp.c | 10 +++-------
3 files changed, 12 insertions(+), 18 deletions(-)
diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h
index 26071021e986..56b6b60a814f 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_common.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_common.h
@@ -159,5 +159,9 @@ enum ip_conntrack_expect_events {
#define NF_CT_EXPECT_INACTIVE 0x2
#define NF_CT_EXPECT_USERSPACE 0x4
+#ifdef __KERNEL__
+#define NF_CT_EXPECT_MASK (NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE | \
+ NF_CT_EXPECT_USERSPACE)
+#endif
#endif /* _UAPI_NF_CONNTRACK_COMMON_H */
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 6e6aeb0ab0a1..3f408f3713bb 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -910,8 +910,8 @@ struct ctnetlink_filter {
};
static const struct nla_policy cta_filter_nla_policy[CTA_FILTER_MAX + 1] = {
- [CTA_FILTER_ORIG_FLAGS] = { .type = NLA_U32 },
- [CTA_FILTER_REPLY_FLAGS] = { .type = NLA_U32 },
+ [CTA_FILTER_ORIG_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL),
+ [CTA_FILTER_REPLY_FLAGS] = NLA_POLICY_MASK(NLA_U32, CTA_FILTER_F_ALL),
};
static int ctnetlink_parse_filter(const struct nlattr *attr,
@@ -925,17 +925,11 @@ static int ctnetlink_parse_filter(const struct nlattr *attr,
if (ret)
return ret;
- if (tb[CTA_FILTER_ORIG_FLAGS]) {
+ if (tb[CTA_FILTER_ORIG_FLAGS])
filter->orig_flags = nla_get_u32(tb[CTA_FILTER_ORIG_FLAGS]);
- if (filter->orig_flags & ~CTA_FILTER_F_ALL)
- return -EOPNOTSUPP;
- }
- if (tb[CTA_FILTER_REPLY_FLAGS]) {
+ if (tb[CTA_FILTER_REPLY_FLAGS])
filter->reply_flags = nla_get_u32(tb[CTA_FILTER_REPLY_FLAGS]);
- if (filter->reply_flags & ~CTA_FILTER_F_ALL)
- return -EOPNOTSUPP;
- }
return 0;
}
@@ -2634,7 +2628,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
[CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING,
.len = NF_CT_HELPER_NAME_LEN - 1 },
[CTA_EXPECT_ZONE] = { .type = NLA_U16 },
- [CTA_EXPECT_FLAGS] = { .type = NLA_U32 },
+ [CTA_EXPECT_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NF_CT_EXPECT_MASK),
[CTA_EXPECT_CLASS] = { .type = NLA_U32 },
[CTA_EXPECT_NAT] = { .type = NLA_NESTED },
[CTA_EXPECT_FN] = { .type = NLA_NUL_STRING },
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index 0c1d086e96cb..b67426c2189b 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -1385,9 +1385,9 @@ static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
}
static const struct nla_policy tcp_nla_policy[CTA_PROTOINFO_TCP_MAX+1] = {
- [CTA_PROTOINFO_TCP_STATE] = { .type = NLA_U8 },
- [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NLA_U8 },
- [CTA_PROTOINFO_TCP_WSCALE_REPLY] = { .type = NLA_U8 },
+ [CTA_PROTOINFO_TCP_STATE] = NLA_POLICY_MAX(NLA_U8, TCP_CONNTRACK_SYN_SENT2),
+ [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = NLA_POLICY_MAX(NLA_U8, TCP_MAX_WSCALE),
+ [CTA_PROTOINFO_TCP_WSCALE_REPLY] = NLA_POLICY_MAX(NLA_U8, TCP_MAX_WSCALE),
[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = { .len = sizeof(struct nf_ct_tcp_flags) },
[CTA_PROTOINFO_TCP_FLAGS_REPLY] = { .len = sizeof(struct nf_ct_tcp_flags) },
};
@@ -1414,10 +1414,6 @@ static int nlattr_to_tcp(struct nlattr *cda[], struct nf_conn *ct)
if (err < 0)
return err;
- if (tb[CTA_PROTOINFO_TCP_STATE] &&
- nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]) >= TCP_CONNTRACK_MAX)
- return -EINVAL;
-
spin_lock_bh(&ct->lock);
if (tb[CTA_PROTOINFO_TCP_STATE])
ct->proto.tcp.state = nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]);
--
2.47.3
^ permalink raw reply related [flat|nested] 22+ messages in thread