Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#include <test_progs.h>
  4#include <linux/pkt_cls.h>
  5
  6#include "cap_helpers.h"
  7#include "test_tc_bpf.skel.h"
  8
  9#define LO_IFINDEX 1
 10
 11#define TEST_DECLARE_OPTS(__fd)                                                                   \
 12	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_h, .handle = 1);                                     \
 13	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_p, .priority = 1);                                   \
 14	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_f, .prog_fd = __fd);                                 \
 15	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hp, .handle = 1, .priority = 1);                     \
 16	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hf, .handle = 1, .prog_fd = __fd);                   \
 17	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_pf, .priority = 1, .prog_fd = __fd);                 \
 18	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpf, .handle = 1, .priority = 1, .prog_fd = __fd);   \
 19	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpi, .handle = 1, .priority = 1, .prog_id = 42);     \
 20	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpr, .handle = 1, .priority = 1,                     \
 21			    .flags = BPF_TC_F_REPLACE);                                            \
 22	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpfi, .handle = 1, .priority = 1, .prog_fd = __fd,   \
 23			    .prog_id = 42);                                                        \
 24	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_prio_max, .handle = 1, .priority = UINT16_MAX + 1);
 25
 26static int test_tc_bpf_basic(const struct bpf_tc_hook *hook, int fd)
 27{
 28	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts, .handle = 1, .priority = 1, .prog_fd = fd);
 29	struct bpf_prog_info info = {};
 30	__u32 info_len = sizeof(info);
 31	int ret;
 32
 33	ret = bpf_prog_get_info_by_fd(fd, &info, &info_len);
 34	if (!ASSERT_OK(ret, "bpf_prog_get_info_by_fd"))
 35		return ret;
 36
 37	ret = bpf_tc_attach(hook, &opts);
 38	if (!ASSERT_OK(ret, "bpf_tc_attach"))
 39		return ret;
 40
 41	if (!ASSERT_EQ(opts.handle, 1, "handle set") ||
 42	    !ASSERT_EQ(opts.priority, 1, "priority set") ||
 43	    !ASSERT_EQ(opts.prog_id, info.id, "prog_id set"))
 44		goto end;
 45
 46	opts.prog_id = 0;
 47	opts.flags = BPF_TC_F_REPLACE;
 48	ret = bpf_tc_attach(hook, &opts);
 49	if (!ASSERT_OK(ret, "bpf_tc_attach replace mode"))
 50		goto end;
 51
 52	opts.flags = opts.prog_fd = opts.prog_id = 0;
 53	ret = bpf_tc_query(hook, &opts);
 54	if (!ASSERT_OK(ret, "bpf_tc_query"))
 55		goto end;
 56
 57	if (!ASSERT_EQ(opts.handle, 1, "handle set") ||
 58	    !ASSERT_EQ(opts.priority, 1, "priority set") ||
 59	    !ASSERT_EQ(opts.prog_id, info.id, "prog_id set"))
 60		goto end;
 61
 62end:
 63	opts.flags = opts.prog_fd = opts.prog_id = 0;
 64	ret = bpf_tc_detach(hook, &opts);
 65	ASSERT_OK(ret, "bpf_tc_detach");
 66	return ret;
 67}
 68
 69static int test_tc_bpf_api(struct bpf_tc_hook *hook, int fd)
 70{
 71	DECLARE_LIBBPF_OPTS(bpf_tc_opts, attach_opts, .handle = 1, .priority = 1, .prog_fd = fd);
 72	DECLARE_LIBBPF_OPTS(bpf_tc_hook, inv_hook, .attach_point = BPF_TC_INGRESS);
 73	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts, .handle = 1, .priority = 1);
 74	int ret;
 75
 76	ret = bpf_tc_hook_create(NULL);
 77	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook = NULL"))
 78		return -EINVAL;
 79
 80	/* hook ifindex = 0 */
 81	ret = bpf_tc_hook_create(&inv_hook);
 82	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook ifindex == 0"))
 83		return -EINVAL;
 84
 85	ret = bpf_tc_hook_destroy(&inv_hook);
 86	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook ifindex == 0"))
 87		return -EINVAL;
 88
 89	ret = bpf_tc_attach(&inv_hook, &attach_opts);
 90	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook ifindex == 0"))
 91		return -EINVAL;
 92	attach_opts.prog_id = 0;
 93
 94	ret = bpf_tc_detach(&inv_hook, &opts);
 95	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook ifindex == 0"))
 96		return -EINVAL;
 97
 98	ret = bpf_tc_query(&inv_hook, &opts);
 99	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook ifindex == 0"))
100		return -EINVAL;
101
102	/* hook ifindex < 0 */
103	inv_hook.ifindex = -1;
104
105	ret = bpf_tc_hook_create(&inv_hook);
106	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook ifindex < 0"))
107		return -EINVAL;
108
109	ret = bpf_tc_hook_destroy(&inv_hook);
110	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook ifindex < 0"))
111		return -EINVAL;
112
113	ret = bpf_tc_attach(&inv_hook, &attach_opts);
114	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook ifindex < 0"))
115		return -EINVAL;
116	attach_opts.prog_id = 0;
117
118	ret = bpf_tc_detach(&inv_hook, &opts);
119	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook ifindex < 0"))
120		return -EINVAL;
121
122	ret = bpf_tc_query(&inv_hook, &opts);
123	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook ifindex < 0"))
124		return -EINVAL;
125
126	inv_hook.ifindex = LO_IFINDEX;
127
128	/* hook.attach_point invalid */
129	inv_hook.attach_point = 0xabcd;
130	ret = bpf_tc_hook_create(&inv_hook);
131	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook.attach_point"))
132		return -EINVAL;
133
134	ret = bpf_tc_hook_destroy(&inv_hook);
135	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook.attach_point"))
136		return -EINVAL;
137
138	ret = bpf_tc_attach(&inv_hook, &attach_opts);
139	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook.attach_point"))
140		return -EINVAL;
141
142	ret = bpf_tc_detach(&inv_hook, &opts);
143	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook.attach_point"))
144		return -EINVAL;
145
146	ret = bpf_tc_query(&inv_hook, &opts);
147	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook.attach_point"))
148		return -EINVAL;
149
150	inv_hook.attach_point = BPF_TC_INGRESS;
151
152	/* hook.attach_point valid, but parent invalid */
153	inv_hook.parent = TC_H_MAKE(1UL << 16, 10);
154	ret = bpf_tc_hook_create(&inv_hook);
155	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook parent"))
156		return -EINVAL;
157
158	ret = bpf_tc_hook_destroy(&inv_hook);
159	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook parent"))
160		return -EINVAL;
161
162	ret = bpf_tc_attach(&inv_hook, &attach_opts);
163	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook parent"))
164		return -EINVAL;
165
166	ret = bpf_tc_detach(&inv_hook, &opts);
167	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook parent"))
168		return -EINVAL;
169
170	ret = bpf_tc_query(&inv_hook, &opts);
171	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook parent"))
172		return -EINVAL;
173
174	inv_hook.attach_point = BPF_TC_CUSTOM;
175	inv_hook.parent = 0;
176	/* These return EOPNOTSUPP instead of EINVAL as parent is checked after
177	 * attach_point of the hook.
178	 */
179	ret = bpf_tc_hook_create(&inv_hook);
180	if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_create invalid hook parent"))
181		return -EINVAL;
182
183	ret = bpf_tc_hook_destroy(&inv_hook);
184	if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_destroy invalid hook parent"))
185		return -EINVAL;
186
187	ret = bpf_tc_attach(&inv_hook, &attach_opts);
188	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook parent"))
189		return -EINVAL;
190
191	ret = bpf_tc_detach(&inv_hook, &opts);
192	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook parent"))
193		return -EINVAL;
194
195	ret = bpf_tc_query(&inv_hook, &opts);
196	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook parent"))
197		return -EINVAL;
198
199	inv_hook.attach_point = BPF_TC_INGRESS;
200
201	/* detach */
202	{
203		TEST_DECLARE_OPTS(fd);
204
205		ret = bpf_tc_detach(NULL, &opts_hp);
206		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook = NULL"))
207			return -EINVAL;
208
209		ret = bpf_tc_detach(hook, NULL);
210		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid opts = NULL"))
211			return -EINVAL;
212
213		ret = bpf_tc_detach(hook, &opts_hpr);
214		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid flags set"))
215			return -EINVAL;
216
217		ret = bpf_tc_detach(hook, &opts_hpf);
218		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid prog_fd set"))
219			return -EINVAL;
220
221		ret = bpf_tc_detach(hook, &opts_hpi);
222		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid prog_id set"))
223			return -EINVAL;
224
225		ret = bpf_tc_detach(hook, &opts_p);
226		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid handle unset"))
227			return -EINVAL;
228
229		ret = bpf_tc_detach(hook, &opts_h);
230		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid priority unset"))
231			return -EINVAL;
232
233		ret = bpf_tc_detach(hook, &opts_prio_max);
234		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid priority > UINT16_MAX"))
235			return -EINVAL;
236	}
237
238	/* query */
239	{
240		TEST_DECLARE_OPTS(fd);
241
242		ret = bpf_tc_query(NULL, &opts);
243		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook = NULL"))
244			return -EINVAL;
245
246		ret = bpf_tc_query(hook, NULL);
247		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid opts = NULL"))
248			return -EINVAL;
249
250		ret = bpf_tc_query(hook, &opts_hpr);
251		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid flags set"))
252			return -EINVAL;
253
254		ret = bpf_tc_query(hook, &opts_hpf);
255		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid prog_fd set"))
256			return -EINVAL;
257
258		ret = bpf_tc_query(hook, &opts_hpi);
259		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid prog_id set"))
260			return -EINVAL;
261
262		ret = bpf_tc_query(hook, &opts_p);
263		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid handle unset"))
264			return -EINVAL;
265
266		ret = bpf_tc_query(hook, &opts_h);
267		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid priority unset"))
268			return -EINVAL;
269
270		ret = bpf_tc_query(hook, &opts_prio_max);
271		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid priority > UINT16_MAX"))
272			return -EINVAL;
273
274		/* when chain is not present, kernel returns -EINVAL */
275		ret = bpf_tc_query(hook, &opts_hp);
276		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query valid handle, priority set"))
277			return -EINVAL;
278	}
279
280	/* attach */
281	{
282		TEST_DECLARE_OPTS(fd);
283
284		ret = bpf_tc_attach(NULL, &opts_hp);
285		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook = NULL"))
286			return -EINVAL;
287
288		ret = bpf_tc_attach(hook, NULL);
289		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid opts = NULL"))
290			return -EINVAL;
291
292		opts_hp.flags = 42;
293		ret = bpf_tc_attach(hook, &opts_hp);
294		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid flags"))
295			return -EINVAL;
296
297		ret = bpf_tc_attach(hook, NULL);
298		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid prog_fd unset"))
299			return -EINVAL;
300
301		ret = bpf_tc_attach(hook, &opts_hpi);
302		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid prog_id set"))
303			return -EINVAL;
304
305		ret = bpf_tc_attach(hook, &opts_pf);
306		if (!ASSERT_OK(ret, "bpf_tc_attach valid handle unset"))
307			return -EINVAL;
308		opts_pf.prog_fd = opts_pf.prog_id = 0;
309		ASSERT_OK(bpf_tc_detach(hook, &opts_pf), "bpf_tc_detach");
310
311		ret = bpf_tc_attach(hook, &opts_hf);
312		if (!ASSERT_OK(ret, "bpf_tc_attach valid priority unset"))
313			return -EINVAL;
314		opts_hf.prog_fd = opts_hf.prog_id = 0;
315		ASSERT_OK(bpf_tc_detach(hook, &opts_hf), "bpf_tc_detach");
316
317		ret = bpf_tc_attach(hook, &opts_prio_max);
318		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid priority > UINT16_MAX"))
319			return -EINVAL;
320
321		ret = bpf_tc_attach(hook, &opts_f);
322		if (!ASSERT_OK(ret, "bpf_tc_attach valid both handle and priority unset"))
323			return -EINVAL;
324		opts_f.prog_fd = opts_f.prog_id = 0;
325		ASSERT_OK(bpf_tc_detach(hook, &opts_f), "bpf_tc_detach");
326	}
327
328	return 0;
329}
330
331void tc_bpf_root(void)
332{
333	DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = LO_IFINDEX,
334			    .attach_point = BPF_TC_INGRESS);
335	struct test_tc_bpf *skel = NULL;
336	bool hook_created = false;
337	int cls_fd, ret;
338
339	skel = test_tc_bpf__open_and_load();
340	if (!ASSERT_OK_PTR(skel, "test_tc_bpf__open_and_load"))
341		return;
342
343	cls_fd = bpf_program__fd(skel->progs.cls);
344
345	ret = bpf_tc_hook_create(&hook);
346	if (ret == 0)
347		hook_created = true;
348
349	ret = ret == -EEXIST ? 0 : ret;
350	if (!ASSERT_OK(ret, "bpf_tc_hook_create(BPF_TC_INGRESS)"))
351		goto end;
352
353	hook.attach_point = BPF_TC_CUSTOM;
354	hook.parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
355	ret = bpf_tc_hook_create(&hook);
356	if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_create invalid hook.attach_point"))
357		goto end;
358
359	ret = test_tc_bpf_basic(&hook, cls_fd);
360	if (!ASSERT_OK(ret, "test_tc_internal ingress"))
361		goto end;
362
363	ret = bpf_tc_hook_destroy(&hook);
364	if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_destroy invalid hook.attach_point"))
365		goto end;
366
367	hook.attach_point = BPF_TC_INGRESS;
368	hook.parent = 0;
369	bpf_tc_hook_destroy(&hook);
370
371	ret = test_tc_bpf_basic(&hook, cls_fd);
372	if (!ASSERT_OK(ret, "test_tc_internal ingress"))
373		goto end;
374
375	bpf_tc_hook_destroy(&hook);
376
377	hook.attach_point = BPF_TC_EGRESS;
378	ret = test_tc_bpf_basic(&hook, cls_fd);
379	if (!ASSERT_OK(ret, "test_tc_internal egress"))
380		goto end;
381
382	bpf_tc_hook_destroy(&hook);
383
384	ret = test_tc_bpf_api(&hook, cls_fd);
385	if (!ASSERT_OK(ret, "test_tc_bpf_api"))
386		goto end;
387
388	bpf_tc_hook_destroy(&hook);
389
390end:
391	if (hook_created) {
392		hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
393		bpf_tc_hook_destroy(&hook);
394	}
395	test_tc_bpf__destroy(skel);
396}
397
398void tc_bpf_non_root(void)
399{
400	struct test_tc_bpf *skel = NULL;
401	__u64 caps = 0;
402	int ret;
403
404	/* In case CAP_BPF and CAP_PERFMON is not set */
405	ret = cap_enable_effective(1ULL << CAP_BPF | 1ULL << CAP_NET_ADMIN, &caps);
406	if (!ASSERT_OK(ret, "set_cap_bpf_cap_net_admin"))
407		return;
408	ret = cap_disable_effective(1ULL << CAP_SYS_ADMIN | 1ULL << CAP_PERFMON, NULL);
409	if (!ASSERT_OK(ret, "disable_cap_sys_admin"))
410		goto restore_cap;
411
412	skel = test_tc_bpf__open_and_load();
413	if (!ASSERT_OK_PTR(skel, "test_tc_bpf__open_and_load"))
414		goto restore_cap;
415
416	test_tc_bpf__destroy(skel);
417
418restore_cap:
419	if (caps)
420		cap_enable_effective(caps, NULL);
421}
422
423void test_tc_bpf(void)
424{
425	if (test__start_subtest("tc_bpf_root"))
426		tc_bpf_root();
427	if (test__start_subtest("tc_bpf_non_root"))
428		tc_bpf_non_root();
429}
v6.2
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#include <test_progs.h>
  4#include <linux/pkt_cls.h>
  5
 
  6#include "test_tc_bpf.skel.h"
  7
  8#define LO_IFINDEX 1
  9
 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,   \
 22			    .prog_id = 42);                                                        \
 23	DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_prio_max, .handle = 1, .priority = UINT16_MAX + 1);
 24
 25static int test_tc_bpf_basic(const struct bpf_tc_hook *hook, int fd)
 26{
 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);
 30	int ret;
 31
 32	ret = bpf_obj_get_info_by_fd(fd, &info, &info_len);
 33	if (!ASSERT_OK(ret, "bpf_obj_get_info_by_fd"))
 34		return ret;
 35
 36	ret = bpf_tc_attach(hook, &opts);
 37	if (!ASSERT_OK(ret, "bpf_tc_attach"))
 38		return ret;
 39
 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"))
 43		goto end;
 44
 45	opts.prog_id = 0;
 46	opts.flags = BPF_TC_F_REPLACE;
 47	ret = bpf_tc_attach(hook, &opts);
 48	if (!ASSERT_OK(ret, "bpf_tc_attach replace mode"))
 49		goto end;
 50
 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"))
 54		goto end;
 55
 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"))
 59		goto end;
 60
 61end:
 62	opts.flags = opts.prog_fd = opts.prog_id = 0;
 63	ret = bpf_tc_detach(hook, &opts);
 64	ASSERT_OK(ret, "bpf_tc_detach");
 65	return ret;
 66}
 67
 68static int test_tc_bpf_api(struct bpf_tc_hook *hook, int fd)
 69{
 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);
 73	int ret;
 74
 75	ret = bpf_tc_hook_create(NULL);
 76	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook = NULL"))
 77		return -EINVAL;
 78
 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"))
 82		return -EINVAL;
 83
 84	ret = bpf_tc_hook_destroy(&inv_hook);
 85	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook ifindex == 0"))
 86		return -EINVAL;
 87
 88	ret = bpf_tc_attach(&inv_hook, &attach_opts);
 89	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook ifindex == 0"))
 90		return -EINVAL;
 91	attach_opts.prog_id = 0;
 92
 93	ret = bpf_tc_detach(&inv_hook, &opts);
 94	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook ifindex == 0"))
 95		return -EINVAL;
 96
 97	ret = bpf_tc_query(&inv_hook, &opts);
 98	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook ifindex == 0"))
 99		return -EINVAL;
100
101	/* hook ifindex < 0 */
102	inv_hook.ifindex = -1;
103
104	ret = bpf_tc_hook_create(&inv_hook);
105	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook ifindex < 0"))
106		return -EINVAL;
107
108	ret = bpf_tc_hook_destroy(&inv_hook);
109	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook ifindex < 0"))
110		return -EINVAL;
111
112	ret = bpf_tc_attach(&inv_hook, &attach_opts);
113	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook ifindex < 0"))
114		return -EINVAL;
115	attach_opts.prog_id = 0;
116
117	ret = bpf_tc_detach(&inv_hook, &opts);
118	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook ifindex < 0"))
119		return -EINVAL;
120
121	ret = bpf_tc_query(&inv_hook, &opts);
122	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook ifindex < 0"))
123		return -EINVAL;
124
125	inv_hook.ifindex = LO_IFINDEX;
126
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"))
131		return -EINVAL;
132
133	ret = bpf_tc_hook_destroy(&inv_hook);
134	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook.attach_point"))
135		return -EINVAL;
136
137	ret = bpf_tc_attach(&inv_hook, &attach_opts);
138	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook.attach_point"))
139		return -EINVAL;
140
141	ret = bpf_tc_detach(&inv_hook, &opts);
142	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook.attach_point"))
143		return -EINVAL;
144
145	ret = bpf_tc_query(&inv_hook, &opts);
146	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook.attach_point"))
147		return -EINVAL;
148
149	inv_hook.attach_point = BPF_TC_INGRESS;
150
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"))
155		return -EINVAL;
156
157	ret = bpf_tc_hook_destroy(&inv_hook);
158	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook parent"))
159		return -EINVAL;
160
161	ret = bpf_tc_attach(&inv_hook, &attach_opts);
162	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook parent"))
163		return -EINVAL;
164
165	ret = bpf_tc_detach(&inv_hook, &opts);
166	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook parent"))
167		return -EINVAL;
168
169	ret = bpf_tc_query(&inv_hook, &opts);
170	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook parent"))
171		return -EINVAL;
172
173	inv_hook.attach_point = BPF_TC_CUSTOM;
174	inv_hook.parent = 0;
175	/* These return EOPNOTSUPP instead of EINVAL as parent is checked after
176	 * attach_point of the hook.
177	 */
178	ret = bpf_tc_hook_create(&inv_hook);
179	if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_create invalid hook parent"))
180		return -EINVAL;
181
182	ret = bpf_tc_hook_destroy(&inv_hook);
183	if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_destroy invalid hook parent"))
184		return -EINVAL;
185
186	ret = bpf_tc_attach(&inv_hook, &attach_opts);
187	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook parent"))
188		return -EINVAL;
189
190	ret = bpf_tc_detach(&inv_hook, &opts);
191	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook parent"))
192		return -EINVAL;
193
194	ret = bpf_tc_query(&inv_hook, &opts);
195	if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook parent"))
196		return -EINVAL;
197
198	inv_hook.attach_point = BPF_TC_INGRESS;
199
200	/* detach */
201	{
202		TEST_DECLARE_OPTS(fd);
203
204		ret = bpf_tc_detach(NULL, &opts_hp);
205		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook = NULL"))
206			return -EINVAL;
207
208		ret = bpf_tc_detach(hook, NULL);
209		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid opts = NULL"))
210			return -EINVAL;
211
212		ret = bpf_tc_detach(hook, &opts_hpr);
213		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid flags set"))
214			return -EINVAL;
215
216		ret = bpf_tc_detach(hook, &opts_hpf);
217		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid prog_fd set"))
218			return -EINVAL;
219
220		ret = bpf_tc_detach(hook, &opts_hpi);
221		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid prog_id set"))
222			return -EINVAL;
223
224		ret = bpf_tc_detach(hook, &opts_p);
225		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid handle unset"))
226			return -EINVAL;
227
228		ret = bpf_tc_detach(hook, &opts_h);
229		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid priority unset"))
230			return -EINVAL;
231
232		ret = bpf_tc_detach(hook, &opts_prio_max);
233		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid priority > UINT16_MAX"))
234			return -EINVAL;
235	}
236
237	/* query */
238	{
239		TEST_DECLARE_OPTS(fd);
240
241		ret = bpf_tc_query(NULL, &opts);
242		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook = NULL"))
243			return -EINVAL;
244
245		ret = bpf_tc_query(hook, NULL);
246		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid opts = NULL"))
247			return -EINVAL;
248
249		ret = bpf_tc_query(hook, &opts_hpr);
250		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid flags set"))
251			return -EINVAL;
252
253		ret = bpf_tc_query(hook, &opts_hpf);
254		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid prog_fd set"))
255			return -EINVAL;
256
257		ret = bpf_tc_query(hook, &opts_hpi);
258		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid prog_id set"))
259			return -EINVAL;
260
261		ret = bpf_tc_query(hook, &opts_p);
262		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid handle unset"))
263			return -EINVAL;
264
265		ret = bpf_tc_query(hook, &opts_h);
266		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid priority unset"))
267			return -EINVAL;
268
269		ret = bpf_tc_query(hook, &opts_prio_max);
270		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid priority > UINT16_MAX"))
271			return -EINVAL;
272
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"))
276			return -EINVAL;
277	}
278
279	/* attach */
280	{
281		TEST_DECLARE_OPTS(fd);
282
283		ret = bpf_tc_attach(NULL, &opts_hp);
284		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook = NULL"))
285			return -EINVAL;
286
287		ret = bpf_tc_attach(hook, NULL);
288		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid opts = NULL"))
289			return -EINVAL;
290
291		opts_hp.flags = 42;
292		ret = bpf_tc_attach(hook, &opts_hp);
293		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid flags"))
294			return -EINVAL;
295
296		ret = bpf_tc_attach(hook, NULL);
297		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid prog_fd unset"))
298			return -EINVAL;
299
300		ret = bpf_tc_attach(hook, &opts_hpi);
301		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid prog_id set"))
302			return -EINVAL;
303
304		ret = bpf_tc_attach(hook, &opts_pf);
305		if (!ASSERT_OK(ret, "bpf_tc_attach valid handle unset"))
306			return -EINVAL;
307		opts_pf.prog_fd = opts_pf.prog_id = 0;
308		ASSERT_OK(bpf_tc_detach(hook, &opts_pf), "bpf_tc_detach");
309
310		ret = bpf_tc_attach(hook, &opts_hf);
311		if (!ASSERT_OK(ret, "bpf_tc_attach valid priority unset"))
312			return -EINVAL;
313		opts_hf.prog_fd = opts_hf.prog_id = 0;
314		ASSERT_OK(bpf_tc_detach(hook, &opts_hf), "bpf_tc_detach");
315
316		ret = bpf_tc_attach(hook, &opts_prio_max);
317		if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid priority > UINT16_MAX"))
318			return -EINVAL;
319
320		ret = bpf_tc_attach(hook, &opts_f);
321		if (!ASSERT_OK(ret, "bpf_tc_attach valid both handle and priority unset"))
322			return -EINVAL;
323		opts_f.prog_fd = opts_f.prog_id = 0;
324		ASSERT_OK(bpf_tc_detach(hook, &opts_f), "bpf_tc_detach");
325	}
326
327	return 0;
328}
329
330void test_tc_bpf(void)
331{
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;
336	int cls_fd, ret;
337
338	skel = test_tc_bpf__open_and_load();
339	if (!ASSERT_OK_PTR(skel, "test_tc_bpf__open_and_load"))
340		return;
341
342	cls_fd = bpf_program__fd(skel->progs.cls);
343
344	ret = bpf_tc_hook_create(&hook);
345	if (ret == 0)
346		hook_created = true;
347
348	ret = ret == -EEXIST ? 0 : ret;
349	if (!ASSERT_OK(ret, "bpf_tc_hook_create(BPF_TC_INGRESS)"))
350		goto end;
351
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"))
356		goto end;
357
358	ret = test_tc_bpf_basic(&hook, cls_fd);
359	if (!ASSERT_OK(ret, "test_tc_internal ingress"))
360		goto end;
361
362	ret = bpf_tc_hook_destroy(&hook);
363	if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_destroy invalid hook.attach_point"))
364		goto end;
365
366	hook.attach_point = BPF_TC_INGRESS;
367	hook.parent = 0;
368	bpf_tc_hook_destroy(&hook);
369
370	ret = test_tc_bpf_basic(&hook, cls_fd);
371	if (!ASSERT_OK(ret, "test_tc_internal ingress"))
372		goto end;
373
374	bpf_tc_hook_destroy(&hook);
375
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"))
379		goto end;
380
381	bpf_tc_hook_destroy(&hook);
382
383	ret = test_tc_bpf_api(&hook, cls_fd);
384	if (!ASSERT_OK(ret, "test_tc_bpf_api"))
385		goto end;
386
387	bpf_tc_hook_destroy(&hook);
388
389end:
390	if (hook_created) {
391		hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
392		bpf_tc_hook_destroy(&hook);
393	}
394	test_tc_bpf__destroy(skel);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395}