Skip to content

Commit 90a3a05

Browse files
ebirgerMartin KaFai Lau
authored andcommitted
selftests/bpf: add xfrm_info tests
Test the xfrm_info kfunc helpers. The test setup creates three name spaces - NS0, NS1, NS2. XFRM tunnels are setup between NS0 and the two other NSs. The kfunc helpers are used to steer traffic from NS0 to the other NSs based on a userspace populated bpf global variable and validate that the return traffic had arrived from the desired NS. Signed-off-by: Eyal Birger <eyal.birger@gmail.com> Link: https://lore.kernel.org/r/20221203084659.1837829-5-eyal.birger@gmail.com Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
1 parent 4f4ac4d commit 90a3a05

File tree

5 files changed

+403
-0
lines changed

5 files changed

+403
-0
lines changed

tools/testing/selftests/bpf/DENYLIST.s390x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,4 @@ xdp_bonding # failed to auto-attach program 'trace_
8585
xdp_bpf2bpf # failed to auto-attach program 'trace_on_entry': -524 (trampoline)
8686
xdp_do_redirect # prog_run_max_size unexpected error: -22 (errno 22)
8787
xdp_synproxy # JIT does not support calling kernel function (kfunc)
88+
xfrm_info # JIT does not support calling kernel function (kfunc)

tools/testing/selftests/bpf/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ CONFIG_IKCONFIG_PROC=y
2323
CONFIG_IMA=y
2424
CONFIG_IMA_READ_POLICY=y
2525
CONFIG_IMA_WRITE_POLICY=y
26+
CONFIG_INET_ESP=y
2627
CONFIG_IP_NF_FILTER=y
2728
CONFIG_IP_NF_RAW=y
2829
CONFIG_IP_NF_TARGET_SYNPROXY=y
@@ -74,3 +75,4 @@ CONFIG_TEST_BPF=y
7475
CONFIG_USERFAULTFD=y
7576
CONFIG_VXLAN=y
7677
CONFIG_XDP_SOCKETS=y
78+
CONFIG_XFRM_INTERFACE=y
Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2+
3+
/*
4+
* Topology:
5+
* ---------
6+
* NS0 namespace | NS1 namespace | NS2 namespace
7+
* | |
8+
* +---------------+ | +---------------+ |
9+
* | ipsec0 |---------| ipsec0 | |
10+
* | 192.168.1.100 | | | 192.168.1.200 | |
11+
* | if_id: bpf | | +---------------+ |
12+
* +---------------+ | |
13+
* | | | +---------------+
14+
* | | | | ipsec0 |
15+
* \------------------------------------------| 192.168.1.200 |
16+
* | | +---------------+
17+
* | |
18+
* | | (overlay network)
19+
* ------------------------------------------------------
20+
* | | (underlay network)
21+
* +--------------+ | +--------------+ |
22+
* | veth01 |----------| veth10 | |
23+
* | 172.16.1.100 | | | 172.16.1.200 | |
24+
* ---------------+ | +--------------+ |
25+
* | |
26+
* +--------------+ | | +--------------+
27+
* | veth02 |-----------------------------------| veth20 |
28+
* | 172.16.2.100 | | | | 172.16.2.200 |
29+
* +--------------+ | | +--------------+
30+
*
31+
*
32+
* Test Packet flow
33+
* -----------
34+
* The tests perform 'ping 192.168.1.200' from the NS0 namespace:
35+
* 1) request is routed to NS0 ipsec0
36+
* 2) NS0 ipsec0 tc egress BPF program is triggered and sets the if_id based
37+
* on the requested value. This makes the ipsec0 device in external mode
38+
* select the destination tunnel
39+
* 3) ping reaches the other namespace (NS1 or NS2 based on which if_id was
40+
* used) and response is sent
41+
* 4) response is received on NS0 ipsec0, tc ingress program is triggered and
42+
* records the response if_id
43+
* 5) requested if_id is compared with received if_id
44+
*/
45+
46+
#include <net/if.h>
47+
#include <linux/rtnetlink.h>
48+
#include <linux/if_link.h>
49+
50+
#include "test_progs.h"
51+
#include "network_helpers.h"
52+
#include "xfrm_info.skel.h"
53+
54+
#define NS0 "xfrm_test_ns0"
55+
#define NS1 "xfrm_test_ns1"
56+
#define NS2 "xfrm_test_ns2"
57+
58+
#define IF_ID_0_TO_1 1
59+
#define IF_ID_0_TO_2 2
60+
#define IF_ID_1 3
61+
#define IF_ID_2 4
62+
63+
#define IP4_ADDR_VETH01 "172.16.1.100"
64+
#define IP4_ADDR_VETH10 "172.16.1.200"
65+
#define IP4_ADDR_VETH02 "172.16.2.100"
66+
#define IP4_ADDR_VETH20 "172.16.2.200"
67+
68+
#define ESP_DUMMY_PARAMS \
69+
"proto esp aead 'rfc4106(gcm(aes))' " \
70+
"0xe4d8f4b4da1df18a3510b3781496daa82488b713 128 mode tunnel "
71+
72+
#define SYS(fmt, ...) \
73+
({ \
74+
char cmd[1024]; \
75+
snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
76+
if (!ASSERT_OK(system(cmd), cmd)) \
77+
goto fail; \
78+
})
79+
80+
#define SYS_NOFAIL(fmt, ...) \
81+
({ \
82+
char cmd[1024]; \
83+
snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
84+
system(cmd); \
85+
})
86+
87+
static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd)
88+
{
89+
LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1, .priority = 1,
90+
.prog_fd = igr_fd);
91+
LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1, .priority = 1,
92+
.prog_fd = egr_fd);
93+
int ret;
94+
95+
ret = bpf_tc_hook_create(hook);
96+
if (!ASSERT_OK(ret, "create tc hook"))
97+
return ret;
98+
99+
if (igr_fd >= 0) {
100+
hook->attach_point = BPF_TC_INGRESS;
101+
ret = bpf_tc_attach(hook, &opts1);
102+
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
103+
bpf_tc_hook_destroy(hook);
104+
return ret;
105+
}
106+
}
107+
108+
if (egr_fd >= 0) {
109+
hook->attach_point = BPF_TC_EGRESS;
110+
ret = bpf_tc_attach(hook, &opts2);
111+
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
112+
bpf_tc_hook_destroy(hook);
113+
return ret;
114+
}
115+
}
116+
117+
return 0;
118+
}
119+
120+
static void cleanup(void)
121+
{
122+
SYS_NOFAIL("test -f /var/run/netns/" NS0 " && ip netns delete " NS0);
123+
SYS_NOFAIL("test -f /var/run/netns/" NS1 " && ip netns delete " NS1);
124+
SYS_NOFAIL("test -f /var/run/netns/" NS2 " && ip netns delete " NS2);
125+
}
126+
127+
static int config_underlay(void)
128+
{
129+
SYS("ip netns add " NS0);
130+
SYS("ip netns add " NS1);
131+
SYS("ip netns add " NS2);
132+
133+
/* NS0 <-> NS1 [veth01 <-> veth10] */
134+
SYS("ip link add veth01 netns " NS0 " type veth peer name veth10 netns " NS1);
135+
SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01");
136+
SYS("ip -net " NS0 " link set dev veth01 up");
137+
SYS("ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10");
138+
SYS("ip -net " NS1 " link set dev veth10 up");
139+
140+
/* NS0 <-> NS2 [veth02 <-> veth20] */
141+
SYS("ip link add veth02 netns " NS0 " type veth peer name veth20 netns " NS2);
142+
SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02");
143+
SYS("ip -net " NS0 " link set dev veth02 up");
144+
SYS("ip -net " NS2 " addr add " IP4_ADDR_VETH20 "/24 dev veth20");
145+
SYS("ip -net " NS2 " link set dev veth20 up");
146+
147+
return 0;
148+
fail:
149+
return -1;
150+
}
151+
152+
static int setup_xfrm_tunnel_ns(const char *ns, const char *ipv4_local,
153+
const char *ipv4_remote, int if_id)
154+
{
155+
/* State: local -> remote */
156+
SYS("ip -net %s xfrm state add src %s dst %s spi 1 "
157+
ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_local, ipv4_remote, if_id);
158+
159+
/* State: local <- remote */
160+
SYS("ip -net %s xfrm state add src %s dst %s spi 1 "
161+
ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_remote, ipv4_local, if_id);
162+
163+
/* Policy: local -> remote */
164+
SYS("ip -net %s xfrm policy add dir out src 0.0.0.0/0 dst 0.0.0.0/0 "
165+
"if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns,
166+
if_id, ipv4_local, ipv4_remote, if_id);
167+
168+
/* Policy: local <- remote */
169+
SYS("ip -net %s xfrm policy add dir in src 0.0.0.0/0 dst 0.0.0.0/0 "
170+
"if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns,
171+
if_id, ipv4_remote, ipv4_local, if_id);
172+
173+
return 0;
174+
fail:
175+
return -1;
176+
}
177+
178+
static int setup_xfrm_tunnel(const char *ns_a, const char *ns_b,
179+
const char *ipv4_a, const char *ipv4_b,
180+
int if_id_a, int if_id_b)
181+
{
182+
return setup_xfrm_tunnel_ns(ns_a, ipv4_a, ipv4_b, if_id_a) ||
183+
setup_xfrm_tunnel_ns(ns_b, ipv4_b, ipv4_a, if_id_b);
184+
}
185+
186+
static struct rtattr *rtattr_add(struct nlmsghdr *nh, unsigned short type,
187+
unsigned short len)
188+
{
189+
struct rtattr *rta =
190+
(struct rtattr *)((uint8_t *)nh + RTA_ALIGN(nh->nlmsg_len));
191+
rta->rta_type = type;
192+
rta->rta_len = RTA_LENGTH(len);
193+
nh->nlmsg_len = RTA_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len);
194+
return rta;
195+
}
196+
197+
static struct rtattr *rtattr_add_str(struct nlmsghdr *nh, unsigned short type,
198+
const char *s)
199+
{
200+
struct rtattr *rta = rtattr_add(nh, type, strlen(s));
201+
202+
memcpy(RTA_DATA(rta), s, strlen(s));
203+
return rta;
204+
}
205+
206+
static struct rtattr *rtattr_begin(struct nlmsghdr *nh, unsigned short type)
207+
{
208+
return rtattr_add(nh, type, 0);
209+
}
210+
211+
static void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr)
212+
{
213+
uint8_t *end = (uint8_t *)nh + nh->nlmsg_len;
214+
215+
attr->rta_len = end - (uint8_t *)attr;
216+
}
217+
218+
static int setup_xfrmi_external_dev(const char *ns)
219+
{
220+
struct {
221+
struct nlmsghdr nh;
222+
struct ifinfomsg info;
223+
unsigned char data[128];
224+
} req;
225+
struct rtattr *link_info, *info_data;
226+
struct nstoken *nstoken;
227+
int ret = -1, sock = -1;
228+
struct nlmsghdr *nh;
229+
230+
memset(&req, 0, sizeof(req));
231+
nh = &req.nh;
232+
nh->nlmsg_len = NLMSG_LENGTH(sizeof(req.info));
233+
nh->nlmsg_type = RTM_NEWLINK;
234+
nh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REQUEST;
235+
236+
rtattr_add_str(nh, IFLA_IFNAME, "ipsec0");
237+
link_info = rtattr_begin(nh, IFLA_LINKINFO);
238+
rtattr_add_str(nh, IFLA_INFO_KIND, "xfrm");
239+
info_data = rtattr_begin(nh, IFLA_INFO_DATA);
240+
rtattr_add(nh, IFLA_XFRM_COLLECT_METADATA, 0);
241+
rtattr_end(nh, info_data);
242+
rtattr_end(nh, link_info);
243+
244+
nstoken = open_netns(ns);
245+
if (!ASSERT_OK_PTR(nstoken, "setns"))
246+
goto done;
247+
248+
sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
249+
if (!ASSERT_GE(sock, 0, "netlink socket"))
250+
goto done;
251+
ret = send(sock, nh, nh->nlmsg_len, 0);
252+
if (!ASSERT_EQ(ret, nh->nlmsg_len, "netlink send length"))
253+
goto done;
254+
255+
ret = 0;
256+
done:
257+
if (sock != -1)
258+
close(sock);
259+
if (nstoken)
260+
close_netns(nstoken);
261+
return ret;
262+
}
263+
264+
static int config_overlay(void)
265+
{
266+
if (setup_xfrm_tunnel(NS0, NS1, IP4_ADDR_VETH01, IP4_ADDR_VETH10,
267+
IF_ID_0_TO_1, IF_ID_1))
268+
goto fail;
269+
if (setup_xfrm_tunnel(NS0, NS2, IP4_ADDR_VETH02, IP4_ADDR_VETH20,
270+
IF_ID_0_TO_2, IF_ID_2))
271+
goto fail;
272+
273+
/* Older iproute2 doesn't support this option */
274+
if (!ASSERT_OK(setup_xfrmi_external_dev(NS0), "xfrmi"))
275+
goto fail;
276+
277+
SYS("ip -net " NS0 " addr add 192.168.1.100/24 dev ipsec0");
278+
SYS("ip -net " NS0 " link set dev ipsec0 up");
279+
280+
SYS("ip -net " NS1 " link add ipsec0 type xfrm if_id %d", IF_ID_1);
281+
SYS("ip -net " NS1 " addr add 192.168.1.200/24 dev ipsec0");
282+
SYS("ip -net " NS1 " link set dev ipsec0 up");
283+
284+
SYS("ip -net " NS2 " link add ipsec0 type xfrm if_id %d", IF_ID_2);
285+
SYS("ip -net " NS2 " addr add 192.168.1.200/24 dev ipsec0");
286+
SYS("ip -net " NS2 " link set dev ipsec0 up");
287+
288+
return 0;
289+
fail:
290+
return -1;
291+
}
292+
293+
static int test_xfrm_ping(struct xfrm_info *skel, u32 if_id)
294+
{
295+
skel->bss->req_if_id = if_id;
296+
297+
SYS("ping -i 0.01 -c 3 -w 10 -q 192.168.1.200 > /dev/null");
298+
299+
if (!ASSERT_EQ(skel->bss->resp_if_id, if_id, "if_id"))
300+
goto fail;
301+
302+
return 0;
303+
fail:
304+
return -1;
305+
}
306+
307+
static void _test_xfrm_info(void)
308+
{
309+
LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS);
310+
int get_xfrm_info_prog_fd, set_xfrm_info_prog_fd;
311+
struct nstoken *nstoken = NULL;
312+
struct xfrm_info *skel;
313+
int ifindex;
314+
315+
/* load and attach bpf progs to ipsec dev tc hook point */
316+
skel = xfrm_info__open_and_load();
317+
if (!ASSERT_OK_PTR(skel, "xfrm_info__open_and_load"))
318+
goto done;
319+
nstoken = open_netns(NS0);
320+
if (!ASSERT_OK_PTR(nstoken, "setns " NS0))
321+
goto done;
322+
ifindex = if_nametoindex("ipsec0");
323+
if (!ASSERT_NEQ(ifindex, 0, "ipsec0 ifindex"))
324+
goto done;
325+
tc_hook.ifindex = ifindex;
326+
set_xfrm_info_prog_fd = bpf_program__fd(skel->progs.set_xfrm_info);
327+
get_xfrm_info_prog_fd = bpf_program__fd(skel->progs.get_xfrm_info);
328+
if (!ASSERT_GE(set_xfrm_info_prog_fd, 0, "bpf_program__fd"))
329+
goto done;
330+
if (!ASSERT_GE(get_xfrm_info_prog_fd, 0, "bpf_program__fd"))
331+
goto done;
332+
if (attach_tc_prog(&tc_hook, get_xfrm_info_prog_fd,
333+
set_xfrm_info_prog_fd))
334+
goto done;
335+
336+
/* perform test */
337+
if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_1), 0, "ping " NS1))
338+
goto done;
339+
if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_2), 0, "ping " NS2))
340+
goto done;
341+
342+
done:
343+
if (nstoken)
344+
close_netns(nstoken);
345+
xfrm_info__destroy(skel);
346+
}
347+
348+
void test_xfrm_info(void)
349+
{
350+
cleanup();
351+
352+
if (!ASSERT_OK(config_underlay(), "config_underlay"))
353+
goto done;
354+
if (!ASSERT_OK(config_overlay(), "config_overlay"))
355+
goto done;
356+
357+
if (test__start_subtest("xfrm_info"))
358+
_test_xfrm_info();
359+
360+
done:
361+
cleanup();
362+
}

tools/testing/selftests/bpf/progs/bpf_tracing_net.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#define IPV6_TCLASS 67
2626
#define IPV6_AUTOFLOWLABEL 70
2727

28+
#define TC_ACT_UNSPEC (-1)
29+
#define TC_ACT_SHOT 2
30+
2831
#define SOL_TCP 6
2932
#define TCP_NODELAY 1
3033
#define TCP_MAXSEG 2

0 commit comments

Comments
 (0)