* [PATCH net v5 0/4] ipmr: forward GSO multicast packets
@ 2026-11-14 18:26 Julius Bairaktaris
2026-11-14 18:26 ` [PATCH net v5 1/4] net: make room for the tunnel header in skb_segment_list() Julius Bairaktaris
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Julius Bairaktaris @ 2026-11-14 18:26 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, dsahern, idosch, willemb,
steffen.klassert, jiayuan.chen, mfleming, almasrymina, bigeasy,
richardbgobert, ecree.xilinx, bsdhenrymartin, linux-kernel
With fraglist GRO or UDP GRO forwarding on, a routed IPv4 multicast
stream arrives as GSO packets. ipmr_prepare_xmit() drops every DF
aggregate larger than the MTU, and ip_encap() sets no tunnel GSO state,
so a GSO packet sent to a VIFF_TUNNEL vif is dropped during
segmentation. With that state set, a fraglist aggregate reaches
skb_segment_list() with tunnel headers, which can write before the head
of a frag_list member; this also happens today when such a packet is
routed into a tunnel device. Checking the segment length instead of
skb->len relies on skb_gso_transport_seglen(), which counts the inner
UDP header of an encapsulated UDP GSO packet 8 bytes short.
Patch 1 makes room for the tunnel headers in skb_segment_list().
Patch 2 counts the inner UDP header of encapsulated UDP GSO packets.
Patch 3 sets up IPXIP4 GSO state, outer IP IDs and length in ip_encap().
Patch 4 lets DF GSO packets pass when their segments fit the MTU.
Tested in QEMU (KVM, KASAN and lockdep) on veth, 3 runs each on net
(30c71476d842) and with this series: an MFC entry to a plain vif and a
tunnel vif, 40 x 1316-byte datagrams per GSO packet, 4000 datagrams per
arm, UDP_SEGMENT, fraglist GRO and UDP GRO forwarding, DF set and clear,
software and device segmentation on egress:
- net: every DF aggregate dropped on both vifs (FragFails 2 per
aggregate), and with software segmentation nothing arrives over the
tunnel vif; fraglist GRO routed into VXLAN reports a KASAN
slab-out-of-bounds write in skb_segment_list(), then panics.
- this series, all 12 arms: 4000 of 4000 datagrams on the plain vif,
FragFails 0; on the tunnel vif 4000 of 4000 IPIP packets with unique
outer IDs with software segmentation, and the 100 GSO packets of 52688
bytes whole with device segmentation.
- VXLAN with DF, 1404-byte outer segments, route MTU 1400: net forwards
all 1000 segments at 1404 bytes through ip_forward(); with this series
none leave, through ip_forward() or the MFC entry, and FragFails
counts them.
- a GRE GSO packet (skb->encapsulation set) multicast-routed to both
vifs, 50 packets: all 1000 segments on the plain vif; none on the
tunnel vif, whose pkt_out stays at 0 (v4 counted 50).
- fraglist GRO routed into VXLAN and IPIP with software segmentation:
every segment reaches the peer, no KASAN report.
Changes in v5:
- Patch 3: drop an already encapsulated GSO packet in
ipmr_prepare_xmit() before the route lookup, so the vif does not count
it, and call iptunnel_handle_offloads() without error handling, since
skb_cow() leaves no header to unclone (Eric Dumazet, Sashiko). Simon's
Reviewed-by stays, as this is the change Eric asked for.
- Outer tot_len 0 (Eric): 50 x 1310-byte datagrams merged by UDP GRO
forwarding make a 65528-byte packet, so the outer header gets tot_len
0 from iph_set_totlen(); before, htons() truncated it to 12. In 3 runs
nft ip forward chains still match it with meta l4proto 4 and
ip protocol 4 (100 of 100), while a netdev egress chain on the output
device does not match meta l4proto 4 (0 of 100), as
nft_set_pktinfo_ipv4_validate() takes the length from iph_totlen().
All 5000 segments reach the peer, with software and device
segmentation.
- skb_gso_transport_seglen() also leaves out the inner SCTP header of
an encapsulated SCTP GSO packet (Sashiko on patch 2). That predates
this series, and multicast routing does not see such packets:
sctp_v4_addr_valid() and sctp_v4_available() reject multicast
addresses.
- Rebase onto net.
- v4: https://lore.kernel.org/netdev/cover.1792838740.git.julius@bairaktaris.de/
Changes in v4:
- Add patch 2 (Sashiko).
- Patch 3: drop GSO packets that are already encapsulated, and set the
outer length with iph_set_totlen() (Sashiko).
- Drop v3 patch 2 (TCP fixed-ID flag on encapsulation): multicast
routing does not carry TCP, and the flag also needs the decap and
GRE/UDP segmentation paths, which go to a separate series (Sashiko).
- Rebase onto net.
- v3: https://lore.kernel.org/netdev/cover.1792293419.git.julius@bairaktaris.de/
Changes in v3:
- Patch 1: name the VXLAN out-of-bounds write (Sashiko).
- Add patch 2 (Sashiko).
- Patch 3: reserve one outer IP ID per segment (Sashiko).
- v2: https://lore.kernel.org/netdev/20261011162957.1328474-1-julius@bairaktaris.de/
Changes in v2:
- Add patches 1 and 3; v1 let DF GSO packets reach ip_encap() on tunnel
vifs, where they were dropped during segmentation (Sashiko).
- Keep the additive MTU comparison and check mtu > encap before
subtracting, so a route MTU below 20 cannot wrap (Sashiko).
- v1: https://lore.kernel.org/netdev/20261010131842.2157365-1-julius@bairaktaris.de/
I used Claude (Anthropic) to help write these patches.
Julius Bairaktaris (4):
net: make room for the tunnel header in skb_segment_list()
net: gso: count the inner UDP header of encapsulated UDP GSO packets
ipmr: set up GSO state for tunnel vifs
ipmr: do not drop GSO packets that fit the MTU after segmentation
net/core/gso.c | 2 ++
net/core/skbuff.c | 4 ++++
net/ipv4/ipmr.c | 17 ++++++++++++++---
3 files changed, 20 insertions(+), 3 deletions(-)
base-commit: 30c71476d842b094651bd7712f6c8ee8716147d0
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v5 1/4] net: make room for the tunnel header in skb_segment_list()
2026-11-14 18:26 [PATCH net v5 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
@ 2026-11-14 18:26 ` Julius Bairaktaris
2026-11-14 18:26 ` [PATCH net v5 2/4] net: gso: count the inner UDP header of encapsulated UDP GSO packets Julius Bairaktaris
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Julius Bairaktaris @ 2026-11-14 18:26 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, dsahern, idosch, willemb,
steffen.klassert, jiayuan.chen, mfleming, almasrymina, bigeasy,
richardbgobert, ecree.xilinx, bsdhenrymartin, linux-kernel
skb_segment_list() pushes each frag_list member back to the outer MAC
header and copies the tunnel headers in front of it, but never checks
that the member has the headroom. A fraglist GRO packet forwarded into
a tunnel and segmented in software then hits skb_under_panic() on IPIP,
and on VXLAN the copy writes before the member's head (KASAN:
slab-out-of-bounds write in skb_segment_list()).
Call skb_cow_head() on each member before pushing it.
Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.")
Tested-by: Henry Martin <bsdhenrymartin@gmail.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Reviewed-by: Eric Dumazet <edumazet@kernel.org>
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
---
net/core/skbuff.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 8da53d5b76fc2..054983616f3e1 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4777,6 +4777,10 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb,
}
}
+ if (!err)
+ err = skb_cow_head(nskb, offset + tnl_hlen -
+ skb_network_offset(nskb));
+
if (!tail)
skb->next = nskb;
else
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH net v5 2/4] net: gso: count the inner UDP header of encapsulated UDP GSO packets
2026-11-14 18:26 [PATCH net v5 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
2026-11-14 18:26 ` [PATCH net v5 1/4] net: make room for the tunnel header in skb_segment_list() Julius Bairaktaris
@ 2026-11-14 18:26 ` Julius Bairaktaris
2026-11-14 18:26 ` [PATCH net v5 3/4] ipmr: set up GSO state for tunnel vifs Julius Bairaktaris
2026-11-14 18:27 ` [PATCH net v5 4/4] ipmr: do not drop GSO packets that fit the MTU after segmentation Julius Bairaktaris
3 siblings, 0 replies; 5+ messages in thread
From: Julius Bairaktaris @ 2026-11-14 18:26 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, dsahern, idosch, willemb,
steffen.klassert, jiayuan.chen, mfleming, almasrymina, bigeasy,
richardbgobert, ecree.xilinx, bsdhenrymartin, linux-kernel
skb_gso_transport_seglen() adds the inner TCP header length for an
encapsulated GSO packet, but not the inner UDP header for
SKB_GSO_UDP_L4, whose gso_size covers the payload only. Every segment
of a UDP GSO packet in a UDP tunnel, built by UDP GRO behind a tunnel
socket or sent with UDP_SEGMENT over VXLAN, is counted 8 bytes short.
ip_forward() and ip_finish_output_gso() then let DF segments that
exceed the MTU by up to 8 bytes leave unfragmented.
Add the inner UDP header as the unencapsulated branch does.
Fixes: ee80d1ebe5ba ("udp: add udp gso")
Reviewed-by: Simon Horman <horms@kernel.org>
Reviewed-by: Eric Dumazet <edumazet@kernel.org>
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
---
net/core/gso.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/core/gso.c b/net/core/gso.c
index b5bd269e56e4e..eb95c9d2819b1 100644
--- a/net/core/gso.c
+++ b/net/core/gso.c
@@ -156,6 +156,8 @@ static unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))
thlen += inner_tcp_hdrlen(skb);
+ else if (shinfo->gso_type & SKB_GSO_UDP_L4)
+ thlen += sizeof(struct udphdr);
} else if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
thlen = tcp_hdrlen(skb);
} else if (unlikely(skb_is_gso_sctp(skb))) {
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH net v5 3/4] ipmr: set up GSO state for tunnel vifs
2026-11-14 18:26 [PATCH net v5 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
2026-11-14 18:26 ` [PATCH net v5 1/4] net: make room for the tunnel header in skb_segment_list() Julius Bairaktaris
2026-11-14 18:26 ` [PATCH net v5 2/4] net: gso: count the inner UDP header of encapsulated UDP GSO packets Julius Bairaktaris
@ 2026-11-14 18:26 ` Julius Bairaktaris
2026-11-14 18:27 ` [PATCH net v5 4/4] ipmr: do not drop GSO packets that fit the MTU after segmentation Julius Bairaktaris
3 siblings, 0 replies; 5+ messages in thread
From: Julius Bairaktaris @ 2026-11-14 18:26 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, dsahern, idosch, willemb,
steffen.klassert, jiayuan.chen, mfleming, almasrymina, bigeasy,
richardbgobert, ecree.xilinx, bsdhenrymartin, linux-kernel
ip_encap() pushes the outer IPv4 header but does not set SKB_GSO_IPXIP4,
skb->encapsulation or the inner headers. A GSO packet forwarded to a
VIFF_TUNNEL vif is then rejected by ipip_gso_segment() and dropped in
validate_xmit_skb(), after being counted as sent. A device with UDP
segmentation offload is handed a GSO packet whose transport header
points at the inner IP header.
Call iptunnel_handle_offloads() and skb_set_inner_ipproto() as
ipip_tunnel_xmit() does, reserve one outer IP ID per segment as
iptunnel_xmit() does, and set the outer length with iph_set_totlen(),
since a GSO packet plus the outer header can exceed 64 KB. A GSO packet
that GRE or UDP tunnel GRO marked as encapsulated already uses the inner
headers for its own tunnel; drop it before the vif counts it, as
segmentation dropped it before.
Fixes: 9fd1ff5d2ac7 ("udp: Support UDP fraglist GRO/GSO.")
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
---
net/ipv4/ipmr.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 836f3f95b20c4..09d6b0ff1ffb7 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1869,6 +1869,10 @@ static void ip_encap(struct net *net, struct sk_buff *skb,
struct iphdr *iph;
const struct iphdr *old_iph = ip_hdr(skb);
+ /* skb_cow() in ipmr_prepare_xmit() leaves no header to unclone */
+ iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4);
+ skb_set_inner_ipproto(skb, IPPROTO_IPIP);
+
skb_push(skb, sizeof(struct iphdr));
skb->transport_header = skb->network_header;
skb_reset_network_header(skb);
@@ -1882,8 +1886,8 @@ static void ip_encap(struct net *net, struct sk_buff *skb,
iph->saddr = saddr;
iph->protocol = IPPROTO_IPIP;
iph->ihl = 5;
- iph->tot_len = htons(skb->len);
- ip_select_ident(net, skb, NULL);
+ iph_set_totlen(iph, skb->len);
+ ip_select_ident_segs(net, skb, NULL, skb_shinfo(skb)->gso_segs ?: 1);
ip_send_check(iph);
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
@@ -1951,6 +1955,9 @@ static int ipmr_prepare_xmit(struct net *net, struct mr_table *mrt,
}
if (vif->flags & VIFF_TUNNEL) {
+ /* The inner headers can describe one tunnel only */
+ if (skb_is_gso(skb) && skb->encapsulation)
+ return -1;
rt = ip_route_output_ports(net, &fl4, NULL,
vif->remote, vif->local,
0, 0,
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH net v5 4/4] ipmr: do not drop GSO packets that fit the MTU after segmentation
2026-11-14 18:26 [PATCH net v5 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
` (2 preceding siblings ...)
2026-11-14 18:26 ` [PATCH net v5 3/4] ipmr: set up GSO state for tunnel vifs Julius Bairaktaris
@ 2026-11-14 18:27 ` Julius Bairaktaris
3 siblings, 0 replies; 5+ messages in thread
From: Julius Bairaktaris @ 2026-11-14 18:27 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, dsahern, idosch, willemb,
steffen.klassert, jiayuan.chen, mfleming, almasrymina, bigeasy,
richardbgobert, ecree.xilinx, bsdhenrymartin, linux-kernel
ipmr_prepare_xmit() drops a packet with DF set when skb->len exceeds the
route MTU, without checking whether it is a GSO packet. A UDP multicast
stream received with fraglist GRO or UDP GRO forwarding enabled is
aggregated into GSO packets, and every aggregate is dropped with
FragFails, while ip_forward() lets the same packets pass by checking
skb_gso_validate_network_len(). Do the same for multicast routing,
leaving room for the outer header on tunnel vifs. ip6mr has no such
check and leaves GSO packets to ip6_output().
On an IPQ8074 router with fraglist GRO on, a 10 Mbit/s stream of
1316-byte DF datagrams routed by igmpproxy loses 14.5 to 15.5% (n=3)
and 0% with this change.
Fixes: 9fd1ff5d2ac7 ("udp: Support UDP fraglist GRO/GSO.")
Reviewed-by: Simon Horman <horms@kernel.org>
Reviewed-by: Eric Dumazet <edumazet@kernel.org>
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
---
net/ipv4/ipmr.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 09d6b0ff1ffb7..45769d2077172 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1940,6 +1940,7 @@ static int ipmr_prepare_xmit(struct net *net, struct mr_table *mrt,
struct rtable *rt;
struct flowi4 fl4;
int encap = 0;
+ unsigned int mtu;
vif_dev = vif_dev_read(vif);
if (!vif_dev)
@@ -1975,7 +1976,10 @@ static int ipmr_prepare_xmit(struct net *net, struct mr_table *mrt,
return -1;
}
- if (skb->len+encap > dst4_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) {
+ mtu = dst4_mtu(&rt->dst);
+ if (skb->len + encap > mtu && (ntohs(iph->frag_off) & IP_DF) &&
+ !(skb_is_gso(skb) && mtu > encap &&
+ skb_gso_validate_network_len(skb, mtu - encap))) {
/* Do not fragment multicasts. Alas, IPv4 does not
* allow to send ICMP, so that packets will disappear
* to blackhole.
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-14 18:27 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14 18:26 [PATCH net v5 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
2026-11-14 18:26 ` [PATCH net v5 1/4] net: make room for the tunnel header in skb_segment_list() Julius Bairaktaris
2026-11-14 18:26 ` [PATCH net v5 2/4] net: gso: count the inner UDP header of encapsulated UDP GSO packets Julius Bairaktaris
2026-11-14 18:26 ` [PATCH net v5 3/4] ipmr: set up GSO state for tunnel vifs Julius Bairaktaris
2026-11-14 18:27 ` [PATCH net v5 4/4] ipmr: do not drop GSO packets that fit the MTU after segmentation Julius Bairaktaris
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.