1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2020 Jesper Dangaard Brouer */ |
3 | |
4 | #include <linux/bpf.h> |
5 | #include <bpf/bpf_helpers.h> |
6 | #include <linux/if_ether.h> |
7 | |
8 | #include <stddef.h> |
9 | #include <stdint.h> |
10 | |
11 | char _license[] SEC("license" ) = "GPL" ; |
12 | |
13 | /* Userspace will update with MTU it can see on device */ |
14 | volatile const int GLOBAL_USER_MTU; |
15 | volatile const __u32 GLOBAL_USER_IFINDEX; |
16 | |
17 | /* BPF-prog will update these with MTU values it can see */ |
18 | __u32 global_bpf_mtu_xdp = 0; |
19 | __u32 global_bpf_mtu_tc = 0; |
20 | |
21 | SEC("xdp" ) |
22 | int xdp_use_helper_basic(struct xdp_md *ctx) |
23 | { |
24 | __u32 mtu_len = 0; |
25 | |
26 | if (bpf_check_mtu(ctx, 0, &mtu_len, 0, 0)) |
27 | return XDP_ABORTED; |
28 | |
29 | return XDP_PASS; |
30 | } |
31 | |
32 | SEC("xdp" ) |
33 | int xdp_use_helper(struct xdp_md *ctx) |
34 | { |
35 | int retval = XDP_PASS; /* Expected retval on successful test */ |
36 | __u32 mtu_len = 0; |
37 | __u32 ifindex = 0; |
38 | int delta = 0; |
39 | |
40 | /* When ifindex is zero, save net_device lookup and use ctx netdev */ |
41 | if (GLOBAL_USER_IFINDEX > 0) |
42 | ifindex = GLOBAL_USER_IFINDEX; |
43 | |
44 | if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) { |
45 | /* mtu_len is also valid when check fail */ |
46 | retval = XDP_ABORTED; |
47 | goto out; |
48 | } |
49 | |
50 | if (mtu_len != GLOBAL_USER_MTU) |
51 | retval = XDP_DROP; |
52 | |
53 | out: |
54 | global_bpf_mtu_xdp = mtu_len; |
55 | return retval; |
56 | } |
57 | |
58 | SEC("xdp" ) |
59 | int xdp_exceed_mtu(struct xdp_md *ctx) |
60 | { |
61 | void *data_end = (void *)(long)ctx->data_end; |
62 | void *data = (void *)(long)ctx->data; |
63 | __u32 ifindex = GLOBAL_USER_IFINDEX; |
64 | __u32 data_len = data_end - data; |
65 | int retval = XDP_ABORTED; /* Fail */ |
66 | __u32 mtu_len = 0; |
67 | int delta; |
68 | int err; |
69 | |
70 | /* Exceed MTU with 1 via delta adjust */ |
71 | delta = GLOBAL_USER_MTU - (data_len - ETH_HLEN) + 1; |
72 | |
73 | err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); |
74 | if (err) { |
75 | retval = XDP_PASS; /* Success in exceeding MTU check */ |
76 | if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) |
77 | retval = XDP_DROP; |
78 | } |
79 | |
80 | global_bpf_mtu_xdp = mtu_len; |
81 | return retval; |
82 | } |
83 | |
84 | SEC("xdp" ) |
85 | int xdp_minus_delta(struct xdp_md *ctx) |
86 | { |
87 | int retval = XDP_PASS; /* Expected retval on successful test */ |
88 | void *data_end = (void *)(long)ctx->data_end; |
89 | void *data = (void *)(long)ctx->data; |
90 | __u32 ifindex = GLOBAL_USER_IFINDEX; |
91 | __u32 data_len = data_end - data; |
92 | __u32 mtu_len = 0; |
93 | int delta; |
94 | |
95 | /* Borderline test case: Minus delta exceeding packet length allowed */ |
96 | delta = -((data_len - ETH_HLEN) + 1); |
97 | |
98 | /* Minus length (adjusted via delta) still pass MTU check, other helpers |
99 | * are responsible for catching this, when doing actual size adjust |
100 | */ |
101 | if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) |
102 | retval = XDP_ABORTED; |
103 | |
104 | global_bpf_mtu_xdp = mtu_len; |
105 | return retval; |
106 | } |
107 | |
108 | SEC("xdp" ) |
109 | int xdp_input_len(struct xdp_md *ctx) |
110 | { |
111 | int retval = XDP_PASS; /* Expected retval on successful test */ |
112 | void *data_end = (void *)(long)ctx->data_end; |
113 | void *data = (void *)(long)ctx->data; |
114 | __u32 ifindex = GLOBAL_USER_IFINDEX; |
115 | __u32 data_len = data_end - data; |
116 | |
117 | /* API allow user give length to check as input via mtu_len param, |
118 | * resulting MTU value is still output in mtu_len param after call. |
119 | * |
120 | * Input len is L3, like MTU and iph->tot_len. |
121 | * Remember XDP data_len is L2. |
122 | */ |
123 | __u32 mtu_len = data_len - ETH_HLEN; |
124 | |
125 | if (bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0)) |
126 | retval = XDP_ABORTED; |
127 | |
128 | global_bpf_mtu_xdp = mtu_len; |
129 | return retval; |
130 | } |
131 | |
132 | SEC("xdp" ) |
133 | int xdp_input_len_exceed(struct xdp_md *ctx) |
134 | { |
135 | int retval = XDP_ABORTED; /* Fail */ |
136 | __u32 ifindex = GLOBAL_USER_IFINDEX; |
137 | int err; |
138 | |
139 | /* API allow user give length to check as input via mtu_len param, |
140 | * resulting MTU value is still output in mtu_len param after call. |
141 | * |
142 | * Input length value is L3 size like MTU. |
143 | */ |
144 | __u32 mtu_len = GLOBAL_USER_MTU; |
145 | |
146 | mtu_len += 1; /* Exceed with 1 */ |
147 | |
148 | err = bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0); |
149 | if (err == BPF_MTU_CHK_RET_FRAG_NEEDED) |
150 | retval = XDP_PASS ; /* Success in exceeding MTU check */ |
151 | |
152 | global_bpf_mtu_xdp = mtu_len; |
153 | return retval; |
154 | } |
155 | |
156 | SEC("tc" ) |
157 | int tc_use_helper(struct __sk_buff *ctx) |
158 | { |
159 | int retval = BPF_OK; /* Expected retval on successful test */ |
160 | __u32 mtu_len = 0; |
161 | int delta = 0; |
162 | |
163 | if (bpf_check_mtu(ctx, 0, &mtu_len, delta, 0)) { |
164 | retval = BPF_DROP; |
165 | goto out; |
166 | } |
167 | |
168 | if (mtu_len != GLOBAL_USER_MTU) |
169 | retval = BPF_REDIRECT; |
170 | out: |
171 | global_bpf_mtu_tc = mtu_len; |
172 | return retval; |
173 | } |
174 | |
175 | SEC("tc" ) |
176 | int tc_exceed_mtu(struct __sk_buff *ctx) |
177 | { |
178 | __u32 ifindex = GLOBAL_USER_IFINDEX; |
179 | int retval = BPF_DROP; /* Fail */ |
180 | __u32 skb_len = ctx->len; |
181 | __u32 mtu_len = 0; |
182 | int delta; |
183 | int err; |
184 | |
185 | /* Exceed MTU with 1 via delta adjust */ |
186 | delta = GLOBAL_USER_MTU - (skb_len - ETH_HLEN) + 1; |
187 | |
188 | err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); |
189 | if (err) { |
190 | retval = BPF_OK; /* Success in exceeding MTU check */ |
191 | if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) |
192 | retval = BPF_DROP; |
193 | } |
194 | |
195 | global_bpf_mtu_tc = mtu_len; |
196 | return retval; |
197 | } |
198 | |
199 | SEC("tc" ) |
200 | int tc_exceed_mtu_da(struct __sk_buff *ctx) |
201 | { |
202 | /* SKB Direct-Access variant */ |
203 | void *data_end = (void *)(long)ctx->data_end; |
204 | void *data = (void *)(long)ctx->data; |
205 | __u32 ifindex = GLOBAL_USER_IFINDEX; |
206 | __u32 data_len = data_end - data; |
207 | int retval = BPF_DROP; /* Fail */ |
208 | __u32 mtu_len = 0; |
209 | int delta; |
210 | int err; |
211 | |
212 | /* Exceed MTU with 1 via delta adjust */ |
213 | delta = GLOBAL_USER_MTU - (data_len - ETH_HLEN) + 1; |
214 | |
215 | err = bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0); |
216 | if (err) { |
217 | retval = BPF_OK; /* Success in exceeding MTU check */ |
218 | if (err != BPF_MTU_CHK_RET_FRAG_NEEDED) |
219 | retval = BPF_DROP; |
220 | } |
221 | |
222 | global_bpf_mtu_tc = mtu_len; |
223 | return retval; |
224 | } |
225 | |
226 | SEC("tc" ) |
227 | int tc_minus_delta(struct __sk_buff *ctx) |
228 | { |
229 | int retval = BPF_OK; /* Expected retval on successful test */ |
230 | __u32 ifindex = GLOBAL_USER_IFINDEX; |
231 | __u32 skb_len = ctx->len; |
232 | __u32 mtu_len = 0; |
233 | int delta; |
234 | |
235 | /* Borderline test case: Minus delta exceeding packet length allowed */ |
236 | delta = -((skb_len - ETH_HLEN) + 1); |
237 | |
238 | /* Minus length (adjusted via delta) still pass MTU check, other helpers |
239 | * are responsible for catching this, when doing actual size adjust |
240 | */ |
241 | if (bpf_check_mtu(ctx, ifindex, &mtu_len, delta, 0)) |
242 | retval = BPF_DROP; |
243 | |
244 | global_bpf_mtu_xdp = mtu_len; |
245 | return retval; |
246 | } |
247 | |
248 | SEC("tc" ) |
249 | int tc_input_len(struct __sk_buff *ctx) |
250 | { |
251 | int retval = BPF_OK; /* Expected retval on successful test */ |
252 | __u32 ifindex = GLOBAL_USER_IFINDEX; |
253 | |
254 | /* API allow user give length to check as input via mtu_len param, |
255 | * resulting MTU value is still output in mtu_len param after call. |
256 | * |
257 | * Input length value is L3 size. |
258 | */ |
259 | __u32 mtu_len = GLOBAL_USER_MTU; |
260 | |
261 | if (bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0)) |
262 | retval = BPF_DROP; |
263 | |
264 | global_bpf_mtu_xdp = mtu_len; |
265 | return retval; |
266 | } |
267 | |
268 | SEC("tc" ) |
269 | int tc_input_len_exceed(struct __sk_buff *ctx) |
270 | { |
271 | int retval = BPF_DROP; /* Fail */ |
272 | __u32 ifindex = GLOBAL_USER_IFINDEX; |
273 | int err; |
274 | |
275 | /* API allow user give length to check as input via mtu_len param, |
276 | * resulting MTU value is still output in mtu_len param after call. |
277 | * |
278 | * Input length value is L3 size like MTU. |
279 | */ |
280 | __u32 mtu_len = GLOBAL_USER_MTU; |
281 | |
282 | mtu_len += 1; /* Exceed with 1 */ |
283 | |
284 | err = bpf_check_mtu(ctx, ifindex, &mtu_len, 0, 0); |
285 | if (err == BPF_MTU_CHK_RET_FRAG_NEEDED) |
286 | retval = BPF_OK; /* Success in exceeding MTU check */ |
287 | |
288 | global_bpf_mtu_xdp = mtu_len; |
289 | return retval; |
290 | } |
291 | |