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 }
363