[PATCH net-next 04/13] amt: send the Membership Query over IPv6
flat view
HOTtoday
From: Omar Ramadan <hidden>
Date: 2026-10-09 12:24:38
Also in:
linux-kselftest, lkml
Subsystem:
amt (automatic multicast tunneling), networking drivers, the rest · Maintainers:
Taehee Yoo, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
After the Request, a relay answers each gateway with a Membership Query through amt_send_membership_query(), which routes and transmits only over IPv4. Rather than adding an IPv6 twin of the function, move its routing and transmit step into amt_udp_xmit(). Given an skb that already carries the AMT message, a union amt_addr destination and the port pair, it sends over the device's family: ip_route_output_key() and udp_tunnel_xmit_skb() for IPv4, as before, and amt_route6() and udp_tunnel6_xmit_skb(), which fills in the mandatory IPv6 UDP checksum, for IPv6. Both use AMT_TOS, as the IPv4 path and the IPv6 control messages do. udp_tunnel_xmit_skb() scrubs the skb in iptunnel_xmit(), but udp_tunnel6_xmit_skb() does not, so the IPv6 path calls skb_scrub_packet() itself. Otherwise the conntrack entry and extensions of the inner packet would stay on the outer one, which conntrack would then pass as an already tracked flow. amt_send_membership_query() sizes its headroom for the outer family with amt_ip_hlen(), builds the AMT header and hands the skb to amt_udp_xmit(). The route lookup now runs after the header push; on a route failure the caller still frees the skb. The relay's General Query, which amt_send_igmp_gq() and amt_send_mld_gq() now send directly through amt_send_membership_query(), takes the IPv6 path with no change to those functions. Assisted-by: LLM Signed-off-by: Omar Ramadan <redacted> --- drivers/net/amt.c | 94 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 26 deletions(-)
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index eaa5637..5e8a74c 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c@@ -88,6 +88,11 @@ static bool amt_v6(const struct amt_dev *amt) return IS_ENABLED(CONFIG_IPV6) && !ipv6_addr_any(&amt->local_ipv6); } +static unsigned int amt_ip_hlen(const struct amt_dev *amt) +{ + return amt_v6(amt) ? sizeof(struct ipv6hdr) : sizeof(struct iphdr); +} + /* Copy the outer source address of a received message by value, so that * the caller may pull the skb afterwards. */
@@ -1098,6 +1103,65 @@ static void amt_req_work(struct work_struct *work) msecs_to_jiffies(100)); } +/* Route an AMT-encapsulated skb to @daddr and send it over the device's + * outer family. The caller has already pushed the AMT header. + */ +static int amt_udp_xmit(struct amt_dev *amt, struct sock *sk, + struct sk_buff *skb, const union amt_addr *daddr, + __be16 sport, __be16 dport) +{ + struct rtable *rt; + struct flowi4 fl4; + + if (amt_v6(amt)) { + struct dst_entry *dst; + + dst = amt_route6(amt, sk, &amt->local_ipv6, &daddr->ip6, + sport, dport); + if (IS_ERR(dst)) { + netdev_dbg(amt->dev, "no route to %pI6c\n", + &daddr->ip6); + return PTR_ERR(dst); + } + /* iptunnel_xmit() scrubs the IPv4 tunnel skb, but + * udp_tunnel6_xmit_skb() does not, so drop the inner + * packet's conntrack and extensions here. Links cannot + * cross netns, so this is never xnet. + */ + skb_scrub_packet(skb, false); + udp_tunnel6_xmit_skb(dst, sk, skb, amt->dev, &amt->local_ipv6, + &daddr->ip6, AMT_TOS, + ip6_dst_hoplimit(dst), 0, sport, dport, + false, 0); + return 0; + } + + memset(&fl4, 0, sizeof(struct flowi4)); + fl4.flowi4_oif = amt->stream_dev->ifindex; + fl4.daddr = daddr->ip4; + fl4.saddr = amt->local_ip; + fl4.flowi4_dscp = inet_dsfield_to_dscp(AMT_TOS); + fl4.flowi4_proto = IPPROTO_UDP; + rt = ip_route_output_key(amt->net, &fl4); + if (IS_ERR(rt)) { + netdev_dbg(amt->dev, "no route to %pI4\n", &daddr->ip4); + return PTR_ERR(rt); + } + + udp_tunnel_xmit_skb(rt, sk, skb, + fl4.saddr, + fl4.daddr, + AMT_TOS, + ip4_dst_hoplimit(&rt->dst), + 0, + sport, + dport, + false, + false, + 0); + return 0; +} + static bool amt_send_membership_update(struct amt_dev *amt, struct sk_buff *skb, bool v6)
@@ -1230,8 +1294,6 @@ static bool amt_send_membership_query(struct amt_dev *amt, bool v6) { struct amt_header_membership_query *amtmq; - struct rtable *rt; - struct flowi4 fl4; struct sock *sk; int err;
@@ -1240,23 +1302,11 @@ static bool amt_send_membership_query(struct amt_dev *amt, return true; err = skb_cow_head(skb, LL_RESERVED_SPACE(amt->dev) + sizeof(*amtmq) + - sizeof(struct iphdr) + sizeof(struct udphdr)); + amt_ip_hlen(amt) + sizeof(struct udphdr)); if (err) return true; skb_reset_inner_headers(skb); - memset(&fl4, 0, sizeof(struct flowi4)); - fl4.flowi4_oif = amt->stream_dev->ifindex; - fl4.daddr = tunnel->addr.ip4; - fl4.saddr = amt->local_ip; - fl4.flowi4_dscp = inet_dsfield_to_dscp(AMT_TOS); - fl4.flowi4_proto = IPPROTO_UDP; - rt = ip_route_output_key(amt->net, &fl4); - if (IS_ERR(rt)) { - netdev_dbg(amt->dev, "no route to %pI4\n", &tunnel->addr.ip4); - return true; - } - amtmq = skb_push(skb, sizeof(*amtmq)); amtmq->version = 0; amtmq->type = AMT_MSG_MEMBERSHIP_QUERY;
@@ -1270,17 +1320,9 @@ static bool amt_send_membership_query(struct amt_dev *amt, skb_set_inner_protocol(skb, htons(ETH_P_IP)); else skb_set_inner_protocol(skb, htons(ETH_P_IPV6)); - udp_tunnel_xmit_skb(rt, sk, skb, - fl4.saddr, - fl4.daddr, - AMT_TOS, - ip4_dst_hoplimit(&rt->dst), - 0, - amt->relay_port, - tunnel->source_port, - false, - false, - 0); + if (amt_udp_xmit(amt, sk, skb, &tunnel->addr, amt->relay_port, + tunnel->source_port)) + return true; amt_update_relay_status(tunnel, AMT_STATUS_SENT_QUERY, true); return false; }
--
2.43.0