1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include <stddef.h>
17#include <stdbool.h>
18#include <string.h>
19#include <linux/bpf.h>
20#include <linux/if_ether.h>
21#include <linux/if_vlan.h>
22#include <linux/in.h>
23#include <linux/pkt_cls.h>
24
25#include <bpf/bpf_helpers.h>
26#include <bpf/bpf_endian.h>
27
28
29
30
31
32
33
34struct _vlan_hdr {
35 __be16 h_vlan_TCI;
36 __be16 h_vlan_encapsulated_proto;
37};
38#define VLAN_PRIO_MASK 0xe000
39#define VLAN_PRIO_SHIFT 13
40#define VLAN_CFI_MASK 0x1000
41#define VLAN_TAG_PRESENT VLAN_CFI_MASK
42#define VLAN_VID_MASK 0x0fff
43#define VLAN_N_VID 4096
44
45struct parse_pkt {
46 __u16 l3_proto;
47 __u16 l3_offset;
48 __u16 vlan_outer;
49 __u16 vlan_inner;
50 __u8 vlan_outer_offset;
51 __u8 vlan_inner_offset;
52};
53
54char _license[] SEC("license") = "GPL";
55
56static __always_inline
57bool parse_eth_frame(struct ethhdr *eth, void *data_end, struct parse_pkt *pkt)
58{
59 __u16 eth_type;
60 __u8 offset;
61
62 offset = sizeof(*eth);
63
64 if ((void *)eth + offset + (2*sizeof(struct _vlan_hdr)) > data_end)
65 return false;
66
67 eth_type = eth->h_proto;
68
69
70 if (eth_type == bpf_htons(ETH_P_8021Q)
71 || eth_type == bpf_htons(ETH_P_8021AD)) {
72 struct _vlan_hdr *vlan_hdr;
73
74 vlan_hdr = (void *)eth + offset;
75 pkt->vlan_outer_offset = offset;
76 pkt->vlan_outer = bpf_ntohs(vlan_hdr->h_vlan_TCI)
77 & VLAN_VID_MASK;
78 eth_type = vlan_hdr->h_vlan_encapsulated_proto;
79 offset += sizeof(*vlan_hdr);
80 }
81
82
83 if (eth_type == bpf_htons(ETH_P_8021Q)
84 || eth_type == bpf_htons(ETH_P_8021AD)) {
85 struct _vlan_hdr *vlan_hdr;
86
87 vlan_hdr = (void *)eth + offset;
88 pkt->vlan_inner_offset = offset;
89 pkt->vlan_inner = bpf_ntohs(vlan_hdr->h_vlan_TCI)
90 & VLAN_VID_MASK;
91 eth_type = vlan_hdr->h_vlan_encapsulated_proto;
92 offset += sizeof(*vlan_hdr);
93 }
94
95 pkt->l3_proto = bpf_ntohs(eth_type);
96 pkt->l3_offset = offset;
97
98 return true;
99}
100
101
102#define TESTVLAN 4011
103
104
105SEC("xdp_drop_vlan_4011")
106int xdp_prognum0(struct xdp_md *ctx)
107{
108 void *data_end = (void *)(long)ctx->data_end;
109 void *data = (void *)(long)ctx->data;
110 struct parse_pkt pkt = { 0 };
111
112 if (!parse_eth_frame(data, data_end, &pkt))
113 return XDP_ABORTED;
114
115
116 if (pkt.vlan_outer == TESTVLAN)
117 return XDP_ABORTED;
118
119
120
121
122
123
124 return XDP_PASS;
125}
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145#define TO_VLAN 0
146
147SEC("xdp_vlan_change")
148int xdp_prognum1(struct xdp_md *ctx)
149{
150 void *data_end = (void *)(long)ctx->data_end;
151 void *data = (void *)(long)ctx->data;
152 struct parse_pkt pkt = { 0 };
153
154 if (!parse_eth_frame(data, data_end, &pkt))
155 return XDP_ABORTED;
156
157
158 if (pkt.vlan_outer == TESTVLAN) {
159 struct _vlan_hdr *vlan_hdr = data + pkt.vlan_outer_offset;
160
161
162 vlan_hdr->h_vlan_TCI =
163 bpf_htons((bpf_ntohs(vlan_hdr->h_vlan_TCI) & 0xf000)
164 | TO_VLAN);
165 }
166
167 return XDP_PASS;
168}
169
170
171
172
173
174
175
176#ifndef ETH_ALEN
177#define ETH_ALEN 6
178#endif
179#define VLAN_HDR_SZ 4
180
181SEC("xdp_vlan_remove_outer")
182int xdp_prognum2(struct xdp_md *ctx)
183{
184 void *data_end = (void *)(long)ctx->data_end;
185 void *data = (void *)(long)ctx->data;
186 struct parse_pkt pkt = { 0 };
187 char *dest;
188
189 if (!parse_eth_frame(data, data_end, &pkt))
190 return XDP_ABORTED;
191
192
193 if (pkt.vlan_outer_offset == 0)
194 return XDP_PASS;
195
196
197 dest = data;
198 dest+= VLAN_HDR_SZ;
199
200
201
202
203 __builtin_memmove(dest, data, ETH_ALEN * 2);
204
205
206
207 bpf_xdp_adjust_head(ctx, VLAN_HDR_SZ);
208
209 return XDP_PASS;
210}
211
212static __always_inline
213void shift_mac_4bytes_16bit(void *data)
214{
215 __u16 *p = data;
216
217 p[7] = p[5];
218 p[6] = p[4];
219 p[5] = p[3];
220 p[4] = p[2];
221 p[3] = p[1];
222 p[2] = p[0];
223}
224
225static __always_inline
226void shift_mac_4bytes_32bit(void *data)
227{
228 __u32 *p = data;
229
230
231
232
233
234
235 p[3] = p[2];
236 p[2] = p[1];
237 p[1] = p[0];
238}
239
240SEC("xdp_vlan_remove_outer2")
241int xdp_prognum3(struct xdp_md *ctx)
242{
243 void *data_end = (void *)(long)ctx->data_end;
244 void *data = (void *)(long)ctx->data;
245 struct ethhdr *orig_eth = data;
246 struct parse_pkt pkt = { 0 };
247
248 if (!parse_eth_frame(orig_eth, data_end, &pkt))
249 return XDP_ABORTED;
250
251
252 if (pkt.vlan_outer_offset == 0)
253 return XDP_PASS;
254
255
256 shift_mac_4bytes_32bit(data);
257
258
259 bpf_xdp_adjust_head(ctx, VLAN_HDR_SZ);
260
261 return XDP_PASS;
262}
263
264
265
266
267
268
269
270SEC("tc_vlan_push")
271int _tc_progA(struct __sk_buff *ctx)
272{
273 bpf_skb_vlan_push(ctx, bpf_htons(ETH_P_8021Q), TESTVLAN);
274
275 return TC_ACT_OK;
276}
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293