From: Omar Ramadan <omar@blockcast.net>
To: Taehee Yoo <ap420073@gmail.com>,
Andrew Lunn <andrew+netdev@lunn.ch>,
"David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Shuah Khan <shuah@kernel.org>
Cc: Simon Horman <horms@kernel.org>,
netdev@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH net-next 03/13] amt: key relay tunnels on a union amt_addr endpoint
Date: Fri, 9 Oct 2026 12:24:16 +0000 [thread overview]
Message-ID: <20261009122426.551178-4-omar@blockcast.net> (raw)
In-Reply-To: <20261009122426.551178-1-omar@blockcast.net>
A relay keeps one struct amt_tunnel_list per gateway and finds it by
the gateway's IPv4 address, stored as __be32 ip4 and compared with
ip_hdr(skb)->saddr. On an IPv6 outer transport that reads bytes 4-7 of
the IPv6 source address as an IPv4 address, and the field cannot hold
the gateway's address anyway.
Store the endpoint as a union amt_addr, which group and source nodes
already use, and move the union above struct amt_tunnel_list so the
tunnel can embed it. Its ip6 member is no longer under CONFIG_IPV6, so
code that only copies, compares or prints an endpoint compiles without
an #if and relies on amt_v6() being false. The tunnel's endpoint grows
from 4 to 16 bytes on every kernel; a kernel without IPv6 also pays 12
more bytes per source node and 24 per group node, which it already
zeroes. The tunnel needs no family of its own: the relay's socket is
bound to one family, so every tunnel has the device's family, which
amt_v6() reports.
amt_outer_saddr() copies the outer source address of a received message
by value into a zeroed union amt_addr. amt_request_handler() takes that
snapshot once and matches and records tunnels with it, comparing with
the existing amt_addr_equal(); the union is zero-padded for IPv4, so
one memcmp() serves both families. As before, a tunnel is matched on
the address alone. When the tunnel limit is reached, an IPv6 device
answers with an ICMPv6 destination unreachable instead of an ICMP one.
It is sent with skb->dev set to the underlying link: amt_rcv() has made
it the amt device, and icmp6_send() routes an error to a link-local
source through skb->dev, where amt_dev_xmit() would drop it.
The response MAC becomes one siphash() over the packed {address, port,
nonce} tuple instead of siphash_3u32() over the IPv4 address. Only the
relay computes and checks the MAC; the gateway echoes it verbatim, so it
cannot tell that the value for an IPv4 endpoint changed.
The IPv4 senders and the relay status debug message are converted to
tunnel->addr.ip4, and the debug message prints an IPv6 endpoint with
%pI6c. No functional change: amt_v6() is still false for every device.
Assisted-by: LLM
Signed-off-by: Omar Ramadan <omar@blockcast.net>
---
drivers/net/amt.c | 73 ++++++++++++++++++++++++++++++++++++-----------
include/net/amt.h | 18 ++++++------
2 files changed, 66 insertions(+), 25 deletions(-)
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index a550f84..eaa5637 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -88,6 +88,19 @@ static bool amt_v6(const struct amt_dev *amt)
return IS_ENABLED(CONFIG_IPV6) && !ipv6_addr_any(&amt->local_ipv6);
}
+/* Copy the outer source address of a received message by value, so that
+ * the caller may pull the skb afterwards.
+ */
+static void amt_outer_saddr(const struct amt_dev *amt,
+ const struct sk_buff *skb, union amt_addr *addr)
+{
+ memset(addr, 0, sizeof(*addr));
+ if (amt_v6(amt))
+ addr->ip6 = ipv6_hdr(skb)->saddr;
+ else
+ addr->ip4 = ip_hdr(skb)->saddr;
+}
+
static void __amt_source_gc_work(void)
{
struct amt_source_node *snode;
@@ -594,10 +607,16 @@ static void __amt_update_relay_status(struct amt_tunnel_list *tunnel,
{
if (validate && tunnel->status >= status)
return;
- netdev_dbg(tunnel->amt->dev,
- "Update Tunnel(IP = %pI4, PORT = %u) status %s -> %s",
- &tunnel->ip4, ntohs(tunnel->source_port),
- status_str[tunnel->status], status_str[status]);
+ if (amt_v6(tunnel->amt))
+ netdev_dbg(tunnel->amt->dev,
+ "Update Tunnel(IP = %pI6c, PORT = %u) status %s -> %s",
+ &tunnel->addr.ip6, ntohs(tunnel->source_port),
+ status_str[tunnel->status], status_str[status]);
+ else
+ netdev_dbg(tunnel->amt->dev,
+ "Update Tunnel(IP = %pI4, PORT = %u) status %s -> %s",
+ &tunnel->addr.ip4, ntohs(tunnel->source_port),
+ status_str[tunnel->status], status_str[status]);
tunnel->status = status;
}
@@ -1173,12 +1192,12 @@ static void amt_send_multicast_data(struct amt_dev *amt,
memset(&fl4, 0, sizeof(struct flowi4));
fl4.flowi4_oif = amt->stream_dev->ifindex;
- fl4.daddr = tunnel->ip4;
+ fl4.daddr = tunnel->addr.ip4;
fl4.saddr = amt->local_ip;
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->ip4);
+ netdev_dbg(amt->dev, "no route to %pI4\n", &tunnel->addr.ip4);
kfree_skb(skb);
return;
}
@@ -1228,13 +1247,13 @@ static bool amt_send_membership_query(struct amt_dev *amt,
skb_reset_inner_headers(skb);
memset(&fl4, 0, sizeof(struct flowi4));
fl4.flowi4_oif = amt->stream_dev->ifindex;
- fl4.daddr = tunnel->ip4;
+ 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->ip4);
+ netdev_dbg(amt->dev, "no route to %pI4\n", &tunnel->addr.ip4);
return true;
}
@@ -2585,7 +2604,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
skb_reset_network_header(skb);
list_for_each_entry_rcu(tunnel, &amt->tunnel_list, list) {
- if (tunnel->ip4 == saddr) {
+ if (tunnel->addr.ip4 == saddr) {
if ((nonce == tunnel->nonce &&
response_mac == tunnel->mac)) {
mod_delayed_work(amt_wq, &tunnel->gc_wq,
@@ -2818,18 +2837,23 @@ static bool amt_discovery_handler(struct amt_dev *amt, struct sk_buff *skb)
static bool amt_request_handler(struct amt_dev *amt, struct sk_buff *skb)
{
+ struct {
+ union amt_addr addr;
+ __be16 port;
+ __be32 nonce;
+ } __packed mac_in;
struct amt_header_request *amtrh;
struct amt_tunnel_list *tunnel;
unsigned long long key;
+ union amt_addr saddr;
struct udphdr *udph;
- struct iphdr *iph;
u64 mac;
int i;
if (!pskb_may_pull(skb, sizeof(*udph) + sizeof(*amtrh)))
return true;
- iph = ip_hdr(skb);
+ amt_outer_saddr(amt, skb, &saddr);
udph = udp_hdr(skb);
amtrh = (struct amt_header_request *)(udp_hdr(skb) + 1);
@@ -2837,12 +2861,24 @@ static bool amt_request_handler(struct amt_dev *amt, struct sk_buff *skb)
return true;
list_for_each_entry_rcu(tunnel, &amt->tunnel_list, list)
- if (tunnel->ip4 == iph->saddr)
+ if (amt_addr_equal(&tunnel->addr, &saddr))
goto send;
spin_lock_bh(&amt->lock);
if (amt->nr_tunnels >= amt->max_tunnels) {
spin_unlock_bh(&amt->lock);
+ if (amt_v6(amt)) {
+ /* amt_rcv() made skb->dev the amt device, but
+ * icmp6_send() routes an error to a link-local
+ * source through skb->dev, and the amt device
+ * drops it. Send it through the underlying link.
+ */
+ skb->dev = amt->stream_dev;
+ icmpv6_ndo_send(skb, ICMPV6_DEST_UNREACH,
+ ICMPV6_ADDR_UNREACH, 0);
+ skb->dev = amt->dev;
+ return true;
+ }
icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
return true;
}
@@ -2856,7 +2892,7 @@ static bool amt_request_handler(struct amt_dev *amt, struct sk_buff *skb)
}
tunnel->source_port = udph->source;
- tunnel->ip4 = iph->saddr;
+ tunnel->addr = saddr;
memcpy(&key, &tunnel->key, sizeof(unsigned long long));
tunnel->amt = amt;
@@ -2876,10 +2912,13 @@ static bool amt_request_handler(struct amt_dev *amt, struct sk_buff *skb)
send:
tunnel->nonce = amtrh->nonce;
- mac = siphash_3u32((__force u32)tunnel->ip4,
- (__force u32)tunnel->source_port,
- (__force u32)tunnel->nonce,
- &tunnel->key);
+ /* The MAC is opaque to the gateway, which only echoes it, so one
+ * siphash over the zero-padded endpoint serves both families.
+ */
+ mac_in.addr = tunnel->addr;
+ mac_in.port = tunnel->source_port;
+ mac_in.nonce = tunnel->nonce;
+ mac = siphash(&mac_in, sizeof(mac_in), &tunnel->key);
tunnel->mac = mac >> 16;
if (!netif_running(amt->dev) || !netif_running(amt->stream_dev))
diff --git a/include/net/amt.h b/include/net/amt.h
index 921944b..77d17fc 100644
--- a/include/net/amt.h
+++ b/include/net/amt.h
@@ -241,6 +241,14 @@ struct amt_relay_headers {
};
} __packed;
+/* ip6 is present without CONFIG_IPV6 too, so that code which only copies
+ * or compares addresses needs no #if; amt_v6() is then false.
+ */
+union amt_addr {
+ __be32 ip4;
+ struct in6_addr ip6;
+};
+
struct amt_tunnel_list {
struct list_head list;
/* Protect All resources under an amt_tunne_list */
@@ -251,7 +259,8 @@ struct amt_tunnel_list {
enum amt_status status;
struct delayed_work gc_wq;
__be16 source_port;
- __be32 ip4;
+ /* Gateway endpoint, in the device's outer family */
+ union amt_addr addr;
__be32 nonce;
siphash_key_t key;
u64 mac:48,
@@ -260,13 +269,6 @@ struct amt_tunnel_list {
struct hlist_head groups[];
};
-union amt_addr {
- __be32 ip4;
-#if IS_ENABLED(CONFIG_IPV6)
- struct in6_addr ip6;
-#endif
-};
-
/* RFC 3810
*
* When the router is in EXCLUDE mode, the router state is represented
--
2.43.0
next prev parent reply other threads:[~2026-10-09 12:24 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-09 12:24 [PATCH net-next 00/13] amt: add an IPv6 outer transport Omar Ramadan
2026-10-09 12:24 ` [PATCH net-next 01/13] amt: create an AF_INET6 encapsulation socket for an IPv6 outer address Omar Ramadan
2026-10-09 12:24 ` [PATCH net-next 02/13] amt: send the Relay Advertisement over IPv6 Omar Ramadan
2026-10-10 12:41 ` netdev-bot+sashiko
2026-10-09 12:24 ` Omar Ramadan [this message]
2026-10-10 12:41 ` [PATCH net-next 03/13] amt: key relay tunnels on a union amt_addr endpoint netdev-bot+sashiko
2026-10-09 12:24 ` [PATCH net-next 04/13] amt: send the Membership Query over IPv6 Omar Ramadan
2026-10-10 12:41 ` netdev-bot+sashiko
2026-10-09 12:24 ` [PATCH net-next 05/13] amt: match the Membership Update tunnel by outer family Omar Ramadan
2026-10-10 12:41 ` netdev-bot+sashiko
2026-10-09 12:24 ` [PATCH net-next 06/13] amt: forward multicast data over IPv6 Omar Ramadan
2026-10-10 12:41 ` netdev-bot+sashiko
2026-10-09 12:24 ` [PATCH net-next 07/13] amt: size the encapsulation headroom by the outer IP version Omar Ramadan
2026-10-09 12:24 ` [PATCH net-next 08/13] amt: send the AMT gateway control plane over IPv6 Omar Ramadan
2026-10-10 12:41 ` netdev-bot+sashiko
2026-10-09 12:24 ` [PATCH net-next 09/13] amt: receive " Omar Ramadan
2026-10-10 12:41 ` netdev-bot+sashiko
2026-10-09 12:24 ` [PATCH net-next 10/13] amt: add netlink attributes for an IPv6 outer transport Omar Ramadan
2026-10-10 12:41 ` netdev-bot+sashiko
2026-10-09 12:24 ` [PATCH net-next 11/13] MAINTAINERS: amt: cover the amt headers and selftests Omar Ramadan
2026-10-09 12:24 ` [PATCH net-next 12/13] selftests: net: add amt_v6.sh for an IPv6 outer transport Omar Ramadan
2026-10-10 12:41 ` netdev-bot+sashiko
2026-10-09 12:24 ` [PATCH net-next 13/13] selftests: net: add amt_gw_v6.sh for the IPv6 netlink attributes Omar Ramadan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261009122426.551178-4-omar@blockcast.net \
--to=omar@blockcast.net \
--cc=andrew+netdev@lunn.ch \
--cc=ap420073@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®