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
attach_tc_prog(struct bpf_tc_hook * hook,int igr_fd,int egr_fd)72 static 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
cleanup(void)105 static 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
config_underlay(void)112 static 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;
133 fail:
134 return -1;
135 }
136
setup_xfrm_tunnel_ns(const char * ns,const char * ipv4_local,const char * ipv4_remote,int if_id)137 static 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;
159 fail:
160 return -1;
161 }
162
setup_xfrm_tunnel(const char * ns_a,const char * ns_b,const char * ipv4_a,const char * ipv4_b,int if_id_a,int if_id_b)163 static 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
rtattr_add(struct nlmsghdr * nh,unsigned short type,unsigned short len)171 static 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
rtattr_add_str(struct nlmsghdr * nh,unsigned short type,const char * s)182 static 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
rtattr_begin(struct nlmsghdr * nh,unsigned short type)191 static struct rtattr *rtattr_begin(struct nlmsghdr *nh, unsigned short type)
192 {
193 return rtattr_add(nh, type, 0);
194 }
195
rtattr_end(struct nlmsghdr * nh,struct rtattr * attr)196 static 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
setup_xfrmi_external_dev(const char * ns)203 static 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;
241 done:
242 if (sock != -1)
243 close(sock);
244 if (nstoken)
245 close_netns(nstoken);
246 return ret;
247 }
248
config_overlay(void)249 static 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;
274 fail:
275 return -1;
276 }
277
test_xfrm_ping(struct xfrm_info * skel,u32 if_id)278 static 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;
288 fail:
289 return -1;
290 }
291
_test_xfrm_info(void)292 static 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
327 done:
328 if (nstoken)
329 close_netns(nstoken);
330 xfrm_info__destroy(skel);
331 }
332
test_xfrm_info(void)333 void 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
345 done:
346 cleanup();
347 }
348