1 // SPDX-License-Identifier: GPL-2.0
3 #include <test_progs.h>
4 #include <linux/pkt_cls.h>
6 #include "test_tc_bpf.skel.h"
10 #define TEST_DECLARE_OPTS(__fd) \
11 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_h, .handle = 1); \
12 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_p, .priority = 1); \
13 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_f, .prog_fd = __fd); \
14 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hp, .handle = 1, .priority = 1); \
15 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hf, .handle = 1, .prog_fd = __fd); \
16 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_pf, .priority = 1, .prog_fd = __fd); \
17 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpf, .handle = 1, .priority = 1, .prog_fd = __fd); \
18 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpi, .handle = 1, .priority = 1, .prog_id = 42); \
19 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpr, .handle = 1, .priority = 1, \
20 .flags = BPF_TC_F_REPLACE); \
21 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpfi, .handle = 1, .priority = 1, .prog_fd = __fd, \
23 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_prio_max, .handle = 1, .priority = UINT16_MAX + 1);
25 static int test_tc_bpf_basic(const struct bpf_tc_hook *hook, int fd)
27 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts, .handle = 1, .priority = 1, .prog_fd = fd);
28 struct bpf_prog_info info = {};
29 __u32 info_len = sizeof(info);
32 ret = bpf_obj_get_info_by_fd(fd, &info, &info_len);
33 if (!ASSERT_OK(ret, "bpf_obj_get_info_by_fd"))
36 ret = bpf_tc_attach(hook, &opts);
37 if (!ASSERT_OK(ret, "bpf_tc_attach"))
40 if (!ASSERT_EQ(opts.handle, 1, "handle set") ||
41 !ASSERT_EQ(opts.priority, 1, "priority set") ||
42 !ASSERT_EQ(opts.prog_id, info.id, "prog_id set"))
46 opts.flags = BPF_TC_F_REPLACE;
47 ret = bpf_tc_attach(hook, &opts);
48 if (!ASSERT_OK(ret, "bpf_tc_attach replace mode"))
51 opts.flags = opts.prog_fd = opts.prog_id = 0;
52 ret = bpf_tc_query(hook, &opts);
53 if (!ASSERT_OK(ret, "bpf_tc_query"))
56 if (!ASSERT_EQ(opts.handle, 1, "handle set") ||
57 !ASSERT_EQ(opts.priority, 1, "priority set") ||
58 !ASSERT_EQ(opts.prog_id, info.id, "prog_id set"))
62 opts.flags = opts.prog_fd = opts.prog_id = 0;
63 ret = bpf_tc_detach(hook, &opts);
64 ASSERT_OK(ret, "bpf_tc_detach");
68 static int test_tc_bpf_api(struct bpf_tc_hook *hook, int fd)
70 DECLARE_LIBBPF_OPTS(bpf_tc_opts, attach_opts, .handle = 1, .priority = 1, .prog_fd = fd);
71 DECLARE_LIBBPF_OPTS(bpf_tc_hook, inv_hook, .attach_point = BPF_TC_INGRESS);
72 DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts, .handle = 1, .priority = 1);
75 ret = bpf_tc_hook_create(NULL);
76 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook = NULL"))
79 /* hook ifindex = 0 */
80 ret = bpf_tc_hook_create(&inv_hook);
81 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook ifindex == 0"))
84 ret = bpf_tc_hook_destroy(&inv_hook);
85 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook ifindex == 0"))
88 ret = bpf_tc_attach(&inv_hook, &attach_opts);
89 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook ifindex == 0"))
91 attach_opts.prog_id = 0;
93 ret = bpf_tc_detach(&inv_hook, &opts);
94 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook ifindex == 0"))
97 ret = bpf_tc_query(&inv_hook, &opts);
98 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook ifindex == 0"))
101 /* hook ifindex < 0 */
102 inv_hook.ifindex = -1;
104 ret = bpf_tc_hook_create(&inv_hook);
105 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook ifindex < 0"))
108 ret = bpf_tc_hook_destroy(&inv_hook);
109 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook ifindex < 0"))
112 ret = bpf_tc_attach(&inv_hook, &attach_opts);
113 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook ifindex < 0"))
115 attach_opts.prog_id = 0;
117 ret = bpf_tc_detach(&inv_hook, &opts);
118 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook ifindex < 0"))
121 ret = bpf_tc_query(&inv_hook, &opts);
122 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook ifindex < 0"))
125 inv_hook.ifindex = LO_IFINDEX;
127 /* hook.attach_point invalid */
128 inv_hook.attach_point = 0xabcd;
129 ret = bpf_tc_hook_create(&inv_hook);
130 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook.attach_point"))
133 ret = bpf_tc_hook_destroy(&inv_hook);
134 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook.attach_point"))
137 ret = bpf_tc_attach(&inv_hook, &attach_opts);
138 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook.attach_point"))
141 ret = bpf_tc_detach(&inv_hook, &opts);
142 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook.attach_point"))
145 ret = bpf_tc_query(&inv_hook, &opts);
146 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook.attach_point"))
149 inv_hook.attach_point = BPF_TC_INGRESS;
151 /* hook.attach_point valid, but parent invalid */
152 inv_hook.parent = TC_H_MAKE(1UL << 16, 10);
153 ret = bpf_tc_hook_create(&inv_hook);
154 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook parent"))
157 ret = bpf_tc_hook_destroy(&inv_hook);
158 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook parent"))
161 ret = bpf_tc_attach(&inv_hook, &attach_opts);
162 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook parent"))
165 ret = bpf_tc_detach(&inv_hook, &opts);
166 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook parent"))
169 ret = bpf_tc_query(&inv_hook, &opts);
170 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook parent"))
173 inv_hook.attach_point = BPF_TC_CUSTOM;
175 /* These return EOPNOTSUPP instead of EINVAL as parent is checked after
176 * attach_point of the hook.
178 ret = bpf_tc_hook_create(&inv_hook);
179 if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_create invalid hook parent"))
182 ret = bpf_tc_hook_destroy(&inv_hook);
183 if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_destroy invalid hook parent"))
186 ret = bpf_tc_attach(&inv_hook, &attach_opts);
187 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook parent"))
190 ret = bpf_tc_detach(&inv_hook, &opts);
191 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook parent"))
194 ret = bpf_tc_query(&inv_hook, &opts);
195 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook parent"))
198 inv_hook.attach_point = BPF_TC_INGRESS;
202 TEST_DECLARE_OPTS(fd);
204 ret = bpf_tc_detach(NULL, &opts_hp);
205 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook = NULL"))
208 ret = bpf_tc_detach(hook, NULL);
209 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid opts = NULL"))
212 ret = bpf_tc_detach(hook, &opts_hpr);
213 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid flags set"))
216 ret = bpf_tc_detach(hook, &opts_hpf);
217 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid prog_fd set"))
220 ret = bpf_tc_detach(hook, &opts_hpi);
221 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid prog_id set"))
224 ret = bpf_tc_detach(hook, &opts_p);
225 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid handle unset"))
228 ret = bpf_tc_detach(hook, &opts_h);
229 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid priority unset"))
232 ret = bpf_tc_detach(hook, &opts_prio_max);
233 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid priority > UINT16_MAX"))
239 TEST_DECLARE_OPTS(fd);
241 ret = bpf_tc_query(NULL, &opts);
242 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook = NULL"))
245 ret = bpf_tc_query(hook, NULL);
246 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid opts = NULL"))
249 ret = bpf_tc_query(hook, &opts_hpr);
250 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid flags set"))
253 ret = bpf_tc_query(hook, &opts_hpf);
254 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid prog_fd set"))
257 ret = bpf_tc_query(hook, &opts_hpi);
258 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid prog_id set"))
261 ret = bpf_tc_query(hook, &opts_p);
262 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid handle unset"))
265 ret = bpf_tc_query(hook, &opts_h);
266 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid priority unset"))
269 ret = bpf_tc_query(hook, &opts_prio_max);
270 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid priority > UINT16_MAX"))
273 /* when chain is not present, kernel returns -EINVAL */
274 ret = bpf_tc_query(hook, &opts_hp);
275 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query valid handle, priority set"))
281 TEST_DECLARE_OPTS(fd);
283 ret = bpf_tc_attach(NULL, &opts_hp);
284 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook = NULL"))
287 ret = bpf_tc_attach(hook, NULL);
288 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid opts = NULL"))
292 ret = bpf_tc_attach(hook, &opts_hp);
293 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid flags"))
296 ret = bpf_tc_attach(hook, NULL);
297 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid prog_fd unset"))
300 ret = bpf_tc_attach(hook, &opts_hpi);
301 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid prog_id set"))
304 ret = bpf_tc_attach(hook, &opts_pf);
305 if (!ASSERT_OK(ret, "bpf_tc_attach valid handle unset"))
307 opts_pf.prog_fd = opts_pf.prog_id = 0;
308 ASSERT_OK(bpf_tc_detach(hook, &opts_pf), "bpf_tc_detach");
310 ret = bpf_tc_attach(hook, &opts_hf);
311 if (!ASSERT_OK(ret, "bpf_tc_attach valid priority unset"))
313 opts_hf.prog_fd = opts_hf.prog_id = 0;
314 ASSERT_OK(bpf_tc_detach(hook, &opts_hf), "bpf_tc_detach");
316 ret = bpf_tc_attach(hook, &opts_prio_max);
317 if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid priority > UINT16_MAX"))
320 ret = bpf_tc_attach(hook, &opts_f);
321 if (!ASSERT_OK(ret, "bpf_tc_attach valid both handle and priority unset"))
323 opts_f.prog_fd = opts_f.prog_id = 0;
324 ASSERT_OK(bpf_tc_detach(hook, &opts_f), "bpf_tc_detach");
330 void test_tc_bpf(void)
332 DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = LO_IFINDEX,
333 .attach_point = BPF_TC_INGRESS);
334 struct test_tc_bpf *skel = NULL;
335 bool hook_created = false;
338 skel = test_tc_bpf__open_and_load();
339 if (!ASSERT_OK_PTR(skel, "test_tc_bpf__open_and_load"))
342 cls_fd = bpf_program__fd(skel->progs.cls);
344 ret = bpf_tc_hook_create(&hook);
348 ret = ret == -EEXIST ? 0 : ret;
349 if (!ASSERT_OK(ret, "bpf_tc_hook_create(BPF_TC_INGRESS)"))
352 hook.attach_point = BPF_TC_CUSTOM;
353 hook.parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
354 ret = bpf_tc_hook_create(&hook);
355 if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_create invalid hook.attach_point"))
358 ret = test_tc_bpf_basic(&hook, cls_fd);
359 if (!ASSERT_OK(ret, "test_tc_internal ingress"))
362 ret = bpf_tc_hook_destroy(&hook);
363 if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_destroy invalid hook.attach_point"))
366 hook.attach_point = BPF_TC_INGRESS;
368 bpf_tc_hook_destroy(&hook);
370 ret = test_tc_bpf_basic(&hook, cls_fd);
371 if (!ASSERT_OK(ret, "test_tc_internal ingress"))
374 bpf_tc_hook_destroy(&hook);
376 hook.attach_point = BPF_TC_EGRESS;
377 ret = test_tc_bpf_basic(&hook, cls_fd);
378 if (!ASSERT_OK(ret, "test_tc_internal egress"))
381 bpf_tc_hook_destroy(&hook);
383 ret = test_tc_bpf_api(&hook, cls_fd);
384 if (!ASSERT_OK(ret, "test_tc_bpf_api"))
387 bpf_tc_hook_destroy(&hook);
391 hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
392 bpf_tc_hook_destroy(&hook);
394 test_tc_bpf__destroy(skel);