]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
xfrm: Preserve vlan tags for transport mode software GRO
authorPaul Davey <paul.davey@alliedtelesis.co.nz>
Tue, 23 Apr 2024 06:00:24 +0000 (18:00 +1200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 May 2024 09:48:06 +0000 (11:48 +0200)
[ Upstream commit 58fbfecab965014b6e3cc956a76b4a96265a1add ]

The software GRO path for esp transport mode uses skb_mac_header_rebuild
prior to re-injecting the packet via the xfrm_napi_dev.  This only
copies skb->mac_len bytes of header which may not be sufficient if the
packet contains 802.1Q tags or other VLAN tags.  Worse copying only the
initial header will leave a packet marked as being VLAN tagged but
without the corresponding tag leading to mangling when it is later
untagged.

The VLAN tags are important when receiving the decrypted esp transport
mode packet after GRO processing to ensure it is received on the correct
interface.

Therefore record the full mac header length in xfrm*_transport_input for
later use in corresponding xfrm*_transport_finish to copy the entire mac
header when rebuilding the mac header for GRO.  The skb->data pointer is
left pointing skb->mac_header bytes after the start of the mac header as
is expected by the network stack and network and transport header
offsets reset to this location.

Fixes: 7785bba299a8 ("esp: Add a software GRO codepath")
Signed-off-by: Paul Davey <paul.davey@alliedtelesis.co.nz>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
include/linux/skbuff.h
include/net/xfrm.h
net/ipv4/xfrm4_input.c
net/ipv6/xfrm6_input.c
net/xfrm/xfrm_input.c

index a210f19958621775452d2be0281a0693249d42c9..31755d496b01d940be51ba9761347b565c7bd5c0 100644 (file)
@@ -2607,6 +2607,21 @@ static inline void skb_mac_header_rebuild(struct sk_buff *skb)
        }
 }
 
+/* Move the full mac header up to current network_header.
+ * Leaves skb->data pointing at offset skb->mac_len into the mac_header.
+ * Must be provided the complete mac header length.
+ */
+static inline void skb_mac_header_rebuild_full(struct sk_buff *skb, u32 full_mac_len)
+{
+       if (skb_mac_header_was_set(skb)) {
+               const unsigned char *old_mac = skb_mac_header(skb);
+
+               skb_set_mac_header(skb, -full_mac_len);
+               memmove(skb_mac_header(skb), old_mac, full_mac_len);
+               __skb_push(skb, full_mac_len - skb->mac_len);
+       }
+}
+
 static inline int skb_checksum_start_offset(const struct sk_buff *skb)
 {
        return skb->csum_start - skb_headroom(skb);
index 7865db2f827e6380b972b234bdb09118d79f1b6c..6fbaf304648f614787ad313719cde0d82242d3fc 100644 (file)
@@ -1028,6 +1028,9 @@ struct xfrm_offload {
 #define CRYPTO_INVALID_PACKET_SYNTAX           64
 #define CRYPTO_INVALID_PROTOCOL                        128
 
+       /* Used to keep whole l2 header for transport mode GRO */
+       __u32                   orig_mac_len;
+
        __u8                    proto;
 };
 
index eac206a290d05930c97f572e55ec0c26f3fefad0..1f50517289fd9654d57dbe9e25fc93e6cd25d9ee 100644 (file)
@@ -61,7 +61,11 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async)
        ip_send_check(iph);
 
        if (xo && (xo->flags & XFRM_GRO)) {
-               skb_mac_header_rebuild(skb);
+               /* The full l2 header needs to be preserved so that re-injecting the packet at l2
+                * works correctly in the presence of vlan tags.
+                */
+               skb_mac_header_rebuild_full(skb, xo->orig_mac_len);
+               skb_reset_network_header(skb);
                skb_reset_transport_header(skb);
                return 0;
        }
index 4907ab241d6bedf9ecfaa02017178e13fef4b980..7dbefbb338ca52d09d36737b4aac62fb423c9655 100644 (file)
@@ -56,7 +56,11 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
        skb_postpush_rcsum(skb, skb_network_header(skb), nhlen);
 
        if (xo && (xo->flags & XFRM_GRO)) {
-               skb_mac_header_rebuild(skb);
+               /* The full l2 header needs to be preserved so that re-injecting the packet at l2
+                * works correctly in the presence of vlan tags.
+                */
+               skb_mac_header_rebuild_full(skb, xo->orig_mac_len);
+               skb_reset_network_header(skb);
                skb_reset_transport_header(skb);
                return 0;
        }
index f3bccab983f05b263a51164a44c7d09c2dc0ab66..0c3fa01ec67a71d45c8456265152c8079979b0f9 100644 (file)
@@ -399,11 +399,15 @@ static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
  */
 static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
 {
+       struct xfrm_offload *xo = xfrm_offload(skb);
        int ihl = skb->data - skb_transport_header(skb);
 
        if (skb->transport_header != skb->network_header) {
                memmove(skb_transport_header(skb),
                        skb_network_header(skb), ihl);
+               if (xo)
+                       xo->orig_mac_len =
+                               skb_mac_header_was_set(skb) ? skb_mac_header_len(skb) : 0;
                skb->network_header = skb->transport_header;
        }
        ip_hdr(skb)->tot_len = htons(skb->len + ihl);
@@ -414,11 +418,15 @@ static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
 static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
 {
 #if IS_ENABLED(CONFIG_IPV6)
+       struct xfrm_offload *xo = xfrm_offload(skb);
        int ihl = skb->data - skb_transport_header(skb);
 
        if (skb->transport_header != skb->network_header) {
                memmove(skb_transport_header(skb),
                        skb_network_header(skb), ihl);
+               if (xo)
+                       xo->orig_mac_len =
+                               skb_mac_header_was_set(skb) ? skb_mac_header_len(skb) : 0;
                skb->network_header = skb->transport_header;
        }
        ipv6_hdr(skb)->payload_len = htons(skb->len + ihl -