Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 #include <test_progs.h>
0004 #include <linux/pkt_cls.h>
0005 
0006 #include "test_tc_bpf.skel.h"
0007 
0008 #define LO_IFINDEX 1
0009 
0010 #define TEST_DECLARE_OPTS(__fd)                                                                   \
0011     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_h, .handle = 1);                                     \
0012     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_p, .priority = 1);                                   \
0013     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_f, .prog_fd = __fd);                                 \
0014     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hp, .handle = 1, .priority = 1);                     \
0015     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hf, .handle = 1, .prog_fd = __fd);                   \
0016     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_pf, .priority = 1, .prog_fd = __fd);                 \
0017     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpf, .handle = 1, .priority = 1, .prog_fd = __fd);   \
0018     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpi, .handle = 1, .priority = 1, .prog_id = 42);     \
0019     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpr, .handle = 1, .priority = 1,                     \
0020                 .flags = BPF_TC_F_REPLACE);                                            \
0021     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_hpfi, .handle = 1, .priority = 1, .prog_fd = __fd,   \
0022                 .prog_id = 42);                                                        \
0023     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts_prio_max, .handle = 1, .priority = UINT16_MAX + 1);
0024 
0025 static int test_tc_bpf_basic(const struct bpf_tc_hook *hook, int fd)
0026 {
0027     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts, .handle = 1, .priority = 1, .prog_fd = fd);
0028     struct bpf_prog_info info = {};
0029     __u32 info_len = sizeof(info);
0030     int ret;
0031 
0032     ret = bpf_obj_get_info_by_fd(fd, &info, &info_len);
0033     if (!ASSERT_OK(ret, "bpf_obj_get_info_by_fd"))
0034         return ret;
0035 
0036     ret = bpf_tc_attach(hook, &opts);
0037     if (!ASSERT_OK(ret, "bpf_tc_attach"))
0038         return ret;
0039 
0040     if (!ASSERT_EQ(opts.handle, 1, "handle set") ||
0041         !ASSERT_EQ(opts.priority, 1, "priority set") ||
0042         !ASSERT_EQ(opts.prog_id, info.id, "prog_id set"))
0043         goto end;
0044 
0045     opts.prog_id = 0;
0046     opts.flags = BPF_TC_F_REPLACE;
0047     ret = bpf_tc_attach(hook, &opts);
0048     if (!ASSERT_OK(ret, "bpf_tc_attach replace mode"))
0049         goto end;
0050 
0051     opts.flags = opts.prog_fd = opts.prog_id = 0;
0052     ret = bpf_tc_query(hook, &opts);
0053     if (!ASSERT_OK(ret, "bpf_tc_query"))
0054         goto end;
0055 
0056     if (!ASSERT_EQ(opts.handle, 1, "handle set") ||
0057         !ASSERT_EQ(opts.priority, 1, "priority set") ||
0058         !ASSERT_EQ(opts.prog_id, info.id, "prog_id set"))
0059         goto end;
0060 
0061 end:
0062     opts.flags = opts.prog_fd = opts.prog_id = 0;
0063     ret = bpf_tc_detach(hook, &opts);
0064     ASSERT_OK(ret, "bpf_tc_detach");
0065     return ret;
0066 }
0067 
0068 static int test_tc_bpf_api(struct bpf_tc_hook *hook, int fd)
0069 {
0070     DECLARE_LIBBPF_OPTS(bpf_tc_opts, attach_opts, .handle = 1, .priority = 1, .prog_fd = fd);
0071     DECLARE_LIBBPF_OPTS(bpf_tc_hook, inv_hook, .attach_point = BPF_TC_INGRESS);
0072     DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts, .handle = 1, .priority = 1);
0073     int ret;
0074 
0075     ret = bpf_tc_hook_create(NULL);
0076     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook = NULL"))
0077         return -EINVAL;
0078 
0079     /* hook ifindex = 0 */
0080     ret = bpf_tc_hook_create(&inv_hook);
0081     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook ifindex == 0"))
0082         return -EINVAL;
0083 
0084     ret = bpf_tc_hook_destroy(&inv_hook);
0085     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook ifindex == 0"))
0086         return -EINVAL;
0087 
0088     ret = bpf_tc_attach(&inv_hook, &attach_opts);
0089     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook ifindex == 0"))
0090         return -EINVAL;
0091     attach_opts.prog_id = 0;
0092 
0093     ret = bpf_tc_detach(&inv_hook, &opts);
0094     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook ifindex == 0"))
0095         return -EINVAL;
0096 
0097     ret = bpf_tc_query(&inv_hook, &opts);
0098     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook ifindex == 0"))
0099         return -EINVAL;
0100 
0101     /* hook ifindex < 0 */
0102     inv_hook.ifindex = -1;
0103 
0104     ret = bpf_tc_hook_create(&inv_hook);
0105     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook ifindex < 0"))
0106         return -EINVAL;
0107 
0108     ret = bpf_tc_hook_destroy(&inv_hook);
0109     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook ifindex < 0"))
0110         return -EINVAL;
0111 
0112     ret = bpf_tc_attach(&inv_hook, &attach_opts);
0113     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook ifindex < 0"))
0114         return -EINVAL;
0115     attach_opts.prog_id = 0;
0116 
0117     ret = bpf_tc_detach(&inv_hook, &opts);
0118     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook ifindex < 0"))
0119         return -EINVAL;
0120 
0121     ret = bpf_tc_query(&inv_hook, &opts);
0122     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook ifindex < 0"))
0123         return -EINVAL;
0124 
0125     inv_hook.ifindex = LO_IFINDEX;
0126 
0127     /* hook.attach_point invalid */
0128     inv_hook.attach_point = 0xabcd;
0129     ret = bpf_tc_hook_create(&inv_hook);
0130     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook.attach_point"))
0131         return -EINVAL;
0132 
0133     ret = bpf_tc_hook_destroy(&inv_hook);
0134     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook.attach_point"))
0135         return -EINVAL;
0136 
0137     ret = bpf_tc_attach(&inv_hook, &attach_opts);
0138     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook.attach_point"))
0139         return -EINVAL;
0140 
0141     ret = bpf_tc_detach(&inv_hook, &opts);
0142     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook.attach_point"))
0143         return -EINVAL;
0144 
0145     ret = bpf_tc_query(&inv_hook, &opts);
0146     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook.attach_point"))
0147         return -EINVAL;
0148 
0149     inv_hook.attach_point = BPF_TC_INGRESS;
0150 
0151     /* hook.attach_point valid, but parent invalid */
0152     inv_hook.parent = TC_H_MAKE(1UL << 16, 10);
0153     ret = bpf_tc_hook_create(&inv_hook);
0154     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_create invalid hook parent"))
0155         return -EINVAL;
0156 
0157     ret = bpf_tc_hook_destroy(&inv_hook);
0158     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_hook_destroy invalid hook parent"))
0159         return -EINVAL;
0160 
0161     ret = bpf_tc_attach(&inv_hook, &attach_opts);
0162     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook parent"))
0163         return -EINVAL;
0164 
0165     ret = bpf_tc_detach(&inv_hook, &opts);
0166     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook parent"))
0167         return -EINVAL;
0168 
0169     ret = bpf_tc_query(&inv_hook, &opts);
0170     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook parent"))
0171         return -EINVAL;
0172 
0173     inv_hook.attach_point = BPF_TC_CUSTOM;
0174     inv_hook.parent = 0;
0175     /* These return EOPNOTSUPP instead of EINVAL as parent is checked after
0176      * attach_point of the hook.
0177      */
0178     ret = bpf_tc_hook_create(&inv_hook);
0179     if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_create invalid hook parent"))
0180         return -EINVAL;
0181 
0182     ret = bpf_tc_hook_destroy(&inv_hook);
0183     if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_destroy invalid hook parent"))
0184         return -EINVAL;
0185 
0186     ret = bpf_tc_attach(&inv_hook, &attach_opts);
0187     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook parent"))
0188         return -EINVAL;
0189 
0190     ret = bpf_tc_detach(&inv_hook, &opts);
0191     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook parent"))
0192         return -EINVAL;
0193 
0194     ret = bpf_tc_query(&inv_hook, &opts);
0195     if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook parent"))
0196         return -EINVAL;
0197 
0198     inv_hook.attach_point = BPF_TC_INGRESS;
0199 
0200     /* detach */
0201     {
0202         TEST_DECLARE_OPTS(fd);
0203 
0204         ret = bpf_tc_detach(NULL, &opts_hp);
0205         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid hook = NULL"))
0206             return -EINVAL;
0207 
0208         ret = bpf_tc_detach(hook, NULL);
0209         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid opts = NULL"))
0210             return -EINVAL;
0211 
0212         ret = bpf_tc_detach(hook, &opts_hpr);
0213         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid flags set"))
0214             return -EINVAL;
0215 
0216         ret = bpf_tc_detach(hook, &opts_hpf);
0217         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid prog_fd set"))
0218             return -EINVAL;
0219 
0220         ret = bpf_tc_detach(hook, &opts_hpi);
0221         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid prog_id set"))
0222             return -EINVAL;
0223 
0224         ret = bpf_tc_detach(hook, &opts_p);
0225         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid handle unset"))
0226             return -EINVAL;
0227 
0228         ret = bpf_tc_detach(hook, &opts_h);
0229         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid priority unset"))
0230             return -EINVAL;
0231 
0232         ret = bpf_tc_detach(hook, &opts_prio_max);
0233         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_detach invalid priority > UINT16_MAX"))
0234             return -EINVAL;
0235     }
0236 
0237     /* query */
0238     {
0239         TEST_DECLARE_OPTS(fd);
0240 
0241         ret = bpf_tc_query(NULL, &opts);
0242         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid hook = NULL"))
0243             return -EINVAL;
0244 
0245         ret = bpf_tc_query(hook, NULL);
0246         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid opts = NULL"))
0247             return -EINVAL;
0248 
0249         ret = bpf_tc_query(hook, &opts_hpr);
0250         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid flags set"))
0251             return -EINVAL;
0252 
0253         ret = bpf_tc_query(hook, &opts_hpf);
0254         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid prog_fd set"))
0255             return -EINVAL;
0256 
0257         ret = bpf_tc_query(hook, &opts_hpi);
0258         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid prog_id set"))
0259             return -EINVAL;
0260 
0261         ret = bpf_tc_query(hook, &opts_p);
0262         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid handle unset"))
0263             return -EINVAL;
0264 
0265         ret = bpf_tc_query(hook, &opts_h);
0266         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid priority unset"))
0267             return -EINVAL;
0268 
0269         ret = bpf_tc_query(hook, &opts_prio_max);
0270         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query invalid priority > UINT16_MAX"))
0271             return -EINVAL;
0272 
0273         /* when chain is not present, kernel returns -EINVAL */
0274         ret = bpf_tc_query(hook, &opts_hp);
0275         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_query valid handle, priority set"))
0276             return -EINVAL;
0277     }
0278 
0279     /* attach */
0280     {
0281         TEST_DECLARE_OPTS(fd);
0282 
0283         ret = bpf_tc_attach(NULL, &opts_hp);
0284         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid hook = NULL"))
0285             return -EINVAL;
0286 
0287         ret = bpf_tc_attach(hook, NULL);
0288         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid opts = NULL"))
0289             return -EINVAL;
0290 
0291         opts_hp.flags = 42;
0292         ret = bpf_tc_attach(hook, &opts_hp);
0293         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid flags"))
0294             return -EINVAL;
0295 
0296         ret = bpf_tc_attach(hook, NULL);
0297         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid prog_fd unset"))
0298             return -EINVAL;
0299 
0300         ret = bpf_tc_attach(hook, &opts_hpi);
0301         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid prog_id set"))
0302             return -EINVAL;
0303 
0304         ret = bpf_tc_attach(hook, &opts_pf);
0305         if (!ASSERT_OK(ret, "bpf_tc_attach valid handle unset"))
0306             return -EINVAL;
0307         opts_pf.prog_fd = opts_pf.prog_id = 0;
0308         ASSERT_OK(bpf_tc_detach(hook, &opts_pf), "bpf_tc_detach");
0309 
0310         ret = bpf_tc_attach(hook, &opts_hf);
0311         if (!ASSERT_OK(ret, "bpf_tc_attach valid priority unset"))
0312             return -EINVAL;
0313         opts_hf.prog_fd = opts_hf.prog_id = 0;
0314         ASSERT_OK(bpf_tc_detach(hook, &opts_hf), "bpf_tc_detach");
0315 
0316         ret = bpf_tc_attach(hook, &opts_prio_max);
0317         if (!ASSERT_EQ(ret, -EINVAL, "bpf_tc_attach invalid priority > UINT16_MAX"))
0318             return -EINVAL;
0319 
0320         ret = bpf_tc_attach(hook, &opts_f);
0321         if (!ASSERT_OK(ret, "bpf_tc_attach valid both handle and priority unset"))
0322             return -EINVAL;
0323         opts_f.prog_fd = opts_f.prog_id = 0;
0324         ASSERT_OK(bpf_tc_detach(hook, &opts_f), "bpf_tc_detach");
0325     }
0326 
0327     return 0;
0328 }
0329 
0330 void test_tc_bpf(void)
0331 {
0332     DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = LO_IFINDEX,
0333                 .attach_point = BPF_TC_INGRESS);
0334     struct test_tc_bpf *skel = NULL;
0335     bool hook_created = false;
0336     int cls_fd, ret;
0337 
0338     skel = test_tc_bpf__open_and_load();
0339     if (!ASSERT_OK_PTR(skel, "test_tc_bpf__open_and_load"))
0340         return;
0341 
0342     cls_fd = bpf_program__fd(skel->progs.cls);
0343 
0344     ret = bpf_tc_hook_create(&hook);
0345     if (ret == 0)
0346         hook_created = true;
0347 
0348     ret = ret == -EEXIST ? 0 : ret;
0349     if (!ASSERT_OK(ret, "bpf_tc_hook_create(BPF_TC_INGRESS)"))
0350         goto end;
0351 
0352     hook.attach_point = BPF_TC_CUSTOM;
0353     hook.parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS);
0354     ret = bpf_tc_hook_create(&hook);
0355     if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_create invalid hook.attach_point"))
0356         goto end;
0357 
0358     ret = test_tc_bpf_basic(&hook, cls_fd);
0359     if (!ASSERT_OK(ret, "test_tc_internal ingress"))
0360         goto end;
0361 
0362     ret = bpf_tc_hook_destroy(&hook);
0363     if (!ASSERT_EQ(ret, -EOPNOTSUPP, "bpf_tc_hook_destroy invalid hook.attach_point"))
0364         goto end;
0365 
0366     hook.attach_point = BPF_TC_INGRESS;
0367     hook.parent = 0;
0368     bpf_tc_hook_destroy(&hook);
0369 
0370     ret = test_tc_bpf_basic(&hook, cls_fd);
0371     if (!ASSERT_OK(ret, "test_tc_internal ingress"))
0372         goto end;
0373 
0374     bpf_tc_hook_destroy(&hook);
0375 
0376     hook.attach_point = BPF_TC_EGRESS;
0377     ret = test_tc_bpf_basic(&hook, cls_fd);
0378     if (!ASSERT_OK(ret, "test_tc_internal egress"))
0379         goto end;
0380 
0381     bpf_tc_hook_destroy(&hook);
0382 
0383     ret = test_tc_bpf_api(&hook, cls_fd);
0384     if (!ASSERT_OK(ret, "test_tc_bpf_api"))
0385         goto end;
0386 
0387     bpf_tc_hook_destroy(&hook);
0388 
0389 end:
0390     if (hook_created) {
0391         hook.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
0392         bpf_tc_hook_destroy(&hook);
0393     }
0394     test_tc_bpf__destroy(skel);
0395 }