bpf: Move the MAC header in bpf_lwt_seg6_adjust_srh()

bpf_skb_net_hdr_push() and bpf_skb_net_hdr_pop() change skb->data and
skb->mac_header by len. That is correct in a tc prog, where skb->data is
the MAC header. But bpf_lwt_seg6_adjust_srh() calls them in an End.BPF
prog, where skb->data is the network header. The MAC header does not move:

  before push  |       free       | MAC header   | IPv6 | SRH | payload
               ^ head             ^ mac_header   ^ data
  today        |       free       | MAC h| IPv6 | SRH   + len | payload
               ^ head     ^ mac_header   ^ data
  this patch   |   free   | MAC header   | IPv6 | SRH   + len | payload
               ^ head     ^ mac_header   ^ data

Users of skb_mac_header() get incorrect bytes until __dev_queue_xmit() sets
skb->mac_header again. skb_cow_head() reserves only len bytes. Thus the
16-bit offset skb->mac_header can wrap. A push also changes the "not set"
value. In both cases skb_mac_header() points after the skb.

Move the MAC header too. Pass len + skb->mac_len to skb_cow_head(). Keep
the "not set" value. The second Fixes tag is for this case. Since that
commit an End.BPF prog can get an skb with skb->mac_header not set.

Fixes: fe94cc290f ("bpf: Add IPv6 Segment Routing helpers")
Fixes: 20a081b798 ("seg6: add VRF support for SRv6 End.DT6 behavior")
Signed-off-by: Chris Mason <mason@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20261101190535.1454146-21-alexei.starovoitov@gmail.com
This commit is contained in:
Chris Mason 2026-11-01 19:05:28 +00:00 committed by Alexei Starovoitov
parent 45bcc58d57
commit 0fcb3f11a9
No known key found for this signature in database

View File

@ -7159,6 +7159,7 @@ BPF_CALL_3(bpf_lwt_seg6_adjust_srh, struct sk_buff *, skb, u32, offset,
{
struct seg6_bpf_srh_state *srh_state =
this_cpu_ptr(&seg6_bpf_srh_states);
bool mac_set = skb_mac_header_was_set(skb);
struct ipv6_sr_hdr *srh = srh_state->srh;
void *srh_end, *srh_tlvs, *ptr;
struct ipv6hdr *hdr;
@ -7184,20 +7185,38 @@ BPF_CALL_3(bpf_lwt_seg6_adjust_srh, struct sk_buff *, skb, u32, offset,
if (unlikely(len > (U8_MAX << 3) - srh_state->hdrlen))
return -EFAULT;
/*
* skb->data is the network header here. The MAC header is in front of
* it, so bpf_skb_net_hdr_push() and bpf_skb_net_hdr_pop() do not move
* it with the other headers.
*/
if (len > 0) {
ret = skb_cow_head(skb, len);
ret = skb_cow_head(skb, len + skb->mac_len);
if (unlikely(ret < 0))
return ret;
/* The push can write over the MAC header, so move it first. */
if (mac_set)
memmove(skb->data - len - skb->mac_len,
skb_mac_header(skb), skb->mac_len);
ret = bpf_skb_net_hdr_push(skb, offset, len);
} else {
ret = bpf_skb_net_hdr_pop(skb, offset, -1 * len);
/* The pop has added -len to skb->mac_header. */
if (likely(!ret) && mac_set)
memmove(skb->data - skb->mac_len,
skb_mac_header(skb) + len, skb->mac_len);
}
bpf_compute_data_pointers(skb);
if (unlikely(ret < 0))
return ret;
if (mac_set)
skb_set_mac_header(skb, -skb->mac_len);
else
skb_unset_mac_header(skb);
hdr = (struct ipv6hdr *)skb->data;
hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));