From 45bcc58d576705d2c001332766e10313f1c55bad Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:27 +0000 Subject: [PATCH] bpf: Don't look for the SRH again in the End.BPF helpers A packet can have two SRHs. Thus input_action_end_bpf() uses IP6_FH_F_SKIP_RH to skip routing headers with segments_left 0. The helpers bpf_lwt_seg6_store_bytes() and bpf_lwt_seg6_adjust_srh() can reallocate the head of the skb. Thus they look for the SRH again, but without this flag. +------------------------+ <- the helpers: srh_state->srh | SRH 1, segments_left 0 | +------------------------+ <- input_action_end_bpf(): srh_state->srh | SRH 2 | and srh_state->hdrlen +------------------------+ Then the helpers check offset against the wrong limits. If a helper changes TLVs, seg6_bpf_has_valid_srh() writes the length of SRH 2 to SRH 1. Neither helper changes the offset of the SRH from skb->data. Calculate the new address from this offset. To add IP6_FH_F_SKIP_RH is not sufficient, because advance_nextseg() can set segments_left of SRH 2 to 0. The helper bpf_lwt_seg6_action() can change the headers and still looks for the SRH. Fixes: fbbc5bc2ab8c ("seg6: ignore routing header with segments left equal to 0") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-20-alexei.starovoitov@gmail.com --- net/core/filter.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index acd1e89351d6..d634850a38c7 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -7024,12 +7024,13 @@ BPF_CALL_4(bpf_lwt_seg6_store_bytes, struct sk_buff *, skb, u32, offset, this_cpu_ptr(&seg6_bpf_srh_states); struct ipv6_sr_hdr *srh = srh_state->srh; void *srh_tlvs, *srh_end, *ptr; - int srhoff = 0; + int srhoff; lockdep_assert_held(&srh_state->bh_lock); if (srh == NULL) return -EINVAL; + srhoff = (unsigned char *)srh - skb->data; srh_tlvs = (void *)((char *)srh + ((srh->first_segment + 1) << 4)); srh_end = (void *)((char *)srh + sizeof(*srh) + srh_state->hdrlen); @@ -7042,8 +7043,7 @@ BPF_CALL_4(bpf_lwt_seg6_store_bytes, struct sk_buff *, skb, u32, offset, if (unlikely(bpf_try_make_writable(skb, offset + len))) return -EFAULT; - if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) - return -EINVAL; + /* The head may have moved. */ srh_state->srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); memcpy(skb->data + offset, from, len); @@ -7162,13 +7162,14 @@ BPF_CALL_3(bpf_lwt_seg6_adjust_srh, struct sk_buff *, skb, u32, offset, struct ipv6_sr_hdr *srh = srh_state->srh; void *srh_end, *srh_tlvs, *ptr; struct ipv6hdr *hdr; - int srhoff = 0; + int srhoff; int ret; lockdep_assert_held(&srh_state->bh_lock); if (unlikely(srh == NULL)) return -EINVAL; + srhoff = (unsigned char *)srh - skb->data; srh_tlvs = (void *)((unsigned char *)srh + sizeof(*srh) + ((srh->first_segment + 1) << 4)); srh_end = (void *)((unsigned char *)srh + sizeof(*srh) + @@ -7200,8 +7201,7 @@ BPF_CALL_3(bpf_lwt_seg6_adjust_srh, struct sk_buff *, skb, u32, offset, hdr = (struct ipv6hdr *)skb->data; hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); - if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) - return -EINVAL; + /* The bytes in front of offset have moved together with skb->data. */ srh_state->srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); srh_state->hdrlen += len; srh_state->valid = false;