* [PATCH net v4 0/4] ipmr: forward GSO multicast packets
@ 2026-11-01 16:42 Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 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-01 16:42 UTC (permalink / raw)
To: netdev
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms,
steffen.klassert, willemb, jiayuan.chen, mfleming, almasrymina,
bigeasy, richardbgobert, ecree.xilinx, 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 (KASAN) on veth, one run per arm: 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 (7076f684d513): 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 and this
series without patch 2 forward all 1000 segments at 1404 bytes through
ip_forward(), and the series without patch 2 also through the MFC
entry; with patch 2 none leave and FragFails counts them.
- a GRE GSO packet (skb->encapsulation set) multicast-routed to both
vifs: all 1000 segments on the plain vif, none on the tunnel vif.
- fraglist GRO routed into VXLAN and IPIP with software segmentation:
every segment reaches the peer, no KASAN report.
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 | 29 ++++++++++++++++++++++-------
3 files changed, 28 insertions(+), 7 deletions(-)
base-commit: 7076f684d5132e6a6145e1da2fa74771ecd052e5
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v4 1/4] net: make room for the tunnel header in skb_segment_list()
2026-11-01 16:42 [PATCH net v4 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
@ 2026-11-01 16:42 ` Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 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-01 16:42 UTC (permalink / raw)
To: netdev
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms,
steffen.klassert, willemb, jiayuan.chen, mfleming, almasrymina,
bigeasy, richardbgobert, ecree.xilinx, 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.")
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 48f6764117243..48291a2d21241 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 v4 2/4] net: gso: count the inner UDP header of encapsulated UDP GSO packets
2026-11-01 16:42 [PATCH net v4 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 1/4] net: make room for the tunnel header in skb_segment_list() Julius Bairaktaris
@ 2026-11-01 16:42 ` Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 3/4] ipmr: set up GSO state for tunnel vifs Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 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-01 16:42 UTC (permalink / raw)
To: netdev
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms,
steffen.klassert, willemb, jiayuan.chen, mfleming, almasrymina,
bigeasy, richardbgobert, ecree.xilinx, 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")
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 v4 3/4] ipmr: set up GSO state for tunnel vifs
2026-11-01 16:42 [PATCH net v4 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 1/4] net: make room for the tunnel header in skb_segment_list() Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 2/4] net: gso: count the inner UDP header of encapsulated UDP GSO packets Julius Bairaktaris
@ 2026-11-01 16:42 ` Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 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-01 16:42 UTC (permalink / raw)
To: netdev
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms,
steffen.klassert, willemb, jiayuan.chen, mfleming, almasrymina,
bigeasy, richardbgobert, ecree.xilinx, 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, as segmentation did before.
Fixes: 9fd1ff5d2ac7 ("udp: Support UDP fraglist GRO/GSO.")
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
---
net/ipv4/ipmr.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index acacaae05cc49..98e38839f8ccb 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1849,12 +1849,21 @@ static struct notifier_block ip_mr_notifier = {
* This avoids tunnel drivers and other mess and gives us the speed so
* important for multicast video.
*/
-static void ip_encap(struct net *net, struct sk_buff *skb,
- __be32 saddr, __be32 daddr)
+static int ip_encap(struct net *net, struct sk_buff *skb,
+ __be32 saddr, __be32 daddr)
{
+ const struct iphdr *old_iph;
struct iphdr *iph;
- const struct iphdr *old_iph = ip_hdr(skb);
+ /* The inner headers can describe one tunnel only */
+ if (skb_is_gso(skb) && skb->encapsulation)
+ return -1;
+
+ if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4))
+ return -1;
+ skb_set_inner_ipproto(skb, IPPROTO_IPIP);
+
+ old_iph = ip_hdr(skb);
skb_push(skb, sizeof(struct iphdr));
skb->transport_header = skb->network_header;
skb_reset_network_header(skb);
@@ -1868,12 +1877,13 @@ 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));
nf_reset_ct(skb);
+ return 0;
}
static inline int ipmr_forward_finish(struct net *net, struct sock *sk,
@@ -1982,7 +1992,8 @@ static int ipmr_prepare_xmit(struct net *net, struct mr_table *mrt,
* What do we do with netfilter? -- RR
*/
if (vif->flags & VIFF_TUNNEL) {
- ip_encap(net, skb, vif->local, vif->remote);
+ if (ip_encap(net, skb, vif->local, vif->remote))
+ return -1;
/* FIXME: extra output firewall step used to be here. --RR */
DEV_STATS_INC(vif_dev, tx_packets);
DEV_STATS_ADD(vif_dev, tx_bytes, skb->len);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH net v4 4/4] ipmr: do not drop GSO packets that fit the MTU after segmentation
2026-11-01 16:42 [PATCH net v4 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
` (2 preceding siblings ...)
2026-11-01 16:42 ` [PATCH net v4 3/4] ipmr: set up GSO state for tunnel vifs Julius Bairaktaris
@ 2026-11-01 16:42 ` Julius Bairaktaris
3 siblings, 0 replies; 5+ messages in thread
From: Julius Bairaktaris @ 2026-11-01 16:42 UTC (permalink / raw)
To: netdev
Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms,
steffen.klassert, willemb, jiayuan.chen, mfleming, almasrymina,
bigeasy, richardbgobert, ecree.xilinx, 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.")
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 98e38839f8ccb..1fb728fb15741 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1932,6 +1932,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)
@@ -1964,7 +1965,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-01 16:42 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-01 16:42 [PATCH net v4 0/4] ipmr: forward GSO multicast packets Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 1/4] net: make room for the tunnel header in skb_segment_list() Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 2/4] net: gso: count the inner UDP header of encapsulated UDP GSO packets Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 3/4] ipmr: set up GSO state for tunnel vifs Julius Bairaktaris
2026-11-01 16:42 ` [PATCH net v4 4/4] ipmr: do not drop GSO packets that fit the MTU after segmentation Julius Bairaktaris
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox