[RFC bpf-next v2 1/3] bpf: Add PPPoE encap support to bpf_skb_adjust_room
flat view
From: ThisSeanZhang <hidden>
Date: 2026-10-03 19:34:01
Also in:
bpf
Subsystem:
bpf [general] (safe dynamic programs and tools), bpf [networking] (tcx & tc bpf, sock_addr), networking [general], the rest · Maintainers:
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Add a new BPF_F_ADJ_ROOM_ENCAP_PPPOE flag to bpf_skb_adjust_room() so
that a TC BPF program can encapsulate an IPv4 or IPv6 packet into a
PPPoE session header:
bpf_skb_adjust_room(skb, PPPOE_SES_HLEN, BPF_ADJ_ROOM_MAC,
BPF_F_ADJ_ROOM_ENCAP_PPPOE);
The flag inserts eight bytes between the MAC and network headers: the
six-byte PPPoE session header followed by the two-byte PPP protocol
field. It then updates the skb metadata, including skb->protocol and
skb->mac_len. The BPF program remains responsible for filling in the
PPPoE header and the Ethernet ethertype, for example with
bpf_skb_store_bytes().
Previously, a TC program could add the header bytes manually, but the
skb would continue to be treated as a plain IP packet because its
protocol metadata was unchanged. With the stale skb->protocol, software
GSO may select a non-PPPoE segmentation handler and process the packet
incorrectly.
This makes the skb metadata compatible with the PPPoE GRO/GSO handlers
registered for ETH_P_PPP_SES. The PPPoE GRO/GSO support must be built in
or loaded before receive-side GRO or software GSO uses those handlers.
Signed-off-by: ThisSeanZhang <redacted>
---
include/uapi/linux/bpf.h | 12 ++++++++++++
net/core/filter.c | 22 ++++++++++++++++++++++
tools/include/uapi/linux/bpf.h | 12 ++++++++++++
3 files changed, 46 insertions(+)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 732b35cc0..30481d040 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h@@ -3036,6 +3036,17 @@ union bpf_attr { * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the * L2 type as Ethernet. * + * * **BPF_F_ADJ_ROOM_ENCAP_PPPOE**: + * Encapsulate the packet in a PPPoE session header. Must be + * used with **BPF_ADJ_ROOM_MAC** mode and *len_diff* equal to + * the size of the PPPoE session header plus the PPP protocol + * field (8 bytes in total). The room is inserted between the + * MAC header and the network header, *skb->protocol* is set + * to **ETH_P_PPP_SES** and *skb->mac_len* is updated + * accordingly. The PPPoE header itself and the ethertype of + * the MAC header are filled in by the BPF program, e.g. via + * **bpf_skb_store_bytes**. + * * * **BPF_F_ADJ_ROOM_DECAP_L3_IPV4**, * **BPF_F_ADJ_ROOM_DECAP_L3_IPV6**: * Indicate the new IP header version after decapsulating the
@@ -6323,6 +6334,7 @@ enum bpf_adj_room_flags { BPF_F_ADJ_ROOM_DECAP_L4_UDP = (1ULL << 10), BPF_F_ADJ_ROOM_DECAP_IPXIP4 = (1ULL << 11), BPF_F_ADJ_ROOM_DECAP_IPXIP6 = (1ULL << 12), + BPF_F_ADJ_ROOM_ENCAP_PPPOE = (1ULL << 13), }; enum {
diff --git a/net/core/filter.c b/net/core/filter.c
index 70dc62167..5b204e316 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c@@ -47,6 +47,7 @@ #include <linux/ratelimit.h> #include <linux/seccomp.h> #include <linux/if_vlan.h> +#include <linux/if_pppox.h> #include <linux/bpf.h> #include <linux/btf.h> #include <net/sch_generic.h>
@@ -3583,6 +3584,7 @@ static u32 bpf_skb_net_base_len(const struct sk_buff *skb) BPF_F_ADJ_ROOM_ENCAP_L4_GRE | \ BPF_F_ADJ_ROOM_ENCAP_L4_UDP | \ BPF_F_ADJ_ROOM_ENCAP_L2_ETH | \ + BPF_F_ADJ_ROOM_ENCAP_PPPOE | \ BPF_F_ADJ_ROOM_ENCAP_L2( \ BPF_ADJ_ROOM_ENCAP_L2_MASK))
@@ -3688,6 +3690,14 @@ static int bpf_skb_net_grow(struct sk_buff *skb, u32 off, u32 len_diff, skb_dst_drop(skb); } + if (flags & BPF_F_ADJ_ROOM_ENCAP_PPPOE) { + /* Network header points at the inserted PPPoE header. */ + skb->protocol = htons(ETH_P_PPP_SES); + skb_reset_mac_len(skb); + if (skb_valid_dst(skb)) + skb_dst_drop(skb); + } + if (skb_is_gso(skb)) { struct skb_shared_info *shinfo = skb_shinfo(skb);
@@ -3874,6 +3884,18 @@ BPF_CALL_4(bpf_skb_adjust_room, struct sk_buff *, skb, s32, len_diff, return -ENOTSUPP; } + if (flags & BPF_F_ADJ_ROOM_ENCAP_PPPOE) { + /* The PPPoE session header has a fixed size and is + * inserted directly after the MAC header. + */ + if (shrink || mode != BPF_ADJ_ROOM_MAC || + len_diff != PPPOE_SES_HLEN || + flags & ((BPF_F_ADJ_ROOM_ENCAP_MASK | + BPF_F_ADJ_ROOM_DECAP_MASK) & + ~BPF_F_ADJ_ROOM_ENCAP_PPPOE)) + return -EINVAL; + } + if (flags & BPF_F_ADJ_ROOM_DECAP_MASK) { u32 len_decap_min = 0;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 732b35cc0..30481d040 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h@@ -3036,6 +3036,17 @@ union bpf_attr { * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the * L2 type as Ethernet. * + * * **BPF_F_ADJ_ROOM_ENCAP_PPPOE**: + * Encapsulate the packet in a PPPoE session header. Must be + * used with **BPF_ADJ_ROOM_MAC** mode and *len_diff* equal to + * the size of the PPPoE session header plus the PPP protocol + * field (8 bytes in total). The room is inserted between the + * MAC header and the network header, *skb->protocol* is set + * to **ETH_P_PPP_SES** and *skb->mac_len* is updated + * accordingly. The PPPoE header itself and the ethertype of + * the MAC header are filled in by the BPF program, e.g. via + * **bpf_skb_store_bytes**. + * * * **BPF_F_ADJ_ROOM_DECAP_L3_IPV4**, * **BPF_F_ADJ_ROOM_DECAP_L3_IPV6**: * Indicate the new IP header version after decapsulating the
@@ -6323,6 +6334,7 @@ enum bpf_adj_room_flags { BPF_F_ADJ_ROOM_DECAP_L4_UDP = (1ULL << 10), BPF_F_ADJ_ROOM_DECAP_IPXIP4 = (1ULL << 11), BPF_F_ADJ_ROOM_DECAP_IPXIP6 = (1ULL << 12), + BPF_F_ADJ_ROOM_ENCAP_PPPOE = (1ULL << 13), }; enum {
--
2.47.3