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