]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
xfrm: Determine inner GSO type from packet inner protocol
authorJianbo Liu <jianbol@nvidia.com>
Tue, 28 Oct 2025 02:22:48 +0000 (04:22 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 6 Dec 2025 21:12:38 +0000 (06:12 +0900)
[ Upstream commit 61fafbee6cfed283c02a320896089f658fa67e56 ]

The GSO segmentation functions for ESP tunnel mode
(xfrm4_tunnel_gso_segment and xfrm6_tunnel_gso_segment) were
determining the inner packet's L2 protocol type by checking the static
x->inner_mode.family field from the xfrm state.

This is unreliable. In tunnel mode, the state's actual inner family
could be defined by x->inner_mode.family or by
x->inner_mode_iaf.family. Checking only the former can lead to a
mismatch with the actual packet being processed, causing GSO to create
segments with the wrong L2 header type.

This patch fixes the bug by deriving the inner mode directly from the
packet's inner protocol stored in XFRM_MODE_SKB_CB(skb)->protocol.

Instead of replicating the code, this patch modifies the
xfrm_ip2inner_mode helper function. It now correctly returns
&x->inner_mode if the selector family (x->sel.family) is already
specified, thereby handling both specific and AF_UNSPEC cases
appropriately.

With this change, ESP GSO can use xfrm_ip2inner_mode to get the
correct inner mode. It doesn't affect existing callers, as the updated
logic now mirrors the checks they were already performing externally.

Fixes: 26dbd66eab80 ("esp: choose the correct inner protocol for GSO on inter address family tunnels")
Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
Reviewed-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
include/net/xfrm.h
net/ipv4/esp4_offload.c
net/ipv6/esp6_offload.c

index 64911162ab5f44957424180d824f9bcd91e420f4..ac5db167370c9e7310446d48829289adababf6d0 100644 (file)
@@ -446,7 +446,8 @@ static inline int xfrm_af2proto(unsigned int family)
 
 static inline const struct xfrm_mode *xfrm_ip2inner_mode(struct xfrm_state *x, int ipproto)
 {
-       if ((ipproto == IPPROTO_IPIP && x->props.family == AF_INET) ||
+       if ((x->sel.family != AF_UNSPEC) ||
+           (ipproto == IPPROTO_IPIP && x->props.family == AF_INET) ||
            (ipproto == IPPROTO_IPV6 && x->props.family == AF_INET6))
                return &x->inner_mode;
        else
index ee848be59e65a7d8f90a6bd09c61732152fad68c..cbfc8b5b15bd23afa6c905397be6cdb564c19f65 100644 (file)
@@ -110,8 +110,10 @@ static struct sk_buff *xfrm4_tunnel_gso_segment(struct xfrm_state *x,
                                                struct sk_buff *skb,
                                                netdev_features_t features)
 {
-       __be16 type = x->inner_mode.family == AF_INET6 ? htons(ETH_P_IPV6)
-                                                      : htons(ETH_P_IP);
+       const struct xfrm_mode *inner_mode = xfrm_ip2inner_mode(x,
+                                       XFRM_MODE_SKB_CB(skb)->protocol);
+       __be16 type = inner_mode->family == AF_INET6 ? htons(ETH_P_IPV6)
+                                                    : htons(ETH_P_IP);
 
        return skb_eth_gso_segment(skb, features, type);
 }
index fc6a5be73263437215e2d95d85151725cc0c64c8..65d628e5000597e3c37be4a470b4290085a3c37d 100644 (file)
@@ -145,8 +145,10 @@ static struct sk_buff *xfrm6_tunnel_gso_segment(struct xfrm_state *x,
                                                struct sk_buff *skb,
                                                netdev_features_t features)
 {
-       __be16 type = x->inner_mode.family == AF_INET ? htons(ETH_P_IP)
-                                                     : htons(ETH_P_IPV6);
+       const struct xfrm_mode *inner_mode = xfrm_ip2inner_mode(x,
+                                       XFRM_MODE_SKB_CB(skb)->protocol);
+       __be16 type = inner_mode->family == AF_INET ? htons(ETH_P_IP)
+                                                   : htons(ETH_P_IPV6);
 
        return skb_eth_gso_segment(skb, features, type);
 }