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