1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2020 Facebook */ 3 #include <uapi/linux/if_link.h> 4 #include <test_progs.h> 5 #include "test_xdp_link.skel.h" 6 7 #define IFINDEX_LO 1 8 9 void test_xdp_link(void) 10 { 11 __u32 duration = 0, id1, id2, id0 = 0, prog_fd1, prog_fd2, err; 12 DECLARE_LIBBPF_OPTS(bpf_xdp_set_link_opts, opts, .old_fd = -1); 13 struct test_xdp_link *skel1 = NULL, *skel2 = NULL; 14 struct bpf_link_info link_info; 15 struct bpf_prog_info prog_info; 16 struct bpf_link *link; 17 __u32 link_info_len = sizeof(link_info); 18 __u32 prog_info_len = sizeof(prog_info); 19 20 skel1 = test_xdp_link__open_and_load(); 21 if (CHECK(!skel1, "skel_load", "skeleton open and load failed\n")) 22 goto cleanup; 23 prog_fd1 = bpf_program__fd(skel1->progs.xdp_handler); 24 25 skel2 = test_xdp_link__open_and_load(); 26 if (CHECK(!skel2, "skel_load", "skeleton open and load failed\n")) 27 goto cleanup; 28 prog_fd2 = bpf_program__fd(skel2->progs.xdp_handler); 29 30 memset(&prog_info, 0, sizeof(prog_info)); 31 err = bpf_obj_get_info_by_fd(prog_fd1, &prog_info, &prog_info_len); 32 if (CHECK(err, "fd_info1", "failed %d\n", -errno)) 33 goto cleanup; 34 id1 = prog_info.id; 35 36 memset(&prog_info, 0, sizeof(prog_info)); 37 err = bpf_obj_get_info_by_fd(prog_fd2, &prog_info, &prog_info_len); 38 if (CHECK(err, "fd_info2", "failed %d\n", -errno)) 39 goto cleanup; 40 id2 = prog_info.id; 41 42 /* set initial prog attachment */ 43 err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, prog_fd1, XDP_FLAGS_REPLACE, &opts); 44 if (CHECK(err, "fd_attach", "initial prog attach failed: %d\n", err)) 45 goto cleanup; 46 47 /* validate prog ID */ 48 err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0); 49 CHECK(err || id0 != id1, "id1_check", 50 "loaded prog id %u != id1 %u, err %d", id0, id1, err); 51 52 /* BPF link is not allowed to replace prog attachment */ 53 link = bpf_program__attach_xdp(skel1->progs.xdp_handler, IFINDEX_LO); 54 if (CHECK(!IS_ERR(link), "link_attach_fail", "unexpected success\n")) { 55 bpf_link__destroy(link); 56 /* best-effort detach prog */ 57 opts.old_fd = prog_fd1; 58 bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, XDP_FLAGS_REPLACE, &opts); 59 goto cleanup; 60 } 61 62 /* detach BPF program */ 63 opts.old_fd = prog_fd1; 64 err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, XDP_FLAGS_REPLACE, &opts); 65 if (CHECK(err, "prog_detach", "failed %d\n", err)) 66 goto cleanup; 67 68 /* now BPF link should attach successfully */ 69 link = bpf_program__attach_xdp(skel1->progs.xdp_handler, IFINDEX_LO); 70 if (CHECK(IS_ERR(link), "link_attach", "failed: %ld\n", PTR_ERR(link))) 71 goto cleanup; 72 skel1->links.xdp_handler = link; 73 74 /* validate prog ID */ 75 err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0); 76 if (CHECK(err || id0 != id1, "id1_check", 77 "loaded prog id %u != id1 %u, err %d", id0, id1, err)) 78 goto cleanup; 79 80 /* BPF prog attach is not allowed to replace BPF link */ 81 opts.old_fd = prog_fd1; 82 err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, prog_fd2, XDP_FLAGS_REPLACE, &opts); 83 if (CHECK(!err, "prog_attach_fail", "unexpected success\n")) 84 goto cleanup; 85 86 /* Can't force-update when BPF link is active */ 87 err = bpf_set_link_xdp_fd(IFINDEX_LO, prog_fd2, 0); 88 if (CHECK(!err, "prog_update_fail", "unexpected success\n")) 89 goto cleanup; 90 91 /* Can't force-detach when BPF link is active */ 92 err = bpf_set_link_xdp_fd(IFINDEX_LO, -1, 0); 93 if (CHECK(!err, "prog_detach_fail", "unexpected success\n")) 94 goto cleanup; 95 96 /* BPF link is not allowed to replace another BPF link */ 97 link = bpf_program__attach_xdp(skel2->progs.xdp_handler, IFINDEX_LO); 98 if (CHECK(!IS_ERR(link), "link_attach_fail", "unexpected success\n")) { 99 bpf_link__destroy(link); 100 goto cleanup; 101 } 102 103 bpf_link__destroy(skel1->links.xdp_handler); 104 skel1->links.xdp_handler = NULL; 105 106 /* new link attach should succeed */ 107 link = bpf_program__attach_xdp(skel2->progs.xdp_handler, IFINDEX_LO); 108 if (CHECK(IS_ERR(link), "link_attach", "failed: %ld\n", PTR_ERR(link))) 109 goto cleanup; 110 skel2->links.xdp_handler = link; 111 112 err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0); 113 if (CHECK(err || id0 != id2, "id2_check", 114 "loaded prog id %u != id2 %u, err %d", id0, id1, err)) 115 goto cleanup; 116 117 /* updating program under active BPF link works as expected */ 118 err = bpf_link__update_program(link, skel1->progs.xdp_handler); 119 if (CHECK(err, "link_upd", "failed: %d\n", err)) 120 goto cleanup; 121 122 memset(&link_info, 0, sizeof(link_info)); 123 err = bpf_obj_get_info_by_fd(bpf_link__fd(link), &link_info, &link_info_len); 124 if (CHECK(err, "link_info", "failed: %d\n", err)) 125 goto cleanup; 126 127 CHECK(link_info.type != BPF_LINK_TYPE_XDP, "link_type", 128 "got %u != exp %u\n", link_info.type, BPF_LINK_TYPE_XDP); 129 CHECK(link_info.prog_id != id1, "link_prog_id", 130 "got %u != exp %u\n", link_info.prog_id, id1); 131 CHECK(link_info.xdp.ifindex != IFINDEX_LO, "link_ifindex", 132 "got %u != exp %u\n", link_info.xdp.ifindex, IFINDEX_LO); 133 134 err = bpf_link__detach(link); 135 if (CHECK(err, "link_detach", "failed %d\n", err)) 136 goto cleanup; 137 138 memset(&link_info, 0, sizeof(link_info)); 139 err = bpf_obj_get_info_by_fd(bpf_link__fd(link), &link_info, &link_info_len); 140 if (CHECK(err, "link_info", "failed: %d\n", err)) 141 goto cleanup; 142 CHECK(link_info.prog_id != id1, "link_prog_id", 143 "got %u != exp %u\n", link_info.prog_id, id1); 144 /* ifindex should be zeroed out */ 145 CHECK(link_info.xdp.ifindex != 0, "link_ifindex", 146 "got %u != exp %u\n", link_info.xdp.ifindex, 0); 147 148 cleanup: 149 test_xdp_link__destroy(skel1); 150 test_xdp_link__destroy(skel2); 151 } 152