0001
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
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
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
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
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
0176
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
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
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
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
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 }