]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfrm: check for PMTU in tunnel mode for packet offload
authorLeon Romanovsky <leonro@nvidia.com>
Wed, 19 Feb 2025 13:51:01 +0000 (15:51 +0200)
committerSteffen Klassert <steffen.klassert@secunet.com>
Fri, 21 Feb 2025 07:08:15 +0000 (08:08 +0100)
In tunnel mode, for the packet offload, there were no PMTU signaling
to the upper level about need to fragment the packet. As a solution,
call to already existing xfrm[4|6]_tunnel_check_size() to perform that.

Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
include/net/xfrm.h
net/xfrm/xfrm_device.c
net/xfrm/xfrm_output.c

index 15997374a594cb7dc041a1758596245206598d92..39365fd2ea175ca307ad71a71d1b1ce8952b4d90 100644 (file)
@@ -1781,6 +1781,15 @@ int xfrm_trans_queue(struct sk_buff *skb,
                                   struct sk_buff *));
 int xfrm_output_resume(struct sock *sk, struct sk_buff *skb, int err);
 int xfrm_output(struct sock *sk, struct sk_buff *skb);
+int xfrm4_tunnel_check_size(struct sk_buff *skb);
+#if IS_ENABLED(CONFIG_IPV6)
+int xfrm6_tunnel_check_size(struct sk_buff *skb);
+#else
+static inline int xfrm6_tunnel_check_size(struct sk_buff *skb)
+{
+       return -EMSGSIZE;
+}
+#endif
 
 #if IS_ENABLED(CONFIG_NET_PKTGEN)
 int pktgen_xfrm_outer_mode_output(struct xfrm_state *x, struct sk_buff *skb);
index f9d985ef30f2ed1ea06e41ec7d358614123f1f1b..d62f76161d83e29c07ec146484533c0a64f43e8e 100644 (file)
@@ -418,12 +418,12 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
        struct dst_entry *dst = skb_dst(skb);
        struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
        struct net_device *dev = x->xso.dev;
+       bool check_tunnel_size;
 
        if (x->xso.type == XFRM_DEV_OFFLOAD_UNSPECIFIED)
                return false;
 
-       if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET ||
-           ((dev == xfrm_dst_path(dst)->dev) && !xdst->child->xfrm)) {
+       if ((dev == xfrm_dst_path(dst)->dev) && !xdst->child->xfrm) {
                mtu = xfrm_state_mtu(x, xdst->child_mtu_cached);
                if (skb->len <= mtu)
                        goto ok;
@@ -435,16 +435,22 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
        return false;
 
 ok:
+       check_tunnel_size = x->xso.type == XFRM_DEV_OFFLOAD_PACKET &&
+                           x->props.mode == XFRM_MODE_TUNNEL;
        switch (x->props.family) {
        case AF_INET:
                /* Check for IPv4 options */
                if (ip_hdr(skb)->ihl != 5)
                        return false;
+               if (check_tunnel_size && xfrm4_tunnel_check_size(skb))
+                       return false;
                break;
        case AF_INET6:
                /* Check for IPv6 extensions */
                if (ipv6_ext_hdr(ipv6_hdr(skb)->nexthdr))
                        return false;
+               if (check_tunnel_size && xfrm6_tunnel_check_size(skb))
+                       return false;
                break;
        default:
                break;
index f7abd42c077d5a6064f659c229846a12b6069cd9..34c8e266641c640bf01e51fe91d4e2cbad565a0c 100644 (file)
@@ -786,7 +786,7 @@ out:
 }
 EXPORT_SYMBOL_GPL(xfrm_output);
 
-static int xfrm4_tunnel_check_size(struct sk_buff *skb)
+int xfrm4_tunnel_check_size(struct sk_buff *skb)
 {
        int mtu, ret = 0;
 
@@ -812,6 +812,7 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
 out:
        return ret;
 }
+EXPORT_SYMBOL_GPL(xfrm4_tunnel_check_size);
 
 static int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb)
 {
@@ -834,7 +835,7 @@ static int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb)
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
-static int xfrm6_tunnel_check_size(struct sk_buff *skb)
+int xfrm6_tunnel_check_size(struct sk_buff *skb)
 {
        int mtu, ret = 0;
        struct dst_entry *dst = skb_dst(skb);
@@ -864,6 +865,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
 out:
        return ret;
 }
+EXPORT_SYMBOL_GPL(xfrm6_tunnel_check_size);
 #endif
 
 static int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb)