]> Git Repo - J-linux.git/blob - tools/testing/selftests/bpf/prog_tests/tc_bpf.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / bpf / prog_tests / tc_bpf.c
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
26 static 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
62 end:
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
69 static 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
331 void 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
390 end:
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
398 void 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
418 restore_cap:
419         if (caps)
420                 cap_enable_effective(caps, NULL);
421 }
422
423 void 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 }
This page took 0.053708 seconds and 4 git commands to generate.