Skip to content

Commit c06cd7b

Browse files
committed
libxdp: Add selftest for device binding
Add a selftest program for libxdp device binding, testing the different permutations of loading a program with and without the flag. Signed-off-by: Jalal Mostafa <[email protected]>
1 parent 7b465a9 commit c06cd7b

File tree

5 files changed

+300
-2
lines changed

5 files changed

+300
-2
lines changed

lib/libxdp/tests/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ test_dispatcher_versions
55
test_xsk_non_privileged
66
test_link_detach
77
test_xsk_umem_flags
8+
test_xdp_devbound

lib/libxdp/tests/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
22

3-
USER_TARGETS := test_xsk_refcnt check_kern_compat test_xdp_frags test_dispatcher_versions test_link_detach test_xsk_umem_flags
3+
USER_TARGETS := test_xsk_refcnt check_kern_compat test_xdp_devbound test_xdp_frags test_dispatcher_versions test_link_detach test_xsk_umem_flags
44
BPF_TARGETS := xdp_dispatcher_v1 xdp_pass
55
USER_LIBS := -lpthread
66

lib/libxdp/tests/test-libxdp.sh

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
22

3-
ALL_TESTS="test_link_so test_link_a test_old_dispatcher test_xdp_frags test_xsk_prog_refcnt_bpffs test_xsk_prog_refcnt_legacy test_xsk_non_privileged test_link_detach test_xsk_umem_flags"
3+
ALL_TESTS="test_link_so test_link_a test_old_dispatcher test_xdp_devbound test_xdp_frags test_xsk_prog_refcnt_bpffs test_xsk_prog_refcnt_legacy test_xsk_non_privileged test_link_detach test_xsk_umem_flags"
44

55
TESTS_DIR=$(dirname "${BASH_SOURCE[0]}")
66

@@ -81,6 +81,16 @@ test_xdp_frags()
8181
ip link delete xdp_veth_small0
8282
}
8383

84+
test_xdp_devbound()
85+
{
86+
skip_if_missing_libxdp_compat
87+
88+
check_mount_bpffs || return 1
89+
ip link add xdp_veth0 type veth peer name xdp_veth1
90+
check_run $TESTS_DIR/test_xdp_devbound xdp_veth1 2>&1
91+
ip link delete xdp_veth0
92+
}
93+
8494
test_old_dispatcher()
8595
{
8696
skip_if_missing_libxdp_compat

lib/libxdp/tests/test_xdp_devbound

-1.55 MB
Binary file not shown.
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#define _GNU_SOURCE
4+
5+
#include <errno.h>
6+
#include <linux/err.h>
7+
#include <net/if.h>
8+
#include <pthread.h>
9+
#include <stdbool.h>
10+
#include <stdlib.h>
11+
#include <string.h>
12+
#include <sys/resource.h>
13+
#include <unistd.h>
14+
15+
#include "test_utils.h"
16+
17+
#include <xdp/libxdp.h>
18+
#include <bpf/libbpf.h>
19+
20+
#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof((_x)[0]))
21+
22+
static bool kern_compat;
23+
24+
static struct xdp_program *load_prog(void)
25+
{
26+
DECLARE_LIBXDP_OPTS(xdp_program_opts, opts, .prog_name = "xdp_pass",
27+
.find_filename = "xdp-dispatcher.o", );
28+
return xdp_program__create(&opts);
29+
}
30+
31+
static int check_attached_progs(int ifindex, int count, bool devbound)
32+
{
33+
struct xdp_multiprog *mp;
34+
int ret;
35+
36+
/* If the kernel does not support device binding, we always expect
37+
* device binding support to be disabled on a returned dispatcher
38+
*/
39+
if (!kern_compat)
40+
devbound = false;
41+
42+
mp = xdp_multiprog__get_from_ifindex(ifindex);
43+
ret = libxdp_get_error(mp);
44+
if (ret) {
45+
fprintf(stderr, "Couldn't get multiprog on ifindex %d: %s\n",
46+
ifindex, strerror(-ret));
47+
return ret;
48+
}
49+
50+
ret = -EINVAL;
51+
52+
if (xdp_multiprog__is_legacy(mp)) {
53+
fprintf(stderr, "Found legacy prog on ifindex %d\n", ifindex);
54+
goto out;
55+
}
56+
57+
if (xdp_multiprog__program_count(mp) != count) {
58+
fprintf(stderr,
59+
"Expected %d programs loaded on ifindex %d, found %d\n",
60+
count, ifindex, xdp_multiprog__program_count(mp));
61+
goto out;
62+
}
63+
64+
if (xdp_multiprog__xdp_dev_bound(mp) != devbound) {
65+
fprintf(stderr,
66+
"Multiprog on ifindex %d %s device binding, expected %s\n",
67+
ifindex,
68+
xdp_multiprog__xdp_dev_bound(mp) ? "supports" :
69+
"does not support",
70+
devbound ? "support" : "no support");
71+
goto out;
72+
}
73+
74+
ret = 0;
75+
76+
out:
77+
xdp_multiprog__close(mp);
78+
return ret;
79+
}
80+
81+
static void print_test_result(const char *func, int ret)
82+
{
83+
fflush(stderr);
84+
fprintf(stderr, "%s:\t%s\n", func, ret ? "FAILED" : "PASSED");
85+
fflush(stdout);
86+
}
87+
88+
static int load_attach_prog(struct xdp_program **prog, int ifindex,
89+
bool devbound)
90+
{
91+
int ret;
92+
93+
*prog = load_prog();
94+
if (!*prog) {
95+
ret = -errno;
96+
fprintf(stderr, "Couldn't load program: %s\n", strerror(-ret));
97+
return ret;
98+
}
99+
100+
ret = xdp_program__set_xdp_dev_bound(*prog, devbound ? ifindex : 0);
101+
if (ret)
102+
return ret;
103+
104+
return xdp_program__attach(*prog, ifindex, XDP_MODE_NATIVE, 0);
105+
}
106+
107+
static int _check_load(int ifindex, bool devbound, bool should_succeed)
108+
{
109+
struct xdp_program *prog = NULL;
110+
bool attached;
111+
int ret;
112+
113+
ret = load_attach_prog(&prog, ifindex, devbound);
114+
attached = !ret;
115+
116+
if (attached != should_succeed) {
117+
ret = -EINVAL;
118+
goto out;
119+
}
120+
121+
if (should_succeed)
122+
ret = check_attached_progs(ifindex, 1, devbound);
123+
else
124+
ret = 0;
125+
126+
out:
127+
if (attached)
128+
xdp_program__detach(prog, ifindex, XDP_MODE_NATIVE, 0);
129+
xdp_program__close(prog);
130+
return ret;
131+
}
132+
133+
static int check_load_devbound(int ifindex)
134+
{
135+
int ret = _check_load(ifindex, true, true);
136+
print_test_result(__func__, ret);
137+
return ret;
138+
}
139+
140+
static int check_load_nodevbound_success(int ifindex)
141+
{
142+
int ret = _check_load(ifindex, false, true);
143+
print_test_result(__func__, ret);
144+
return ret;
145+
}
146+
147+
static int check_load_devbound_multi(int ifindex)
148+
{
149+
struct xdp_program *prog1 = NULL, *prog2 = NULL;
150+
int ret;
151+
152+
ret = load_attach_prog(&prog1, ifindex, true);
153+
if (ret)
154+
goto out;
155+
156+
ret = load_attach_prog(&prog2, ifindex, true);
157+
if (ret)
158+
goto out_prog1;
159+
160+
ret = check_attached_progs(ifindex, 2, true);
161+
162+
xdp_program__detach(prog2, ifindex, XDP_MODE_NATIVE, 0);
163+
out_prog1:
164+
xdp_program__detach(prog1, ifindex, XDP_MODE_NATIVE, 0);
165+
out:
166+
xdp_program__close(prog2);
167+
xdp_program__close(prog1);
168+
print_test_result(__func__, ret);
169+
return ret;
170+
}
171+
172+
static int check_load_mix(int ifindex)
173+
{
174+
struct xdp_program *prog1 = NULL, *prog2 = NULL;
175+
int ret;
176+
177+
ret = load_attach_prog(&prog1, ifindex, true);
178+
if (ret)
179+
goto out;
180+
181+
/* First program attached, dispatcher supports device binding */
182+
ret = check_attached_progs(ifindex, 1, true);
183+
if (ret)
184+
goto out;
185+
186+
ret = load_attach_prog(&prog2, ifindex, false);
187+
if (!ret) {
188+
xdp_program__detach(prog2, ifindex, XDP_MODE_NATIVE, 0);
189+
ret = -EINVAL;
190+
goto out_prog1;
191+
}
192+
193+
/* Still only a single program loaded, with device binding */
194+
ret = check_attached_progs(ifindex, 1, true);
195+
196+
out_prog1:
197+
xdp_program__detach(prog1, ifindex, XDP_MODE_NATIVE, 0);
198+
199+
out:
200+
xdp_program__close(prog2);
201+
xdp_program__close(prog1);
202+
print_test_result(__func__, ret);
203+
return ret;
204+
}
205+
206+
static bool check_devbound_compat(void)
207+
{
208+
struct xdp_program *test_prog;
209+
struct bpf_program *prog;
210+
struct bpf_object *obj;
211+
bool ret = false;
212+
int err;
213+
214+
test_prog = load_prog();
215+
if (!test_prog)
216+
return false;
217+
218+
obj = xdp_program__bpf_obj(test_prog);
219+
if (!obj)
220+
goto out;
221+
222+
prog = bpf_object__find_program_by_name(obj, "xdp_pass");
223+
if (!prog)
224+
goto out;
225+
226+
bpf_program__set_flags(prog, BPF_F_XDP_DEV_BOUND_ONLY);
227+
err = bpf_object__load(obj);
228+
if (!err) {
229+
printf("Kernel supports XDP programs with device binding\n");
230+
ret = true;
231+
} else {
232+
printf("Kernel DOES NOT support XDP programs with device binding\n");
233+
}
234+
fflush(stdout);
235+
236+
out:
237+
xdp_program__close(test_prog);
238+
return ret;
239+
}
240+
241+
static void usage(char *progname)
242+
{
243+
fprintf(stderr, "Usage: %s <ifname>\n", progname);
244+
exit(EXIT_FAILURE);
245+
}
246+
247+
int main(int argc, char **argv)
248+
{
249+
struct rlimit r = { RLIM_INFINITY, RLIM_INFINITY };
250+
int ifindex, ret = 0;
251+
252+
if (setrlimit(RLIMIT_MEMLOCK, &r)) {
253+
fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n",
254+
strerror(errno));
255+
exit(EXIT_FAILURE);
256+
}
257+
258+
char *envval;
259+
260+
envval = secure_getenv("VERBOSE_TESTS");
261+
262+
silence_libbpf_logging();
263+
if (envval && envval[0] == '1')
264+
verbose_libxdp_logging();
265+
else
266+
silence_libxdp_logging();
267+
268+
if (argc != 2)
269+
usage(argv[0]);
270+
271+
ifindex = if_nametoindex(argv[1]);
272+
if (!ifindex) {
273+
fprintf(stderr, "Interface '%s' not found.\n", argv[1]);
274+
usage(argv[0]);
275+
}
276+
277+
kern_compat = check_devbound_compat();
278+
279+
ret = check_load_devbound(kern_compat ? ifindex : 0);
280+
ret = check_load_nodevbound_success(ifindex) || ret;
281+
if (kern_compat) {
282+
ret = check_load_devbound_multi(ifindex) || ret;
283+
ret = check_load_mix(ifindex) || ret;
284+
}
285+
286+
return ret;
287+
}

0 commit comments

Comments
 (0)