Skip to content

Commit 58fbfec

Browse files
Paul Daveyklassert
authored andcommitted
xfrm: Preserve vlan tags for transport mode software GRO
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: 7785bba ("esp: Add a software GRO codepath") Signed-off-by: Paul Davey <[email protected]> Signed-off-by: Steffen Klassert <[email protected]>
1 parent 8b06a24 commit 58fbfec

File tree

5 files changed

+36
-2
lines changed

5 files changed

+36
-2
lines changed

include/linux/skbuff.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3031,6 +3031,21 @@ static inline void skb_mac_header_rebuild(struct sk_buff *skb)
30313031
}
30323032
}
30333033

3034+
/* Move the full mac header up to current network_header.
3035+
* Leaves skb->data pointing at offset skb->mac_len into the mac_header.
3036+
* Must be provided the complete mac header length.
3037+
*/
3038+
static inline void skb_mac_header_rebuild_full(struct sk_buff *skb, u32 full_mac_len)
3039+
{
3040+
if (skb_mac_header_was_set(skb)) {
3041+
const unsigned char *old_mac = skb_mac_header(skb);
3042+
3043+
skb_set_mac_header(skb, -full_mac_len);
3044+
memmove(skb_mac_header(skb), old_mac, full_mac_len);
3045+
__skb_push(skb, full_mac_len - skb->mac_len);
3046+
}
3047+
}
3048+
30343049
static inline int skb_checksum_start_offset(const struct sk_buff *skb)
30353050
{
30363051
return skb->csum_start - skb_headroom(skb);

include/net/xfrm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,9 @@ struct xfrm_offload {
10491049
#define CRYPTO_INVALID_PACKET_SYNTAX 64
10501050
#define CRYPTO_INVALID_PROTOCOL 128
10511051

1052+
/* Used to keep whole l2 header for transport mode GRO */
1053+
__u32 orig_mac_len;
1054+
10521055
__u8 proto;
10531056
__u8 inner_ipproto;
10541057
};

net/ipv4/xfrm4_input.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async)
6363
ip_send_check(iph);
6464

6565
if (xo && (xo->flags & XFRM_GRO)) {
66-
skb_mac_header_rebuild(skb);
66+
/* The full l2 header needs to be preserved so that re-injecting the packet at l2
67+
* works correctly in the presence of vlan tags.
68+
*/
69+
skb_mac_header_rebuild_full(skb, xo->orig_mac_len);
70+
skb_reset_network_header(skb);
6771
skb_reset_transport_header(skb);
6872
return 0;
6973
}

net/ipv6/xfrm6_input.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
5858
skb_postpush_rcsum(skb, skb_network_header(skb), nhlen);
5959

6060
if (xo && (xo->flags & XFRM_GRO)) {
61-
skb_mac_header_rebuild(skb);
61+
/* The full l2 header needs to be preserved so that re-injecting the packet at l2
62+
* works correctly in the presence of vlan tags.
63+
*/
64+
skb_mac_header_rebuild_full(skb, xo->orig_mac_len);
65+
skb_reset_network_header(skb);
6266
skb_reset_transport_header(skb);
6367
return 0;
6468
}

net/xfrm/xfrm_input.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,15 @@ static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
389389
*/
390390
static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
391391
{
392+
struct xfrm_offload *xo = xfrm_offload(skb);
392393
int ihl = skb->data - skb_transport_header(skb);
393394

394395
if (skb->transport_header != skb->network_header) {
395396
memmove(skb_transport_header(skb),
396397
skb_network_header(skb), ihl);
398+
if (xo)
399+
xo->orig_mac_len =
400+
skb_mac_header_was_set(skb) ? skb_mac_header_len(skb) : 0;
397401
skb->network_header = skb->transport_header;
398402
}
399403
ip_hdr(skb)->tot_len = htons(skb->len + ihl);
@@ -404,11 +408,15 @@ static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
404408
static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
405409
{
406410
#if IS_ENABLED(CONFIG_IPV6)
411+
struct xfrm_offload *xo = xfrm_offload(skb);
407412
int ihl = skb->data - skb_transport_header(skb);
408413

409414
if (skb->transport_header != skb->network_header) {
410415
memmove(skb_transport_header(skb),
411416
skb_network_header(skb), ihl);
417+
if (xo)
418+
xo->orig_mac_len =
419+
skb_mac_header_was_set(skb) ? skb_mac_header_len(skb) : 0;
412420
skb->network_header = skb->transport_header;
413421
}
414422
ipv6_hdr(skb)->payload_len = htons(skb->len + ihl -

0 commit comments

Comments
 (0)