linux/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (c) 2019 Facebook */
   3#include <test_progs.h>
   4#include <network_helpers.h>
   5#include <bpf/btf.h>
   6
   7typedef int (*test_cb)(struct bpf_object *obj);
   8
   9static int check_data_map(struct bpf_object *obj, int prog_cnt, bool reset)
  10{
  11        struct bpf_map *data_map = NULL, *map;
  12        __u64 *result = NULL;
  13        const int zero = 0;
  14        __u32 duration = 0;
  15        int ret = -1, i;
  16
  17        result = malloc((prog_cnt + 32 /* spare */) * sizeof(__u64));
  18        if (CHECK(!result, "alloc_memory", "failed to alloc memory"))
  19                return -ENOMEM;
  20
  21        bpf_object__for_each_map(map, obj)
  22                if (bpf_map__is_internal(map)) {
  23                        data_map = map;
  24                        break;
  25                }
  26        if (CHECK(!data_map, "find_data_map", "data map not found\n"))
  27                goto out;
  28
  29        ret = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, result);
  30        if (CHECK(ret, "get_result",
  31                  "failed to get output data: %d\n", ret))
  32                goto out;
  33
  34        for (i = 0; i < prog_cnt; i++) {
  35                if (CHECK(result[i] != 1, "result",
  36                          "fexit_bpf2bpf result[%d] failed err %llu\n",
  37                          i, result[i]))
  38                        goto out;
  39                result[i] = 0;
  40        }
  41        if (reset) {
  42                ret = bpf_map_update_elem(bpf_map__fd(data_map), &zero, result, 0);
  43                if (CHECK(ret, "reset_result", "failed to reset result\n"))
  44                        goto out;
  45        }
  46
  47        ret = 0;
  48out:
  49        free(result);
  50        return ret;
  51}
  52
  53static void test_fexit_bpf2bpf_common(const char *obj_file,
  54                                      const char *target_obj_file,
  55                                      int prog_cnt,
  56                                      const char **prog_name,
  57                                      bool run_prog,
  58                                      test_cb cb)
  59{
  60        struct bpf_object *obj = NULL, *tgt_obj;
  61        __u32 retval, tgt_prog_id, info_len;
  62        struct bpf_prog_info prog_info = {};
  63        struct bpf_program **prog = NULL;
  64        struct bpf_link **link = NULL;
  65        int err, tgt_fd, i;
  66        struct btf *btf;
  67
  68        err = bpf_prog_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
  69                            &tgt_obj, &tgt_fd);
  70        if (!ASSERT_OK(err, "tgt_prog_load"))
  71                return;
  72        DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
  73                            .attach_prog_fd = tgt_fd,
  74                           );
  75
  76        info_len = sizeof(prog_info);
  77        err = bpf_obj_get_info_by_fd(tgt_fd, &prog_info, &info_len);
  78        if (!ASSERT_OK(err, "tgt_fd_get_info"))
  79                goto close_prog;
  80
  81        tgt_prog_id = prog_info.id;
  82        btf = bpf_object__btf(tgt_obj);
  83
  84        link = calloc(sizeof(struct bpf_link *), prog_cnt);
  85        if (!ASSERT_OK_PTR(link, "link_ptr"))
  86                goto close_prog;
  87
  88        prog = calloc(sizeof(struct bpf_program *), prog_cnt);
  89        if (!ASSERT_OK_PTR(prog, "prog_ptr"))
  90                goto close_prog;
  91
  92        obj = bpf_object__open_file(obj_file, &opts);
  93        if (!ASSERT_OK_PTR(obj, "obj_open"))
  94                goto close_prog;
  95
  96        err = bpf_object__load(obj);
  97        if (!ASSERT_OK(err, "obj_load"))
  98                goto close_prog;
  99
 100        for (i = 0; i < prog_cnt; i++) {
 101                struct bpf_link_info link_info;
 102                char *tgt_name;
 103                __s32 btf_id;
 104
 105                tgt_name = strstr(prog_name[i], "/");
 106                if (!ASSERT_OK_PTR(tgt_name, "tgt_name"))
 107                        goto close_prog;
 108                btf_id = btf__find_by_name_kind(btf, tgt_name + 1, BTF_KIND_FUNC);
 109
 110                prog[i] = bpf_object__find_program_by_title(obj, prog_name[i]);
 111                if (!ASSERT_OK_PTR(prog[i], prog_name[i]))
 112                        goto close_prog;
 113
 114                link[i] = bpf_program__attach_trace(prog[i]);
 115                if (!ASSERT_OK_PTR(link[i], "attach_trace"))
 116                        goto close_prog;
 117
 118                info_len = sizeof(link_info);
 119                memset(&link_info, 0, sizeof(link_info));
 120                err = bpf_obj_get_info_by_fd(bpf_link__fd(link[i]),
 121                                             &link_info, &info_len);
 122                ASSERT_OK(err, "link_fd_get_info");
 123                ASSERT_EQ(link_info.tracing.attach_type,
 124                          bpf_program__get_expected_attach_type(prog[i]),
 125                          "link_attach_type");
 126                ASSERT_EQ(link_info.tracing.target_obj_id, tgt_prog_id, "link_tgt_obj_id");
 127                ASSERT_EQ(link_info.tracing.target_btf_id, btf_id, "link_tgt_btf_id");
 128        }
 129
 130        if (cb) {
 131                err = cb(obj);
 132                if (err)
 133                        goto close_prog;
 134        }
 135
 136        if (!run_prog)
 137                goto close_prog;
 138
 139        err = bpf_prog_test_run(tgt_fd, 1, &pkt_v6, sizeof(pkt_v6),
 140                                NULL, NULL, &retval, NULL);
 141        ASSERT_OK(err, "prog_run");
 142        ASSERT_EQ(retval, 0, "prog_run_ret");
 143
 144        if (check_data_map(obj, prog_cnt, false))
 145                goto close_prog;
 146
 147close_prog:
 148        for (i = 0; i < prog_cnt; i++)
 149                bpf_link__destroy(link[i]);
 150        bpf_object__close(obj);
 151        bpf_object__close(tgt_obj);
 152        free(link);
 153        free(prog);
 154}
 155
 156static void test_target_no_callees(void)
 157{
 158        const char *prog_name[] = {
 159                "fexit/test_pkt_md_access",
 160        };
 161        test_fexit_bpf2bpf_common("./fexit_bpf2bpf_simple.o",
 162                                  "./test_pkt_md_access.o",
 163                                  ARRAY_SIZE(prog_name),
 164                                  prog_name, true, NULL);
 165}
 166
 167static void test_target_yes_callees(void)
 168{
 169        const char *prog_name[] = {
 170                "fexit/test_pkt_access",
 171                "fexit/test_pkt_access_subprog1",
 172                "fexit/test_pkt_access_subprog2",
 173                "fexit/test_pkt_access_subprog3",
 174        };
 175        test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o",
 176                                  "./test_pkt_access.o",
 177                                  ARRAY_SIZE(prog_name),
 178                                  prog_name, true, NULL);
 179}
 180
 181static void test_func_replace(void)
 182{
 183        const char *prog_name[] = {
 184                "fexit/test_pkt_access",
 185                "fexit/test_pkt_access_subprog1",
 186                "fexit/test_pkt_access_subprog2",
 187                "fexit/test_pkt_access_subprog3",
 188                "freplace/get_skb_len",
 189                "freplace/get_skb_ifindex",
 190                "freplace/get_constant",
 191                "freplace/test_pkt_write_access_subprog",
 192        };
 193        test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o",
 194                                  "./test_pkt_access.o",
 195                                  ARRAY_SIZE(prog_name),
 196                                  prog_name, true, NULL);
 197}
 198
 199static void test_func_replace_verify(void)
 200{
 201        const char *prog_name[] = {
 202                "freplace/do_bind",
 203        };
 204        test_fexit_bpf2bpf_common("./freplace_connect4.o",
 205                                  "./connect4_prog.o",
 206                                  ARRAY_SIZE(prog_name),
 207                                  prog_name, false, NULL);
 208}
 209
 210static int test_second_attach(struct bpf_object *obj)
 211{
 212        const char *prog_name = "freplace/get_constant";
 213        const char *tgt_name = prog_name + 9; /* cut off freplace/ */
 214        const char *tgt_obj_file = "./test_pkt_access.o";
 215        struct bpf_program *prog = NULL;
 216        struct bpf_object *tgt_obj;
 217        __u32 duration = 0, retval;
 218        struct bpf_link *link;
 219        int err = 0, tgt_fd;
 220
 221        prog = bpf_object__find_program_by_title(obj, prog_name);
 222        if (CHECK(!prog, "find_prog", "prog %s not found\n", prog_name))
 223                return -ENOENT;
 224
 225        err = bpf_prog_load(tgt_obj_file, BPF_PROG_TYPE_UNSPEC,
 226                            &tgt_obj, &tgt_fd);
 227        if (CHECK(err, "second_prog_load", "file %s err %d errno %d\n",
 228                  tgt_obj_file, err, errno))
 229                return err;
 230
 231        link = bpf_program__attach_freplace(prog, tgt_fd, tgt_name);
 232        if (!ASSERT_OK_PTR(link, "second_link"))
 233                goto out;
 234
 235        err = bpf_prog_test_run(tgt_fd, 1, &pkt_v6, sizeof(pkt_v6),
 236                                NULL, NULL, &retval, &duration);
 237        if (CHECK(err || retval, "ipv6",
 238                  "err %d errno %d retval %d duration %d\n",
 239                  err, errno, retval, duration))
 240                goto out;
 241
 242        err = check_data_map(obj, 1, true);
 243        if (err)
 244                goto out;
 245
 246out:
 247        bpf_link__destroy(link);
 248        bpf_object__close(tgt_obj);
 249        return err;
 250}
 251
 252static void test_func_replace_multi(void)
 253{
 254        const char *prog_name[] = {
 255                "freplace/get_constant",
 256        };
 257        test_fexit_bpf2bpf_common("./freplace_get_constant.o",
 258                                  "./test_pkt_access.o",
 259                                  ARRAY_SIZE(prog_name),
 260                                  prog_name, true, test_second_attach);
 261}
 262
 263static void test_fmod_ret_freplace(void)
 264{
 265        struct bpf_object *freplace_obj = NULL, *pkt_obj, *fmod_obj = NULL;
 266        const char *freplace_name = "./freplace_get_constant.o";
 267        const char *fmod_ret_name = "./fmod_ret_freplace.o";
 268        DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
 269        const char *tgt_name = "./test_pkt_access.o";
 270        struct bpf_link *freplace_link = NULL;
 271        struct bpf_program *prog;
 272        __u32 duration = 0;
 273        int err, pkt_fd;
 274
 275        err = bpf_prog_load(tgt_name, BPF_PROG_TYPE_UNSPEC,
 276                            &pkt_obj, &pkt_fd);
 277        /* the target prog should load fine */
 278        if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n",
 279                  tgt_name, err, errno))
 280                return;
 281        opts.attach_prog_fd = pkt_fd;
 282
 283        freplace_obj = bpf_object__open_file(freplace_name, &opts);
 284        if (!ASSERT_OK_PTR(freplace_obj, "freplace_obj_open"))
 285                goto out;
 286
 287        err = bpf_object__load(freplace_obj);
 288        if (CHECK(err, "freplace_obj_load", "err %d\n", err))
 289                goto out;
 290
 291        prog = bpf_program__next(NULL, freplace_obj);
 292        freplace_link = bpf_program__attach_trace(prog);
 293        if (!ASSERT_OK_PTR(freplace_link, "freplace_attach_trace"))
 294                goto out;
 295
 296        opts.attach_prog_fd = bpf_program__fd(prog);
 297        fmod_obj = bpf_object__open_file(fmod_ret_name, &opts);
 298        if (!ASSERT_OK_PTR(fmod_obj, "fmod_obj_open"))
 299                goto out;
 300
 301        err = bpf_object__load(fmod_obj);
 302        if (CHECK(!err, "fmod_obj_load", "loading fmod_ret should fail\n"))
 303                goto out;
 304
 305out:
 306        bpf_link__destroy(freplace_link);
 307        bpf_object__close(freplace_obj);
 308        bpf_object__close(fmod_obj);
 309        bpf_object__close(pkt_obj);
 310}
 311
 312
 313static void test_func_sockmap_update(void)
 314{
 315        const char *prog_name[] = {
 316                "freplace/cls_redirect",
 317        };
 318        test_fexit_bpf2bpf_common("./freplace_cls_redirect.o",
 319                                  "./test_cls_redirect.o",
 320                                  ARRAY_SIZE(prog_name),
 321                                  prog_name, false, NULL);
 322}
 323
 324static void test_obj_load_failure_common(const char *obj_file,
 325                                          const char *target_obj_file)
 326
 327{
 328        /*
 329         * standalone test that asserts failure to load freplace prog
 330         * because of invalid return code.
 331         */
 332        struct bpf_object *obj = NULL, *pkt_obj;
 333        int err, pkt_fd;
 334        __u32 duration = 0;
 335
 336        err = bpf_prog_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
 337                            &pkt_obj, &pkt_fd);
 338        /* the target prog should load fine */
 339        if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n",
 340                  target_obj_file, err, errno))
 341                return;
 342        DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
 343                            .attach_prog_fd = pkt_fd,
 344                           );
 345
 346        obj = bpf_object__open_file(obj_file, &opts);
 347        if (!ASSERT_OK_PTR(obj, "obj_open"))
 348                goto close_prog;
 349
 350        /* It should fail to load the program */
 351        err = bpf_object__load(obj);
 352        if (CHECK(!err, "bpf_obj_load should fail", "err %d\n", err))
 353                goto close_prog;
 354
 355close_prog:
 356        bpf_object__close(obj);
 357        bpf_object__close(pkt_obj);
 358}
 359
 360static void test_func_replace_return_code(void)
 361{
 362        /* test invalid return code in the replaced program */
 363        test_obj_load_failure_common("./freplace_connect_v4_prog.o",
 364                                     "./connect4_prog.o");
 365}
 366
 367static void test_func_map_prog_compatibility(void)
 368{
 369        /* test with spin lock map value in the replaced program */
 370        test_obj_load_failure_common("./freplace_attach_probe.o",
 371                                     "./test_attach_probe.o");
 372}
 373
 374void test_fexit_bpf2bpf(void)
 375{
 376        if (test__start_subtest("target_no_callees"))
 377                test_target_no_callees();
 378        if (test__start_subtest("target_yes_callees"))
 379                test_target_yes_callees();
 380        if (test__start_subtest("func_replace"))
 381                test_func_replace();
 382        if (test__start_subtest("func_replace_verify"))
 383                test_func_replace_verify();
 384        if (test__start_subtest("func_sockmap_update"))
 385                test_func_sockmap_update();
 386        if (test__start_subtest("func_replace_return_code"))
 387                test_func_replace_return_code();
 388        if (test__start_subtest("func_map_prog_compatibility"))
 389                test_func_map_prog_compatibility();
 390        if (test__start_subtest("func_replace_multi"))
 391                test_func_replace_multi();
 392        if (test__start_subtest("fmod_ret_freplace"))
 393                test_fmod_ret_freplace();
 394}
 395