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
serial_test_xdp_link(void)9 void serial_test_xdp_link(void)
10 {
11 struct test_xdp_link *skel1 = NULL, *skel2 = NULL;
12 __u32 id1, id2, id0 = 0, prog_fd1, prog_fd2;
13 LIBBPF_OPTS(bpf_xdp_attach_opts, opts);
14 struct bpf_link_info link_info;
15 struct bpf_prog_info prog_info;
16 struct bpf_link *link;
17 int err;
18 __u32 link_info_len = sizeof(link_info);
19 __u32 prog_info_len = sizeof(prog_info);
20
21 skel1 = test_xdp_link__open_and_load();
22 if (!ASSERT_OK_PTR(skel1, "skel_load"))
23 goto cleanup;
24 prog_fd1 = bpf_program__fd(skel1->progs.xdp_handler);
25
26 skel2 = test_xdp_link__open_and_load();
27 if (!ASSERT_OK_PTR(skel2, "skel_load"))
28 goto cleanup;
29 prog_fd2 = bpf_program__fd(skel2->progs.xdp_handler);
30
31 memset(&prog_info, 0, sizeof(prog_info));
32 err = bpf_prog_get_info_by_fd(prog_fd1, &prog_info, &prog_info_len);
33 if (!ASSERT_OK(err, "fd_info1"))
34 goto cleanup;
35 id1 = prog_info.id;
36
37 memset(&prog_info, 0, sizeof(prog_info));
38 err = bpf_prog_get_info_by_fd(prog_fd2, &prog_info, &prog_info_len);
39 if (!ASSERT_OK(err, "fd_info2"))
40 goto cleanup;
41 id2 = prog_info.id;
42
43 /* set initial prog attachment */
44 err = bpf_xdp_attach(IFINDEX_LO, prog_fd1, XDP_FLAGS_REPLACE, &opts);
45 if (!ASSERT_OK(err, "fd_attach"))
46 goto cleanup;
47
48 /* validate prog ID */
49 err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0);
50 if (!ASSERT_OK(err, "id1_check_err") || !ASSERT_EQ(id0, id1, "id1_check_val"))
51 goto cleanup;
52
53 /* BPF link is not allowed to replace prog attachment */
54 link = bpf_program__attach_xdp(skel1->progs.xdp_handler, IFINDEX_LO);
55 if (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
56 bpf_link__destroy(link);
57 /* best-effort detach prog */
58 opts.old_prog_fd = prog_fd1;
59 bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_REPLACE, &opts);
60 goto cleanup;
61 }
62
63 /* detach BPF program */
64 opts.old_prog_fd = prog_fd1;
65 err = bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_REPLACE, &opts);
66 if (!ASSERT_OK(err, "prog_detach"))
67 goto cleanup;
68
69 /* now BPF link should attach successfully */
70 link = bpf_program__attach_xdp(skel1->progs.xdp_handler, IFINDEX_LO);
71 if (!ASSERT_OK_PTR(link, "link_attach"))
72 goto cleanup;
73 skel1->links.xdp_handler = link;
74
75 /* validate prog ID */
76 err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0);
77 if (!ASSERT_OK(err, "id1_check_err") || !ASSERT_EQ(id0, id1, "id1_check_val"))
78 goto cleanup;
79
80 /* BPF prog attach is not allowed to replace BPF link */
81 opts.old_prog_fd = prog_fd1;
82 err = bpf_xdp_attach(IFINDEX_LO, prog_fd2, XDP_FLAGS_REPLACE, &opts);
83 if (!ASSERT_ERR(err, "prog_attach_fail"))
84 goto cleanup;
85
86 /* Can't force-update when BPF link is active */
87 err = bpf_xdp_attach(IFINDEX_LO, prog_fd2, 0, NULL);
88 if (!ASSERT_ERR(err, "prog_update_fail"))
89 goto cleanup;
90
91 /* Can't force-detach when BPF link is active */
92 err = bpf_xdp_detach(IFINDEX_LO, 0, NULL);
93 if (!ASSERT_ERR(err, "prog_detach_fail"))
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 (!ASSERT_ERR_PTR(link, "link_attach_should_fail")) {
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 (!ASSERT_OK_PTR(link, "link_attach"))
109 goto cleanup;
110 skel2->links.xdp_handler = link;
111
112 err = bpf_xdp_query_id(IFINDEX_LO, 0, &id0);
113 if (!ASSERT_OK(err, "id2_check_err") || !ASSERT_EQ(id0, id2, "id2_check_val"))
114 goto cleanup;
115
116 /* updating program under active BPF link works as expected */
117 err = bpf_link__update_program(link, skel1->progs.xdp_handler);
118 if (!ASSERT_OK(err, "link_upd"))
119 goto cleanup;
120
121 memset(&link_info, 0, sizeof(link_info));
122 err = bpf_link_get_info_by_fd(bpf_link__fd(link),
123 &link_info, &link_info_len);
124 if (!ASSERT_OK(err, "link_info"))
125 goto cleanup;
126
127 ASSERT_EQ(link_info.type, BPF_LINK_TYPE_XDP, "link_type");
128 ASSERT_EQ(link_info.prog_id, id1, "link_prog_id");
129 ASSERT_EQ(link_info.xdp.ifindex, IFINDEX_LO, "link_ifindex");
130
131 /* updating program under active BPF link with different type fails */
132 err = bpf_link__update_program(link, skel1->progs.tc_handler);
133 if (!ASSERT_ERR(err, "link_upd_invalid"))
134 goto cleanup;
135
136 err = bpf_link__detach(link);
137 if (!ASSERT_OK(err, "link_detach"))
138 goto cleanup;
139
140 memset(&link_info, 0, sizeof(link_info));
141 err = bpf_link_get_info_by_fd(bpf_link__fd(link),
142 &link_info, &link_info_len);
143
144 ASSERT_OK(err, "link_info");
145 ASSERT_EQ(link_info.prog_id, id1, "link_prog_id");
146 /* ifindex should be zeroed out */
147 ASSERT_EQ(link_info.xdp.ifindex, 0, "link_ifindex");
148
149 cleanup:
150 test_xdp_link__destroy(skel1);
151 test_xdp_link__destroy(skel2);
152 }
153