mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
45bcc58d57
commit
0fcb3f11a9
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user