Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: correctly handle tunneled traffic on IPV6_CSUM GSO fallback

NETIF_F_IPV6_CSUM only advertises support for checksum offload of
packets without IPv6 extension headers. Packets with extension
headers must fall back onto software checksumming. Since TSO
depends on checksum offload, those must revert to GSO.

The below commit introduces that fallback. It always checks
network header length. For tunneled packets, the inner header length
must be checked instead. Extend the check accordingly.

A special case is tunneled packets without inner IP protocol. Such as
RFC 6951 SCTP in UDP. Those are not standard IPv6 followed by
transport header either, so also must revert to the software GSO path.

Cc: stable@vger.kernel.org
Fixes: 864e3396976e ("net: gso: Forbid IPv6 TSO with extensions on devices with only IPV6_CSUM")
Reported-by: Tangxin Xie <xietangxin@yeah.net>
Closes: https://lore.kernel.org/netdev/0414e7e2-9a1c-4d7c-a99d-b9039cf68f40@yeah.net/
Suggested-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20260320190148.2409107-1-willemdebruijn.kernel@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Willem de Bruijn and committed by
Paolo Abeni
c4336a07 d9c2a509

+17 -5
+17 -5
net/core/dev.c
··· 3769 3769 return vlan_features_check(skb, features); 3770 3770 } 3771 3771 3772 + static bool skb_gso_has_extension_hdr(const struct sk_buff *skb) 3773 + { 3774 + if (!skb->encapsulation) 3775 + return ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 || 3776 + (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && 3777 + vlan_get_protocol(skb) == htons(ETH_P_IPV6))) && 3778 + skb_transport_header_was_set(skb) && 3779 + skb_network_header_len(skb) != sizeof(struct ipv6hdr)); 3780 + else 3781 + return (!skb_inner_network_header_was_set(skb) || 3782 + ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 || 3783 + (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && 3784 + inner_ip_hdr(skb)->version == 6)) && 3785 + skb_inner_network_header_len(skb) != sizeof(struct ipv6hdr))); 3786 + } 3787 + 3772 3788 static netdev_features_t gso_features_check(const struct sk_buff *skb, 3773 3789 struct net_device *dev, 3774 3790 netdev_features_t features) ··· 3832 3816 * so neither does TSO that depends on it. 3833 3817 */ 3834 3818 if (features & NETIF_F_IPV6_CSUM && 3835 - (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 || 3836 - (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && 3837 - vlan_get_protocol(skb) == htons(ETH_P_IPV6))) && 3838 - skb_transport_header_was_set(skb) && 3839 - skb_network_header_len(skb) != sizeof(struct ipv6hdr)) 3819 + skb_gso_has_extension_hdr(skb)) 3840 3820 features &= ~(NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4); 3841 3821 3842 3822 return features;