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 
7 typedef int (*test_cb)(struct bpf_object *obj);
8 
9 static 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;
48 out:
49 	free(result);
50 	return ret;
51 }
52 
53 static 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 tgt_prog_id, info_len;
62 	struct bpf_prog_info prog_info = {};
63 	struct bpf_program **prog = NULL, *p;
64 	struct bpf_link **link = NULL;
65 	int err, tgt_fd, i;
66 	struct btf *btf;
67 	LIBBPF_OPTS(bpf_test_run_opts, topts,
68 		.data_in = &pkt_v6,
69 		.data_size_in = sizeof(pkt_v6),
70 		.repeat = 1,
71 	);
72 
73 	err = bpf_prog_test_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
74 			    &tgt_obj, &tgt_fd);
75 	if (!ASSERT_OK(err, "tgt_prog_load"))
76 		return;
77 
78 	info_len = sizeof(prog_info);
79 	err = bpf_obj_get_info_by_fd(tgt_fd, &prog_info, &info_len);
80 	if (!ASSERT_OK(err, "tgt_fd_get_info"))
81 		goto close_prog;
82 
83 	tgt_prog_id = prog_info.id;
84 	btf = bpf_object__btf(tgt_obj);
85 
86 	link = calloc(sizeof(struct bpf_link *), prog_cnt);
87 	if (!ASSERT_OK_PTR(link, "link_ptr"))
88 		goto close_prog;
89 
90 	prog = calloc(sizeof(struct bpf_program *), prog_cnt);
91 	if (!ASSERT_OK_PTR(prog, "prog_ptr"))
92 		goto close_prog;
93 
94 	obj = bpf_object__open_file(obj_file, NULL);
95 	if (!ASSERT_OK_PTR(obj, "obj_open"))
96 		goto close_prog;
97 
98 	bpf_object__for_each_program(p, obj) {
99 		err = bpf_program__set_attach_target(p, tgt_fd, NULL);
100 		ASSERT_OK(err, "set_attach_target");
101 	}
102 
103 	err = bpf_object__load(obj);
104 	if (!ASSERT_OK(err, "obj_load"))
105 		goto close_prog;
106 
107 	for (i = 0; i < prog_cnt; i++) {
108 		struct bpf_link_info link_info;
109 		struct bpf_program *pos;
110 		const char *pos_sec_name;
111 		char *tgt_name;
112 		__s32 btf_id;
113 
114 		tgt_name = strstr(prog_name[i], "/");
115 		if (!ASSERT_OK_PTR(tgt_name, "tgt_name"))
116 			goto close_prog;
117 		btf_id = btf__find_by_name_kind(btf, tgt_name + 1, BTF_KIND_FUNC);
118 
119 		prog[i] = NULL;
120 		bpf_object__for_each_program(pos, obj) {
121 			pos_sec_name = bpf_program__section_name(pos);
122 			if (pos_sec_name && !strcmp(pos_sec_name, prog_name[i])) {
123 				prog[i] = pos;
124 				break;
125 			}
126 		}
127 		if (!ASSERT_OK_PTR(prog[i], prog_name[i]))
128 			goto close_prog;
129 
130 		link[i] = bpf_program__attach_trace(prog[i]);
131 		if (!ASSERT_OK_PTR(link[i], "attach_trace"))
132 			goto close_prog;
133 
134 		info_len = sizeof(link_info);
135 		memset(&link_info, 0, sizeof(link_info));
136 		err = bpf_obj_get_info_by_fd(bpf_link__fd(link[i]),
137 					     &link_info, &info_len);
138 		ASSERT_OK(err, "link_fd_get_info");
139 		ASSERT_EQ(link_info.tracing.attach_type,
140 			  bpf_program__expected_attach_type(prog[i]),
141 			  "link_attach_type");
142 		ASSERT_EQ(link_info.tracing.target_obj_id, tgt_prog_id, "link_tgt_obj_id");
143 		ASSERT_EQ(link_info.tracing.target_btf_id, btf_id, "link_tgt_btf_id");
144 	}
145 
146 	if (cb) {
147 		err = cb(obj);
148 		if (err)
149 			goto close_prog;
150 	}
151 
152 	if (!run_prog)
153 		goto close_prog;
154 
155 	err = bpf_prog_test_run_opts(tgt_fd, &topts);
156 	ASSERT_OK(err, "prog_run");
157 	ASSERT_EQ(topts.retval, 0, "prog_run_ret");
158 
159 	if (check_data_map(obj, prog_cnt, false))
160 		goto close_prog;
161 
162 close_prog:
163 	for (i = 0; i < prog_cnt; i++)
164 		bpf_link__destroy(link[i]);
165 	bpf_object__close(obj);
166 	bpf_object__close(tgt_obj);
167 	free(link);
168 	free(prog);
169 }
170 
171 static void test_target_no_callees(void)
172 {
173 	const char *prog_name[] = {
174 		"fexit/test_pkt_md_access",
175 	};
176 	test_fexit_bpf2bpf_common("./fexit_bpf2bpf_simple.o",
177 				  "./test_pkt_md_access.o",
178 				  ARRAY_SIZE(prog_name),
179 				  prog_name, true, NULL);
180 }
181 
182 static void test_target_yes_callees(void)
183 {
184 	const char *prog_name[] = {
185 		"fexit/test_pkt_access",
186 		"fexit/test_pkt_access_subprog1",
187 		"fexit/test_pkt_access_subprog2",
188 		"fexit/test_pkt_access_subprog3",
189 	};
190 	test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o",
191 				  "./test_pkt_access.o",
192 				  ARRAY_SIZE(prog_name),
193 				  prog_name, true, NULL);
194 }
195 
196 static void test_func_replace(void)
197 {
198 	const char *prog_name[] = {
199 		"fexit/test_pkt_access",
200 		"fexit/test_pkt_access_subprog1",
201 		"fexit/test_pkt_access_subprog2",
202 		"fexit/test_pkt_access_subprog3",
203 		"freplace/get_skb_len",
204 		"freplace/get_skb_ifindex",
205 		"freplace/get_constant",
206 		"freplace/test_pkt_write_access_subprog",
207 	};
208 	test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o",
209 				  "./test_pkt_access.o",
210 				  ARRAY_SIZE(prog_name),
211 				  prog_name, true, NULL);
212 }
213 
214 static void test_func_replace_verify(void)
215 {
216 	const char *prog_name[] = {
217 		"freplace/do_bind",
218 	};
219 	test_fexit_bpf2bpf_common("./freplace_connect4.o",
220 				  "./connect4_prog.o",
221 				  ARRAY_SIZE(prog_name),
222 				  prog_name, false, NULL);
223 }
224 
225 static int test_second_attach(struct bpf_object *obj)
226 {
227 	const char *prog_name = "security_new_get_constant";
228 	const char *tgt_name = "get_constant";
229 	const char *tgt_obj_file = "./test_pkt_access.o";
230 	struct bpf_program *prog = NULL;
231 	struct bpf_object *tgt_obj;
232 	struct bpf_link *link;
233 	int err = 0, tgt_fd;
234 	LIBBPF_OPTS(bpf_test_run_opts, topts,
235 		.data_in = &pkt_v6,
236 		.data_size_in = sizeof(pkt_v6),
237 		.repeat = 1,
238 	);
239 
240 	prog = bpf_object__find_program_by_name(obj, prog_name);
241 	if (!ASSERT_OK_PTR(prog, "find_prog"))
242 		return -ENOENT;
243 
244 	err = bpf_prog_test_load(tgt_obj_file, BPF_PROG_TYPE_UNSPEC,
245 			    &tgt_obj, &tgt_fd);
246 	if (!ASSERT_OK(err, "second_prog_load"))
247 		return err;
248 
249 	link = bpf_program__attach_freplace(prog, tgt_fd, tgt_name);
250 	if (!ASSERT_OK_PTR(link, "second_link"))
251 		goto out;
252 
253 	err = bpf_prog_test_run_opts(tgt_fd, &topts);
254 	if (!ASSERT_OK(err, "ipv6 test_run"))
255 		goto out;
256 	if (!ASSERT_OK(topts.retval, "ipv6 retval"))
257 		goto out;
258 
259 	err = check_data_map(obj, 1, true);
260 	if (err)
261 		goto out;
262 
263 out:
264 	bpf_link__destroy(link);
265 	bpf_object__close(tgt_obj);
266 	return err;
267 }
268 
269 static void test_func_replace_multi(void)
270 {
271 	const char *prog_name[] = {
272 		"freplace/get_constant",
273 	};
274 	test_fexit_bpf2bpf_common("./freplace_get_constant.o",
275 				  "./test_pkt_access.o",
276 				  ARRAY_SIZE(prog_name),
277 				  prog_name, true, test_second_attach);
278 }
279 
280 static void test_fmod_ret_freplace(void)
281 {
282 	struct bpf_object *freplace_obj = NULL, *pkt_obj, *fmod_obj = NULL;
283 	const char *freplace_name = "./freplace_get_constant.o";
284 	const char *fmod_ret_name = "./fmod_ret_freplace.o";
285 	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
286 	const char *tgt_name = "./test_pkt_access.o";
287 	struct bpf_link *freplace_link = NULL;
288 	struct bpf_program *prog;
289 	__u32 duration = 0;
290 	int err, pkt_fd, attach_prog_fd;
291 
292 	err = bpf_prog_test_load(tgt_name, BPF_PROG_TYPE_UNSPEC,
293 			    &pkt_obj, &pkt_fd);
294 	/* the target prog should load fine */
295 	if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n",
296 		  tgt_name, err, errno))
297 		return;
298 
299 	freplace_obj = bpf_object__open_file(freplace_name, NULL);
300 	if (!ASSERT_OK_PTR(freplace_obj, "freplace_obj_open"))
301 		goto out;
302 
303 	prog = bpf_object__next_program(freplace_obj, NULL);
304 	err = bpf_program__set_attach_target(prog, pkt_fd, NULL);
305 	ASSERT_OK(err, "freplace__set_attach_target");
306 
307 	err = bpf_object__load(freplace_obj);
308 	if (CHECK(err, "freplace_obj_load", "err %d\n", err))
309 		goto out;
310 
311 	freplace_link = bpf_program__attach_trace(prog);
312 	if (!ASSERT_OK_PTR(freplace_link, "freplace_attach_trace"))
313 		goto out;
314 
315 	fmod_obj = bpf_object__open_file(fmod_ret_name, NULL);
316 	if (!ASSERT_OK_PTR(fmod_obj, "fmod_obj_open"))
317 		goto out;
318 
319 	attach_prog_fd = bpf_program__fd(prog);
320 	prog = bpf_object__next_program(fmod_obj, NULL);
321 	err = bpf_program__set_attach_target(prog, attach_prog_fd, NULL);
322 	ASSERT_OK(err, "fmod_ret_set_attach_target");
323 
324 	err = bpf_object__load(fmod_obj);
325 	if (CHECK(!err, "fmod_obj_load", "loading fmod_ret should fail\n"))
326 		goto out;
327 
328 out:
329 	bpf_link__destroy(freplace_link);
330 	bpf_object__close(freplace_obj);
331 	bpf_object__close(fmod_obj);
332 	bpf_object__close(pkt_obj);
333 }
334 
335 
336 static void test_func_sockmap_update(void)
337 {
338 	const char *prog_name[] = {
339 		"freplace/cls_redirect",
340 	};
341 	test_fexit_bpf2bpf_common("./freplace_cls_redirect.o",
342 				  "./test_cls_redirect.o",
343 				  ARRAY_SIZE(prog_name),
344 				  prog_name, false, NULL);
345 }
346 
347 static void test_obj_load_failure_common(const char *obj_file,
348 					 const char *target_obj_file)
349 {
350 	/*
351 	 * standalone test that asserts failure to load freplace prog
352 	 * because of invalid return code.
353 	 */
354 	struct bpf_object *obj = NULL, *pkt_obj;
355 	struct bpf_program *prog;
356 	int err, pkt_fd;
357 	__u32 duration = 0;
358 
359 	err = bpf_prog_test_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
360 			    &pkt_obj, &pkt_fd);
361 	/* the target prog should load fine */
362 	if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n",
363 		  target_obj_file, err, errno))
364 		return;
365 
366 	obj = bpf_object__open_file(obj_file, NULL);
367 	if (!ASSERT_OK_PTR(obj, "obj_open"))
368 		goto close_prog;
369 
370 	prog = bpf_object__next_program(obj, NULL);
371 	err = bpf_program__set_attach_target(prog, pkt_fd, NULL);
372 	ASSERT_OK(err, "set_attach_target");
373 
374 	/* It should fail to load the program */
375 	err = bpf_object__load(obj);
376 	if (CHECK(!err, "bpf_obj_load should fail", "err %d\n", err))
377 		goto close_prog;
378 
379 close_prog:
380 	bpf_object__close(obj);
381 	bpf_object__close(pkt_obj);
382 }
383 
384 static void test_func_replace_return_code(void)
385 {
386 	/* test invalid return code in the replaced program */
387 	test_obj_load_failure_common("./freplace_connect_v4_prog.o",
388 				     "./connect4_prog.o");
389 }
390 
391 static void test_func_map_prog_compatibility(void)
392 {
393 	/* test with spin lock map value in the replaced program */
394 	test_obj_load_failure_common("./freplace_attach_probe.o",
395 				     "./test_attach_probe.o");
396 }
397 
398 static void test_func_replace_global_func(void)
399 {
400 	const char *prog_name[] = {
401 		"freplace/test_pkt_access",
402 	};
403 
404 	test_fexit_bpf2bpf_common("./freplace_global_func.o",
405 				  "./test_pkt_access.o",
406 				  ARRAY_SIZE(prog_name),
407 				  prog_name, false, NULL);
408 }
409 
410 /* NOTE: affect other tests, must run in serial mode */
411 void serial_test_fexit_bpf2bpf(void)
412 {
413 	if (test__start_subtest("target_no_callees"))
414 		test_target_no_callees();
415 	if (test__start_subtest("target_yes_callees"))
416 		test_target_yes_callees();
417 	if (test__start_subtest("func_replace"))
418 		test_func_replace();
419 	if (test__start_subtest("func_replace_verify"))
420 		test_func_replace_verify();
421 	if (test__start_subtest("func_sockmap_update"))
422 		test_func_sockmap_update();
423 	if (test__start_subtest("func_replace_return_code"))
424 		test_func_replace_return_code();
425 	if (test__start_subtest("func_map_prog_compatibility"))
426 		test_func_map_prog_compatibility();
427 	if (test__start_subtest("func_replace_multi"))
428 		test_func_replace_multi();
429 	if (test__start_subtest("fmod_ret_freplace"))
430 		test_fmod_ret_freplace();
431 	if (test__start_subtest("func_replace_global_func"))
432 		test_func_replace_global_func();
433 }
434