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
72static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd)
73{
74	LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1, .priority = 1,
75		    .prog_fd = igr_fd);
76	LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1, .priority = 1,
77		    .prog_fd = egr_fd);
78	int ret;
79
80	ret = bpf_tc_hook_create(hook);
81	if (!ASSERT_OK(ret, "create tc hook"))
82		return ret;
83
84	if (igr_fd >= 0) {
85		hook->attach_point = BPF_TC_INGRESS;
86		ret = bpf_tc_attach(hook, &opts1);
87		if (!ASSERT_OK(ret, "bpf_tc_attach")) {
88			bpf_tc_hook_destroy(hook);
89			return ret;
90		}
91	}
92
93	if (egr_fd >= 0) {
94		hook->attach_point = BPF_TC_EGRESS;
95		ret = bpf_tc_attach(hook, &opts2);
96		if (!ASSERT_OK(ret, "bpf_tc_attach")) {
97			bpf_tc_hook_destroy(hook);
98			return ret;
99		}
100	}
101
102	return 0;
103}
104
105static void cleanup(void)
106{
107	SYS_NOFAIL("test -f /var/run/netns/" NS0 " && ip netns delete " NS0);
108	SYS_NOFAIL("test -f /var/run/netns/" NS1 " && ip netns delete " NS1);
109	SYS_NOFAIL("test -f /var/run/netns/" NS2 " && ip netns delete " NS2);
110}
111
112static int config_underlay(void)
113{
114	SYS(fail, "ip netns add " NS0);
115	SYS(fail, "ip netns add " NS1);
116	SYS(fail, "ip netns add " NS2);
117
118	/* NS0 <-> NS1 [veth01 <-> veth10] */
119	SYS(fail, "ip link add veth01 netns " NS0 " type veth peer name veth10 netns " NS1);
120	SYS(fail, "ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01");
121	SYS(fail, "ip -net " NS0 " link set dev veth01 up");
122	SYS(fail, "ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10");
123	SYS(fail, "ip -net " NS1 " link set dev veth10 up");
124
125	/* NS0 <-> NS2 [veth02 <-> veth20] */
126	SYS(fail, "ip link add veth02 netns " NS0 " type veth peer name veth20 netns " NS2);
127	SYS(fail, "ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02");
128	SYS(fail, "ip -net " NS0 " link set dev veth02 up");
129	SYS(fail, "ip -net " NS2 " addr add " IP4_ADDR_VETH20 "/24 dev veth20");
130	SYS(fail, "ip -net " NS2 " link set dev veth20 up");
131
132	return 0;
133fail:
134	return -1;
135}
136
137static int setup_xfrm_tunnel_ns(const char *ns, const char *ipv4_local,
138				const char *ipv4_remote, int if_id)
139{
140	/* State: local -> remote */
141	SYS(fail, "ip -net %s xfrm state add src %s dst %s spi 1 "
142	    ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_local, ipv4_remote, if_id);
143
144	/* State: local <- remote */
145	SYS(fail, "ip -net %s xfrm state add src %s dst %s spi 1 "
146	    ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_remote, ipv4_local, if_id);
147
148	/* Policy: local -> remote */
149	SYS(fail, "ip -net %s xfrm policy add dir out src 0.0.0.0/0 dst 0.0.0.0/0 "
150	    "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns,
151	    if_id, ipv4_local, ipv4_remote, if_id);
152
153	/* Policy: local <- remote */
154	SYS(fail, "ip -net %s xfrm policy add dir in src 0.0.0.0/0 dst 0.0.0.0/0 "
155	    "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns,
156	    if_id, ipv4_remote, ipv4_local, if_id);
157
158	return 0;
159fail:
160	return -1;
161}
162
163static int setup_xfrm_tunnel(const char *ns_a, const char *ns_b,
164			     const char *ipv4_a, const char *ipv4_b,
165			     int if_id_a, int if_id_b)
166{
167	return setup_xfrm_tunnel_ns(ns_a, ipv4_a, ipv4_b, if_id_a) ||
168		setup_xfrm_tunnel_ns(ns_b, ipv4_b, ipv4_a, if_id_b);
169}
170
171static struct rtattr *rtattr_add(struct nlmsghdr *nh, unsigned short type,
172				 unsigned short len)
173{
174	struct rtattr *rta =
175		(struct rtattr *)((uint8_t *)nh + RTA_ALIGN(nh->nlmsg_len));
176	rta->rta_type = type;
177	rta->rta_len = RTA_LENGTH(len);
178	nh->nlmsg_len = RTA_ALIGN(nh->nlmsg_len) + RTA_ALIGN(rta->rta_len);
179	return rta;
180}
181
182static struct rtattr *rtattr_add_str(struct nlmsghdr *nh, unsigned short type,
183				     const char *s)
184{
185	struct rtattr *rta = rtattr_add(nh, type, strlen(s));
186
187	memcpy(RTA_DATA(rta), s, strlen(s));
188	return rta;
189}
190
191static struct rtattr *rtattr_begin(struct nlmsghdr *nh, unsigned short type)
192{
193	return rtattr_add(nh, type, 0);
194}
195
196static void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr)
197{
198	uint8_t *end = (uint8_t *)nh + nh->nlmsg_len;
199
200	attr->rta_len = end - (uint8_t *)attr;
201}
202
203static int setup_xfrmi_external_dev(const char *ns)
204{
205	struct {
206		struct nlmsghdr nh;
207		struct ifinfomsg info;
208		unsigned char data[128];
209	} req;
210	struct rtattr *link_info, *info_data;
211	struct nstoken *nstoken;
212	int ret = -1, sock = -1;
213	struct nlmsghdr *nh;
214
215	memset(&req, 0, sizeof(req));
216	nh = &req.nh;
217	nh->nlmsg_len = NLMSG_LENGTH(sizeof(req.info));
218	nh->nlmsg_type = RTM_NEWLINK;
219	nh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REQUEST;
220
221	rtattr_add_str(nh, IFLA_IFNAME, "ipsec0");
222	link_info = rtattr_begin(nh, IFLA_LINKINFO);
223	rtattr_add_str(nh, IFLA_INFO_KIND, "xfrm");
224	info_data = rtattr_begin(nh, IFLA_INFO_DATA);
225	rtattr_add(nh, IFLA_XFRM_COLLECT_METADATA, 0);
226	rtattr_end(nh, info_data);
227	rtattr_end(nh, link_info);
228
229	nstoken = open_netns(ns);
230	if (!ASSERT_OK_PTR(nstoken, "setns"))
231		goto done;
232
233	sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
234	if (!ASSERT_GE(sock, 0, "netlink socket"))
235		goto done;
236	ret = send(sock, nh, nh->nlmsg_len, 0);
237	if (!ASSERT_EQ(ret, nh->nlmsg_len, "netlink send length"))
238		goto done;
239
240	ret = 0;
241done:
242	if (sock != -1)
243		close(sock);
244	if (nstoken)
245		close_netns(nstoken);
246	return ret;
247}
248
249static int config_overlay(void)
250{
251	if (setup_xfrm_tunnel(NS0, NS1, IP4_ADDR_VETH01, IP4_ADDR_VETH10,
252			      IF_ID_0_TO_1, IF_ID_1))
253		goto fail;
254	if (setup_xfrm_tunnel(NS0, NS2, IP4_ADDR_VETH02, IP4_ADDR_VETH20,
255			      IF_ID_0_TO_2, IF_ID_2))
256		goto fail;
257
258	/* Older iproute2 doesn't support this option */
259	if (!ASSERT_OK(setup_xfrmi_external_dev(NS0), "xfrmi"))
260		goto fail;
261
262	SYS(fail, "ip -net " NS0 " addr add 192.168.1.100/24 dev ipsec0");
263	SYS(fail, "ip -net " NS0 " link set dev ipsec0 up");
264
265	SYS(fail, "ip -net " NS1 " link add ipsec0 type xfrm if_id %d", IF_ID_1);
266	SYS(fail, "ip -net " NS1 " addr add 192.168.1.200/24 dev ipsec0");
267	SYS(fail, "ip -net " NS1 " link set dev ipsec0 up");
268
269	SYS(fail, "ip -net " NS2 " link add ipsec0 type xfrm if_id %d", IF_ID_2);
270	SYS(fail, "ip -net " NS2 " addr add 192.168.1.200/24 dev ipsec0");
271	SYS(fail, "ip -net " NS2 " link set dev ipsec0 up");
272
273	return 0;
274fail:
275	return -1;
276}
277
278static int test_xfrm_ping(struct xfrm_info *skel, u32 if_id)
279{
280	skel->bss->req_if_id = if_id;
281
282	SYS(fail, "ping -i 0.01 -c 3 -w 10 -q 192.168.1.200 > /dev/null");
283
284	if (!ASSERT_EQ(skel->bss->resp_if_id, if_id, "if_id"))
285		goto fail;
286
287	return 0;
288fail:
289	return -1;
290}
291
292static void _test_xfrm_info(void)
293{
294	LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS);
295	int get_xfrm_info_prog_fd, set_xfrm_info_prog_fd;
296	struct nstoken *nstoken = NULL;
297	struct xfrm_info *skel;
298	int ifindex;
299
300	/* load and attach bpf progs to ipsec dev tc hook point */
301	skel = xfrm_info__open_and_load();
302	if (!ASSERT_OK_PTR(skel, "xfrm_info__open_and_load"))
303		goto done;
304	nstoken = open_netns(NS0);
305	if (!ASSERT_OK_PTR(nstoken, "setns " NS0))
306		goto done;
307	ifindex = if_nametoindex("ipsec0");
308	if (!ASSERT_NEQ(ifindex, 0, "ipsec0 ifindex"))
309		goto done;
310	tc_hook.ifindex = ifindex;
311	set_xfrm_info_prog_fd = bpf_program__fd(skel->progs.set_xfrm_info);
312	get_xfrm_info_prog_fd = bpf_program__fd(skel->progs.get_xfrm_info);
313	if (!ASSERT_GE(set_xfrm_info_prog_fd, 0, "bpf_program__fd"))
314		goto done;
315	if (!ASSERT_GE(get_xfrm_info_prog_fd, 0, "bpf_program__fd"))
316		goto done;
317	if (attach_tc_prog(&tc_hook, get_xfrm_info_prog_fd,
318			   set_xfrm_info_prog_fd))
319		goto done;
320
321	/* perform test */
322	if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_1), 0, "ping " NS1))
323		goto done;
324	if (!ASSERT_EQ(test_xfrm_ping(skel, IF_ID_0_TO_2), 0, "ping " NS2))
325		goto done;
326
327done:
328	if (nstoken)
329		close_netns(nstoken);
330	xfrm_info__destroy(skel);
331}
332
333void test_xfrm_info(void)
334{
335	cleanup();
336
337	if (!ASSERT_OK(config_underlay(), "config_underlay"))
338		goto done;
339	if (!ASSERT_OK(config_overlay(), "config_overlay"))
340		goto done;
341
342	if (test__start_subtest("xfrm_info"))
343		_test_xfrm_info();
344
345done:
346	cleanup();
347}
348