FAILED: patch "[PATCH] netfilter: nf_queue: pin bridge device while NFQUEUE holds" failed to apply to 5.15-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x c9c9b37f8c5505224e8d206184df3bb668ee00cf
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026072042-collide-upturned-2e18@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From c9c9b37f8c5505224e8d206184df3bb668ee00cf Mon Sep 17 00:00:00 2001
From: Haoze Xie <[email protected]>
Date: Mon, 8 Jun 2026 13:43:44 +0800
Subject: [PATCH] netfilter: nf_queue: pin bridge device while NFQUEUE holds
fake dst
The br_netfilter fake rtable is embedded in struct net_bridge and is attached to bridged packets with skb_dst_set_noref(). If such a packet is queued to NFQUEUE, __nf_queue() upgrades that fake dst with skb_dst_force().
At that point the queued skb can hold a real dst reference after bridge teardown has started. The problem is not that every bridged packet needs its own dst reference. The problem is that NFQUEUE can keep the bridge private fake dst alive after unregister begins.
Fix this by keeping the bridge fake dst model unchanged and pinning the bridge master device only while the packet sits in NFQUEUE. Record the bridge device in nf_queue_entry when the queued skb carries a bridge fake dst, take a device reference for the queue lifetime, and drop it when the queue entry is freed.
Also make sure queued entries are reaped when that bridge device goes down, and drop the redundant nf_bridge_info_exists() test from the fake dst detection.
This keeps netdev_priv(br->dev) alive until verdict completion, so the embedded fake rtable and its metrics backing storage cannot be freed out from under dst_release(). It also avoids the constant refcount bump and avoids using ipv4-specific dst helpers for IPv6 bridge traffic.
include/net/netfilter/nf_queue.h +1 -0
@@ -18,6 +18,7 @@ struct nf_queue_entry {unsigned int id;unsigned int hook_index;/* index in hook_entries->hook[] */ #if IS_ENABLED (CONFIG_BRIDGE_NETFILTER ) +struct net_device *bridge_dev;struct net_device *physin;struct net_device *physout;#endif
net/netfilter/nf_queue.c +14 -0
@@ -68,6 +68,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)nf_queue_sock_put (state->sk);#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER ) +dev_put (entry->bridge_dev);dev_put (entry->physin);dev_put (entry->physout);#endif
@@ -84,6 +85,8 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) {#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER )const struct sk_buff *skb = entry->skb; +struct dst_entry *dst =skb_dst (skb); +struct net_device *dev =NULL ;if (nf_bridge_info_exists (skb)) { entry->physin =nf_bridge_get_physindev (skb, entry->state.net);
@@ -92,6 +95,16 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) entry->physin =NULL ; entry->physout = NULL; } + +if (entry->state.pf ==NFPROTO_BRIDGE && + dst && (dst->flags &DST_FAKE_RTABLE )) + dev =dst_dev_rcu (dst); + +/* Must hold a reference on the bridge device: dst_hold() protects +* the dst itself, but the fake rtable is embedded in bridge-private +* storage that netdevice teardown can free independently. +*/ + entry->bridge_dev = dev;#endif }
@@ -108,6 +121,7 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)dev_hold (state->out);#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER ) +dev_hold (entry->bridge_dev);dev_hold (entry->physin);dev_hold (entry->physout);#endif
net/netfilter/nfnetlink_queue.c +3 -0
@@ -1262,6 +1262,9 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)if (physinif == ifindex || physoutif == ifindex)return 1 ; + +if (entry->bridge_dev && entry->bridge_dev->ifindex == ifindex) +return 1 ;#endif if (entry->skb_dev && entry->skb_dev->ifindex == ifindex)return 1 ;
Sasha Levin
maintainerPATCH 1/6 tcp: convert to dev_net_rcu()
From: Eric Dumazet <[email protected]>
[ Upstream commit e7b9ecce562ca6a1de32c56c597fa45e08c44ec0 ]
TCP uses of dev_net() are under RCU protection, change them to dev_net_rcu() to get LOCKDEP support.
Stable-dep-of: c9c9b37f8c55 ("netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst")
--- include/net/inet6_hashtables.h | 2 +- include/net/inet_hashtables.h | 2 +- net/ipv4/tcp_ipv4.c | 8 ++++---- net/ipv4/tcp_metrics.c | 6 +++--- net/ipv6/tcp_ipv6.c | 10 +++++----- 5 files changed, 14 insertions(+), 14 deletions(-)
include/net/inet6_hashtables.h +1 -1
@@ -106,7 +106,7 @@ static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo,if (sk)return sk; -return __inet6_lookup (dev_net (skb_dst (skb)->dev), hashinfo, skb, +return __inet6_lookup (dev_net_rcu (skb_dst (skb)->dev), hashinfo, skb, doff, &ipv6_hdr (skb)->saddr, sport, &ipv6_hdr (skb)->daddr,ntohs (dport), iif, sdif, refcounted);
include/net/inet_hashtables.h +1 -1
@@ -356,7 +356,7 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo,if (sk)return sk; -return __inet_lookup (dev_net (skb_dst (skb)->dev), hashinfo, skb, +return __inet_lookup (dev_net_rcu (skb_dst (skb)->dev), hashinfo, skb, doff, iph->saddr, sport, iph->daddr, dport,inet_iif (skb), sdif, refcounted);
net/ipv4/tcp_ipv4.c +4 -4
@@ -486,7 +486,7 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)struct request_sock *fastopen;u32 seq, snd_una;int err; -struct net *net =dev_net (skb->dev); +struct net *net =dev_net_rcu (skb->dev); sk = __inet_lookup_established(net, &tcp_hashinfo, iph->daddr, th->dest, iph->saddr, ntohs(th->source),
@@ -718,7 +718,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) arg.iov[0 ].iov_base = (unsigned char *)&rep; arg.iov[0 ].iov_len =sizeof (rep.th); - net = sk ?sock_net (sk) :dev_net (skb_dst (skb)->dev); + net = sk ?sock_net (sk) :dev_net_rcu (skb_dst (skb)->dev);#ifdef CONFIG_TCP_MD5SIG rcu_read_lock (); hash_location =tcp_parse_md5sig_option (th);
@@ -1794,7 +1794,7 @@ int tcp_v4_early_demux(struct sk_buff *skb)if (th->doff <sizeof (struct tcphdr ) /4 )return 0 ; - sk =__inet_lookup_established (dev_net (skb->dev), &tcp_hashinfo, + sk =__inet_lookup_established (dev_net_rcu (skb->dev), &tcp_hashinfo, iph->saddr, th->source, iph->daddr,ntohs (th->dest), skb->skb_iif,inet_sdif (skb));
@@ -1989,7 +1989,7 @@ static void tcp_v4_fill_cb(struct sk_buff *skb, const struct iphdr *iph,int tcp_v4_rcv (struct sk_buff *skb) { -struct net *net =dev_net (skb->dev); +struct net *net =dev_net_rcu (skb->dev);struct sk_buff *skb_to_free;int sdif =inet_sdif (skb);int dif =inet_iif (skb);
net/ipv4/tcp_metrics.c +3 -3
@@ -170,7 +170,7 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst,bool reclaim = false;spin_lock_bh (&tcp_metrics_lock); - net =dev_net (dst->dev); + net =dev_net_rcu (dst->dev);/* While waiting for the spin-lock the cache might have been populated * with this entry and so we have to check again.
@@ -273,7 +273,7 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req,return NULL ; } - net =dev_net (dst->dev); + net =dev_net_rcu (dst->dev); hash ^=net_hash_mix (net); hash =hash_32 (hash, tcp_metrics_hash_log);
@@ -318,7 +318,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk,else return NULL ; - net =dev_net (dst->dev); + net =dev_net_rcu (dst->dev); hash ^=net_hash_mix (net); hash =hash_32 (hash, tcp_metrics_hash_log);
net/ipv6/tcp_ipv6.c +5 -5
@@ -382,7 +382,7 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, {const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data;const struct tcphdr *th = (struct tcphdr *)(skb->data+offset); -struct net *net =dev_net (skb->dev); +struct net *net =dev_net_rcu (skb->dev);struct request_sock *fastopen;struct ipv6_pinfo *np;struct tcp_sock *tp;
@@ -897,7 +897,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32struct tcphdr *t1;struct sk_buff *buff;struct flowi6 fl6; -struct net *net = sk ?sock_net (sk) :dev_net (skb_dst (skb)->dev); +struct net *net = sk ?sock_net (sk) :dev_net_rcu (skb_dst (skb)->dev);struct sock *ctl_sk = net->ipv6.tcp_sk;unsigned int tot_len =sizeof (struct tcphdr );__be32 mrst =0 , *topt;
@@ -1047,7 +1047,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)if (!sk && !ipv6_unicast_destination (skb))return ; - net = sk ?sock_net (sk) :dev_net (skb_dst (skb)->dev); + net = sk ?sock_net (sk) :dev_net_rcu (skb_dst (skb)->dev);#ifdef CONFIG_TCP_MD5SIG rcu_read_lock (); hash_location =tcp_parse_md5sig_option (th);
@@ -1633,7 +1633,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)bool refcounted;struct sock *sk;int ret; -struct net *net =dev_net (skb->dev); +struct net *net =dev_net_rcu (skb->dev);if (skb->pkt_type !=PACKET_HOST ) goto discard_it;
@@ -1871,7 +1871,7 @@ void tcp_v6_early_demux(struct sk_buff *skb)return ;/* Note : We use inet6_iif() here, not tcp_v6_iif() */ - sk =__inet6_lookup_established (dev_net (skb->dev), &tcp_hashinfo, + sk =__inet6_lookup_established (dev_net_rcu (skb->dev), &tcp_hashinfo, &hdr->saddr, th->source, &hdr->daddr,ntohs (th->dest),inet6_iif (skb),inet6_sdif (skb));
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/6 net: dst: annotate data-races around dst->input
From: Eric Dumazet <[email protected]>
[ Upstream commit f1c5fd34891a1c242885f48c2e4dc52df180f311 ]
dst_dev_put() can overwrite dst->input while other cpus might read this field (for instance from dst_input())
Add READ_ONCE()/WRITE_ONCE() annotations to suppress potential issues.
We will likely need full RCU protection later.
Stable-dep-of: c9c9b37f8c55 ("netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst")
--- include/net/dst.h | 2 +- include/net/lwtunnel.h | 4 ++-- net/core/dst.c | 2 +- net/ipv4/route.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-)
include/net/dst.h +1 -1
@@ -459,7 +459,7 @@ INDIRECT_CALLABLE_DECLARE(int ip_local_deliver(struct sk_buff *));/* Input packet from network to transport. */ static inline int dst_input (struct sk_buff *skb) { -return INDIRECT_CALL_INET (skb_dst (skb)->input, +return INDIRECT_CALL_INET (READ_ONCE (skb_dst (skb)->input), ip6_input, ip_local_deliver, skb); }
include/net/lwtunnel.h +2 -2
@@ -142,8 +142,8 @@ static inline void lwtunnel_set_redirect(struct dst_entry *dst) dst->output = lwtunnel_output; }if (lwtunnel_input_redirect (dst->lwtstate)) { - dst->lwtstate->orig_input = dst->input; - dst->input = lwtunnel_input; + dst->lwtstate->orig_input =READ_ONCE (dst->input); +WRITE_ONCE (dst->input, lwtunnel_input); } }#else
net/core/dst.c +1 -1
@@ -149,7 +149,7 @@ void dst_dev_put(struct dst_entry *dst) dst->obsolete =DST_OBSOLETE_DEAD ;if (dst->ops->ifdown) dst->ops->ifdown (dst, dev, true); - dst->input = dst_discard; +WRITE_ONCE (dst->input, dst_discard); dst->output = dst_discard_out; dst->dev = blackhole_netdev;dev_hold (dst->dev);
net/ipv4/route.c +1 -1
@@ -1703,7 +1703,7 @@ struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt) new_rt->rt_gw6 = rt->rt_gw6;INIT_LIST_HEAD (&new_rt->rt_uncached); - new_rt->dst.input = rt->dst.input; + new_rt->dst.input =READ_ONCE (rt->dst.input); new_rt->dst.output = rt->dst.output; new_rt->dst.error = rt->dst.error; new_rt->dst.lastuse = jiffies;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/6 net: dst: annotate data-races around dst->output
In reply to Sasha LevinFrom: Eric Dumazet <[email protected]>
[ Upstream commit 2dce8c52a98995c4719def6f88629ab1581c0b82 ]
dst_dev_put() can overwrite dst->output while other cpus might read this field (for instance from dst_output())
Add READ_ONCE()/WRITE_ONCE() annotations to suppress potential issues.
We will likely need RCU protection in the future.
Stable-dep-of: c9c9b37f8c55 ("netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst")
--- include/net/dst.h | 2 +- include/net/lwtunnel.h | 4 ++-- net/core/dst.c | 2 +- net/ipv4/route.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-)
include/net/dst.h +1 -1
@@ -449,7 +449,7 @@ INDIRECT_CALLABLE_DECLARE(int ip_output(struct net *, struct sock *,/* Output packet to network from transport. */ static inline int dst_output (struct net *net,struct sock *sk,struct sk_buff *skb) { -return INDIRECT_CALL_INET (skb_dst (skb)->output, +return INDIRECT_CALL_INET (READ_ONCE (skb_dst (skb)->output), ip6_output, ip_output, net, sk, skb); }
include/net/lwtunnel.h +2 -2
@@ -138,8 +138,8 @@ int bpf_lwt_push_ip_encap(struct sk_buff *skb, void *hdr, u32 len,static inline void lwtunnel_set_redirect (struct dst_entry *dst) {if (lwtunnel_output_redirect (dst->lwtstate)) { - dst->lwtstate->orig_output = dst->output; - dst->output = lwtunnel_output; + dst->lwtstate->orig_output =READ_ONCE (dst->output); +WRITE_ONCE (dst->output, lwtunnel_output); }if (lwtunnel_input_redirect (dst->lwtstate)) { dst->lwtstate->orig_input =READ_ONCE (dst->input);
net/core/dst.c +1 -1
@@ -150,7 +150,7 @@ void dst_dev_put(struct dst_entry *dst)if (dst->ops->ifdown) dst->ops->ifdown (dst, dev, true);WRITE_ONCE (dst->input, dst_discard); - dst->output = dst_discard_out; +WRITE_ONCE (dst->output, dst_discard_out); dst->dev = blackhole_netdev;dev_hold (dst->dev);dev_put (dev);
net/ipv4/route.c +1 -1
@@ -1704,7 +1704,7 @@ struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt)INIT_LIST_HEAD (&new_rt->rt_uncached); new_rt->dst.input =READ_ONCE (rt->dst.input); - new_rt->dst.output = rt->dst.output; + new_rt->dst.output =READ_ONCE (rt->dst.output); new_rt->dst.error = rt->dst.error; new_rt->dst.lastuse = jiffies; new_rt->dst.lwtstate =lwtstate_get (rt->dst.lwtstate);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/6 net: dst: add four helpers to annotate data-races around dst->dev
In reply to Sasha LevinFrom: Eric Dumazet <[email protected]>
[ Upstream commit 88fe14253e181878c2ddb51a298ae8c468a63010 ]
dst->dev is read locklessly in many contexts,
and written in dst_dev_put().
Fixing all the races is going to need many changes.
We probably will have to add full RCU protection.
Add three helpers to ease this painful process.
static inline struct net_device *dst_dev(const struct dst_entry *dst)
{
return READ_ONCE(dst->dev);
}static inline struct net_device *skb_dst_dev(const struct sk_buff *skb)
{
return dst_dev(skb_dst(skb));
}static inline struct net *skb_dst_dev_net(const struct sk_buff *skb)
{
return dev_net(skb_dst_dev(skb));
}static inline struct net *skb_dst_dev_net_rcu(const struct sk_buff *skb)
{
return dev_net_rcu(skb_dst_dev(skb));
}Stable-dep-of: c9c9b37f8c55 ("netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst")
--- include/net/dst.h | 20 ++++++++++++++++++++ net/core/dst.c | 4 ++-- net/core/sock.c | 6 +++--- 3 files changed, 25 insertions(+), 5 deletions(-)
include/net/dst.h +20 -0
@@ -566,6 +566,26 @@ static inline struct net_device *skb_dst_dev_rcu(const struct sk_buff *skb)return dst_dev_rcu (skb_dst (skb)); } +static inline struct net_device *dst_dev (const struct dst_entry *dst) +{ +return READ_ONCE (dst->dev); +} + +static inline struct net_device *skb_dst_dev (const struct sk_buff *skb) +{ +return dst_dev (skb_dst (skb)); +} + +static inline struct net *skb_dst_dev_net (const struct sk_buff *skb) +{ +return dev_net (skb_dst_dev (skb)); +} + +static inline struct net *skb_dst_dev_net_rcu (const struct sk_buff *skb) +{ +return dev_net_rcu (skb_dst_dev (skb)); +} +struct dst_entry *dst_blackhole_check (struct dst_entry *dst,u32 cookie);void dst_blackhole_update_pmtu (struct dst_entry *dst,struct sock *sk,struct sk_buff *skb,u32 mtu,bool confirm_neigh);
net/core/dst.c +2 -2
@@ -151,7 +151,7 @@ void dst_dev_put(struct dst_entry *dst) dst->ops->ifdown (dst, dev, true);WRITE_ONCE (dst->input, dst_discard);WRITE_ONCE (dst->output, dst_discard_out); - dst->dev = blackhole_netdev; +WRITE_ONCE (dst->dev, blackhole_netdev);dev_hold (dst->dev);dev_put (dev); }
@@ -272,7 +272,7 @@ unsigned int dst_blackhole_mtu(const struct dst_entry *dst) {unsigned int mtu =dst_metric_raw (dst,RTAX_MTU ); -return mtu ? : dst->dev->mtu; +return mtu ? :dst_dev (dst)->mtu; }EXPORT_SYMBOL_GPL (dst_blackhole_mtu);
net/core/sock.c +3 -3
@@ -2193,7 +2193,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) {u32 max_segs =1 ; - sk->sk_route_caps = dst->dev->features | sk->sk_route_forced_caps; + sk->sk_route_caps =dst_dev (dst)->features | sk->sk_route_forced_caps;if (sk->sk_route_caps &NETIF_F_GSO ) sk->sk_route_caps |=NETIF_F_GSO_SOFTWARE ; sk->sk_route_caps &= ~sk->sk_route_nocaps;
@@ -2202,8 +2202,8 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) sk->sk_route_caps &= ~NETIF_F_GSO_MASK ; }else { sk->sk_route_caps |=NETIF_F_SG |NETIF_F_HW_CSUM ; - sk->sk_gso_max_size = dst->dev->gso_max_size; - max_segs =max_t (u32, dst->dev->gso_max_segs,1 ); + sk->sk_gso_max_size =dst_dev (dst)->gso_max_size; + max_segs =max_t (u32,dst_dev (dst)->gso_max_segs,1 ); } } sk->sk_gso_max_segs = max_segs;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/6 ipv4: adopt dst_dev, skb_dst_dev and skb_dst_dev_net[_rcu]
In reply to Sasha LevinFrom: Eric Dumazet <[email protected]>
[ Upstream commit a74fc62eec155ca5a6da8ff3856f3dc87fe24558 ]
Use the new helpers as a first step to deal with
potential dst->dev races.
Stable-dep-of: c9c9b37f8c55 ("netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst")
--- include/net/inet_hashtables.h | 2 +- include/net/ip.h | 11 ++++++----- include/net/route.h | 2 +- net/ipv4/icmp.c | 24 +++++++++++++----------- net/ipv4/igmp.c | 2 +- net/ipv4/ip_fragment.c | 2 +- net/ipv4/ip_output.c | 4 ++-- net/ipv4/ip_vti.c | 4 ++-- net/ipv4/netfilter.c | 4 ++-- net/ipv4/route.c | 6 +++--- net/ipv4/tcp_fastopen.c | 4 +++- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_metrics.c | 8 ++++---- net/ipv4/xfrm4_output.c | 2 +- 14 files changed, 41 insertions(+), 36 deletions(-)
include/net/inet_hashtables.h +1 -1
@@ -356,7 +356,7 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo,if (sk)return sk; -return __inet_lookup (dev_net_rcu (skb_dst (skb)->dev), hashinfo, skb, +return __inet_lookup (skb_dst_dev_net_rcu (skb), hashinfo, skb, doff, iph->saddr, sport, iph->daddr, dport,inet_iif (skb), sdif, refcounted);
include/net/ip.h +6 -5
@@ -441,7 +441,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, bool forwarding) { const struct rtable *rt = container_of(dst, struct rtable, dst); -struct net *net =dev_net (dst->dev); +struct net *net =dev_net (dst_dev (dst));unsigned int mtu; if (READ_ONCE(net->ipv4.sysctl_ip_fwd_use_pmtu) ||
@@ -457,7 +457,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,if (mtu) goto out; - mtu =READ_ONCE (dst->dev->mtu); + mtu =READ_ONCE (dst_dev (dst)->mtu);if (unlikely (ip_mtu_locked (dst))) {if (rt->rt_uses_gateway && mtu >576 )
@@ -473,16 +473,17 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,static inline unsigned int ip_skb_dst_mtu (struct sock *sk,const struct sk_buff *skb) { +const struct dst_entry *dst =skb_dst (skb);unsigned int mtu;if (!sk || !sk_fullsock (sk) ||ip_sk_use_pmtu (sk)) {bool forwarding =IPCB (skb)->flags &IPSKB_FORWARDED ; -return ip_dst_mtu_maybe_forward (skb_dst (skb), forwarding); +return ip_dst_mtu_maybe_forward (dst, forwarding); } - mtu =min (READ_ONCE (skb_dst (skb)->dev->mtu),IP_MAX_MTU ); -return mtu -lwtunnel_headroom (skb_dst (skb)->lwtstate, mtu); + mtu =min (READ_ONCE (dst_dev (dst)->mtu),IP_MAX_MTU ); +return mtu -lwtunnel_headroom (dst->lwtstate, mtu); } struct dst_metrics *ip_fib_metrics_init(struct net *net, struct nlattr *fc_mx,
include/net/route.h +1 -1
@@ -356,7 +356,7 @@ static inline int ip4_dst_hoplimit(const struct dst_entry *dst)const struct net *net;rcu_read_lock (); - net =dev_net_rcu (dst->dev); + net =dev_net_rcu (dst_dev (dst)); hoplimit =READ_ONCE (net->ipv4.sysctl_ip_default_ttl);rcu_read_unlock (); }
net/ipv4/icmp.c +13 -11
@@ -318,6 +318,7 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, {struct dst_entry *dst = &rt->dst;struct inet_peer *peer; +struct net_device *dev;bool rc = true;int vif;
@@ -325,10 +326,11 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, goto out;/* No rate limit on loopback */ -if (dst->dev && (dst->dev->flags&IFF_LOOPBACK )) + dev =dst_dev (dst); +if (dev && (dev->flags &IFF_LOOPBACK )) goto out; - vif =l3mdev_master_ifindex (dst->dev); + vif =l3mdev_master_ifindex (dev); peer =inet_getpeer_v4 (net->ipv4.peers, fl4->daddr, vif,1 ); rc =inet_peer_xrlim_allow (peer,READ_ONCE (net->ipv4.sysctl_icmp_ratelimit));
@@ -474,13 +476,13 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)*/ static struct net_device *icmp_get_route_lookup_dev (struct sk_buff *skb) { -struct net_device *route_lookup_dev =NULL ; +struct net_device *dev = skb->dev; +const struct dst_entry *dst; -if (skb->dev) - route_lookup_dev = skb->dev; -else if (skb_dst (skb)) - route_lookup_dev =skb_dst (skb)->dev; -return route_lookup_dev; +if (dev) +return dev; + dst =skb_dst (skb); +return dst ?dst_dev (dst) :NULL ; } static struct rtable *icmp_route_lookup(struct net *net,
@@ -870,7 +872,7 @@ static bool icmp_unreach(struct sk_buff *skb)struct net *net;u32 info =0 ; - net =dev_net (skb_dst (skb)->dev); + net =skb_dst_dev_net (skb);/* * Incomplete header ?
@@ -1011,7 +1013,7 @@ static bool icmp_echo(struct sk_buff *skb)struct icmp_bxm icmp_param;struct net *net; - net =dev_net (skb_dst (skb)->dev); + net =skb_dst_dev_net (skb);/* should there be an ICMP stat for ignored echos? */ if (net->ipv4.sysctl_icmp_echo_ignore_all)return true;
@@ -1188,7 +1190,7 @@ static bool icmp_timestamp(struct sk_buff *skb)return true; out_err: -__ICMP_INC_STATS (dev_net (skb_dst (skb)->dev),ICMP_MIB_INERRORS ); +__ICMP_INC_STATS (skb_dst_dev_net (skb),ICMP_MIB_INERRORS );return false; }
net/ipv4/igmp.c +1 -1
@@ -424,7 +424,7 @@ static int igmpv3_sendpack(struct sk_buff *skb) pig->csum =ip_compute_csum (igmp_hdr (skb), igmplen); -return ip_local_out (dev_net (skb_dst (skb)->dev), skb->sk, skb); +return ip_local_out (skb_dst_dev_net (skb), skb->sk, skb); }static int grec_size (struct ip_mc_list *pmc,int type,int gdel,int sdel)
net/ipv4/ip_fragment.c +1 -1
@@ -475,7 +475,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,/* Process an incoming IP datagram fragment. */ int ip_defrag (struct net *net,struct sk_buff *skb,u32 user) { -struct net_device *dev = skb->dev ? :skb_dst (skb)->dev; +struct net_device *dev = skb->dev ? :skb_dst_dev (skb);int vif =l3mdev_master_ifindex_rcu (dev);struct ipq *qp;
net/ipv4/ip_output.c +2 -2
@@ -113,7 +113,7 @@ int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) skb->protocol =htons (ETH_P_IP );return nf_hook (NFPROTO_IPV4 ,NF_INET_LOCAL_OUT , - net, sk, skb,NULL ,skb_dst (skb)->dev, + net, sk, skb,NULL ,skb_dst_dev (skb), dst_output); }
@@ -195,7 +195,7 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s {struct dst_entry *dst =skb_dst (skb);struct rtable *rt = (struct rtable *)dst; -struct net_device *dev = dst->dev; +struct net_device *dev =dst_dev (dst);unsigned int hh_len =LL_RESERVED_SPACE (dev);struct neighbour *neigh;bool is_v6gw = false;
net/ipv4/ip_vti.c +2 -2
@@ -226,7 +226,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, goto tx_error_icmp; } - tdev = dst->dev; + tdev =dst_dev (dst);if (tdev == dev) {dst_release (dst);
@@ -256,7 +256,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, xmit:skb_scrub_packet (skb, !net_eq (tunnel->net,dev_net (dev)));skb_dst_set (skb, dst); - skb->dev =skb_dst (skb)->dev; + skb->dev =skb_dst_dev (skb); err =dst_output (tunnel->net, skb->sk, skb);if (net_xmit_eval (err) ==0 )
net/ipv4/netfilter.c +2 -2
@@ -19,12 +19,12 @@/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */ int ip_route_me_harder (struct net *net,struct sock *sk,struct sk_buff *skb,unsigned int addr_type) { +struct net_device *dev =skb_dst_dev (skb);const struct iphdr *iph =ip_hdr (skb);struct rtable *rt;struct flowi4 fl4 = {};__be32 saddr = iph->saddr;__u8 flags; -struct net_device *dev =skb_dst (skb)->dev;struct flow_keys flkeys;unsigned int hh_len;
@@ -74,7 +74,7 @@ int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, un#endif /* Change in oif may mean change in hh_len. */ - hh_len =skb_dst (skb)->dev->hard_header_len; + hh_len =skb_dst_dev (skb)->hard_header_len;if (skb_headroom (skb) < hh_len &&pskb_expand_head (skb,HH_DATA_ALIGN (hh_len -skb_headroom (skb)),0 ,GFP_ATOMIC ))
net/ipv4/route.c +3 -3
@@ -424,7 +424,7 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, const void *daddr) { const struct rtable *rt = container_of(dst, struct rtable, dst); -struct net_device *dev = dst->dev; +struct net_device *dev =dst_dev (dst);struct neighbour *n;rcu_read_lock_bh ();
@@ -451,7 +451,7 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,static void ipv4_confirm_neigh (const struct dst_entry *dst,const void *daddr) { const struct rtable *rt = container_of(dst, struct rtable, dst); -struct net_device *dev = dst->dev; +struct net_device *dev =dst_dev (dst);const __be32 *pkey = daddr;if (rt->rt_gw_family ==AF_INET ) {
@@ -1045,7 +1045,7 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)return ;rcu_read_lock (); - net =dev_net_rcu (dst->dev); + net =dev_net_rcu (dst_dev (dst));if (mtu < net->ipv4.ip_rt_min_pmtu) { lock = true; mtu =min (old_mtu, net->ipv4.ip_rt_min_pmtu);
net/ipv4/tcp_fastopen.c +3 -1
@@ -559,6 +559,7 @@ bool tcp_fastopen_active_should_disable(struct sock *sk)void tcp_fastopen_active_disable_ofo_check (struct sock *sk) {struct tcp_sock *tp =tcp_sk (sk); +struct net_device *dev;struct dst_entry *dst;struct sk_buff *skb;
@@ -576,7 +577,8 @@ void tcp_fastopen_active_disable_ofo_check(struct sock *sk) }else if (tp->syn_fastopen_ch &&atomic_read (&sock_net (sk)->ipv4.tfo_active_disable_times)) { dst =sk_dst_get (sk); -if (!(dst && dst->dev && (dst->dev->flags &IFF_LOOPBACK ))) + dev = dst ?dst_dev (dst) :NULL ; +if (!(dev && (dev->flags &IFF_LOOPBACK )))atomic_set (&sock_net (sk)->ipv4.tfo_active_disable_times,0 );dst_release (dst); }
net/ipv4/tcp_ipv4.c +1 -1
@@ -718,7 +718,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) arg.iov[0 ].iov_base = (unsigned char *)&rep; arg.iov[0 ].iov_len =sizeof (rep.th); - net = sk ?sock_net (sk) :dev_net_rcu (skb_dst (skb)->dev); + net = sk ?sock_net (sk) :skb_dst_dev_net_rcu (skb);#ifdef CONFIG_TCP_MD5SIG rcu_read_lock (); hash_location =tcp_parse_md5sig_option (th);
net/ipv4/tcp_metrics.c +4 -4
@@ -166,11 +166,11 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, unsigned int hash) {struct tcp_metrics_block *tm; -struct net *net;bool reclaim = false; +struct net *net;spin_lock_bh (&tcp_metrics_lock); - net =dev_net_rcu (dst->dev); + net =dev_net_rcu (dst_dev (dst));/* While waiting for the spin-lock the cache might have been populated * with this entry and so we have to check again.
@@ -273,7 +273,7 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req,return NULL ; } - net =dev_net_rcu (dst->dev); + net =dev_net_rcu (dst_dev (dst)); hash ^=net_hash_mix (net); hash =hash_32 (hash, tcp_metrics_hash_log);
@@ -318,7 +318,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk,else return NULL ; - net =dev_net_rcu (dst->dev); + net =dev_net_rcu (dst_dev (dst)); hash ^=net_hash_mix (net); hash =hash_32 (hash, tcp_metrics_hash_log);
net/ipv4/xfrm4_output.c +1 -1
@@ -31,7 +31,7 @@ static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb)int xfrm4_output (struct net *net,struct sock *sk,struct sk_buff *skb) {return NF_HOOK_COND (NFPROTO_IPV4 ,NF_INET_POST_ROUTING , - net, sk, skb, skb->dev,skb_dst (skb)->dev, + net, sk, skb, skb->dev,skb_dst_dev (skb), __xfrm4_output, !(IPCB (skb)->flags &IPSKB_REROUTED )); }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 6/6 netfilter: nf_queue: pin bridge device while NFQUEUE holds fake dst
In reply to Sasha LevinFrom: Haoze Xie <[email protected]>
[ Upstream commit c9c9b37f8c5505224e8d206184df3bb668ee00cf ]
The br_netfilter fake rtable is embedded in struct net_bridge and is attached to bridged packets with skb_dst_set_noref(). If such a packet is queued to NFQUEUE, __nf_queue() upgrades that fake dst with skb_dst_force().
At that point the queued skb can hold a real dst reference after bridge teardown has started. The problem is not that every bridged packet needs its own dst reference. The problem is that NFQUEUE can keep the bridge private fake dst alive after unregister begins.
Fix this by keeping the bridge fake dst model unchanged and pinning the bridge master device only while the packet sits in NFQUEUE. Record the bridge device in nf_queue_entry when the queued skb carries a bridge fake dst, take a device reference for the queue lifetime, and drop it when the queue entry is freed.
Also make sure queued entries are reaped when that bridge device goes down, and drop the redundant nf_bridge_info_exists() test from the fake dst detection.
This keeps netdev_priv(br->dev) alive until verdict completion, so the embedded fake rtable and its metrics backing storage cannot be freed out from under dst_release(). It also avoids the constant refcount bump and avoids using ipv4-specific dst helpers for IPv6 bridge traffic.
--- include/net/netfilter/nf_queue.h | 1 + net/netfilter/nf_queue.c | 14 ++++++++++++++ net/netfilter/nfnetlink_queue.c | 3 +++ 3 files changed, 18 insertions(+)
include/net/netfilter/nf_queue.h +1 -0
@@ -16,6 +16,7 @@ struct nf_queue_entry {unsigned int id;unsigned int hook_index;/* index in hook_entries->hook[] */ #if IS_ENABLED (CONFIG_BRIDGE_NETFILTER ) +struct net_device *bridge_dev;struct net_device *physin;struct net_device *physout;#endif
net/netfilter/nf_queue.c +14 -0
@@ -67,6 +67,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)nf_queue_sock_put (state->sk);#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER ) +dev_put (entry->bridge_dev);dev_put (entry->physin);dev_put (entry->physout);#endif
@@ -83,6 +84,8 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) {#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER )const struct sk_buff *skb = entry->skb; +struct dst_entry *dst =skb_dst (skb); +struct net_device *dev =NULL ;struct nf_bridge_info *nf_bridge; nf_bridge =nf_bridge_info_get (skb);
@@ -93,6 +96,16 @@ static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry) entry->physin =NULL ; entry->physout = NULL; } + +if (entry->state.pf ==NFPROTO_BRIDGE && + dst && (dst->flags &DST_FAKE_RTABLE )) + dev =dst_dev_rcu (dst); + +/* Must hold a reference on the bridge device: dst_hold() protects +* the dst itself, but the fake rtable is embedded in bridge-private +* storage that netdevice teardown can free independently. +*/ + entry->bridge_dev = dev;#endif }
@@ -109,6 +122,7 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)dev_hold (state->out);#if IS_ENABLED (CONFIG_BRIDGE_NETFILTER ) +dev_hold (entry->bridge_dev);dev_hold (entry->physin);dev_hold (entry->physout);#endif
net/netfilter/nfnetlink_queue.c +3 -0
@@ -943,6 +943,9 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)if (physinif == ifindex || physoutif == ifindex)return 1 ; + +if (entry->bridge_dev && entry->bridge_dev->ifindex == ifindex) +return 1 ;#endif if (entry->skb_dev && entry->skb_dev->ifindex == ifindex)return 1 ;
Signature · 1 line
2.53.0