xref: /linux/tools/testing/selftests/bpf/prog_tests/xdp_devmap_attach.c (revision ae28ed4578e6d5a481e39c5a9827f27048661fdd)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <arpa/inet.h>
3 #include <uapi/linux/bpf.h>
4 #include <linux/if_link.h>
5 #include <network_helpers.h>
6 #include <net/if.h>
7 #include <test_progs.h>
8 
9 #include "test_xdp_devmap_helpers.skel.h"
10 #include "test_xdp_devmap_tailcall.skel.h"
11 #include "test_xdp_with_devmap_frags_helpers.skel.h"
12 #include "test_xdp_with_devmap_helpers.skel.h"
13 
14 #define IFINDEX_LO 1
15 #define TEST_NS "devmap_attach_ns"
16 
17 static void test_xdp_with_devmap_helpers(void)
18 {
19 	struct test_xdp_with_devmap_helpers *skel = NULL;
20 	struct bpf_prog_info info = {};
21 	struct bpf_devmap_val val = {
22 		.ifindex = IFINDEX_LO,
23 	};
24 	__u32 len = sizeof(info);
25 	int err, dm_fd, dm_fd_redir, map_fd;
26 	struct nstoken *nstoken = NULL;
27 	char data[ETH_HLEN] = {};
28 	__u32 idx = 0;
29 
30 	SYS(out_close, "ip netns add %s", TEST_NS);
31 	nstoken = open_netns(TEST_NS);
32 	if (!ASSERT_OK_PTR(nstoken, "open_netns"))
33 		goto out_close;
34 	SYS(out_close, "ip link set dev lo up");
35 
36 	skel = test_xdp_with_devmap_helpers__open_and_load();
37 	if (!ASSERT_OK_PTR(skel, "test_xdp_with_devmap_helpers__open_and_load"))
38 		goto out_close;
39 
40 	dm_fd_redir = bpf_program__fd(skel->progs.xdp_redir_prog);
41 	err = bpf_xdp_attach(IFINDEX_LO, dm_fd_redir, XDP_FLAGS_SKB_MODE, NULL);
42 	if (!ASSERT_OK(err, "Generic attach of program with 8-byte devmap"))
43 		goto out_close;
44 
45 	dm_fd = bpf_program__fd(skel->progs.xdp_dummy_dm);
46 	map_fd = bpf_map__fd(skel->maps.dm_ports);
47 	err = bpf_prog_get_info_by_fd(dm_fd, &info, &len);
48 	if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
49 		goto out_close;
50 
51 	val.bpf_prog.fd = dm_fd;
52 	err = bpf_map_update_elem(map_fd, &idx, &val, 0);
53 	ASSERT_OK(err, "Add program to devmap entry");
54 
55 	err = bpf_map_lookup_elem(map_fd, &idx, &val);
56 	ASSERT_OK(err, "Read devmap entry");
57 	ASSERT_EQ(info.id, val.bpf_prog.id, "Match program id to devmap entry prog_id");
58 
59 	/* send a packet to trigger any potential bugs in there */
60 	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
61 			    .data_in = &data,
62 			    .data_size_in = sizeof(data),
63 			    .flags = BPF_F_TEST_XDP_LIVE_FRAMES,
64 			    .repeat = 1,
65 		);
66 	err = bpf_prog_test_run_opts(dm_fd_redir, &opts);
67 	ASSERT_OK(err, "XDP test run");
68 
69 	/* wait for the packets to be flushed */
70 	kern_sync_rcu();
71 
72 	err = bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_SKB_MODE, NULL);
73 	ASSERT_OK(err, "XDP program detach");
74 
75 	/* can not attach BPF_XDP_DEVMAP program to a device */
76 	err = bpf_xdp_attach(IFINDEX_LO, dm_fd, XDP_FLAGS_SKB_MODE, NULL);
77 	if (!ASSERT_NEQ(err, 0, "Attach of BPF_XDP_DEVMAP program"))
78 		bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_SKB_MODE, NULL);
79 
80 	val.ifindex = 1;
81 	val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_dummy_prog);
82 	err = bpf_map_update_elem(map_fd, &idx, &val, 0);
83 	ASSERT_NEQ(err, 0, "Add non-BPF_XDP_DEVMAP program to devmap entry");
84 
85 	/* Try to attach BPF_XDP program with frags to devmap when we have
86 	 * already loaded a BPF_XDP program on the map
87 	 */
88 	idx = 1;
89 	val.ifindex = 1;
90 	val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_dummy_dm_frags);
91 	err = bpf_map_update_elem(map_fd, &idx, &val, 0);
92 	ASSERT_NEQ(err, 0, "Add BPF_XDP program with frags to devmap entry");
93 
94 out_close:
95 	close_netns(nstoken);
96 	SYS_NOFAIL("ip netns del %s", TEST_NS);
97 	test_xdp_with_devmap_helpers__destroy(skel);
98 }
99 
100 static void test_neg_xdp_devmap_helpers(void)
101 {
102 	struct test_xdp_devmap_helpers *skel;
103 
104 	skel = test_xdp_devmap_helpers__open_and_load();
105 	if (!ASSERT_EQ(skel, NULL,
106 		    "Load of XDP program accessing egress ifindex without attach type")) {
107 		test_xdp_devmap_helpers__destroy(skel);
108 	}
109 }
110 
111 static void test_xdp_devmap_tailcall(enum bpf_attach_type prog_dev,
112 				     enum bpf_attach_type prog_tail,
113 				     bool expect_reject)
114 {
115 	struct test_xdp_devmap_tailcall *skel;
116 	int err;
117 
118 	skel = test_xdp_devmap_tailcall__open();
119 	if (!ASSERT_OK_PTR(skel, "test_xdp_devmap_tailcall__open"))
120 		return;
121 
122 	bpf_program__set_expected_attach_type(skel->progs.xdp_devmap, prog_dev);
123 	bpf_program__set_expected_attach_type(skel->progs.xdp_entry, prog_tail);
124 
125 	err = test_xdp_devmap_tailcall__load(skel);
126 	if (expect_reject)
127 		ASSERT_ERR(err, "test_xdp_devmap_tailcall__load");
128 	else
129 		ASSERT_OK(err, "test_xdp_devmap_tailcall__load");
130 
131 	test_xdp_devmap_tailcall__destroy(skel);
132 }
133 
134 static void test_xdp_with_devmap_frags_helpers(void)
135 {
136 	struct test_xdp_with_devmap_frags_helpers *skel;
137 	struct bpf_prog_info info = {};
138 	struct bpf_devmap_val val = {
139 		.ifindex = IFINDEX_LO,
140 	};
141 	__u32 len = sizeof(info);
142 	int err, dm_fd_frags, map_fd;
143 	__u32 idx = 0;
144 
145 	skel = test_xdp_with_devmap_frags_helpers__open_and_load();
146 	if (!ASSERT_OK_PTR(skel, "test_xdp_with_devmap_helpers__open_and_load"))
147 		return;
148 
149 	dm_fd_frags = bpf_program__fd(skel->progs.xdp_dummy_dm_frags);
150 	map_fd = bpf_map__fd(skel->maps.dm_ports);
151 	err = bpf_prog_get_info_by_fd(dm_fd_frags, &info, &len);
152 	if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
153 		goto out_close;
154 
155 	val.bpf_prog.fd = dm_fd_frags;
156 	err = bpf_map_update_elem(map_fd, &idx, &val, 0);
157 	ASSERT_OK(err, "Add frags program to devmap entry");
158 
159 	err = bpf_map_lookup_elem(map_fd, &idx, &val);
160 	ASSERT_OK(err, "Read devmap entry");
161 	ASSERT_EQ(info.id, val.bpf_prog.id,
162 		  "Match program id to devmap entry prog_id");
163 
164 	/* Try to attach BPF_XDP program to devmap when we have
165 	 * already loaded a BPF_XDP program with frags on the map
166 	 */
167 	idx = 1;
168 	val.ifindex = 1;
169 	val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_dummy_dm);
170 	err = bpf_map_update_elem(map_fd, &idx, &val, 0);
171 	ASSERT_NEQ(err, 0, "Add BPF_XDP program to devmap entry");
172 
173 out_close:
174 	test_xdp_with_devmap_frags_helpers__destroy(skel);
175 }
176 
177 static void test_xdp_with_devmap_helpers_veth(void)
178 {
179 	struct test_xdp_with_devmap_helpers *skel = NULL;
180 	struct bpf_prog_info info = {};
181 	struct bpf_devmap_val val = {};
182 	struct nstoken *nstoken = NULL;
183 	__u32 len = sizeof(info);
184 	int err, dm_fd, dm_fd_redir, map_fd, ifindex_dst;
185 	char data[ETH_HLEN] = {};
186 	__u32 idx = 0;
187 
188 	SYS(out_close, "ip netns add %s", TEST_NS);
189 	nstoken = open_netns(TEST_NS);
190 	if (!ASSERT_OK_PTR(nstoken, "open_netns"))
191 		goto out_close;
192 
193 	SYS(out_close, "ip link add veth_src type veth peer name veth_dst");
194 	SYS(out_close, "ip link set dev veth_src up");
195 	SYS(out_close, "ip link set dev veth_dst up");
196 
197 	val.ifindex = if_nametoindex("veth_src");
198 	ifindex_dst = if_nametoindex("veth_dst");
199 	if (!ASSERT_NEQ(val.ifindex, 0, "val.ifindex") ||
200 	    !ASSERT_NEQ(ifindex_dst, 0, "ifindex_dst"))
201 		goto out_close;
202 
203 	skel = test_xdp_with_devmap_helpers__open_and_load();
204 	if (!ASSERT_OK_PTR(skel, "test_xdp_with_devmap_helpers__open_and_load"))
205 		goto out_close;
206 
207 	dm_fd_redir = bpf_program__fd(skel->progs.xdp_redir_prog);
208 	err = bpf_xdp_attach(val.ifindex, dm_fd_redir, XDP_FLAGS_DRV_MODE, NULL);
209 	if (!ASSERT_OK(err, "Attach of program with 8-byte devmap"))
210 		goto out_close;
211 
212 	dm_fd = bpf_program__fd(skel->progs.xdp_dummy_dm);
213 	map_fd = bpf_map__fd(skel->maps.dm_ports);
214 	err = bpf_prog_get_info_by_fd(dm_fd, &info, &len);
215 	if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
216 		goto out_close;
217 
218 	val.bpf_prog.fd = dm_fd;
219 	err = bpf_map_update_elem(map_fd, &idx, &val, 0);
220 	ASSERT_OK(err, "Add program to devmap entry");
221 
222 	err = bpf_map_lookup_elem(map_fd, &idx, &val);
223 	ASSERT_OK(err, "Read devmap entry");
224 	ASSERT_EQ(info.id, val.bpf_prog.id, "Match program id to devmap entry prog_id");
225 
226 	/* attach dummy to other side to enable reception */
227 	dm_fd = bpf_program__fd(skel->progs.xdp_dummy_prog);
228 	err = bpf_xdp_attach(ifindex_dst, dm_fd, XDP_FLAGS_DRV_MODE, NULL);
229 	if (!ASSERT_OK(err, "Attach of dummy XDP"))
230 		goto out_close;
231 
232 	/* send a packet to trigger any potential bugs in there */
233 	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
234 			    .data_in = &data,
235 			    .data_size_in = sizeof(data),
236 			    .flags = BPF_F_TEST_XDP_LIVE_FRAMES,
237 			    .repeat = 1,
238 		);
239 	err = bpf_prog_test_run_opts(dm_fd_redir, &opts);
240 	ASSERT_OK(err, "XDP test run");
241 
242 	/* wait for the packets to be flushed */
243 	kern_sync_rcu();
244 
245 	err = bpf_xdp_detach(val.ifindex, XDP_FLAGS_DRV_MODE, NULL);
246 	ASSERT_OK(err, "XDP program detach");
247 
248 	err = bpf_xdp_detach(ifindex_dst, XDP_FLAGS_DRV_MODE, NULL);
249 	ASSERT_OK(err, "XDP program detach");
250 
251 out_close:
252 	close_netns(nstoken);
253 	SYS_NOFAIL("ip netns del %s", TEST_NS);
254 	test_xdp_with_devmap_helpers__destroy(skel);
255 }
256 
257 void serial_test_xdp_devmap_attach(void)
258 {
259 	if (test__start_subtest("DEVMAP with programs in entries"))
260 		test_xdp_with_devmap_helpers();
261 
262 	if (test__start_subtest("DEVMAP with frags programs in entries"))
263 		test_xdp_with_devmap_frags_helpers();
264 
265 	if (test__start_subtest("Verifier check of DEVMAP programs")) {
266 		test_neg_xdp_devmap_helpers();
267 		test_xdp_devmap_tailcall(BPF_XDP_DEVMAP, BPF_XDP_DEVMAP, false);
268 		test_xdp_devmap_tailcall(0, 0, true);
269 		test_xdp_devmap_tailcall(BPF_XDP_DEVMAP, 0, true);
270 		test_xdp_devmap_tailcall(0, BPF_XDP_DEVMAP, true);
271 	}
272 
273 	if (test__start_subtest("DEVMAP with programs in entries on veth"))
274 		test_xdp_with_devmap_helpers_veth();
275 }
276