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 serial_test_xdp_link(void)
10 {
11 	DECLARE_LIBBPF_OPTS(bpf_xdp_set_link_opts, opts, .old_fd = -1);
12 	struct test_xdp_link *skel1 = NULL, *skel2 = NULL;
13 	__u32 id1, id2, id0 = 0, prog_fd1, prog_fd2;
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_obj_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_obj_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_set_link_xdp_fd_opts(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_get_link_xdp_id(IFINDEX_LO, &id0, 0);
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_fd = prog_fd1;
59 		bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, XDP_FLAGS_REPLACE, &opts);
60 		goto cleanup;
61 	}
62 
63 	/* detach BPF program */
64 	opts.old_fd = prog_fd1;
65 	err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, 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_get_link_xdp_id(IFINDEX_LO, &id0, 0);
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_fd = prog_fd1;
82 	err = bpf_set_link_xdp_fd_opts(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_set_link_xdp_fd(IFINDEX_LO, prog_fd2, 0);
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_set_link_xdp_fd(IFINDEX_LO, -1, 0);
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_get_link_xdp_id(IFINDEX_LO, &id0, 0);
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_obj_get_info_by_fd(bpf_link__fd(link), &link_info, &link_info_len);
123 	if (!ASSERT_OK(err, "link_info"))
124 		goto cleanup;
125 
126 	ASSERT_EQ(link_info.type, BPF_LINK_TYPE_XDP, "link_type");
127 	ASSERT_EQ(link_info.prog_id, id1, "link_prog_id");
128 	ASSERT_EQ(link_info.xdp.ifindex, IFINDEX_LO, "link_ifindex");
129 
130 	/* updating program under active BPF link with different type fails */
131 	err = bpf_link__update_program(link, skel1->progs.tc_handler);
132 	if (!ASSERT_ERR(err, "link_upd_invalid"))
133 		goto cleanup;
134 
135 	err = bpf_link__detach(link);
136 	if (!ASSERT_OK(err, "link_detach"))
137 		goto cleanup;
138 
139 	memset(&link_info, 0, sizeof(link_info));
140 	err = bpf_obj_get_info_by_fd(bpf_link__fd(link), &link_info, &link_info_len);
141 
142 	ASSERT_OK(err, "link_info");
143 	ASSERT_EQ(link_info.prog_id, id1, "link_prog_id");
144 	/* ifindex should be zeroed out */
145 	ASSERT_EQ(link_info.xdp.ifindex, 0, "link_ifindex");
146 
147 cleanup:
148 	test_xdp_link__destroy(skel1);
149 	test_xdp_link__destroy(skel2);
150 }
151