From d9618a6196953718f7470de5d2e84121bed063f7 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:31 +0000 Subject: [PATCH] bpf: Require BPF_LWT_REROUTE when an lwt_in prog changes the protocol When an lwt_in prog returns BPF_OK, bpf_input() calls orig_input(), the input handler of the route. This handler is for one IP version and needs a dst of the same family. But BPF_LWT_ENCAP_IP lets the prog push a header of the other IP version and thus change skb->protocol. Then the handler reads and changes a header of the other IP version. The push of an SRH also replaces the dst, because seg6_lookup_nexthop() puts an rt6_info into the skb. Then ip_forward() uses the rt6_info as a struct rtable. Drop the skb with -EINVAL when skb->protocol changes, as bpf_xmit() does. Drop it also when the family of the dst changes, because skb->protocol does not always show this change. Today no such skb leaves as the prog made it. A prog works as before if it returns BPF_LWT_REROUTE, or if it pushes an SRH on an IPv6 route. The commit in the first Fixes tag lets a prog change skb->protocol. The commit in the second tag adds the check to bpf_xmit() only. Fixes: 52f278774e79 ("bpf: implement BPF_LWT_ENCAP_IP mode in bpf_lwt_push_encap") Fixes: 3bd0b15281af ("bpf: add handling of BPF_LWT_REROUTE to lwt_bpf.c") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-24-alexei.starovoitov@gmail.com --- net/core/lwt_bpf.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index 3a946e8709b1..60f4eda7dae9 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -165,6 +165,16 @@ static int bpf_input(struct sk_buff *skb) bpf_lwt_reset_cb(skb, proto); if (ret == BPF_LWT_REROUTE) return bpf_lwt_input_reroute(skb); + /* + * orig_input() is for the old protocol only, see bpf_xmit(). + * It also needs a dst of the old family. BPF_LWT_ENCAP_SEG6 + * puts an IPv6 dst into the skb. + */ + if (skb->protocol != proto || + skb_dst(skb)->ops->family != dst->ops->family) { + kfree_skb(skb); + return -EINVAL; + } } if (unlikely(!dst->lwtstate->orig_input)) {