* [PATCH net 00/10] Netfilter/IPVS fixes for net
@ 2026-10-19 15:02 Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 01/10] netfilter: nf_conntrack: avoid truncating IPv6 nexthdr offset Pablo Neira Ayuso
` (9 more replies)
0 siblings, 10 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
Hi,
The following patchset contains Netfilter/IPVS for net:
1) Fix IPv6 nexthdr offset truncation in fragment reassembly,
this has been uncovered by LLM, from Jeremy Jean.
2) Restrict checksums in nfqueue to known protocols and offset,
after this patch the plan is to re-enable nfqueue in userns,
this came via LLM report. from Florian Westhphal.
3) Fix races when handling overload flag in IPVS, this patch
is not perfect but Julian considers this improves the situation.
This was uncovered by LLM. From Julian Anastasov.
4) Error handling for IPVS sync_threshold sysctl, from Joel Granados.
5) Use skb_vlan_tag_get_id() to retrieve the VLAN id, discard tci
bits, when handling VLAN in the flowtable, from Shuangfeng He.
6) Initialize IPCB for inet/ingress reject, uncovered by LLM.
7) Restrict completion of binary search array to anonymous set.
8) Restrict DCCP option matching to avoid out-of-bound read,
from Sung Byeongchan.
9) Reset transport header offset in flowtable after tunnel
decapsulation to fix issue with qdisc xmit path,
from Lorenzo Bianconi.
10) Fix inverted matching in ctnetlink dump filtering,
from Liu Chao.
Please, pull these changes from:
git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git nf-26-10-19
Thanks.
----------------------------------------------------------------
The following changes since commit ab699d296e066eb64f51d2a8a1d42237a3b186c8:
Merge branch 'net-always-dissect-gso-packets-in-__virtio_net_hdr_to_skb' (2026-10-06 15:35:42 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git nf-26-10-19
for you to fetch changes up to cd37ab22a74e05aae5c16c67295cbf5188b16268:
netfilter: nf_conntrack_netlink: fix inverted IPv6 tuple filter (2026-10-19 14:14:03 +0200)
----------------------------------------------------------------
netfilter pull request 26-10-19
----------------------------------------------------------------
Florian Westphal (1):
netfilter: nft_payload: restrict checksum offsets to known values
Joel Granados (1):
ipvs: skip committing sync_threshold when proc_dointvec fails
Julian Anastasov (1):
ipvs: fix more races around the overload flag
Jérémy Jean (1):
netfilter: nf_conntrack: avoid truncating IPv6 nexthdr offset
Kyle Zeng (1):
netfilter: nf_reject: initialize IPCB at inet ingress
Liu Chao (1):
netfilter: nf_conntrack_netlink: fix inverted IPv6 tuple filter
Lorenzo Bianconi (1):
netfilter: flowtable: reset transport header after nf_flow_encap_pop()
Pablo Neira Ayuso (1):
netfilter: nft_set_rbtree: narrow down range completion to anonymous sets
Shuangfeng He (1):
netfilter: flowtable: use the vlan id, not the full tci, in the tuple key
Sung Byeongchan (1):
netfilter: xt_dccp: handle reserved options as single-byte
include/net/ip_vs.h | 50 +++++++++++++++++++++------------
net/ipv4/netfilter/nf_reject_ipv4.c | 5 ++++
net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +-
net/ipv6/netfilter/nf_reject_ipv6.c | 5 ++++
net/netfilter/ipvs/ip_vs_conn.c | 8 +++---
net/netfilter/ipvs/ip_vs_ctl.c | 41 ++++++++++++++-------------
net/netfilter/ipvs/ip_vs_dh.c | 16 ++---------
net/netfilter/ipvs/ip_vs_fo.c | 2 +-
net/netfilter/ipvs/ip_vs_lblc.c | 4 +--
net/netfilter/ipvs/ip_vs_lblcr.c | 8 +++---
net/netfilter/ipvs/ip_vs_lc.c | 2 +-
net/netfilter/ipvs/ip_vs_mh.c | 2 +-
net/netfilter/ipvs/ip_vs_nq.c | 2 +-
net/netfilter/ipvs/ip_vs_ovf.c | 2 +-
net/netfilter/ipvs/ip_vs_rr.c | 2 +-
net/netfilter/ipvs/ip_vs_sed.c | 4 +--
net/netfilter/ipvs/ip_vs_sh.c | 2 +-
net/netfilter/ipvs/ip_vs_twos.c | 4 +--
net/netfilter/ipvs/ip_vs_wlc.c | 4 +--
net/netfilter/ipvs/ip_vs_wrr.c | 2 +-
net/netfilter/nf_conntrack_netlink.c | 8 +++---
net/netfilter/nf_flow_table_ip.c | 6 ++--
net/netfilter/nft_payload.c | 40 +++++++++++++++++++++-----
net/netfilter/nft_set_rbtree.c | 3 +-
net/netfilter/xt_dccp.c | 2 +-
25 files changed, 134 insertions(+), 92 deletions(-)
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net 01/10] netfilter: nf_conntrack: avoid truncating IPv6 nexthdr offset
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
2026-10-19 15:55 ` netdev-bot+sinfo
2026-10-19 15:02 ` [PATCH net 02/10] netfilter: nft_payload: restrict checksum offsets to known values Pablo Neira Ayuso
` (8 subsequent siblings)
9 siblings, 1 reply; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
From: Jérémy Jean <Jeremy.Jean@oss.cyber.gouv.fr>
find_prev_fhdr() returns the offset of the Next Header field before an
IPv6 Fragment header through an int pointer, but stores the offset in a
u8 local. An extension header chain can put this field beyond byte 255,
causing the offset to wrap before it is saved in the fragment queue.
Reassembly later uses the wrapped value to update the preceding Next
Header field. This overwrites another byte in the IPv6 header chain and
can make conntrack parse a different transport tuple from the one used
by IPv6 local delivery. A two-fragment packet can, for example, be
recorded as UDP destination port 53 while its payload is delivered to
UDP port 9999.
Use int for the working offset, matching the output argument and the
other offset variables in this path.
Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.")
Assisted-by: Codex:gpt-5
Signed-off-by: Jérémy Jean <Jeremy.Jean@oss.cyber.gouv.fr>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 599c49bf0a0a..be72c4346f8b 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -398,7 +398,7 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
{
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
const int netoff = skb_network_offset(skb);
- u8 prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr);
+ int prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr);
int start = netoff + sizeof(struct ipv6hdr);
int len = skb->len - start;
u8 prevhdr = NEXTHDR_IPV6;
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net 02/10] netfilter: nft_payload: restrict checksum offsets to known values
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 01/10] netfilter: nf_conntrack: avoid truncating IPv6 nexthdr offset Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 03/10] ipvs: fix more races around the overload flag Pablo Neira Ayuso
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
From: Florian Westphal <fw@strlen.de>
We need to prevent userspace from corrupting e.g. tcp->doff, because
many locations in conntrack and conntrack helpers rely on
nf_conntrack_in() having validated the packet headers.
nft_payload allows to alter headers later which invalidates this
assumption.
The 'Fixes' commit restricts writes to safe fields, but there is
another side channel: the checksum location.
Restrict this too. Reported via sashiko/gemini.
Fixes: 112e447d17f7 ("netfilter: validate L4 headers after userspace packet writes")
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nft_payload.c | 40 ++++++++++++++++++++++++++++++-------
1 file changed, 33 insertions(+), 7 deletions(-)
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
index e315d35f73d4..5b76af68b704 100644
--- a/net/netfilter/nft_payload.c
+++ b/net/netfilter/nft_payload.c
@@ -20,6 +20,7 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <net/gre.h>
+#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
@@ -1008,11 +1009,14 @@ static bool nft_payload_validate_inet_csum_offset(const struct nft_ctx *ctx,
if (priv->csum_flags) /* makes no sense, asks for "re-update" of L4 checksum */
return false;
- /* no further check here; offset can't be negative so bogus
- * offsets can corrupt L4 or payload but not l3 headers.
- * We already allow arbitrary l4/inner payload writes.
- */
- return true;
+ /* Validate csum_offset is one of the supported transport header checksums */
+ if (priv->csum_offset == offsetof(struct tcphdr, check) ||
+ priv->csum_offset == offsetof(struct udphdr, check) ||
+ priv->csum_offset == offsetof(struct icmphdr, checksum) ||
+ priv->csum_offset == offsetof(struct icmp6hdr, icmp6_cksum))
+ return true;
+
+ return false;
case NFT_PAYLOAD_INNER_HEADER:
return true;
case NFT_PAYLOAD_TUN_HEADER:
@@ -1046,6 +1050,27 @@ static bool nft_payload_csum_nh_write_ok(const struct nft_payload_set *priv,
return false;
}
+static bool nft_payload_csum_th_write_ok(const struct nft_payload_set *priv,
+ const struct nft_pktinfo *pkt)
+{
+ if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
+ return false;
+
+ switch (pkt->tprot) {
+ case IPPROTO_TCP:
+ return priv->csum_offset == offsetof(struct tcphdr, check);
+ case IPPROTO_UDP:
+ case IPPROTO_UDPLITE:
+ return priv->csum_offset == offsetof(struct udphdr, check);
+ case IPPROTO_ICMPV6:
+ return priv->csum_offset == offsetof(struct icmp6hdr, icmp6_cksum);
+ case IPPROTO_ICMP:
+ return priv->csum_offset == offsetof(struct icmphdr, checksum);
+ }
+
+ return false;
+}
+
static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt,
const struct nft_payload_set *priv)
{
@@ -1055,9 +1080,10 @@ static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt,
case NFT_PAYLOAD_NETWORK_HEADER:
return nft_payload_csum_nh_write_ok(priv, pkt);
case NFT_PAYLOAD_TRANSPORT_HEADER:
+ return nft_payload_csum_th_write_ok(priv, pkt);
case NFT_PAYLOAD_INNER_HEADER:
- /* neither offsets are validated, offsets cannot be
- * negative so real l3 headers cannot be mangled.
+ /* offset is not validated, offset cannot be
+ * negative so real l3/l4 headers cannot be mangled.
*/
return true;
case NFT_PAYLOAD_TUN_HEADER:
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net 03/10] ipvs: fix more races around the overload flag
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 01/10] netfilter: nf_conntrack: avoid truncating IPv6 nexthdr offset Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 02/10] netfilter: nft_payload: restrict checksum offsets to known values Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 04/10] ipvs: skip committing sync_threshold when proc_dointvec fails Pablo Neira Ayuso
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
From: Julian Anastasov <ja@ssi.bg>
Sashiko warns that we still have races when the dest overload
thresholds are updated. If we narrow the range between
thresholds the lockless readers (ip_vs_bind_dest and
ip_vs_unbind_dest) may use old threshold values and
as result to skip the required flag change.
To solve the problem ip_vs_bind_dest() and ip_vs_unbind_dest()
will have the chance as before to change the overload
state not only at the exact threshold positions but
at any conn count below the lower and above the upper thresholds.
To make it work without entering the locked region for
every connection, we introduce l_threshold_val. Together with
u_threshold_val we will switch between two states:
1. not overloaded: where l_threshold_val is set to 0
to close the door for more ip_vs_unbind_dest() calls
to clear the overload state.
2. overloaded: where u_threshold_val is set to INT_MAX
to close the door for more ip_vs_bind_dest() calls
to set the overload state.
If a race occurs, multiple CPUs can repeat the state change
but this should be for short time window.
With the introduction of l_threshold_val the
IP_VS_DEST_F_OVERLOAD flag and the 'flags' field are not
needed anymore. We will use the ip_vs_dest_is_overloaded()
helper for all places that read the overload state.
Do not check IP_VS_DEST_CF_AVAILABLE in ip_vs_dh.c because
we do not hold reference to dest after it is removed and
rely on RCU to access the available dests.
To reduce the cacheline accesses while selecting destination,
keep the mostly used fields (weight and l_threshold_val)
at the first read-mostly cacheline together with the hash
nodes.
Fixes: 8f843441c4e7 ("ipvs: properly update the overload flag on dest edit")
Link: https://sashiko.dev/#/patchset/20260810190621.894119-1-pablo%40netfilter.org
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
include/net/ip_vs.h | 50 ++++++++++++++++++++------------
net/netfilter/ipvs/ip_vs_conn.c | 8 ++---
net/netfilter/ipvs/ip_vs_ctl.c | 38 ++++++++++++------------
net/netfilter/ipvs/ip_vs_dh.c | 16 ++--------
net/netfilter/ipvs/ip_vs_fo.c | 2 +-
net/netfilter/ipvs/ip_vs_lblc.c | 4 +--
net/netfilter/ipvs/ip_vs_lblcr.c | 8 ++---
net/netfilter/ipvs/ip_vs_lc.c | 2 +-
net/netfilter/ipvs/ip_vs_mh.c | 2 +-
net/netfilter/ipvs/ip_vs_nq.c | 2 +-
net/netfilter/ipvs/ip_vs_ovf.c | 2 +-
net/netfilter/ipvs/ip_vs_rr.c | 2 +-
net/netfilter/ipvs/ip_vs_sed.c | 4 +--
net/netfilter/ipvs/ip_vs_sh.c | 2 +-
net/netfilter/ipvs/ip_vs_twos.c | 4 +--
net/netfilter/ipvs/ip_vs_wlc.c | 4 +--
net/netfilter/ipvs/ip_vs_wrr.c | 2 +-
17 files changed, 76 insertions(+), 76 deletions(-)
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index 32fde731bceb..9a83bff32569 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -36,9 +36,6 @@
#define IP_VS_HDR_INVERSE 1
#define IP_VS_HDR_ICMP 2
-/* Destination Server Flags */
-#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */
-
/* Destination Server Config Flags */
#define IP_VS_DEST_CF_AVAILABLE 0x0001 /* server is available */
@@ -973,32 +970,43 @@ struct ip_vs_dest_dst {
* and so on.
*/
struct ip_vs_dest {
+ /* Cacheline for hash table nodes - read-mostly */
+
struct list_head n_list; /* for the dests in the service */
struct hlist_node d_list; /* for table with all the dests */
- u16 af; /* address family */
- __be16 port; /* port number of the server */
- union nf_inet_addr addr; /* IP address of the server */
- volatile unsigned int flags; /* dest status flags */
- atomic_t conn_flags; /* flags to copy to conn */
atomic_t weight; /* server weight */
- unsigned long cflags; /* config flags */
atomic_t last_weight; /* server latest weight */
+
+ /* connection thresholds */
+ u32 l_threshold_val;/* used lower threshold */
+ u32 u_threshold_val;/* used upper threshold */
+ /* 32/48 */
+ u32 l_threshold; /* lower threshold */
+ u32 u_threshold; /* upper threshold */
+
+ unsigned long cflags; /* config flags */
+
+ /* 44/64 */
+ atomic_t conn_flags; /* flags to copy to conn */
+
__u16 tun_type; /* tunnel type */
__be16 tun_port; /* tunnel port */
__u16 tun_flags; /* tunnel flags */
- refcount_t refcnt; /* reference counter */
- struct ip_vs_stats stats; /* statistics */
- unsigned long idle_start; /* start time, jiffies */
+ u16 af; /* address family */
+ __be16 port; /* port number of the server */
+ /* 60/80 */
+ union nf_inet_addr addr; /* IP address of the server */
- /* connection counters and thresholds */
- atomic_t activeconns; /* active connections */
+ /* connection counters */
atomic_t totalconns; /* total connections */
+ atomic_t activeconns; /* active connections */
atomic_t persistconns; /* persistent connections */
- __u32 u_threshold; /* upper threshold */
- __u32 l_threshold; /* lower threshold */
- __u32 l_threshold_val;/* used lower threshold */
+
+ refcount_t refcnt; /* reference counter */
+ struct ip_vs_stats stats; /* statistics */
+ unsigned long idle_start; /* start time, jiffies */
/* for destination cache */
spinlock_t dst_lock; /* lock of dst_cache */
@@ -1915,7 +1923,13 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest)
kfree(dest);
}
-void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode);
+void ip_vs_dest_update_overload(struct ip_vs_dest *dest, bool overload);
+
+/* Check if dest is in overloaded state */
+static inline bool ip_vs_dest_is_overloaded(const struct ip_vs_dest *dest)
+{
+ return READ_ONCE(dest->l_threshold_val);
+}
/* IPVS sync daemon data and function prototypes
* (from ip_vs_sync.c)
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index cb009208826f..94f25fb7d87e 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -1153,8 +1153,8 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
if (!(flags & IP_VS_CONN_F_INACTIVE))
atomic_inc(&dest->activeconns);
tc = atomic_inc_return(&dest->totalconns);
- if (tc == READ_ONCE(dest->u_threshold))
- ip_vs_dest_update_overload(dest, 1);
+ if (tc >= READ_ONCE(dest->u_threshold_val))
+ ip_vs_dest_update_overload(dest, true);
} else {
/* It is a persistent connection/template, so increase
the persistent connection counter */
@@ -1246,8 +1246,8 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
if (!(cp->flags & IP_VS_CONN_F_INACTIVE))
atomic_dec(&dest->activeconns);
tc = atomic_fetch_dec(&dest->totalconns);
- if (tc == READ_ONCE(dest->l_threshold_val))
- ip_vs_dest_update_overload(dest, -1);
+ if (tc <= READ_ONCE(dest->l_threshold_val))
+ ip_vs_dest_update_overload(dest, false);
} else {
/* It is a persistent connection/template, so decrease
the persistent connection counter */
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 4c1c739446b7..3003547dcabe 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -1309,32 +1309,32 @@ void ip_vs_stats_free(struct ip_vs_stats *stats)
* - conns reach u_threshold and exceed it: set the flag
* - conns go below l_threshold (or 75% of u_threshold): clear the flag
*/
-static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
+static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, bool overload)
{
int conns;
u32 l, u;
lockdep_assert_held(&dest->dst_lock);
u = READ_ONCE(dest->u_threshold);
- if (!u)
- goto unset;
- l = READ_ONCE(dest->l_threshold_val);
- conns = atomic_read(&dest->totalconns);
- if (conns >= (mode > 0 ? l : u)) {
- dest->flags |= IP_VS_DEST_F_OVERLOAD;
- return;
+ if (u) {
+ /* Low threshold defaults to 75% of upper threshold */
+ l = READ_ONCE(dest->l_threshold) ? : (u - (u >> 2));
+ conns = atomic_read(&dest->totalconns);
+ if (conns >= (overload ? l : u)) {
+ WRITE_ONCE(dest->u_threshold_val, INT_MAX);
+ WRITE_ONCE(dest->l_threshold_val, l);
+ return;
+ }
}
- if (conns >= (mode < 0 ? u : l))
- return;
-unset:
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+ WRITE_ONCE(dest->u_threshold_val, u ? : INT_MAX);
+ WRITE_ONCE(dest->l_threshold_val, 0);
}
-void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
+void ip_vs_dest_update_overload(struct ip_vs_dest *dest, bool overload)
{
spin_lock_bh(&dest->dst_lock);
- __ip_vs_dest_update_overload(dest, mode);
+ __ip_vs_dest_update_overload(dest, overload);
spin_unlock_bh(&dest->dst_lock);
}
@@ -1406,15 +1406,13 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
if (READ_ONCE(dest->u_threshold) != udest->u_threshold ||
READ_ONCE(dest->l_threshold) != udest->l_threshold) {
+ bool overload;
+
spin_lock_bh(&dest->dst_lock);
WRITE_ONCE(dest->u_threshold, udest->u_threshold);
WRITE_ONCE(dest->l_threshold, udest->l_threshold);
- /* Low threshold defaults to 75% of upper threshold */
- WRITE_ONCE(dest->l_threshold_val,
- udest->l_threshold ? :
- (udest->u_threshold -
- (udest->u_threshold >> 2)));
- __ip_vs_dest_update_overload(dest, 0);
+ overload = ip_vs_dest_is_overloaded(dest);
+ __ip_vs_dest_update_overload(dest, overload);
spin_unlock_bh(&dest->dst_lock);
}
diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c
index 43abed7a26a6..95e52950adbb 100644
--- a/net/netfilter/ipvs/ip_vs_dh.c
+++ b/net/netfilter/ipvs/ip_vs_dh.c
@@ -195,16 +195,6 @@ static int ip_vs_dh_dest_changed(struct ip_vs_service *svc,
}
-/*
- * If the dest flags is set with IP_VS_DEST_F_OVERLOAD,
- * consider that the server is overloaded here.
- */
-static inline int is_overloaded(struct ip_vs_dest *dest)
-{
- return dest->flags & IP_VS_DEST_F_OVERLOAD;
-}
-
-
/*
* Destination hashing scheduling
*/
@@ -219,10 +209,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
s = (struct ip_vs_dh_state *) svc->sched_data;
dest = ip_vs_dh_get(svc->af, s, &iph->daddr);
- if (!dest ||
- !(dest->cflags & IP_VS_DEST_CF_AVAILABLE)
- || atomic_read(&dest->weight) <= 0
- || is_overloaded(dest)) {
+ if (!dest || atomic_read(&dest->weight) <= 0 ||
+ ip_vs_dest_is_overloaded(dest)) {
ip_vs_scheduler_err(svc, "no destination available");
return NULL;
}
diff --git a/net/netfilter/ipvs/ip_vs_fo.c b/net/netfilter/ipvs/ip_vs_fo.c
index d657b47c6511..e07fa33f6d52 100644
--- a/net/netfilter/ipvs/ip_vs_fo.c
+++ b/net/netfilter/ipvs/ip_vs_fo.c
@@ -29,7 +29,7 @@ ip_vs_fo_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
* Find virtual server with highest weight and send it traffic
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) > hw) {
hweight = dest;
hw = atomic_read(&dest->weight);
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c
index 55af77a56929..e1111c3b2721 100644
--- a/net/netfilter/ipvs/ip_vs_lblc.c
+++ b/net/netfilter/ipvs/ip_vs_lblc.c
@@ -418,7 +418,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
* new connection.
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (ip_vs_dest_is_overloaded(dest))
continue;
if (atomic_read(&dest->weight) > 0) {
least = dest;
@@ -433,7 +433,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_dest_conn_overhead(dest);
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c
index 858393b1d2d1..dfb5bf83750d 100644
--- a/net/netfilter/ipvs/ip_vs_lblcr.c
+++ b/net/netfilter/ipvs/ip_vs_lblcr.c
@@ -166,7 +166,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
/* select the first destination server, whose weight > 0 */
list_for_each_entry_rcu(e, &set->list, list) {
least = e->dest;
- if (least->flags & IP_VS_DEST_F_OVERLOAD)
+ if (ip_vs_dest_is_overloaded(least))
continue;
if ((atomic_read(&least->weight) > 0) &&
@@ -181,7 +181,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
nextstage:
list_for_each_entry_continue_rcu(e, &set->list, list) {
dest = e->dest;
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_dest_conn_overhead(dest);
@@ -580,7 +580,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
* new connection.
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (ip_vs_dest_is_overloaded(dest))
continue;
if (atomic_read(&dest->weight) > 0) {
@@ -596,7 +596,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_dest_conn_overhead(dest);
diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c
index 18b976a7c4d2..9002f491bff6 100644
--- a/net/netfilter/ipvs/ip_vs_lc.c
+++ b/net/netfilter/ipvs/ip_vs_lc.c
@@ -38,7 +38,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
+ if (ip_vs_dest_is_overloaded(dest) ||
atomic_read(&dest->weight) == 0)
continue;
doh = ip_vs_dest_conn_overhead(dest);
diff --git a/net/netfilter/ipvs/ip_vs_mh.c b/net/netfilter/ipvs/ip_vs_mh.c
index 020863047562..d70b23aec5fe 100644
--- a/net/netfilter/ipvs/ip_vs_mh.c
+++ b/net/netfilter/ipvs/ip_vs_mh.c
@@ -80,7 +80,7 @@ static inline void generate_hash_secret(hsiphash_key_t *hash1,
static inline bool is_unavailable(struct ip_vs_dest *dest)
{
return atomic_read(&dest->weight) <= 0 ||
- dest->flags & IP_VS_DEST_F_OVERLOAD;
+ ip_vs_dest_is_overloaded(dest);
}
/* Returns hash value for IPVS MH entry */
diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c
index ada158c610ce..7cc28902086e 100644
--- a/net/netfilter/ipvs/ip_vs_nq.c
+++ b/net/netfilter/ipvs/ip_vs_nq.c
@@ -72,7 +72,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD ||
+ if (ip_vs_dest_is_overloaded(dest) ||
!atomic_read(&dest->weight))
continue;
diff --git a/net/netfilter/ipvs/ip_vs_ovf.c b/net/netfilter/ipvs/ip_vs_ovf.c
index c5c67df80a0b..eaa3a7ae6efa 100644
--- a/net/netfilter/ipvs/ip_vs_ovf.c
+++ b/net/netfilter/ipvs/ip_vs_ovf.c
@@ -33,7 +33,7 @@ ip_vs_ovf_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
w = atomic_read(&dest->weight);
- if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
+ if (ip_vs_dest_is_overloaded(dest) ||
atomic_read(&dest->activeconns) > w ||
w == 0)
continue;
diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c
index 4125ee561cdc..bd17cdd092f5 100644
--- a/net/netfilter/ipvs/ip_vs_rr.c
+++ b/net/netfilter/ipvs/ip_vs_rr.c
@@ -66,7 +66,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
list_for_each_entry_continue_rcu(dest,
&svc->destinations,
n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) > 0)
/* HIT */
goto out;
diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c
index 245a323c84cd..7925f4d28fce 100644
--- a/net/netfilter/ipvs/ip_vs_sed.c
+++ b/net/netfilter/ipvs/ip_vs_sed.c
@@ -75,7 +75,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) > 0) {
least = dest;
loh = ip_vs_sed_dest_overhead(least);
@@ -90,7 +90,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_sed_dest_overhead(dest);
if ((__s64)loh * atomic_read(&dest->weight) >
diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c
index cd67066e3b26..c24dd5cb7e47 100644
--- a/net/netfilter/ipvs/ip_vs_sh.c
+++ b/net/netfilter/ipvs/ip_vs_sh.c
@@ -73,7 +73,7 @@ struct ip_vs_sh_state {
static inline bool is_unavailable(struct ip_vs_dest *dest)
{
return atomic_read(&dest->weight) <= 0 ||
- dest->flags & IP_VS_DEST_F_OVERLOAD;
+ ip_vs_dest_is_overloaded(dest);
}
/*
diff --git a/net/netfilter/ipvs/ip_vs_twos.c b/net/netfilter/ipvs/ip_vs_twos.c
index dbb7f5fd4688..86186122a1f0 100644
--- a/net/netfilter/ipvs/ip_vs_twos.c
+++ b/net/netfilter/ipvs/ip_vs_twos.c
@@ -52,7 +52,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct ip_vs_service *svc,
/* Generate a random weight between [0,sum of all weights) */
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD)) {
+ if (!ip_vs_dest_is_overloaded(dest)) {
weight = atomic_read(&dest->weight);
if (weight > 0) {
total_weight += weight;
@@ -75,7 +75,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct ip_vs_service *svc,
/* Pick two weighted servers */
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (ip_vs_dest_is_overloaded(dest))
continue;
weight = atomic_read(&dest->weight);
diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c
index 9da445ca09a1..08e9e22f72bf 100644
--- a/net/netfilter/ipvs/ip_vs_wlc.c
+++ b/net/netfilter/ipvs/ip_vs_wlc.c
@@ -47,7 +47,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) > 0) {
least = dest;
loh = ip_vs_dest_conn_overhead(least);
@@ -62,7 +62,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (ip_vs_dest_is_overloaded(dest))
continue;
doh = ip_vs_dest_conn_overhead(dest);
if ((__s64)loh * atomic_read(&dest->weight) >
diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c
index 2dcff1040da5..fad69bae5a22 100644
--- a/net/netfilter/ipvs/ip_vs_wrr.c
+++ b/net/netfilter/ipvs/ip_vs_wrr.c
@@ -176,7 +176,7 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
list_for_each_entry_continue_rcu(dest,
&svc->destinations,
n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!ip_vs_dest_is_overloaded(dest) &&
atomic_read(&dest->weight) >= mark->cw)
goto found;
if (dest == stop)
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net 04/10] ipvs: skip committing sync_threshold when proc_dointvec fails
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
` (2 preceding siblings ...)
2026-10-19 15:02 ` [PATCH net 03/10] ipvs: fix more races around the overload flag Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 05/10] netfilter: flowtable: use the vlan id, not the full tci, in the tuple key Pablo Neira Ayuso
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
From: Joel Granados <joel.granados@kernel.org>
Forward rc value and skip updating sysctl_sync_threshold when
proc_dointvec fails. This targets a write such as "5 x" which would fail
with -EINVAL but would store the first element anyway.
Signed-off-by: Joel Granados <joel.granados@kernel.org>
Acked-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/ipvs/ip_vs_ctl.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 3003547dcabe..ce9fe20d6106 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -2452,6 +2452,8 @@ proc_do_sync_threshold(const struct ctl_table *table, int write,
mutex_lock(&ipvs->sync_mutex);
memcpy(val, valp, sizeof(val));
rc = proc_dointvec(&tmp, write, buffer, lenp, ppos);
+ if (rc)
+ goto out;
if (write) {
if (val[0] < 0 || val[1] < 0 ||
(val[0] >= val[1] && val[1]))
@@ -2459,6 +2461,7 @@ proc_do_sync_threshold(const struct ctl_table *table, int write,
else
memcpy(valp, val, sizeof(val));
}
+out:
mutex_unlock(&ipvs->sync_mutex);
return rc;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net 05/10] netfilter: flowtable: use the vlan id, not the full tci, in the tuple key
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
` (3 preceding siblings ...)
2026-10-19 15:02 ` [PATCH net 04/10] ipvs: skip committing sync_threshold when proc_dointvec fails Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 06/10] netfilter: nf_reject: initialize IPCB at inet ingress Pablo Neira Ayuso
` (4 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
From: Shuangfeng He <huangya90@gmail.com>
nf_flow_tuple_encap() stores the skb vlan tag in the encap tuple id
using skb_vlan_tag_get(), which returns the unmasked TCI including
the 3 PCP priority bits. The flowtable entry, however, is programmed
by nf_dev_path_info() with vlan_dev_vlan_id(), a plain 12-bit VID.
When a NIC delivers RX vlan offload skbs with a non-zero priority
(an RTL9617C EPON ONU was observed handing over vid 818 skbs with
tci 0x6332, PCP 3), the runtime lookup key never matches the
programmed entry, so every packet misses the flow table and
software flow offload is silently defeated.
Mask both parse sites to the 12-bit VID so the lookup key matches
for any priority. The in-header variant has the same problem for
frames received with PCP != 0 on a non-offload path.
The egress side is unaffected: nf_flow_encap_push() restores tags
from the programmed tuple id, which never carried priority bits.
Measured on an RTL9617C ONU (PPPoE over VLAN 818 WAN, NATed 6-flow
download over a 1G EPON line), before -> after:
download throughput 377 Mbps -> 936 Mbps
CPU0 %soft at that rate 91.5% -> 70.9%
Fixes: 4cd91f7c290f ("netfilter: flowtable: add vlan support")
Signed-off-by: Shuangfeng He <huangya90@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_flow_table_ip.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index c8c29a9a1684..2dec15d402ca 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -170,14 +170,14 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
int i = 0;
if (skb_vlan_tag_present(skb)) {
- tuple->encap[i].id = skb_vlan_tag_get(skb);
+ tuple->encap[i].id = skb_vlan_tag_get_id(skb);
tuple->encap[i].proto = skb->vlan_proto;
i++;
}
switch (skb->protocol) {
case htons(ETH_P_8021Q):
veth = (struct vlan_ethhdr *)skb_mac_header(skb);
- tuple->encap[i].id = ntohs(veth->h_vlan_TCI);
+ tuple->encap[i].id = ntohs(veth->h_vlan_TCI) & VLAN_VID_MASK;
tuple->encap[i].proto = skb->protocol;
offset += VLAN_HLEN;
break;
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net 06/10] netfilter: nf_reject: initialize IPCB at inet ingress
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
` (4 preceding siblings ...)
2026-10-19 15:02 ` [PATCH net 05/10] netfilter: flowtable: use the vlan id, not the full tci, in the tuple key Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 07/10] netfilter: nft_set_rbtree: narrow down range completion to anonymous sets Pablo Neira Ayuso
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
From: Kyle Zeng <kylebot@openai.com>
The inet ingress hook runs before ip_rcv_core() and ip6_rcv_core(), so
neither IPCB nor IP6CB has been initialized when nft_reject_inet passes
a packet to nf_send_unreach() or nf_send_unreach6(). skb->cb can
therefore retain metadata from an earlier protocol layer.
For IPv4, icmp_send() treats IPCB(skb)->opt as parsed IPv4 options.
Stale option offsets can make __ip_options_echo() copy an
attacker-controlled length into its 40-byte reply option buffer and
overflow the stack. On an unpatched v7.2-rc5 kernel, the IPv4
reproducer produced:
BUG: KASAN: stack-out-of-bounds in __ip_options_echo()
Write of size 255
For IPv6, icmpv6_send() consumes IP6CB(skb) as parsed IPv6 metadata. It
always uses iif for source selection and, when MIP6 is enabled, uses
dsthao as an extension-header offset. These fields must not be stale.
For NF_INET_INGRESS, clear the address-family-specific control block and
restore its input interface before constructing the ICMP error. This
matches similar initialization normally performed by ip_rcv_core() and
ip6_rcv_core().
Fixes: 117ca1f8920c ("netfilter: nft_reject_inet: allow to use reject from inet ingress")
Cc: stable@vger.kernel.org
Assisted-by: Codex:gpt-5.6-sol Codex:gpt-5.5-cyber
Signed-off-by: Kyle Zeng <kylebot@openai.com>
Co-developed-by: David Lee <david.lee@trailofbits.com>
Signed-off-by: David Lee <david.lee@trailofbits.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/ipv4/netfilter/nf_reject_ipv4.c | 5 +++++
net/ipv6/netfilter/nf_reject_ipv6.c | 5 +++++
2 files changed, 10 insertions(+)
diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c
index 59ec465a9df9..7cb33af671e2 100644
--- a/net/ipv4/netfilter/nf_reject_ipv4.c
+++ b/net/ipv4/netfilter/nf_reject_ipv4.c
@@ -357,6 +357,11 @@ void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
if (!skb_valid_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0)
return;
+ if (hook == NF_INET_INGRESS) {
+ memset(IPCB(skb_in), 0, sizeof(*IPCB(skb_in)));
+ IPCB(skb_in)->iif = skb_in->skb_iif;
+ }
+
if (skb_csum_unnecessary(skb_in) ||
!nf_reject_verify_csum(skb_in, dataoff, proto)) {
icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c
index 07cdaa10da0d..5e08d8529cc8 100644
--- a/net/ipv6/netfilter/nf_reject_ipv6.c
+++ b/net/ipv6/netfilter/nf_reject_ipv6.c
@@ -444,6 +444,11 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in,
if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
skb_in->dev = net->loopback_dev;
+ if (hooknum == NF_INET_INGRESS) {
+ memset(IP6CB(skb_in), 0, sizeof(*IP6CB(skb_in)));
+ IP6CB(skb_in)->iif = skb_in->skb_iif;
+ }
+
if (!skb_valid_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0)
return;
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net 07/10] netfilter: nft_set_rbtree: narrow down range completion to anonymous sets
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
` (5 preceding siblings ...)
2026-10-19 15:02 ` [PATCH net 06/10] netfilter: nf_reject: initialize IPCB at inet ingress Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 08/10] netfilter: xt_dccp: handle reserved options as single-byte Pablo Neira Ayuso
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
For anonymous sets, when adjacent ranges are found, the end element is
not added to the set to pack the set representation. Use next start
element to complete this interval.
Jérémy Jean says:
Consider two ranges in a netfilter verdict map:
* keys 10-19 accept packets, and
* keys 30-39 jump to chain 'victim'.
Now, delete the end marker at 20. The lookup array now uses element 30
as the first range's end, so an INTERVAL_END query for key 15 returns
element 30, including its jump verdict and chain pointer. This can make
KASAN reports a UAF after the GC releases reference chain for 30 that
has expired:
BUG: KASAN: slab-use-after-free in nft_verdict_dump+0xdd/0x110
Read of size 8 at addr ff110000009dfb58 by task repro/50
...
Call Trace:
...
nft_verdict_dump+0xdd/0x110
nft_data_dump+0x4d/0x80
nf_tables_fill_setelem+0x30b/0x8a0
? __asan_memcpy+0x3c/0x60
nf_tables_fill_setelem_info+0x1b0/0x200
nft_get_set_elem+0x3fd/0x4b0
...
Freed by task 24:
...
kfree+0x1a5/0x280
nf_tables_trans_destroy_work+0x2cb/0x520
Fixes: 2aa34191f06f ("netfilter: nft_set_rbtree: use binary search array in get command")
Reported-by: Jérémy Jean <Jeremy.Jean@oss.cyber.gouv.fr>
Link: https://patch.msgid.link/netfilter-devel/20261005200404.3544062-2-Jeremy.Jean@oss.cyber.gouv.fr/
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nft_set_rbtree.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
index 12431b55752f..fbe1750f1ca2 100644
--- a/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -1091,7 +1091,8 @@ static void nft_rbtree_commit(struct nft_set *set)
* representation. Use next start element to complete this
* interval.
*/
- if (nft_rbtree_interval_start(rbe) &&
+ if (nft_set_is_anonymous(set) &&
+ nft_rbtree_interval_start(rbe) &&
nft_rbtree_interval_start(prev_rbe) &&
priv->array_next->intervals[num_intervals].from)
priv->array_next->intervals[num_intervals++].to = &prev_rbe->ext;
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net 08/10] netfilter: xt_dccp: handle reserved options as single-byte
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
` (6 preceding siblings ...)
2026-10-19 15:02 ` [PATCH net 07/10] netfilter: nft_set_rbtree: narrow down range completion to anonymous sets Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 09/10] netfilter: flowtable: reset transport header after nf_flow_encap_pop() Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 10/10] netfilter: nf_conntrack_netlink: fix inverted IPv6 tuple filter Pablo Neira Ayuso
9 siblings, 0 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
From: Sung Byeongchan <tjdqudcks0424@naver.com>
The DCCP UAPI defines option numbers 3 through 31 as reserved. Like
Padding and Mandatory, these options occupy one byte. dccp_find_option()
only treats values below 2 as single-byte and interprets the byte after a
reserved option as a TLV length.
This can skip a later option and invert an XT_DCCP_OPTION rule result. A
local production-hook fixture installed the same DROP rule for IPv4 and
IPv6. In three fresh boots per kernel, a target-only packet was dropped
20/20 times, while adding reserved option 3 before the same target let
20/20 packets reach the protected receiver. The fixed kernel dropped all
trigger packets in three fresh boots and preserved the normal controls.
Treat every option up to DCCPO_MAX_RESERVED as single-byte, matching the
protocol definition. Triggering requires
CONFIG_NETFILTER_XTABLES_LEGACY, CONFIG_NETFILTER_XT_MATCH_DCCP, an
option-based rule, and remote protocol-33 reachability. The touched source
is unchanged at 0c2669a9f4a1d607e7591ae50ccf3c432a0aff08, where the patch
applies and builds cleanly.
Fixes: 1d3de414eb20 ("[NETFILTER]: New iptables DCCP protocol header match")
Assisted-by: LLM
Signed-off-by: Sung Byeongchan <tjdqudcks0424@naver.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/xt_dccp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c
index 3db81e041af9..23a4e3e8df7d 100644
--- a/net/netfilter/xt_dccp.c
+++ b/net/netfilter/xt_dccp.c
@@ -62,7 +62,7 @@ dccp_find_option(u_int8_t option,
return true;
}
- if (op[i] < 2 || i == optlen - 1)
+ if (op[i] <= DCCPO_MAX_RESERVED || i == optlen - 1)
i++;
else
i += op[i + 1] ? : 1;
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net 09/10] netfilter: flowtable: reset transport header after nf_flow_encap_pop()
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
` (7 preceding siblings ...)
2026-10-19 15:02 ` [PATCH net 08/10] netfilter: xt_dccp: handle reserved options as single-byte Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 10/10] netfilter: nf_conntrack_netlink: fix inverted IPv6 tuple filter Pablo Neira Ayuso
9 siblings, 0 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
From: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
nf_flow_encap_pop() pulls vlan/pppoe or ip tunnel headers and resets the
network header, but leaves the transport header pointing into the removed
outer header. Reset the transport header to the inner L4 header, otherwise
later consumers (e.g. GSO segmentation) parse a bogus header.
Fixes: 4cd91f7c290f ("netfilter: flowtable: add vlan support")
Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_flow_table_ip.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index 2dec15d402ca..bb894c4f2b4a 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -502,6 +502,7 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
nf_flow_encap_pop(ctx, skb, tuplehash);
thoff -= ctx->offset;
+ skb_set_transport_header(skb, thoff);
iph = ip_hdr(skb);
nf_flow_nat_ip(flow, skb, thoff, dir, iph);
@@ -1097,6 +1098,7 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
flow_offload_refresh(flow_table, flow, false);
nf_flow_encap_pop(ctx, skb, tuplehash);
+ skb_set_transport_header(skb, sizeof(*ip6h));
ip6h = ipv6_hdr(skb);
nf_flow_nat_ipv6(flow, skb, dir, ip6h);
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net 10/10] netfilter: nf_conntrack_netlink: fix inverted IPv6 tuple filter
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
` (8 preceding siblings ...)
2026-10-19 15:02 ` [PATCH net 09/10] netfilter: flowtable: reset transport header after nf_flow_encap_pop() Pablo Neira Ayuso
@ 2026-10-19 15:02 ` Pablo Neira Ayuso
9 siblings, 0 replies; 12+ messages in thread
From: Pablo Neira Ayuso @ 2026-10-19 15:02 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja
From: Liu Chao <liuc63@xiaopeng.com>
ctnetlink_filter_match_tuple() returns 0 (no match) when the IPv6
source or destination address differs from the filter, because the
!ipv6_addr_cmp() test is inverted. ipv6_addr_cmp() returns 0 when
the addresses are equal, so the negation makes the branch trigger on
a match instead of a mismatch, which is the opposite of the IPv4
path above.
Drop the '!' so the IPv6 branch returns 0 only when the addresses
differ, matching the IPv4 semantics and the caller's expectation.
Fixes: cb8aa9a3affb ("netfilter: ctnetlink: add kernel side filtering for dump")
Cc: stable@vger.kernel.org
Signed-off-by: Liu Chao <liuc63@xiaopeng.com>
Reviewed-by: Weibin Liu <liuwb@xiaopeng.com>
Reviewed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_conntrack_netlink.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 4e5d7c701436..cc8695d5356d 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1098,13 +1098,13 @@ static int ctnetlink_filter_match_tuple(struct nf_conntrack_tuple *filter_tuple,
break;
case NFPROTO_IPV6:
if ((flags & CTA_FILTER_FLAG(CTA_IP_SRC)) &&
- !ipv6_addr_cmp(&filter_tuple->src.u3.in6,
- &ct_tuple->src.u3.in6))
+ ipv6_addr_cmp(&filter_tuple->src.u3.in6,
+ &ct_tuple->src.u3.in6))
return 0;
if ((flags & CTA_FILTER_FLAG(CTA_IP_DST)) &&
- !ipv6_addr_cmp(&filter_tuple->dst.u3.in6,
- &ct_tuple->dst.u3.in6))
+ ipv6_addr_cmp(&filter_tuple->dst.u3.in6,
+ &ct_tuple->dst.u3.in6))
return 0;
break;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH net 01/10] netfilter: nf_conntrack: avoid truncating IPv6 nexthdr offset
2026-10-19 15:02 ` [PATCH net 01/10] netfilter: nf_conntrack: avoid truncating IPv6 nexthdr offset Pablo Neira Ayuso
@ 2026-10-19 15:55 ` netdev-bot+sinfo
0 siblings, 0 replies; 12+ messages in thread
From: netdev-bot+sinfo @ 2026-10-19 15:55 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, horms, fw,
ja
Hi!
This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:
- How the issue was discovered, e.g. hit in production, hit during
development, syzbot report, manual code inspection, LLM or static
analysis tool scan.
- Whether the issue was actually triggered, or is only theoretical
(e.g. found by code inspection). If it was triggered please include
the symptoms, like the stack trace or error messages.
Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.
The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-10-19 15:56 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 15:02 [PATCH net 00/10] Netfilter/IPVS fixes for net Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 01/10] netfilter: nf_conntrack: avoid truncating IPv6 nexthdr offset Pablo Neira Ayuso
2026-10-19 15:55 ` netdev-bot+sinfo
2026-10-19 15:02 ` [PATCH net 02/10] netfilter: nft_payload: restrict checksum offsets to known values Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 03/10] ipvs: fix more races around the overload flag Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 04/10] ipvs: skip committing sync_threshold when proc_dointvec fails Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 05/10] netfilter: flowtable: use the vlan id, not the full tci, in the tuple key Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 06/10] netfilter: nf_reject: initialize IPCB at inet ingress Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 07/10] netfilter: nft_set_rbtree: narrow down range completion to anonymous sets Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 08/10] netfilter: xt_dccp: handle reserved options as single-byte Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 09/10] netfilter: flowtable: reset transport header after nf_flow_encap_pop() Pablo Neira Ayuso
2026-10-19 15:02 ` [PATCH net 10/10] netfilter: nf_conntrack_netlink: fix inverted IPv6 tuple filter Pablo Neira Ayuso
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox