Netfilter/IPVS fixes for net
Subsystems (6)
Pablo Neira Ayuso
maintainercover Netfilter/IPVS fixes for net
Hi,
The following batch contains Netfilter/IPVS fixes for net. This batch includes a mix of IPVS follow ups related to Sashiko reports, as well as crash fixes for connection tracking expectation, helpers, ipset and nf_tables mostly for old bugs. This also includes a fix for the flowtable tunnel selftest.
1) Use s32 instead of s16 to calculate the remaining payload containing SIP messages, otherwise underflow is possible allowing out-of-bound memory access beyond the skb->data area. From Xiang Mei.
2) Fix the counter check in the flowtable selftest for tunnels, from Lorenzo Bianconi.
3) Add and use nf_ct_expect_related_pair() to add the RTP and RTCP expectations under the expectation lock, this is required by the SIP and H.323 NAT helpers. This fixes a possible reinsertion of an expectation with the DEAD flag set on while looping to find consecutive ports.
4) Fix ipset UaF during table resize by blocking comment updates on kernel-side adds. From David Lee.
5) Do not propagate the IP_VS_CONN_F_ONE_PACKET flag when using IPVS state synchronization, otherwise reaching stale freed from ip_vs_conn struct is possible, Zhiling Zou.
6) Adjust the hn1 hash node when the forwarding method changes between MASQ and non-MASQ for an already hashed connection. This can leave stale hash nodes pointing to a freed struct ip_vs_conn and trigger UaF while reading /proc/net/ip_vs_conn. From Julian Anastasov.
7) nft_object rhltable needs to be per table, just like chain rhltable, otherwise UaF from object lookup path while netns is being released. There is also the nlevent path that can reach stale objects. Placing this rhltable under the table hierarchy fixes this issue.
8) Reject invalid combined usage of hashlimit tables with and without XT_HASHLIMIT_RATE_MATCH flag mode, otherwise access to uninitialized .burst field of dsthash_ent is possible.
9) Fix checksum validations in IPVS performed from LOCAL_IN, from Julian Anastasov.
10) Fix incorrect packet offset to layer 4 protocol in IPVS, uncovered by Sashiko, from Julian Anastasov.
11) Skip the mangling of ICMP replies for non-first fragments, also reported by Sashiko. Also from Julian.
12) Clear ip_vs_conn flags under the spinlock to fix a possible data race. From Julian Anastasov.
13) Fix incorrect calculation of the payload bitmask in the nf_tables hardware offload support, leading to UBSAN splat. From Xiang Mei.
Julian Anastasov clarifies that some of the IPVS patches might still result in pre-existing issues reports by Sashiko, they are ready to follow up on that.
Please, pull these changes from:
git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git nf-26-07-22
Thanks.
----------------------------------------------------------------
The following changes since commit fca68249b6f5e84859b200b54cb5e0aef98f2b3a:
Merge branch 'net-fix-two-issues-in-sk_clone-error-path' (2026-07-21 09:15:01 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git nf-26-07-22
for you to fetch changes up to 09e0d3e6d4f401e8216227657d5e412553785607:
netfilter: nft_payload: fix mask build for partial field offload (2026-07-22 20:47:50 +0200)
----------------------------------------------------------------
netfilter pull request 26-07-22
----------------------------------------------------------------
David Lee (1):
netfilter: ipset: do not update comments from kernel-side hash adds
Julian Anastasov (5):
ipvs: adjust double hashing when fwd method changes
ipvs: fix the checksum validations
ipvs: fix places with wrong packet offsets
ipvs: do not mangle ICMP replies for non-first fragments
ipvs: clear the nfct flag under lock
Lorenzo Bianconi (1):
selftests: netfilter: nft_flowtable.sh: fix offload counter verification for tunnel tests
Pablo Neira Ayuso (3):
netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair()
netfilter: nf_tables: make nft_object rhltable per table
netfilter: xt_hashlimit: validate hashtable supports XT_HASHLIMIT_RATE_MATCH
Xiang Mei (1):
netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in sip_help_tcp()
Xiang Mei (Microsoft) (1):
netfilter: nft_payload: fix mask build for partial field offload
Zhiling Zou (1):
ipvs: do not propagate one-packet flag to synced conns
include/linux/netfilter/nf_conntrack_sip.h | 2 +- include/net/ip_vs.h | 48 +++++- include/net/netfilter/nf_conntrack_expect.h | 3 + include/net/netfilter/nf_tables.h | 4 +- net/ipv4/netfilter/nf_nat_h323.c | 22 +-- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- net/netfilter/ipvs/ip_vs_app.c | 4 +- net/netfilter/ipvs/ip_vs_conn.c | 192 ++++++++++++++++----- net/netfilter/ipvs/ip_vs_core.c | 190 ++++++++++---------- net/netfilter/ipvs/ip_vs_proto_sctp.c | 19 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 48 ++---- net/netfilter/ipvs/ip_vs_proto_udp.c | 54 ++---- net/netfilter/ipvs/ip_vs_xmit.c | 42 +++-- net/netfilter/nf_conntrack_expect.c | 35 +++- net/netfilter/nf_conntrack_sip.c | 2 +- net/netfilter/nf_nat_sip.c | 22 +-- net/netfilter/nf_tables_api.c | 34 ++-- net/netfilter/nft_payload.c | 12 +- net/netfilter/xt_hashlimit.c | 16 +- .../selftests/net/netfilter/nft_flowtable.sh | 14 +- 20 files changed, 452 insertions(+), 313 deletions(-)
Pablo Neira Ayuso
maintainerPATCH 4/13 netfilter: ipset: do not update comments from kernel-side hash adds
From: David Lee <[email protected]>
mtype_resize() copies comment pointers with memcpy(), not the comment objects themselves. During the window after an entry has been copied but before the table swap and backlog replay, the old table is still published for packet-side updates while the replacement-table entry already holds the same ip_set_comment_rcu pointer.
If xt_SET --add-set ... --exist hits that old entry in this window, mtype_add() calls ip_set_init_comment() even though packet-side adds carry no comment payload. That call frees the shared comment through the old entry, so the replacement-table entry now holds a stale pointer. When the queued add is replayed on the new table, mtype_add() calls ip_set_init_comment() again and strlen() dereferences the stale pointer.
Fix this in mtype_add() by skipping ip_set_init_comment() when ext->target marks a packet-side add. Userspace adds still update comments, while packet-side adds can no longer free comment storage shared with a resize copy.
--- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/netfilter/ipset/ip_set_hash_gen.h +1 -1
@@ -1005,7 +1005,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,#endif if (SET_WITH_COUNTER (set))ip_set_init_counter (ext_counter (data, set), ext); -if (SET_WITH_COMMENT (set)) +if (SET_WITH_COMMENT (set) && !ext->target)ip_set_init_comment (set,ext_comment (data, set), ext);if (SET_WITH_SKBINFO (set))ip_set_init_skbinfo (ext_skbinfo (data, set), ext);
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 1/13 netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in sip_help_tcp()
In reply to Pablo Neira AyusoFrom: Xiang Mei <[email protected]>
sip_help_tcp() stores the size change of each NAT-rewritten SIP message in s16 diff and accumulates it in s16 tdiff, but a single message can grow by more than S16_MAX while the packet stays under the 65535 enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long Contact list expands the message by tens of kilobytes. diff then wraps, and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, so the next iteration's ct_sip_get_header() reads past the linearized skb tail.
Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the 65535 byte packet limit, and the seqadj core is already s32 (nf_ct_seqadj_set() takes s32), so no previously accepted input is rejected.
BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) nf_confirm (net/netfilter/nf_conntrack_proto.c:183) nf_hook_slow (net/netfilter/core.c:619) ip6_output (net/ipv6/ip6_output.c:246) ip6_forward (net/ipv6/ip6_output.c:690) ipv6_rcv (net/ipv6/ip6_input.c:351) __netif_receive_skb_one_core (net/core/dev.c:6212) process_backlog (net/core/dev.c:6676) __napi_poll (net/core/dev.c:7735) net_rx_action (net/core/dev.c:7955) handle_softirqs (kernel/softirq.c:622) run_ksoftirqd (kernel/softirq.c:1076) ...
--- include/linux/netfilter/nf_conntrack_sip.h | 2 +- net/netfilter/nf_conntrack_sip.c | 2 +- net/netfilter/nf_nat_sip.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
include/linux/netfilter/nf_conntrack_sip.h +1 -1
@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { unsigned int *datalen);void (*seq_adjust)(struct sk_buff *skb, -unsigned int protoff,s16 off); +unsigned int protoff,s32 off); unsigned int (*expect)(struct sk_buff *skb, unsigned int protoff,
net/netfilter/nf_conntrack_sip.c +1 -1
@@ -1663,7 +1663,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,unsigned int matchoff, matchlen;unsigned int msglen, origlen;const char *dptr, *end; -s16 diff, tdiff =0 ; +s32 diff, tdiff =0 ;int ret =NF_ACCEPT ;unsigned long clen;bool term;
net/netfilter/nf_nat_sip.c +1 -1
@@ -321,7 +321,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, }static void nf_nat_sip_seq_adjust (struct sk_buff *skb,unsigned int protoff, -s16 off) +s32 off) {enum ip_conntrack_info ctinfo;struct nf_conn *ct =nf_ct_get (skb, &ctinfo);
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 2/13 selftests: netfilter: nft_flowtable.sh: fix offload counter verification for tunnel tests
In reply to Pablo Neira AyusoFrom: Lorenzo Bianconi <[email protected]>
The IPIP and IP6IP6 tunnel tests call check_counters() to verify flowtable offloading occurred, but the flow-add rule only matches meta oif "veth1". When traffic is routed through a tunnel device, oif is the tunnel interface (tun0, tun6, etc.), not veth1, so the flow-add rule never fires, no flowtable entry is created, and counters stay at zero — producing a silent false pass. Fix by adding tunnel-specific flow-add rules for each tunnel interface. These match TCP dport 12345 traffic before the bare accept rule, set ct mark, add the flow to the flowtable, and increment routed_orig. The existing routed_repl rule on veth0 already handles the reply direction since decapsulated reply packets exit through the physical interface.
Also add check_counters() for the IP6IP6 non-VLAN and IP6IP6-over-VLAN tests which previously used a bare PASS message.
--- .../selftests/net/netfilter/nft_flowtable.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
tools/testing/selftests/net/netfilter/nft_flowtable.sh +12 -2
@@ -617,7 +617,11 @@ ip -6 -net "$nsr2" route add default via fee1:3::1 ip -net "$ns2" route add default via 10.0.2.1 ip -6 -net "$ns2" route add default via dead:2::1 +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun0 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0 accept' +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun6 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6 accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward \ 'meta oif "veth0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept'
@@ -629,7 +633,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel"; then fi if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then - echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel" + check_counters "flow offload for ns1/ns2 IP6IP6 tunnel" else echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel" 1>&2 ip netns exec "$nsr1" nft list ruleset
@@ -642,6 +646,8 @@ ip -net "$nsr1" link set veth1.10 up ip -net "$nsr1" addr add 192.168.20.1/24 dev veth1.10 ip -net "$nsr1" addr add fee1:4::1/64 dev veth1.10 nodad ip netns exec "$nsr1" sysctl net.ipv4.conf.veth1/10.forwarding=1 > /dev/null +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif veth1.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif veth1.10 accept' ip -net "$nsr1" link add name tun0.10 type ipip local 192.168.20.1 remote 192.168.20.2
@@ -649,6 +655,8 @@ ip -net "$nsr1" link set tun0.10 up ip -net "$nsr1" addr add 192.168.200.1/24 dev tun0.10 ip -net "$nsr1" route change default via 192.168.200.2 ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun0.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0.10 accept' ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 encaplimit none
@@ -656,6 +664,8 @@ ip -net "$nsr1" link set tun6.10 up ip -net "$nsr1" addr add fee1:5::1/64 dev tun6.10 nodad ip -6 -net "$nsr1" route delete default ip -6 -net "$nsr1" route add default via fee1:5::2 +ip netns exec "$nsr1" nft -a insert rule inet filter forward \ + 'meta oif tun6.10 tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept' ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6.10 accept' ip -net "$nsr2" link add link veth0 name veth0.10 type vlan id 10
@@ -683,7 +693,7 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IPIP tunnel over vlan"; then fi if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then - echo "PASS: flow offload for ns1/ns2 IP6IP6 tunnel over vlan" + check_counters "flow offload for ns1/ns2 IP6IP6 tunnel over vlan" else echo "FAIL: flow offload for ns1/ns2 with IP6IP6 tunnel over vlan" 1>&2 ip netns exec "$nsr1" nft list ruleset
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 5/13 ipvs: do not propagate one-packet flag to synced conns
In reply to Pablo Neira AyusoFrom: Zhiling Zou <[email protected]>
Synced connections can be created before their destination exists. When the destination is later added, ip_vs_bind_dest() copies connection flags from the destination into cp->flags.
IP_VS_CONN_F_ONE_PACKET connections are not synced. If a synced connection inherits IP_VS_CONN_F_ONE_PACKET while it is already hashed, expiry can treat it as a one-packet connection and skip unlinking the existing conn_tab node, leaving stale hash nodes pointing at a freed struct ip_vs_conn.
Drop IP_VS_CONN_F_ONE_PACKET from destination flags when binding synced connections.
--- net/netfilter/ipvs/ip_vs_conn.c | 3 +++ 1 file changed, 3 insertions(+)
net/netfilter/ipvs/ip_vs_conn.c +3 -0
@@ -1014,6 +1014,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) flags = cp->flags;/* Bind with the destination and its corresponding transmitter */ if (flags &IP_VS_CONN_F_SYNC ) { +/* Synced conns are hashed, so they can not get this flag */ + conn_flags &= ~IP_VS_CONN_F_ONE_PACKET ; +/* if the connection is not template and is created * by sync, preserve the activity flag. */
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 6/13 ipvs: adjust double hashing when fwd method changes
In reply to Pablo Neira AyusoFrom: Julian Anastasov <[email protected]>
Synced conns can be created with one forwarding method and later updated with different one after the dest server is configured. This needs adjusting the hashing for node hn1 because only MASQ supports double hashing.
Modify conn_tab_lock() to support seeking for hash node hn0 together with adding for hn1. By this way we can safely modify the forwarding method and hn1.hash_key under bucket lock for the first node hn0. The forwarding method is also protected by cp->lock as it is part of cp->flags.
Fix the usage of stale idx/idx2 values in conn_tab_lock after jumping to the retry label. Instead, use idx/idx2 values just to order the locking for the old/new tables.
--- net/netfilter/ipvs/ip_vs_conn.c | 189 +++++++++++++++++++++++++------- 1 file changed, 147 insertions(+), 42 deletions(-)
net/netfilter/ipvs/ip_vs_conn.c +147 -42
@@ -70,25 +70,45 @@ static struct kmem_cache *ip_vs_conn_cachep __read_mostly;* bucket or hash table * - hash table resize works like rehash but always rehashes into new table * - bit lock on bucket serializes all operations that modify the chain +* - on resize, bucket from the old table is locked before bucket from the +* new table * - cp->lock protects conn fields like cp->flags, cp->dest */ -/* Lock conn_tab bucket for conn hash/unhash, not for rehash */ +/** +* conn_tab_lock - Lock conn_tab buckets for conn hash/unhash, not for rehash +* @t: hash table for hn0, new_tbl when new_hash=true +* @t2: hash table for hn1, new_tbl when new_hash2=true +* @cp: connection +* @hash_key: hash key for hn0 +* @hash_key2: hash key for hn1 +* @use2: using hn1 (double hashing) based on the forwarding method +* @new_hash: mode for hn0, hash node (true) or seek node (false) +* @new_hash2: mode for hn1, hash node (true) or seek node (false) +* @head_ret: returned head for hn0 +* @head2_ret: returned head for hn1 +* +* We support 3 modes: +* - seek mode for both nodes, used for unhashing +* - hash mode for both nodes, used for hashing +* - seek hn0 and hash hn1, used when forwarding method is changed +*/ static __always_inline void -conn_tab_lock (struct ip_vs_rht *t,struct ip_vs_conn *cp,u32 hash_key, -u32 hash_key2,bool use2,bool new_hash, -struct hlist_bl_head **head_ret,struct hlist_bl_head **head2_ret) +conn_tab_lock (struct ip_vs_rht *t,struct ip_vs_rht *t2,struct ip_vs_conn *cp, +u32 hash_key,u32 hash_key2,bool use2,bool new_hash, +bool new_hash2,struct hlist_bl_head **head_ret, +struct hlist_bl_head **head2_ret) {struct hlist_bl_head *head, *head2;u32 hash_key_new, hash_key_new2; -struct ip_vs_rht *t2 = t; -u32 idx, idx2; +int idx =0 , idx2 =0 ; + +/* Advance idx2 when new_hash is not set but hash_key2 +* is for new table +*/ +if (new_hash2 && use2 && t != t2) + idx2++; - idx = hash_key & t->mask; -if (use2) - idx2 = hash_key2 & t->mask; -else - idx2 = idx;if (!new_hash) {/* We need to lock the bucket in the right table */
@@ -100,46 +120,45 @@ conn_tab_lock(struct ip_vs_rht *t, struct ip_vs_conn *cp, u32 hash_key,* both nodes in different tables, use idx/idx2 * for proper lock ordering for heads. */ - idx = hash_key & t->mask; - idx |=IP_VS_RHT_TABLE_ID_MASK ; - } -if (use2) { -if (!ip_vs_rht_same_table (t2, hash_key2)) { -/* It is already moved to new table */ - t2 =rcu_dereference (t2->new_tbl); - idx2 = hash_key2 & t2->mask; - idx2 |=IP_VS_RHT_TABLE_ID_MASK ; - } - }else { - idx2 = idx; + idx++; } } +if (use2 && !new_hash2 && !ip_vs_rht_same_table (t2, hash_key2)) { +/* It is already moved to new table */ + t2 =rcu_dereference (t2->new_tbl); + idx2++; + } +if (!use2) + idx2 = idx; head = t->buckets + (hash_key & t->mask); head2 = use2 ? t2->buckets + (hash_key2 & t2->mask) : head; -local_bh_disable (); -/* Do not touch seqcount, this is a safe operation */ - -if (idx <= idx2) { +if (idx > idx2 || (head > head2 && idx == idx2)) { +hlist_bl_lock (head2);hlist_bl_lock (head); -if (head != head2) -hlist_bl_lock (head2); }else { -hlist_bl_lock (head2);hlist_bl_lock (head); +if (head != head2) +hlist_bl_lock (head2); }if (!new_hash) { +bool changed; +/* Ensure hash_key is read under lock */ hash_key_new =READ_ONCE (cp->hn0.hash_key); - hash_key_new2 =READ_ONCE (cp->hn1.hash_key); + changed = hash_key != hash_key_new; +if (use2 && !new_hash2) { + hash_key_new2 =READ_ONCE (cp->hn1.hash_key); + changed |= hash_key2 != hash_key_new2; + }else { + hash_key_new2 = hash_key2; + }/* Hash changed ? */ -if (hash_key != hash_key_new || - (hash_key2 != hash_key_new2 && use2)) { +if (changed) {if (head != head2)hlist_bl_unlock (head2);hlist_bl_unlock (head); -local_bh_enable (); hash_key = hash_key_new; hash_key2 = hash_key_new2; goto retry;
@@ -155,7 +174,6 @@ static inline void conn_tab_unlock(struct hlist_bl_head *head,if (head != head2)hlist_bl_unlock (head2);hlist_bl_unlock (head); -local_bh_enable (); }static void ip_vs_conn_expire (struct timer_list *t);
@@ -268,8 +286,9 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) use2 = false; } -conn_tab_lock (t, cp, hash_key, hash_key2, use2, true/* new_hash */ , - &head, &head2); +local_bh_disable (); +conn_tab_lock (t, t, cp, hash_key, hash_key2, use2, true/* new_hash */ , + true/* new_hash2 */ , &head, &head2); cp->flags |=IP_VS_CONN_F_HASHED ;WRITE_ONCE (cp->hn0.hash_key, hash_key);
@@ -280,6 +299,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)hlist_bl_add_head_rcu (&cp->hn1.node, head2);conn_tab_unlock (head, head2); +local_bh_enable (); ret =1 ;/* Schedule resizing if load increases */
@@ -306,18 +326,20 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)return refcount_dec_if_one (&cp->refcnt);rcu_read_lock (); +local_bh_disable (); t =rcu_dereference (ipvs->conn_tab); hash_key =READ_ONCE (cp->hn0.hash_key); hash_key2 =READ_ONCE (cp->hn1.hash_key); use2 =ip_vs_conn_use_hash2 (cp); -conn_tab_lock (t, cp, hash_key, hash_key2, use2, false/* new_hash */ , - &head, &head2); +conn_tab_lock (t, t, cp, hash_key, hash_key2, use2, false/* new_hash */ , + false/* new_hash2 */ , &head, &head2);if (cp->flags &IP_VS_CONN_F_HASHED ) {/* Decrease refcnt and unlink conn only if we are last user */ -if (refcount_dec_if_one (&cp->refcnt)) { +if (use2 ==ip_vs_conn_use_hash2 (cp) && +refcount_dec_if_one (&cp->refcnt)) {hlist_bl_del_rcu (&cp->hn0.node);if (use2)hlist_bl_del_rcu (&cp->hn1.node);
@@ -328,6 +350,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)conn_tab_unlock (head, head2); +local_bh_enable ();rcu_read_unlock ();return ret;
@@ -632,6 +655,7 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)int ntbl;int dir; +restart:/* No packets from inside, so we can do it in 2 steps. */ dir = use2 ?1 :0 ;
@@ -686,6 +710,23 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)/* Protect the cp->flags modification */ spin_lock_bh (&cp->lock); +/* Recheck the forwarding method under lock */ +if (use2 !=ip_vs_conn_use_hash2 (cp)) { + use2 = !use2; +if (use2) { +spin_unlock_bh (&cp->lock); +/* Restart with new use2 value */ + goto restart; + } +if (dir) { +/* Not started yet, so just skip dir 1 */ +spin_unlock_bh (&cp->lock); + dir--; + goto next_dir; + } +/* Just finish dir 0 */ + } +/* Lock seqcount only for the old bucket, even if we are on new table * because it affects the del operation, not the adding. */
@@ -752,6 +793,61 @@ void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport) goto next_dir; } +/* Change forwarding method for hashed conn */ +static void ip_vs_conn_change_fwd_mask (struct ip_vs_conn *cp,u32 new_flags) +{ +struct netns_ipvs *ipvs = cp->ipvs; +struct hlist_bl_head *head, *head2; +u32 hash2, hash_key, hash_key2; +struct ip_vs_rht *t, *t2; + +/* See ip_vs_conn_use_hash2() for reference */ +if ((cp->flags &IP_VS_CONN_F_TEMPLATE ) || +/* No change in double hashing ? */ + (IP_VS_FWD_METHOD (cp) ==IP_VS_CONN_F_MASQ ) == + ((new_flags &IP_VS_CONN_F_FWD_MASK ) ==IP_VS_CONN_F_MASQ )) { + cp->flags = new_flags; +return ; + } + t =rcu_dereference (ipvs->conn_tab); +if (ip_vs_conn_use_hash2 (cp)) { +/* Stop double hashing */ + hash_key =READ_ONCE (cp->hn0.hash_key); + hash_key2 =READ_ONCE (cp->hn1.hash_key); + +conn_tab_lock (t, t, cp, hash_key, hash_key2, true/* use2 */ , + false/* new_hash */ , false/* new_hash2 */ , + &head, &head2); + +/* Keep both hash keys in same table */ + hash_key =READ_ONCE (cp->hn0.hash_key); +WRITE_ONCE (cp->hn1.hash_key, hash_key); +hlist_bl_del_rcu (&cp->hn1.node); + cp->flags = new_flags; + +conn_tab_unlock (head, head2); + }else { +/* Start double hashing */ + + hash_key =READ_ONCE (cp->hn0.hash_key); + + t2 =rcu_dereference (t->new_tbl); + hash2 =ip_vs_conn_hashkey_conn (t2, cp, true); + hash_key2 =ip_vs_rht_build_hash_key (t2, hash2); + +/* Change the forwarding method under locked hn0 */ +conn_tab_lock (t, t2, cp, hash_key, hash_key2, true/* use2 */ , + false/* new_hash */ , true/* new_hash2 */ , + &head, &head2); + +WRITE_ONCE (cp->hn1.hash_key, hash_key2); + cp->flags = new_flags; +hlist_bl_add_head_rcu (&cp->hn1.node, head2); + +conn_tab_unlock (head, head2); + } +} +/* Get default load factor to map conn_count/u_thresh to t->size */ static int ip_vs_conn_default_load_factor (struct netns_ipvs *ipvs) {
@@ -1024,9 +1120,18 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) conn_flags &= ~IP_VS_CONN_F_INACTIVE ;/* connections inherit forwarding method from dest */ flags &= ~(IP_VS_CONN_F_FWD_MASK |IP_VS_CONN_F_NOOUTPUT ); + flags |= conn_flags; +/* Changing forwarding method for hashed conn can +* happen only under locks +*/ +if (cp->flags &IP_VS_CONN_F_HASHED ) +ip_vs_conn_change_fwd_mask (cp, flags); +else + cp->flags = flags; + }else { + flags |= conn_flags; + cp->flags = flags; } - flags |= conn_flags; - cp->flags = flags; cp->dest = dest; IP_VS_DBG_BUF(7, "Bind-dest %s c:%s:%d v:%s:%d "
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 3/13 netfilter: nf_conntrack_expect: add and use nf_ct_expect_related_pair()
In reply to Pablo Neira AyusoAdd a new function to insert a pair of expectations, this is required by the SIP and H323 NAT helpers. The spinlock is held to check if there is a slot for both expectations, in such case, insert them.
This removes the need for nf_ct_unexpect_related() inside the loop to find a pair of consecutive ports, otherwise inserting expectations whose dead flag is already set on can happen.
Bump master_help->expecting for the expectation class after checking if the expectation fits in the master expectation list, which is needed for this new _pair() function variant to run the eviction routine including the preallocated slot for the first expectation in the pair.
--- include/net/netfilter/nf_conntrack_expect.h | 3 ++ net/ipv4/netfilter/nf_nat_h323.c | 22 +++++-------- net/netfilter/nf_conntrack_expect.c | 35 ++++++++++++++++++++- net/netfilter/nf_nat_sip.c | 20 ++++-------- 4 files changed, 50 insertions(+), 30 deletions(-)
include/net/netfilter/nf_conntrack_expect.h +3 -0
@@ -161,6 +161,9 @@ static inline int nf_ct_expect_related(struct nf_conntrack_expect *expect,return nf_ct_expect_related_report (expect,0 ,0 , flags); } +int nf_ct_expect_related_pair (struct nf_conntrack_expect *expect[], +unsigned int flag); +struct nf_conn_help ;void nf_ct_expectation_gc (struct nf_conn_help *master_help);
net/ipv4/netfilter/nf_nat_h323.c +7 -15
@@ -182,6 +182,7 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,struct nf_conntrack_expect *rtp_exp, struct nf_conntrack_expect *rtcp_exp) { + struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };struct nf_ct_h323_master *info =nfct_help_data (ct);int dir =CTINFO2DIR (ctinfo);int i;
@@ -227,22 +228,13 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,int ret; rtp_exp->tuple.dst.u.udp.port =htons (nated_port); - ret =nf_ct_expect_related (rtp_exp,0 ); + rtcp_exp->tuple.dst.u.udp.port =htons (nated_port +1 ); + ret =nf_ct_expect_related_pair (rtp_pair,0 );if (ret ==0 ) { - rtcp_exp->tuple.dst.u.udp.port = -htons (nated_port +1 ); - ret =nf_ct_expect_related (rtcp_exp,0 ); -if (ret ==0 ) -break ; -else if (ret == -EBUSY ) { -nf_ct_unexpect_related (rtp_exp); -continue ; - }else if (ret <0 ) { -nf_ct_unexpect_related (rtp_exp); - nated_port =0 ; -break ; - } - }else if (ret != -EBUSY ) { +break ; + }else if (ret == -EBUSY ) { +continue ; + }else if (ret <0 ) { nated_port =0 ;break ; }
net/netfilter/nf_conntrack_expect.c +34 -1
@@ -427,7 +427,6 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp, exp->timeout += helper->expect_policy[exp->class].timeout *HZ ;hlist_add_head_rcu (&exp->lnode, &master_help->expectations); - master_help->expecting[exp->class]++;hlist_add_head_rcu (&exp->hnode, &nf_ct_expect_hash[h]); cnet =nf_ct_pernet (net);
@@ -534,6 +533,7 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,if (ret <0 ) goto out; + master_help->expecting[expect->class]++;nf_ct_expect_insert (expect, master_help);nf_ct_expect_event_report (IPEXP_NEW , expect, portid, report);
@@ -546,6 +546,39 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect, }EXPORT_SYMBOL_GPL (nf_ct_expect_related_report); +int nf_ct_expect_related_pair (struct nf_conntrack_expect *expect[], +unsigned int flags) +{ +struct nf_conn_help *master_help; +int i, ret; + +spin_lock_bh (&nf_conntrack_expect_lock); + master_help =nfct_help (expect[0 ]->master); +if (!master_help || master_help !=nfct_help (expect[1 ]->master)) { + ret = -EINVAL ; + goto out; + } + +for (i =0 ; i <2 ; i++) { + ret =__nf_ct_expect_check (expect[i], master_help, flags); +if (ret <0 ) { +if (i ==1 ) + master_help->expecting[expect[0 ]->class]--; + goto out; + } + master_help->expecting[expect[i]->class]++; + } + +for (i =0 ; i <2 ; i++) { +nf_ct_expect_insert (expect[i], master_help); +nf_ct_expect_event_report (IPEXP_NEW , expect[i],0 ,0 ); + } +out: +spin_unlock_bh (&nf_conntrack_expect_lock); +return ret; +} +EXPORT_SYMBOL_GPL (nf_ct_expect_related_pair); +void nf_ct_expect_iterate_destroy (bool (*iter)(struct nf_conntrack_expect *e,void *data),void *data) {
net/netfilter/nf_nat_sip.c +6 -14
@@ -592,6 +592,7 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,unsigned int medialen, union nf_inet_addr *rtp_addr) { + struct nf_conntrack_expect *rtp_pair[2] = { rtp_exp, rtcp_exp };enum ip_conntrack_info ctinfo;struct nf_conn *ct =nf_ct_get (skb, &ctinfo);enum ip_conntrack_dir dir =CTINFO2DIR (ctinfo);
@@ -622,24 +623,15 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,int ret; rtp_exp->tuple.dst.u.udp.port =htons (port); - ret =nf_ct_expect_related (rtp_exp, -NF_CT_EXP_F_SKIP_MASTER ); -if (ret == -EBUSY ) -continue ; -else if (ret <0 ) { - port =0 ; -break ; - } rtcp_exp->tuple.dst.u.udp.port =htons (port +1 ); - ret =nf_ct_expect_related (rtcp_exp, -NF_CT_EXP_F_SKIP_MASTER ); + + ret =nf_ct_expect_related_pair (rtp_pair, +NF_CT_EXP_F_SKIP_MASTER );if (ret ==0 )break ; -else if (ret == -EBUSY ) { -nf_ct_unexpect_related (rtp_exp); +else if (ret == -EBUSY )continue ; - }else if (ret <0 ) { -nf_ct_unexpect_related (rtp_exp); +else if (ret <0 ) { port =0 ;break ; }
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 7/13 netfilter: nf_tables: make nft_object rhltable per table
In reply to Pablo Neira AyusoThe nft_object rhltable is global, this allows for accessing objects that are being dismangled from lookup path by other existing netns. Given the nft_obj_destroy() releases the object inmediately, this might lead to use-after-free of these objects that are being released. Make the existing rhltable per table to address this issue to deal with with the nft_rcv_nl_event() path too.
Update nft_obj_lookup() to take the table as non-const, otherwise, compiler complains when passing the objname_ht to rhltable_lookup().
--- include/net/netfilter/nf_tables.h | 4 +++- net/netfilter/nf_tables_api.c | 34 +++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-)
include/net/netfilter/nf_tables.h +3 -1
@@ -1294,6 +1294,7 @@ static inline void nft_use_inc_restore(u32 *use)* @sets: sets in the table * @objects: stateful objects in the table * @flowtables: flow tables in the table +* @objname_ht: hashtable for objects lookup by name * @hgenerator: handle generator state * @handle: table handle * @use: number of chain references to this table
@@ -1313,6 +1314,7 @@ struct nft_table {struct list_head sets;struct list_head objects;struct list_head flowtables; +struct rhltable objname_ht;u64 hgenerator;u64 handle;u32 use;
@@ -1400,7 +1402,7 @@ static inline void *nft_obj_data(const struct nft_object *obj)#define nft_expr_obj (expr) *((struct nft_object **)nft_expr_priv(expr))struct nft_object *nft_obj_lookup (const struct net *net, -const struct nft_table *table, +struct nft_table *table,const struct nlattr *nla,u32 objtype,u8 genmask);
net/netfilter/nf_tables_api.c +16 -18
@@ -45,8 +45,6 @@ enum { NFT_VALIDATE_DO, }; -static struct rhltable nft_objname_ht; -static u32 nft_chain_hash (const void *data,u32 len,u32 seed);static u32 nft_chain_hash_obj (const void *data,u32 len,u32 seed);static int nft_chain_hash_cmp (struct rhashtable_compare_arg *,const void *);
@@ -1635,6 +1633,10 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,if (err) goto err_chain_ht; + err =rhltable_init (&table->objname_ht, &nft_objname_ht_params); +if (err <0 ) + goto err_obj_ht; +INIT_LIST_HEAD (&table->chains);INIT_LIST_HEAD (&table->sets);INIT_LIST_HEAD (&table->objects);
@@ -1653,6 +1655,8 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,list_add_tail_rcu (&table->list, &nft_net->tables);return 0 ; err_trans: +rhltable_destroy (&table->objname_ht); +err_obj_ht:rhltable_destroy (&table->chains_ht); err_chain_ht:kfree (table->udata);
@@ -1819,6 +1823,7 @@ static void nf_tables_table_destroy(struct nft_table *table)return ;rhltable_destroy (&table->chains_ht); +rhltable_destroy (&table->objname_ht);kfree (table->name);kfree (table->udata);kfree (table);
@@ -8086,7 +8091,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type)EXPORT_SYMBOL_GPL (nft_unregister_obj);struct nft_object *nft_obj_lookup (const struct net *net, -const struct nft_table *table, +struct nft_table *table,const struct nlattr *nla,u32 objtype,u8 genmask) {
@@ -8102,7 +8107,7 @@ struct nft_object *nft_obj_lookup(const struct net *net, !lockdep_commit_lock_is_held(net));rcu_read_lock (); - list =rhltable_lookup (&nft_objname_ht, &k, nft_objname_ht_params); + list =rhltable_lookup (&table->objname_ht, &k, nft_objname_ht_params);if (!list) goto out;
@@ -8382,7 +8387,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,if (err <0 ) goto err_trans; - err =rhltable_insert (&nft_objname_ht, &obj->rhlhead, + err =rhltable_insert (&table->objname_ht, &obj->rhlhead, nft_objname_ht_params);if (err <0 ) goto err_obj_ht;
@@ -8567,8 +8572,8 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info,struct netlink_ext_ack *extack = info->extack;u8 genmask =nft_genmask_cur (info->net);u8 family = info->nfmsg->nfgen_family; -const struct nft_table *table;struct net *net = info->net; +struct nft_table *table;struct nft_object *obj;struct sk_buff *skb2;u32 objtype;
@@ -10437,9 +10442,9 @@ static void nf_tables_commit_chain(struct net *net, struct nft_chain *chain)nf_tables_commit_chain_free_rules_old (g0); } -static void nft_obj_del (struct nft_object *obj) +static void nft_obj_del (struct nft_table *table,struct nft_object *obj) { -rhltable_remove (&nft_objname_ht, &obj->rhlhead, nft_objname_ht_params); +rhltable_remove (&table->objname_ht, &obj->rhlhead, nft_objname_ht_params);list_del_rcu (&obj->list); }
@@ -11124,7 +11129,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)break ;case NFT_MSG_DELOBJ :case NFT_MSG_DESTROYOBJ : -nft_obj_del (nft_trans_obj (trans)); +nft_obj_del (table,nft_trans_obj (trans));nf_tables_obj_notify (&ctx,nft_trans_obj (trans), trans->msg_type);break ;
@@ -11416,7 +11421,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)nft_trans_destroy (trans); }else {nft_use_dec_restore (&table->use); -nft_obj_del (nft_trans_obj (trans)); +nft_obj_del (table,nft_trans_obj (trans)); }break ;case NFT_MSG_DELOBJ :
@@ -12043,7 +12048,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table)nft_set_destroy (&ctx, set); }list_for_each_entry_safe (obj, ne, &table->objects, list) { -nft_obj_del (obj); +nft_obj_del (table, obj);nft_use_dec (&table->use);nft_obj_destroy (&ctx, obj); }
@@ -12225,10 +12230,6 @@ static int __init nf_tables_module_init(void)if (err <0 ) goto err_netdev_notifier; - err =rhltable_init (&nft_objname_ht, &nft_objname_ht_params); -if (err <0 ) - goto err_rht_objname; - err =nft_offload_init ();if (err <0 ) goto err_offload;
@@ -12251,8 +12252,6 @@ static int __init nf_tables_module_init(void) err_netlink_notifier:nft_offload_exit (); err_offload: -rhltable_destroy (&nft_objname_ht); -err_rht_objname:unregister_netdevice_notifier (&nf_tables_flowtable_notifier); err_netdev_notifier:nf_tables_core_module_exit ();
@@ -12274,7 +12273,6 @@ static void __exit nf_tables_module_exit(void)unregister_pernet_subsys (&nf_tables_net_ops);cancel_work_sync (&trans_gc_work);rcu_barrier (); -rhltable_destroy (&nft_objname_ht);nf_tables_core_module_exit (); }
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 8/13 netfilter: xt_hashlimit: validate hashtable supports XT_HASHLIMIT_RATE_MATCH
In reply to Pablo Neira AyusoThe XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the dsthash_ent structure which represents an entry in the hashtable. There is a union area which uses a different layout to express the rate match mode.
Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode flag is requested by two or more different rules that refer to the same hashtable. Otherwise, uninitialized access to the burst field in the union is possible.
Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by revision less than 3 too.
--- net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
net/netfilter/xt_hashlimit.c +15 -1
@@ -117,6 +117,7 @@ struct xt_hashlimit_htable {refcount_t use;u_int8_t family;bool rnd_initialized; +bool ratematch;struct hashlimit_cfg3 cfg;/* config */
@@ -323,6 +324,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,kvfree (hinfo);return -ENOMEM ; } + hinfo->ratematch = !!(cfg->mode &XT_HASHLIMIT_RATE_MATCH );spin_lock_init (&hinfo->lock);switch (revision) {
@@ -872,7 +874,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, }/* Check for overflow. */ -if (revision >=3 && cfg->mode &XT_HASHLIMIT_RATE_MATCH ) { +if (cfg->mode &XT_HASHLIMIT_RATE_MATCH ) { +if (revision <3 ) +return -EINVAL ; +if (cfg->avg ==0 || cfg->avg >U32_MAX ) {pr_info_ratelimited ("invalid rate\n" );return -ERANGE ;
@@ -905,6 +910,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,mutex_unlock (&hashlimit_mutex);return ret; } + }else { +if ((cfg->mode &XT_HASHLIMIT_RATE_MATCH && + !(*hinfo)->ratematch) || + (!(cfg->mode &XT_HASHLIMIT_RATE_MATCH ) && + (*hinfo)->ratematch)) { +mutex_unlock (&hashlimit_mutex); +htable_put (*hinfo); +return -EINVAL ; + } }mutex_unlock (&hashlimit_mutex);
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 9/13 ipvs: fix the checksum validations
In reply to Pablo Neira AyusoFrom: Julian Anastasov <[email protected]>
ip_vs_in_icmp_v6() is missing checksum validation for ICMPv6 packets from clients. In fact, as for TCP/UDP we should validate the checksum for ICMP packets only when we mangle the packets on MASQ or on reply for tunnel.
Also, Sashiko points out that handle_response_icmp() being common for IPv4 and IPv6 is missing the pseudo-header calculation while validating ICMPv6 messages from real servers which is a problem if checksum is not validated by the hardware.
Fix the problems by creating ip_vs_checksum_common_check() helper and use it for TCP/UDP/ICMP both for IPv4 and IPv6. Rely on the nf_checksum() for validating the ICMP messages but use it also for TCP and UDP.
Use correct IP offset for IP_VS_DBG_RL_PKT for TCP/UDP/SCTP.
IPVS packets (TCP/UDP/SCTP/ICMP) do not need checksum validation on LOCAL_OUT (local clients or local real servers) and on FORWARD (traffic from servers on LAN). Do it only on LOCAL_IN, in case nf_checksum() is not called on PRE_ROUTING.
Also, ip_vs_checksum_complete() can be marked static.
--- include/net/ip_vs.h | 34 ++++++++++++++++-- net/netfilter/ipvs/ip_vs_core.c | 20 +++++++++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 15 ++++---- net/netfilter/ipvs/ip_vs_proto_tcp.c | 44 +++++------------------ net/netfilter/ipvs/ip_vs_proto_udp.c | 50 ++++++--------------------- 5 files changed, 77 insertions(+), 86 deletions(-)
include/net/ip_vs.h +32 -2
@@ -25,7 +25,9 @@#include <linux/netfilter.h> /* for union nf_inet_addr */ #include <linux/ip.h> #include <linux/ipv6.h> /* for struct ipv6hdr */ +#include <net/route.h> #include <net/ipv6.h> +#include <net/ip6_fib.h> #if IS_ENABLED (CONFIG_NF_CONNTRACK )#include <net/netfilter/nf_conntrack.h> #endif
@@ -2066,8 +2068,6 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir);#endif -__sum16 ip_vs_checksum_complete (struct sk_buff *skb,int offset); -static inline __wsum ip_vs_check_diff4 (__be32 old,__be32 new,__wsum oldsum) {__be32 diff[2 ] = { ~old, new };
@@ -2093,6 +2093,36 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)return csum_partial (diff,sizeof (diff), oldsum); } +static inline bool ip_vs_checksum_needed (struct sk_buff *skb,int af) +{ +/* Checksum unnecessary or already validated? */ +if (skb_csum_unnecessary (skb)) +return false; +/* LOCAL_OUT ? */ +if (!skb->dev || skb->dev->flags &IFF_LOOPBACK ) +return false; +/* !LOCAL_IN (FORWARD) ? */ +if (af ==AF_INET6 ) { +if (!(dst_rt6_info (skb_dst (skb))->rt6i_flags &RTF_LOCAL )) +return false; + }else { +if (!(skb_rtable (skb)->rt_flags &RTCF_LOCAL )) +return false; + } +return true; +} + +/** +* ip_vs_checksum_common_check - validate checksum for TCP/UDP/ICMP +*/ +static inline bool ip_vs_checksum_common_check (struct sk_buff *skb, +int offset,int proto,int af) +{ +if (!ip_vs_checksum_needed (skb, af)) +return true; +return !nf_checksum (skb,NF_INET_LOCAL_IN , offset, proto, af); +} +/* Forget current conntrack (unconfirmed) and attach notrack entry */ static inline void ip_vs_notrack (struct sk_buff *skb) {
net/netfilter/ipvs/ip_vs_core.c +17 -3
@@ -867,7 +867,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }#endif -__sum16 ip_vs_checksum_complete (struct sk_buff *skb,int offset) +static __sum16 ip_vs_checksum_complete (struct sk_buff *skb,int offset) {return csum_fold (skb_checksum (skb, offset, skb->len - offset,0 )); }
@@ -1038,13 +1038,14 @@ static int handle_response_icmp(int af, struct sk_buff *skb, unsigned int offset, unsigned int ihl, unsigned int hooknum) { + int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;unsigned int verdict =NF_DROP ;if (IP_VS_FWD_METHOD (cp) !=IP_VS_CONN_F_MASQ ) goto after_nat;/* Ensure the checksum is correct */ -if (!skb_csum_unnecessary (skb) &&ip_vs_checksum_complete (skb, ihl)) { +if (!ip_vs_checksum_common_check (skb, ihl, iproto, af)) {/* Failed checksum! */ IP_VS_DBG_BUF (1 ,"Forward ICMP: failed checksum from %s!\n" ,IP_VS_DBG_ADDR (af, snet));
@@ -1898,7 +1899,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, verdict =NF_DROP ;/* Ensure the checksum is correct */ -if (!skb_csum_unnecessary (skb) &&ip_vs_checksum_complete (skb, ihl)) { +if ((IP_VS_FWD_METHOD (cp) ==IP_VS_CONN_F_MASQ || tunnel) && + !ip_vs_checksum_common_check (skb, ihl,IPPROTO_ICMP ,AF_INET )) {/* Failed checksum! */ IP_VS_DBG (1 ,"Incoming ICMP: failed checksum from %pI4!\n" , &iph->saddr);
@@ -2064,6 +2066,18 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb, goto out; } + verdict =NF_DROP ; + +/* Ensure the checksum is correct */ +if (IP_VS_FWD_METHOD (cp) ==IP_VS_CONN_F_MASQ && + !ip_vs_checksum_common_check (skb, iph->len,IPPROTO_ICMPV6 , +AF_INET6 )) { +/* Failed checksum! */ +IP_VS_DBG (1 ,"Incoming ICMPv6: failed checksum from %pI6c!\n" , + &iph->saddr); + goto out; + } +/* do the statistics and put it back */ ip_vs_in_stats (cp, skb);
net/netfilter/ipvs/ip_vs_proto_sctp.c +9 -6
@@ -11,7 +11,7 @@static int sctp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, -unsigned int sctphoff); +struct ip_vs_iphdr *iph); static int sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,int ret;/* Some checks before mangling */ -if (!sctp_csum_check (cp->af, skb, pp, sctphoff)) +if (!sctp_csum_check (cp->af, skb, pp, iph))return 0 ;/* Call application helper if needed */
@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,int ret;/* Some checks before mangling */ -if (!sctp_csum_check (cp->af, skb, pp, sctphoff)) +if (!sctp_csum_check (cp->af, skb, pp, iph))return 0 ;/* Call application helper if needed */
@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,static int sctp_csum_check (int af,struct sk_buff *skb,struct ip_vs_protocol *pp, -unsigned int sctphoff) +struct ip_vs_iphdr *iph) { +unsigned int sctphoff = iph->len;struct sctphdr *sh;__le32 cmp, val; +if (!ip_vs_checksum_needed (skb, af)) +return 1 ; sh = (struct sctphdr *)(skb->data + sctphoff); cmp = sh->checksum; val =sctp_compute_cksum (skb, sctphoff);if (val != cmp) {/* CRC failure, dump it. */ -IP_VS_DBG_RL_PKT (0 , af, pp, skb,0 , -"Failed checksum for" ); +IP_VS_DBG_RL_PKT (0 , af, pp, skb, iph->off, +"Failed checksum for" );return 0 ; }return 1 ;
net/netfilter/ipvs/ip_vs_proto_tcp.c +8 -36
70 lines not on this page · raw message
net/netfilter/ipvs/ip_vs_proto_udp.c +11 -39
80 lines not on this page · raw message
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 10/13 ipvs: fix places with wrong packet offsets
In reply to Pablo Neira AyusoFrom: Julian Anastasov <[email protected]>
The offsets we use to packet headers and payloads should be based on skb->data. We even already respect non-zero network offset in ip_vs_fill_iph_skb() but some places do it wrongly and support only zero offset which is expected for the IP layer where IPVS has hooks.
Change all places that instead of skb->data use offsets based on the network header (skb_network_header, ip_hdr, etc) because this doubles the network offset as noted by Sashiko.
For ip_vs_nat_icmp_v6() we can even rely on the IPv6 header parsing done by the caller.
--- include/net/ip_vs.h | 15 +-- net/netfilter/ipvs/ip_vs_app.c | 4 +- net/netfilter/ipvs/ip_vs_core.c | 133 +++++++++++++------------- net/netfilter/ipvs/ip_vs_proto_sctp.c | 4 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 4 +- net/netfilter/ipvs/ip_vs_proto_udp.c | 4 +- net/netfilter/ipvs/ip_vs_xmit.c | 26 ++--- 7 files changed, 97 insertions(+), 93 deletions(-)
include/net/ip_vs.h +9 -6
@@ -1974,8 +1974,9 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,int ip_vs_dr_xmit (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,struct ip_vs_iphdr *iph);int ip_vs_icmp_xmit (struct sk_buff *skb,struct ip_vs_conn *cp, -struct ip_vs_protocol *pp,int offset, -unsigned int hooknum,struct ip_vs_iphdr *iph); +struct ip_vs_protocol *pp,unsigned int toff, +unsigned int wlen,unsigned int hooknum, +struct ip_vs_iphdr *ciph);void ip_vs_dest_dst_rcu_free (struct rcu_head *head);#ifdef CONFIG_IP_VS_IPV6
@@ -1988,8 +1989,9 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,int ip_vs_dr_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,struct ip_vs_iphdr *iph);int ip_vs_icmp_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp, -struct ip_vs_protocol *pp,int offset, -unsigned int hooknum,struct ip_vs_iphdr *iph); +struct ip_vs_protocol *pp,unsigned int toff, +unsigned int wlen,unsigned int hooknum, +struct ip_vs_iphdr *ciph);#endif #ifdef CONFIG_SYSCTL
@@ -2061,11 +2063,12 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) }void ip_vs_nat_icmp (struct sk_buff *skb,struct ip_vs_protocol *pp, -struct ip_vs_conn *cp,int dir); +struct ip_vs_conn *cp,int dir,unsigned int toff);#ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6 (struct sk_buff *skb,struct ip_vs_protocol *pp, -struct ip_vs_conn *cp,int dir); +struct ip_vs_conn *cp,int dir,unsigned int toff, +struct ip_vs_iphdr *ciph);#endif static inline __wsum ip_vs_check_diff4 (__be32 old,__be32 new,__wsum oldsum)
net/netfilter/ipvs/ip_vs_app.c +2 -2
@@ -367,7 +367,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,if (skb_ensure_writable (skb, ipvsh->len +sizeof (*th)))return 0 ; - th = (struct tcphdr *)(skb_network_header (skb) + ipvsh->len); + th = (struct tcphdr *)(skb->data + ipvsh->len);/* * Remember seq number in case this pkt gets resized
@@ -443,7 +443,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,if (skb_ensure_writable (skb, ipvsh->len +sizeof (*th)))return 0 ; - th = (struct tcphdr *)(skb_network_header (skb) + ipvsh->len); + th = (struct tcphdr *)(skb->data + ipvsh->len);/* * Remember seq number in case this pkt gets resized
net/netfilter/ipvs/ip_vs_core.c +66 -67
332 lines not on this page · raw message
net/netfilter/ipvs/ip_vs_proto_sctp.c +2 -2
@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - sctph = (void *)skb_network_header (skb) + sctphoff; + sctph = (void *)skb->data + sctphoff;/* Only update csum if we really have to */ if (sctph->source != cp->vport || payload_csum ||
@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - sctph = (void *)skb_network_header (skb) + sctphoff; + sctph = (void *)skb->data + sctphoff;/* Only update csum if we really have to */ if (sctph->dest != cp->dport || payload_csum ||
net/netfilter/ipvs/ip_vs_proto_tcp.c +2 -2
@@ -179,7 +179,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - tcph = (void *)skb_network_header (skb) + tcphoff; + tcph = (void *)skb->data + tcphoff; tcph->source = cp->vport;/* Adjust TCP checksums */
@@ -260,7 +260,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - tcph = (void *)skb_network_header (skb) + tcphoff; + tcph = (void *)skb->data + tcphoff; tcph->dest = cp->dport;/*
net/netfilter/ipvs/ip_vs_proto_udp.c +2 -2
@@ -170,7 +170,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - udph = (void *)skb_network_header (skb) + udphoff; + udph = (void *)skb->data + udphoff; udph->source = cp->vport;/*
@@ -254,7 +254,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, payload_csum = true; } - udph = (void *)skb_network_header (skb) + udphoff; + udph = (void *)skb->data + udphoff; udph->dest = cp->dport;/*
net/netfilter/ipvs/ip_vs_xmit.c +14 -12
@@ -1502,8 +1502,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,*/ int ip_vs_icmp_xmit (struct sk_buff *skb,struct ip_vs_conn *cp, -struct ip_vs_protocol *pp,int offset,unsigned int hooknum, -struct ip_vs_iphdr *iph) +struct ip_vs_protocol *pp,unsigned int toff, +unsigned int wlen,unsigned int hooknum, +struct ip_vs_iphdr *ciph) {struct rtable *rt;/* Route to the other host */ int rc;
@@ -1515,7 +1516,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,translate address/port back */ if (IP_VS_FWD_METHOD (cp) !=IP_VS_CONN_F_MASQ ) {if (cp->packet_xmit) - rc = cp->packet_xmit (skb, cp, pp, iph); + rc = cp->packet_xmit (skb, cp, pp, ciph);else rc =NF_ACCEPT ;/* do not touch skb anymore */
@@ -1533,7 +1534,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,IP_VS_RT_MODE_LOCAL |IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; local =__ip_vs_get_out_rt (cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, -NULL , iph); +NULL , ciph);if (local <0 ) goto tx_error; rt =skb_rtable (skb);
@@ -1565,13 +1566,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, }/* copy-on-write the packet before mangling it */ -if (skb_ensure_writable (skb, offset)) +if (skb_ensure_writable (skb, wlen)) goto tx_error;if (skb_cow (skb, rt->dst.dev->hard_header_len)) goto tx_error; -ip_vs_nat_icmp (skb, pp, cp,0 ); +ip_vs_nat_icmp (skb, pp, cp,0 , toff);/* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df =1 ;
@@ -1587,8 +1588,9 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,#ifdef CONFIG_IP_VS_IPV6 int ip_vs_icmp_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp, -struct ip_vs_protocol *pp,int offset,unsigned int hooknum, -struct ip_vs_iphdr *ipvsh) +struct ip_vs_protocol *pp,unsigned int toff, +unsigned int wlen,unsigned int hooknum, +struct ip_vs_iphdr *ciph) {struct rt6_info *rt;/* Route to the other host */ int rc;
@@ -1600,7 +1602,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,translate address/port back */ if (IP_VS_FWD_METHOD (cp) !=IP_VS_CONN_F_MASQ ) {if (cp->packet_xmit) - rc = cp->packet_xmit (skb, cp, pp, ipvsh); + rc = cp->packet_xmit (skb, cp, pp, ciph);else rc =NF_ACCEPT ;/* do not touch skb anymore */
@@ -1617,7 +1619,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,IP_VS_RT_MODE_LOCAL |IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; local =__ip_vs_get_out_rt_v6 (cp->ipvs, cp->af, skb, cp->dest, - &cp->daddr.in6,NULL , ipvsh,0 , rt_mode); + &cp->daddr.in6,NULL , ciph,0 , rt_mode);if (local <0 ) goto tx_error; rt =dst_rt6_info (skb_dst (skb));
@@ -1649,13 +1651,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, }/* copy-on-write the packet before mangling it */ -if (skb_ensure_writable (skb, offset)) +if (skb_ensure_writable (skb, wlen)) goto tx_error;if (skb_cow (skb, rt->dst.dev->hard_header_len)) goto tx_error; -ip_vs_nat_icmp_v6 (skb, pp, cp,0 ); +ip_vs_nat_icmp_v6 (skb, pp, cp,0 , toff, ciph);/* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df =1 ;
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 11/13 ipvs: do not mangle ICMP replies for non-first fragments
In reply to Pablo Neira AyusoFrom: Julian Anastasov <[email protected]>
Sashiko warns that ip_vs_nat_icmp() unconditionally mangles the payload for embedded non-first IPv4 fragments. The problem is in the very old inverted pp->dont_defrag check which should not continue when embedded is a non-first TCP/UDP/SCTP fragment.
Check for embedded non-first fragment is also missing from ip_vs_out_icmp_v6(), it is needed before any connection lookups that expect ports after the network headers.
Drop the blocking code from ip_vs_in_icmp_v6() which prevents ICMPv6 from local clients to use non-MASQ forwarding.
--- include/net/ip_vs.h | 11 +++--- net/netfilter/ipvs/ip_vs_core.c | 61 ++++++++++++--------------------- net/netfilter/ipvs/ip_vs_xmit.c | 28 +++++++++++---- 3 files changed, 48 insertions(+), 52 deletions(-)
include/net/ip_vs.h +5 -6
@@ -1975,8 +1975,7 @@ int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);int ip_vs_icmp_xmit (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,unsigned int toff, -unsigned int wlen,unsigned int hooknum, -struct ip_vs_iphdr *ciph); +unsigned int hooknum,struct ip_vs_iphdr *ciph);void ip_vs_dest_dst_rcu_free (struct rcu_head *head);#ifdef CONFIG_IP_VS_IPV6
@@ -1990,8 +1989,7 @@ int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);int ip_vs_icmp_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,unsigned int toff, -unsigned int wlen,unsigned int hooknum, -struct ip_vs_iphdr *ciph); +unsigned int hooknum,struct ip_vs_iphdr *ciph);#endif #ifdef CONFIG_SYSCTL
@@ -2063,12 +2061,13 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) }void ip_vs_nat_icmp (struct sk_buff *skb,struct ip_vs_protocol *pp, -struct ip_vs_conn *cp,int dir,unsigned int toff); +struct ip_vs_conn *cp,int dir,unsigned int toff, +bool has_ports);#ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6 (struct sk_buff *skb,struct ip_vs_protocol *pp,struct ip_vs_conn *cp,int dir,unsigned int toff, -struct ip_vs_iphdr *ciph); +bool has_ports,struct ip_vs_iphdr *ciph);#endif static inline __wsum ip_vs_check_diff4 (__be32 old,__be32 new,__wsum oldsum)
net/netfilter/ipvs/ip_vs_core.c +21 -40
161 lines not on this page · raw message
net/netfilter/ipvs/ip_vs_xmit.c +22 -6
@@ -1503,13 +1503,14 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,int ip_vs_icmp_xmit (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,unsigned int toff, -unsigned int wlen,unsigned int hooknum, -struct ip_vs_iphdr *ciph) +unsigned int hooknum,struct ip_vs_iphdr *ciph) {struct rtable *rt;/* Route to the other host */ int rc;int local;int rt_mode, was_input; +bool has_ports = false; +unsigned int wlen;/* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be forwarded directly here, because there is no need to
@@ -1565,6 +1566,13 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, goto tx_error; } + wlen = ciph->len; +if (ciph->protocol ==IPPROTO_TCP || ciph->protocol ==IPPROTO_UDP || + ciph->protocol ==IPPROTO_SCTP ) { + wlen +=2 *sizeof (__u16);/* Also mangle ports */ + has_ports = true; + } +/* copy-on-write the packet before mangling it */ if (skb_ensure_writable (skb, wlen)) goto tx_error;
@@ -1572,7 +1580,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,if (skb_cow (skb, rt->dst.dev->hard_header_len)) goto tx_error; -ip_vs_nat_icmp (skb, pp, cp,0 , toff); +ip_vs_nat_icmp (skb, pp, cp,0 , toff, has_ports);/* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df =1 ;
@@ -1589,10 +1597,11 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,int ip_vs_icmp_xmit_v6 (struct sk_buff *skb,struct ip_vs_conn *cp,struct ip_vs_protocol *pp,unsigned int toff, -unsigned int wlen,unsigned int hooknum, -struct ip_vs_iphdr *ciph) +unsigned int hooknum,struct ip_vs_iphdr *ciph) { +bool has_ports = false;struct rt6_info *rt;/* Route to the other host */ +unsigned int wlen;int rc;int local;int rt_mode;
@@ -1650,6 +1659,13 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, goto tx_error; } + wlen = ciph->len; +if (ciph->protocol ==IPPROTO_TCP || ciph->protocol ==IPPROTO_UDP || + ciph->protocol ==IPPROTO_SCTP ) { + wlen +=2 *sizeof (__u16);/* Also mangle ports */ + has_ports = true; + } +/* copy-on-write the packet before mangling it */ if (skb_ensure_writable (skb, wlen)) goto tx_error;
@@ -1657,7 +1673,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,if (skb_cow (skb, rt->dst.dev->hard_header_len)) goto tx_error; -ip_vs_nat_icmp_v6 (skb, pp, cp,0 , toff, ciph); +ip_vs_nat_icmp_v6 (skb, pp, cp,0 , toff, has_ports, ciph);/* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df =1 ;
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 12/13 ipvs: clear the nfct flag under lock
In reply to Pablo Neira AyusoFrom: Julian Anastasov <[email protected]>
Sashiko warns that cp->flags should be changed under cp->lock
--- net/netfilter/ipvs/ip_vs_core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
net/netfilter/ipvs/ip_vs_core.c +8 -2
@@ -2194,8 +2194,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state }if (resched) { -if (!old_ct) +if (!old_ct) { +spin_lock_bh (&cp->lock); cp->flags &= ~IP_VS_CONN_F_NFCT ; +spin_unlock_bh (&cp->lock); + }if (!atomic_read (&cp->n_control))ip_vs_conn_expire_now (cp);__ip_vs_conn_put (cp);
@@ -2211,8 +2214,11 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stateif (sysctl_expire_nodest_conn (ipvs)) {bool old_ct =ip_vs_conn_uses_old_conntrack (cp, skb); -if (!old_ct) +if (!old_ct) { +spin_lock_bh (&cp->lock); cp->flags &= ~IP_VS_CONN_F_NFCT ; +spin_unlock_bh (&cp->lock); + }ip_vs_conn_expire_now (cp);__ip_vs_conn_put (cp);
Signature · 1 line
2.47.3
Pablo Neira Ayuso
maintainerPATCH 13/13 netfilter: nft_payload: fix mask build for partial field offload
In reply to Pablo Neira AyusoFrom: "Xiang Mei (Microsoft)" <[email protected]>
nft_payload_offload_mask() builds the offload match mask for a payload expression that covers only part of a header field. For a partial IPv6 address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which is undefined on the 32-bit int operand. It also trims only one word, so the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 the trim is skipped entirely), leaving the mask covering more bytes than the rule matches.
UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 shift exponent 120 is too large for 32-bit type 'int' ...
The match is byte-granular and struct nft_data is zero-initialised, so the correct mask is simply the first priv_len bytes set to 0xff. Set those bytes directly and drop the word/shift trimming; this removes the undefined shift and no longer over-masks the trailing bytes.
--- net/netfilter/nft_payload.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-)
net/netfilter/nft_payload.c +1 -11
@@ -259,9 +259,7 @@ static int nft_payload_dump(struct sk_buff *skb,static bool nft_payload_offload_mask (struct nft_offload_reg *reg,u32 priv_len,u32 field_len) { -unsigned int remainder, delta, k;struct nft_data mask = {}; -__be32 remainder_mask;if (priv_len == field_len) {memset (®->mask,0xff , priv_len);
@@ -270,15 +268,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,return false; } -memset (&mask,0xff , field_len); - remainder = priv_len %sizeof (u32); -if (remainder) { - k = priv_len /sizeof (u32); - delta = field_len - priv_len; - remainder_mask =htonl (~((1 << (delta *BITS_PER_BYTE )) -1 )); - mask.data[k] = (__force u32 )remainder_mask; - } - +memset (&mask,0xff , priv_len);memcpy (®->mask, &mask, field_len);return true;
Signature · 1 line
2.47.3
Jakub Kicinski
maintainerRe: [PATCH net 09/13] ipvs: fix the checksum validations
In reply to Pablo Neira AyusoQuoted 3 lines · On Wed, 22 Jul 2026 23:14:16 +0200 Pablo Neira Ayuso wrote:
+/**
+ *ip_vs_checksum_common_check- validate checksum for TCP/UDP/ICMP
+ */
this not-really-kdoc comment causes a bunch of new warnings, and this file was previously warning free. Which in turn makes our AI reviewer ignore the series. Could you respin?
Quoted 7 lines
include/net/ip_vs.h +7
-0+static inline bool ip_vs_checksum_common_check (struct sk_buff *skb, +int offset,int proto,int af) +{ +if (!ip_vs_checksum_needed (skb, af)) +return true; +return !nf_checksum (skb,NF_INET_LOCAL_IN , offset, proto, af); +}
Pablo Neira Ayuso
maintainerRe: [PATCH net 09/13] ipvs: fix the checksum validations
Hi,
Quoted 8 lines · Jakub Kicinski
Quoted 3 lines · On Wed, 22 Jul 2026 23:14:16 +0200 Pablo Neira Ayuso wrote:
+/**
+ *ip_vs_checksum_common_check- validate checksum for TCP/UDP/ICMP
+ */this not-really-kdoc comment causes a bunch of new warnings, and this file was previously warning free. Which in turn makes our AI reviewer ignore the series. Could you respin?
Yes, I can drop the patches that have the kdoc issues and respin.
Jakub Kicinski
maintainerRe: [PATCH net 09/13] ipvs: fix the checksum validations
Quoted 8 lines · On Thu, 23 Jul 2026 17:39:02 +0200 Pablo Neira Ayuso wrote:
Hi,
Quoted 5 lines · Jakub Kicinski
Quoted 3 lines · On Wed, 22 Jul 2026 23:14:16 +0200 Pablo Neira Ayuso wrote:
+/**
+ *ip_vs_checksum_common_check- validate checksum for TCP/UDP/ICMP
+ */
Quoted 5 lines
Quoted 3 lines
this not-really-kdoc comment causes a bunch of new warnings, and this file was previously warning free. Which in turn makes our AI reviewer ignore the series. Could you respin?
Yes, I can drop the patches that have the kdoc issues and respin.
Thanks! We're seeing extra delays due to lore outage overnight and a lot of submissions suddenly appearing in the pipeline. Looks like this will have to wait for rc6 :(
Pablo Neira Ayuso
maintainerRe: [PATCH net 09/13] ipvs: fix the checksum validations
Quoted 13 lines · Jakub Kicinski
Quoted 12 lines · On Thu, 23 Jul 2026 17:39:02 +0200 Pablo Neira Ayuso wrote:
Hi,
Quoted 8 lines · Jakub Kicinski
Quoted 3 lines · On Wed, 22 Jul 2026 23:14:16 +0200 Pablo Neira Ayuso wrote:
+/**
+ *ip_vs_checksum_common_check- validate checksum for TCP/UDP/ICMP
+ */this not-really-kdoc comment causes a bunch of new warnings, and this file was previously warning free. Which in turn makes our AI reviewer ignore the series. Could you respin?
Quoted 5 lines
Quoted 1 line
Yes, I can drop the patches that have the kdoc issues and respin.
Thanks! We're seeing extra delays due to lore outage overnight and a lot of submissions suddenly appearing in the pipeline. Looks like this will have to wait for rc6 :(
OK, no problem, thanks for explaining.