linux/tools/testing/selftests/bpf/prog_tests/tc_bpf.c
<<
>>
Prefs
   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}
 396