| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (c) 2020 Facebook */ |
| #include <uapi/linux/if_link.h> |
| #include <test_progs.h> |
| #include "test_xdp_link.skel.h" |
| |
| #define IFINDEX_LO 1 |
| |
| void serial_test_xdp_link(void) |
| { |
| struct test_xdp_link *skel1 = NULL, *skel2 = NULL; |
| __u32 id1, id2, id0 = 0, prog_fd1, prog_fd2; |
| LIBBPF_OPTS(bpf_xdp_attach_opts, opts); |
| struct bpf_link_info link_info; |
| struct bpf_prog_info prog_info; |
| struct bpf_link *link; |
| int err; |
| __u32 link_info_len = sizeof(link_info); |
| __u32 prog_info_len = sizeof(prog_info); |
| |
| skel1 = test_xdp_link__open_and_load(); |
| if (!ASSERT_OK_PTR(skel1, "skel_load")) |
| goto cleanup; |
| prog_fd1 = bpf_program__fd(skel1->progs.xdp_handler); |
| |
| skel2 = test_xdp_link__open_and_load(); |
| if (!ASSERT_OK_PTR(skel2, "skel_load")) |
| goto cleanup; |
| prog_fd2 = bpf_program__fd(skel2->progs.xdp_handler); |
| |
| memset(&prog_info, 0, sizeof(prog_info)); |
| err = bpf_prog_get_info_by_fd(prog_fd1, &prog_info, &prog_info_len); |
| if (!ASSERT_OK(err, "fd_info1")) |
| goto cleanup; |
| id1 = prog_info.id; |
| |
| memset(&prog_info, 0, sizeof(prog_info)); |
| err = bpf_prog_get_info_by_fd(prog_fd2, &prog_info, &prog_info_len); |
| if (!ASSERT_OK(err, "fd_info2")) |
| goto cleanup; |
| id2 = prog_info.id; |
| |
| /* set initial prog attachment */ |
| err = bpf_xdp_attach(IFINDEX_LO, prog_fd1, XDP_FLAGS_REPLACE, &opts); |
| if (!ASSERT_OK(err, "fd_attach")) |
| goto cleanup; |
| |
| /* validate prog ID */ |
| err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0); |
| if (!ASSERT_OK(err, "id1_check_err") || !ASSERT_EQ(id0, id1, "id1_check_val")) |
| goto cleanup; |
| |
| /* BPF link is not allowed to replace prog attachment */ |
| link = bpf_program__attach_xdp(skel1->progs.xdp_handler, IFINDEX_LO); |
| if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) { |
| bpf_link__destroy(link); |
| /* best-effort detach prog */ |
| opts.old_prog_fd = prog_fd1; |
| bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_REPLACE, &opts); |
| goto cleanup; |
| } |
| |
| /* detach BPF program */ |
| opts.old_prog_fd = prog_fd1; |
| err = bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_REPLACE, &opts); |
| if (!ASSERT_OK(err, "prog_detach")) |
| goto cleanup; |
| |
| /* now BPF link should attach successfully */ |
| link = bpf_program__attach_xdp(skel1->progs.xdp_handler, IFINDEX_LO); |
| if (!ASSERT_OK_PTR(link, "link_attach")) |
| goto cleanup; |
| skel1->links.xdp_handler = link; |
| |
| /* validate prog ID */ |
| err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0); |
| if (!ASSERT_OK(err, "id1_check_err") || !ASSERT_EQ(id0, id1, "id1_check_val")) |
| goto cleanup; |
| |
| /* BPF prog attach is not allowed to replace BPF link */ |
| opts.old_prog_fd = prog_fd1; |
| err = bpf_xdp_attach(IFINDEX_LO, prog_fd2, XDP_FLAGS_REPLACE, &opts); |
| if (!ASSERT_ERR(err, "prog_attach_fail")) |
| goto cleanup; |
| |
| /* Can't force-update when BPF link is active */ |
| err = bpf_xdp_attach(IFINDEX_LO, prog_fd2, 0, NULL); |
| if (!ASSERT_ERR(err, "prog_update_fail")) |
| goto cleanup; |
| |
| /* Can't force-detach when BPF link is active */ |
| err = bpf_xdp_detach(IFINDEX_LO, 0, NULL); |
| if (!ASSERT_ERR(err, "prog_detach_fail")) |
| goto cleanup; |
| |
| /* BPF link is not allowed to replace another BPF link */ |
| link = bpf_program__attach_xdp(skel2->progs.xdp_handler, IFINDEX_LO); |
| if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) { |
| bpf_link__destroy(link); |
| goto cleanup; |
| } |
| |
| bpf_link__destroy(skel1->links.xdp_handler); |
| skel1->links.xdp_handler = NULL; |
| |
| /* new link attach should succeed */ |
| link = bpf_program__attach_xdp(skel2->progs.xdp_handler, IFINDEX_LO); |
| if (!ASSERT_OK_PTR(link, "link_attach")) |
| goto cleanup; |
| skel2->links.xdp_handler = link; |
| |
| err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0); |
| if (!ASSERT_OK(err, "id2_check_err") || !ASSERT_EQ(id0, id2, "id2_check_val")) |
| goto cleanup; |
| |
| /* updating program under active BPF link works as expected */ |
| err = bpf_link__update_program(link, skel1->progs.xdp_handler); |
| if (!ASSERT_OK(err, "link_upd")) |
| goto cleanup; |
| |
| memset(&link_info, 0, sizeof(link_info)); |
| err = bpf_link_get_info_by_fd(bpf_link__fd(link), |
| &link_info, &link_info_len); |
| if (!ASSERT_OK(err, "link_info")) |
| goto cleanup; |
| |
| ASSERT_EQ(link_info.type, BPF_LINK_TYPE_XDP, "link_type"); |
| ASSERT_EQ(link_info.prog_id, id1, "link_prog_id"); |
| ASSERT_EQ(link_info.xdp.ifindex, IFINDEX_LO, "link_ifindex"); |
| |
| /* updating program under active BPF link with different type fails */ |
| err = bpf_link__update_program(link, skel1->progs.tc_handler); |
| if (!ASSERT_ERR(err, "link_upd_invalid")) |
| goto cleanup; |
| |
| err = bpf_link__detach(link); |
| if (!ASSERT_OK(err, "link_detach")) |
| goto cleanup; |
| |
| memset(&link_info, 0, sizeof(link_info)); |
| err = bpf_link_get_info_by_fd(bpf_link__fd(link), |
| &link_info, &link_info_len); |
| |
| ASSERT_OK(err, "link_info"); |
| ASSERT_EQ(link_info.prog_id, id1, "link_prog_id"); |
| /* ifindex should be zeroed out */ |
| ASSERT_EQ(link_info.xdp.ifindex, 0, "link_ifindex"); |
| |
| cleanup: |
| test_xdp_link__destroy(skel1); |
| test_xdp_link__destroy(skel2); |
| } |