[RFC net-next v2 1/3] ipv6: ndisc: export the route attach as ndisc_attach_dst()
From: Xiang Mei (Microsoft) <hidden>
Date: 2026-07-29 23:01:32
Also in:
lkml
Subsystem:
networking [general], networking [ipv4/ipv6], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds
ndisc_send_skb() pushes the IPv6 header and transmits in one go, so a
caller that has to build the link-layer header and transmit the skb
itself cannot use it. bonding needs that split to insert VLAN tags
into its NS monitor probes.
Such a caller still has to attach a route before handing the packet to
netfilter: ip6t_mangle_out() calls ip6_route_me_harder() whenever a
rule changes the addresses, mark, hop limit or flow label, and that
dereferences skb_dst(skb) unconditionally. Neither icmp6_dst_alloc()
nor icmpv6_flow_init() is exported, so a module cannot attach one
itself.
Move the route attach into ndisc_attach_dst() and export it. It is a
no-op when the skb already carries a route, so ndisc_send_skb() is
unchanged for existing callers.
Signed-off-by: Xiang Mei (Microsoft) <redacted>
---
v2: v1 fixed the crash, ignoring the data corruption.
v2 tries to fix both.
include/net/ndisc.h | 3 +++
net/ipv6/ndisc.c | 53 ++++++++++++++++++++++++++++++++-------------
2 files changed, 41 insertions(+), 15 deletions(-)
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 3da1a6f8d3f9..f3e3c3e5c61e 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h@@ -411,6 +411,9 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, const struct in6_addr *daddr, const struct in6_addr *saddr, u64 nonce); +int ndisc_attach_dst(struct sk_buff *skb, const struct in6_addr *daddr, + const struct in6_addr *saddr); + void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, const struct in6_addr *saddr);
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index fe36b3f51285..91ffbfec9e5d 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c@@ -465,13 +465,44 @@ static void ip6_nd_hdr(struct sk_buff *skb, hdr->daddr = *daddr; } +/* Attach a route to a locally generated ndisc message; @skb is not consumed + * on failure. Split out of ndisc_send_skb() for callers that build the + * link-layer header themselves. + */ +int ndisc_attach_dst(struct sk_buff *skb, const struct in6_addr *daddr, + const struct in6_addr *saddr) +{ + u8 type = icmp6_hdr(skb)->icmp6_type; + struct dst_entry *dst; + struct flowi6 fl6; + struct sock *sk; + struct net *net; + + if (skb_dst(skb)) + return 0; + + rcu_read_lock(); + net = dev_net_rcu(skb->dev); + sk = net->ipv6.ndisc_sk; + icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex); + dst = icmp6_dst_alloc(skb->dev, &fl6); + rcu_read_unlock(); + + if (IS_ERR(dst)) + return PTR_ERR(dst); + + skb_dst_set(skb, dst); + return 0; +} +EXPORT_SYMBOL_GPL(ndisc_attach_dst); + void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, const struct in6_addr *saddr) { struct icmp6hdr *icmp6h = icmp6_hdr(skb); - struct dst_entry *dst = skb_dst(skb); struct net_device *dev; struct inet6_dev *idev; + struct dst_entry *dst; struct net *net; struct sock *sk; int err;
@@ -479,24 +510,16 @@ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, type = icmp6h->icmp6_type; + if (ndisc_attach_dst(skb, daddr, saddr)) { + kfree_skb(skb); + return; + } + rcu_read_lock(); net = dev_net_rcu(skb->dev); sk = net->ipv6.ndisc_sk; - if (!dst) { - struct flowi6 fl6; - int oif = skb->dev->ifindex; - - icmpv6_flow_init(sk, &fl6, type, saddr, daddr, oif); - dst = icmp6_dst_alloc(skb->dev, &fl6); - if (IS_ERR(dst)) { - rcu_read_unlock(); - kfree_skb(skb); - return; - } - - skb_dst_set(skb, dst); - } + dst = skb_dst(skb); icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
--
2.43.0