[PATCH net-next 07/13] amt: size the encapsulation headroom by the outer IP version

From: Omar Ramadan

Date: Fri Oct 09 2026 - 08:28:43 EST


The amt device reserves AMT_RELAY_HLEN or AMT_GW_HLEN of headroom and
subtracts the same amount from the stream device's MTU. Both assume a
20-byte IPv4 outer header. An IPv6 outer header is 40 bytes, so an IPv6
relay's MTU admits Multicast Data up to 10 bytes too long for the stream
device once encapsulated, which the relay's tunnel MTU check then drops.

Compute the headroom in amt_hlen() from amt_ip_hlen() and the AMT
headers of the mode, and use it wherever the device sizes its headroom
and MTU: once for both modes at newlink, and on NETDEV_CHANGEMTU of the
stream device. AMT_RELAY_HLEN and AMT_GW_HLEN have no other user and
are removed.

No functional change: amt_v6() is still false for every device.

Assisted-by: LLM
Signed-off-by: Omar Ramadan <omar@xxxxxxxxxxxxx>
---
drivers/net/amt.c | 31 +++++++++++++++----------------
include/net/amt.h | 6 ------
2 files changed, 15 insertions(+), 22 deletions(-)

diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index 412d23c..969ecfe 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -93,6 +93,16 @@ static unsigned int amt_ip_hlen(const struct amt_dev *amt)
return amt_v6(amt) ? sizeof(struct ipv6hdr) : sizeof(struct iphdr);
}

+/* Headroom the device reserves for its outer encapsulation. */
+static unsigned int amt_hlen(const struct amt_dev *amt)
+{
+ unsigned int hlen = amt_ip_hlen(amt) + sizeof(struct udphdr);
+
+ if (amt->mode == AMT_MODE_RELAY)
+ return hlen + sizeof(struct amt_relay_headers);
+ return hlen + sizeof(struct amt_gw_headers);
+}
+
/* Copy the outer source address of a received message by value, so that
* the caller may pull the skb afterwards.
*/
@@ -3513,11 +3523,6 @@ static int amt_newlink(struct net_device *dev,
if (amt->mode == AMT_MODE_RELAY) {
amt->qrv = READ_ONCE(amt->net->ipv4.sysctl_igmp_qrv);
amt->qri = 10;
- dev->needed_headroom = amt->stream_dev->needed_headroom +
- AMT_RELAY_HLEN;
- dev->mtu = amt->stream_dev->mtu - AMT_RELAY_HLEN;
- dev->max_mtu = dev->mtu;
- dev->min_mtu = ETH_MIN_MTU + AMT_RELAY_HLEN;
} else {
if (!data[IFLA_AMT_DISCOVERY_IP]) {
NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_AMT_DISCOVERY_IP],
@@ -3538,13 +3543,11 @@ static int amt_newlink(struct net_device *dev,
"discovery must be unicast");
goto err;
}
-
- dev->needed_headroom = amt->stream_dev->needed_headroom +
- AMT_GW_HLEN;
- dev->mtu = amt->stream_dev->mtu - AMT_GW_HLEN;
- dev->max_mtu = dev->mtu;
- dev->min_mtu = ETH_MIN_MTU + AMT_GW_HLEN;
}
+ dev->needed_headroom = amt->stream_dev->needed_headroom + amt_hlen(amt);
+ dev->mtu = amt->stream_dev->mtu - amt_hlen(amt);
+ dev->max_mtu = dev->mtu;
+ dev->min_mtu = ETH_MIN_MTU + amt_hlen(amt);
amt->qi = AMT_INIT_QUERY_INTERVAL;

err = register_netdevice(dev);
@@ -3676,11 +3679,7 @@ static int amt_device_event(struct notifier_block *unused,
unregister_netdevice_many(&list);
break;
case NETDEV_CHANGEMTU:
- if (amt->mode == AMT_MODE_RELAY)
- new_mtu = dev->mtu - AMT_RELAY_HLEN;
- else
- new_mtu = dev->mtu - AMT_GW_HLEN;
-
+ new_mtu = dev->mtu - amt_hlen(amt);
dev_set_mtu(amt->dev, new_mtu);
break;
}
diff --git a/include/net/amt.h b/include/net/amt.h
index 77d17fc..d8798a9 100644
--- a/include/net/amt.h
+++ b/include/net/amt.h
@@ -398,12 +398,6 @@ struct amt_dev {
#define IANA_AMT_UDP_PORT 2268
#define AMT_MAX_TUNNELS 128
#define AMT_MAX_REQS 128
-#define AMT_GW_HLEN (sizeof(struct iphdr) + \
- sizeof(struct udphdr) + \
- sizeof(struct amt_gw_headers))
-#define AMT_RELAY_HLEN (sizeof(struct iphdr) + \
- sizeof(struct udphdr) + \
- sizeof(struct amt_relay_headers))

static inline bool netif_is_amt(const struct net_device *dev)
{
--
2.43.0