]> git.ipfire.org Git - thirdparty/linux.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)
committerSteffen Klassert <steffen.klassert@secunet.com>
Thu, 30 Oct 2025 10:52:31 +0000 (11:52 +0100)
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>
include/net/xfrm.h
net/ipv4/esp4_offload.c
net/ipv6/esp6_offload.c

index f3014e4f54fc362d59b17408561d9e7ca075e72f..0a14daaa5dd407f7701aeb0a229da499f6314648 100644 (file)
@@ -536,7 +536,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 e0d94270da28a320c3075ff3d31792fd97770dd7..05828d4cb6cdbbad8cf2e67718eefe6101d31cd9 100644 (file)
@@ -122,8 +122,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 7b41fb4f00b5876471107f606df977f0a47675c7..22410243ebe88dbd2931be9b53bb2d49239e2a5b 100644 (file)
@@ -158,8 +158,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);
 }