1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
3 /*
4 * This test sets up 3 netns (src <-> fwd <-> dst). There is no direct veth link
5 * between src and dst. The netns fwd has veth links to each src and dst. The
6 * client is in src and server in dst. The test installs a TC BPF program to each
7 * host facing veth in fwd which calls into i) bpf_redirect_neigh() to perform the
8 * neigh addr population and redirect or ii) bpf_redirect_peer() for namespace
9 * switch from ingress side; it also installs a checker prog on the egress side
10 * to drop unexpected traffic.
11 */
12
13 #define _GNU_SOURCE
14
15 #include <arpa/inet.h>
16 #include <linux/if.h>
17 #include <linux/if_tun.h>
18 #include <linux/limits.h>
19 #include <linux/sysctl.h>
20 #include <sched.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <sys/mount.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26
27 #include "test_progs.h"
28 #include "network_helpers.h"
29 #include "test_tc_neigh_fib.skel.h"
30 #include "test_tc_neigh.skel.h"
31 #include "test_tc_peer.skel.h"
32
33 #define NS_SRC "ns_src"
34 #define NS_FWD "ns_fwd"
35 #define NS_DST "ns_dst"
36
37 #define IP4_SRC "172.16.1.100"
38 #define IP4_DST "172.16.2.100"
39 #define IP4_TUN_SRC "172.17.1.100"
40 #define IP4_TUN_FWD "172.17.1.200"
41 #define IP4_PORT 9004
42
43 #define IP6_SRC "0::1:dead:beef:cafe"
44 #define IP6_DST "0::2:dead:beef:cafe"
45 #define IP6_TUN_SRC "1::1:dead:beef:cafe"
46 #define IP6_TUN_FWD "1::2:dead:beef:cafe"
47 #define IP6_PORT 9006
48
49 #define IP4_SLL "169.254.0.1"
50 #define IP4_DLL "169.254.0.2"
51 #define IP4_NET "169.254.0.0"
52
53 #define MAC_DST_FWD "00:11:22:33:44:55"
54 #define MAC_DST "00:22:33:44:55:66"
55
56 #define IFADDR_STR_LEN 18
57 #define PING_ARGS "-i 0.2 -c 3 -w 10 -q"
58
59 #define SRC_PROG_PIN_FILE "/sys/fs/bpf/test_tc_src"
60 #define DST_PROG_PIN_FILE "/sys/fs/bpf/test_tc_dst"
61 #define CHK_PROG_PIN_FILE "/sys/fs/bpf/test_tc_chk"
62
63 #define TIMEOUT_MILLIS 10000
64
65 #define log_err(MSG, ...) \
66 fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \
67 __FILE__, __LINE__, strerror(errno), ##__VA_ARGS__)
68
69 static const char * const namespaces[] = {NS_SRC, NS_FWD, NS_DST, NULL};
70
write_file(const char * path,const char * newval)71 static int write_file(const char *path, const char *newval)
72 {
73 FILE *f;
74
75 f = fopen(path, "r+");
76 if (!f)
77 return -1;
78 if (fwrite(newval, strlen(newval), 1, f) != 1) {
79 log_err("writing to %s failed", path);
80 fclose(f);
81 return -1;
82 }
83 fclose(f);
84 return 0;
85 }
86
87 struct nstoken {
88 int orig_netns_fd;
89 };
90
setns_by_fd(int nsfd)91 static int setns_by_fd(int nsfd)
92 {
93 int err;
94
95 err = setns(nsfd, CLONE_NEWNET);
96 close(nsfd);
97
98 if (!ASSERT_OK(err, "setns"))
99 return err;
100
101 /* Switch /sys to the new namespace so that e.g. /sys/class/net
102 * reflects the devices in the new namespace.
103 */
104 err = unshare(CLONE_NEWNS);
105 if (!ASSERT_OK(err, "unshare"))
106 return err;
107
108 err = umount2("/sys", MNT_DETACH);
109 if (!ASSERT_OK(err, "umount2 /sys"))
110 return err;
111
112 err = mount("sysfs", "/sys", "sysfs", 0, NULL);
113 if (!ASSERT_OK(err, "mount /sys"))
114 return err;
115
116 err = mount("bpffs", "/sys/fs/bpf", "bpf", 0, NULL);
117 if (!ASSERT_OK(err, "mount /sys/fs/bpf"))
118 return err;
119
120 return 0;
121 }
122
123 /**
124 * open_netns() - Switch to specified network namespace by name.
125 *
126 * Returns token with which to restore the original namespace
127 * using close_netns().
128 */
open_netns(const char * name)129 static struct nstoken *open_netns(const char *name)
130 {
131 int nsfd;
132 char nspath[PATH_MAX];
133 int err;
134 struct nstoken *token;
135
136 token = malloc(sizeof(struct nstoken));
137 if (!ASSERT_OK_PTR(token, "malloc token"))
138 return NULL;
139
140 token->orig_netns_fd = open("/proc/self/ns/net", O_RDONLY);
141 if (!ASSERT_GE(token->orig_netns_fd, 0, "open /proc/self/ns/net"))
142 goto fail;
143
144 snprintf(nspath, sizeof(nspath), "%s/%s", "/var/run/netns", name);
145 nsfd = open(nspath, O_RDONLY | O_CLOEXEC);
146 if (!ASSERT_GE(nsfd, 0, "open netns fd"))
147 goto fail;
148
149 err = setns_by_fd(nsfd);
150 if (!ASSERT_OK(err, "setns_by_fd"))
151 goto fail;
152
153 return token;
154 fail:
155 free(token);
156 return NULL;
157 }
158
close_netns(struct nstoken * token)159 static void close_netns(struct nstoken *token)
160 {
161 ASSERT_OK(setns_by_fd(token->orig_netns_fd), "setns_by_fd");
162 free(token);
163 }
164
netns_setup_namespaces(const char * verb)165 static int netns_setup_namespaces(const char *verb)
166 {
167 const char * const *ns = namespaces;
168 char cmd[128];
169
170 while (*ns) {
171 snprintf(cmd, sizeof(cmd), "ip netns %s %s", verb, *ns);
172 if (!ASSERT_OK(system(cmd), cmd))
173 return -1;
174 ns++;
175 }
176 return 0;
177 }
178
179 struct netns_setup_result {
180 int ifindex_veth_src_fwd;
181 int ifindex_veth_dst_fwd;
182 };
183
get_ifaddr(const char * name,char * ifaddr)184 static int get_ifaddr(const char *name, char *ifaddr)
185 {
186 char path[PATH_MAX];
187 FILE *f;
188 int ret;
189
190 snprintf(path, PATH_MAX, "/sys/class/net/%s/address", name);
191 f = fopen(path, "r");
192 if (!ASSERT_OK_PTR(f, path))
193 return -1;
194
195 ret = fread(ifaddr, 1, IFADDR_STR_LEN, f);
196 if (!ASSERT_EQ(ret, IFADDR_STR_LEN, "fread ifaddr")) {
197 fclose(f);
198 return -1;
199 }
200 fclose(f);
201 return 0;
202 }
203
get_ifindex(const char * name)204 static int get_ifindex(const char *name)
205 {
206 char path[PATH_MAX];
207 char buf[32];
208 FILE *f;
209 int ret;
210
211 snprintf(path, PATH_MAX, "/sys/class/net/%s/ifindex", name);
212 f = fopen(path, "r");
213 if (!ASSERT_OK_PTR(f, path))
214 return -1;
215
216 ret = fread(buf, 1, sizeof(buf), f);
217 if (!ASSERT_GT(ret, 0, "fread ifindex")) {
218 fclose(f);
219 return -1;
220 }
221 fclose(f);
222 return atoi(buf);
223 }
224
225 #define SYS(fmt, ...) \
226 ({ \
227 char cmd[1024]; \
228 snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
229 if (!ASSERT_OK(system(cmd), cmd)) \
230 goto fail; \
231 })
232
netns_setup_links_and_routes(struct netns_setup_result * result)233 static int netns_setup_links_and_routes(struct netns_setup_result *result)
234 {
235 struct nstoken *nstoken = NULL;
236 char veth_src_fwd_addr[IFADDR_STR_LEN+1] = {};
237
238 SYS("ip link add veth_src type veth peer name veth_src_fwd");
239 SYS("ip link add veth_dst type veth peer name veth_dst_fwd");
240
241 SYS("ip link set veth_dst_fwd address " MAC_DST_FWD);
242 SYS("ip link set veth_dst address " MAC_DST);
243
244 if (get_ifaddr("veth_src_fwd", veth_src_fwd_addr))
245 goto fail;
246
247 result->ifindex_veth_src_fwd = get_ifindex("veth_src_fwd");
248 if (result->ifindex_veth_src_fwd < 0)
249 goto fail;
250 result->ifindex_veth_dst_fwd = get_ifindex("veth_dst_fwd");
251 if (result->ifindex_veth_dst_fwd < 0)
252 goto fail;
253
254 SYS("ip link set veth_src netns " NS_SRC);
255 SYS("ip link set veth_src_fwd netns " NS_FWD);
256 SYS("ip link set veth_dst_fwd netns " NS_FWD);
257 SYS("ip link set veth_dst netns " NS_DST);
258
259 /** setup in 'src' namespace */
260 nstoken = open_netns(NS_SRC);
261 if (!ASSERT_OK_PTR(nstoken, "setns src"))
262 goto fail;
263
264 SYS("ip addr add " IP4_SRC "/32 dev veth_src");
265 SYS("ip addr add " IP6_SRC "/128 dev veth_src nodad");
266 SYS("ip link set dev veth_src up");
267
268 SYS("ip route add " IP4_DST "/32 dev veth_src scope global");
269 SYS("ip route add " IP4_NET "/16 dev veth_src scope global");
270 SYS("ip route add " IP6_DST "/128 dev veth_src scope global");
271
272 SYS("ip neigh add " IP4_DST " dev veth_src lladdr %s",
273 veth_src_fwd_addr);
274 SYS("ip neigh add " IP6_DST " dev veth_src lladdr %s",
275 veth_src_fwd_addr);
276
277 close_netns(nstoken);
278
279 /** setup in 'fwd' namespace */
280 nstoken = open_netns(NS_FWD);
281 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
282 goto fail;
283
284 /* The fwd netns automatically gets a v6 LL address / routes, but also
285 * needs v4 one in order to start ARP probing. IP4_NET route is added
286 * to the endpoints so that the ARP processing will reply.
287 */
288 SYS("ip addr add " IP4_SLL "/32 dev veth_src_fwd");
289 SYS("ip addr add " IP4_DLL "/32 dev veth_dst_fwd");
290 SYS("ip link set dev veth_src_fwd up");
291 SYS("ip link set dev veth_dst_fwd up");
292
293 SYS("ip route add " IP4_SRC "/32 dev veth_src_fwd scope global");
294 SYS("ip route add " IP6_SRC "/128 dev veth_src_fwd scope global");
295 SYS("ip route add " IP4_DST "/32 dev veth_dst_fwd scope global");
296 SYS("ip route add " IP6_DST "/128 dev veth_dst_fwd scope global");
297
298 close_netns(nstoken);
299
300 /** setup in 'dst' namespace */
301 nstoken = open_netns(NS_DST);
302 if (!ASSERT_OK_PTR(nstoken, "setns dst"))
303 goto fail;
304
305 SYS("ip addr add " IP4_DST "/32 dev veth_dst");
306 SYS("ip addr add " IP6_DST "/128 dev veth_dst nodad");
307 SYS("ip link set dev veth_dst up");
308
309 SYS("ip route add " IP4_SRC "/32 dev veth_dst scope global");
310 SYS("ip route add " IP4_NET "/16 dev veth_dst scope global");
311 SYS("ip route add " IP6_SRC "/128 dev veth_dst scope global");
312
313 SYS("ip neigh add " IP4_SRC " dev veth_dst lladdr " MAC_DST_FWD);
314 SYS("ip neigh add " IP6_SRC " dev veth_dst lladdr " MAC_DST_FWD);
315
316 close_netns(nstoken);
317
318 return 0;
319 fail:
320 if (nstoken)
321 close_netns(nstoken);
322 return -1;
323 }
324
netns_load_bpf(void)325 static int netns_load_bpf(void)
326 {
327 SYS("tc qdisc add dev veth_src_fwd clsact");
328 SYS("tc filter add dev veth_src_fwd ingress bpf da object-pinned "
329 SRC_PROG_PIN_FILE);
330 SYS("tc filter add dev veth_src_fwd egress bpf da object-pinned "
331 CHK_PROG_PIN_FILE);
332
333 SYS("tc qdisc add dev veth_dst_fwd clsact");
334 SYS("tc filter add dev veth_dst_fwd ingress bpf da object-pinned "
335 DST_PROG_PIN_FILE);
336 SYS("tc filter add dev veth_dst_fwd egress bpf da object-pinned "
337 CHK_PROG_PIN_FILE);
338
339 return 0;
340 fail:
341 return -1;
342 }
343
test_tcp(int family,const char * addr,__u16 port)344 static void test_tcp(int family, const char *addr, __u16 port)
345 {
346 int listen_fd = -1, accept_fd = -1, client_fd = -1;
347 char buf[] = "testing testing";
348 int n;
349 struct nstoken *nstoken;
350
351 nstoken = open_netns(NS_DST);
352 if (!ASSERT_OK_PTR(nstoken, "setns dst"))
353 return;
354
355 listen_fd = start_server(family, SOCK_STREAM, addr, port, 0);
356 if (!ASSERT_GE(listen_fd, 0, "listen"))
357 goto done;
358
359 close_netns(nstoken);
360 nstoken = open_netns(NS_SRC);
361 if (!ASSERT_OK_PTR(nstoken, "setns src"))
362 goto done;
363
364 client_fd = connect_to_fd(listen_fd, TIMEOUT_MILLIS);
365 if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
366 goto done;
367
368 accept_fd = accept(listen_fd, NULL, NULL);
369 if (!ASSERT_GE(accept_fd, 0, "accept"))
370 goto done;
371
372 if (!ASSERT_OK(settimeo(accept_fd, TIMEOUT_MILLIS), "settimeo"))
373 goto done;
374
375 n = write(client_fd, buf, sizeof(buf));
376 if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
377 goto done;
378
379 n = read(accept_fd, buf, sizeof(buf));
380 ASSERT_EQ(n, sizeof(buf), "recv from server");
381
382 done:
383 if (nstoken)
384 close_netns(nstoken);
385 if (listen_fd >= 0)
386 close(listen_fd);
387 if (accept_fd >= 0)
388 close(accept_fd);
389 if (client_fd >= 0)
390 close(client_fd);
391 }
392
test_ping(int family,const char * addr)393 static int test_ping(int family, const char *addr)
394 {
395 SYS("ip netns exec " NS_SRC " %s " PING_ARGS " %s > /dev/null", ping_command(family), addr);
396 return 0;
397 fail:
398 return -1;
399 }
400
test_connectivity(void)401 static void test_connectivity(void)
402 {
403 test_tcp(AF_INET, IP4_DST, IP4_PORT);
404 test_ping(AF_INET, IP4_DST);
405 test_tcp(AF_INET6, IP6_DST, IP6_PORT);
406 test_ping(AF_INET6, IP6_DST);
407 }
408
set_forwarding(bool enable)409 static int set_forwarding(bool enable)
410 {
411 int err;
412
413 err = write_file("/proc/sys/net/ipv4/ip_forward", enable ? "1" : "0");
414 if (!ASSERT_OK(err, "set ipv4.ip_forward=0"))
415 return err;
416
417 err = write_file("/proc/sys/net/ipv6/conf/all/forwarding", enable ? "1" : "0");
418 if (!ASSERT_OK(err, "set ipv6.forwarding=0"))
419 return err;
420
421 return 0;
422 }
423
test_tc_redirect_neigh_fib(struct netns_setup_result * setup_result)424 static void test_tc_redirect_neigh_fib(struct netns_setup_result *setup_result)
425 {
426 struct nstoken *nstoken = NULL;
427 struct test_tc_neigh_fib *skel = NULL;
428 int err;
429
430 nstoken = open_netns(NS_FWD);
431 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
432 return;
433
434 skel = test_tc_neigh_fib__open();
435 if (!ASSERT_OK_PTR(skel, "test_tc_neigh_fib__open"))
436 goto done;
437
438 if (!ASSERT_OK(test_tc_neigh_fib__load(skel), "test_tc_neigh_fib__load"))
439 goto done;
440
441 err = bpf_program__pin(skel->progs.tc_src, SRC_PROG_PIN_FILE);
442 if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE))
443 goto done;
444
445 err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE);
446 if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE))
447 goto done;
448
449 err = bpf_program__pin(skel->progs.tc_dst, DST_PROG_PIN_FILE);
450 if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE))
451 goto done;
452
453 if (netns_load_bpf())
454 goto done;
455
456 /* bpf_fib_lookup() checks if forwarding is enabled */
457 if (!ASSERT_OK(set_forwarding(true), "enable forwarding"))
458 goto done;
459
460 test_connectivity();
461
462 done:
463 if (skel)
464 test_tc_neigh_fib__destroy(skel);
465 close_netns(nstoken);
466 }
467
test_tc_redirect_neigh(struct netns_setup_result * setup_result)468 static void test_tc_redirect_neigh(struct netns_setup_result *setup_result)
469 {
470 struct nstoken *nstoken = NULL;
471 struct test_tc_neigh *skel = NULL;
472 int err;
473
474 nstoken = open_netns(NS_FWD);
475 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
476 return;
477
478 skel = test_tc_neigh__open();
479 if (!ASSERT_OK_PTR(skel, "test_tc_neigh__open"))
480 goto done;
481
482 skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd;
483 skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
484
485 err = test_tc_neigh__load(skel);
486 if (!ASSERT_OK(err, "test_tc_neigh__load"))
487 goto done;
488
489 err = bpf_program__pin(skel->progs.tc_src, SRC_PROG_PIN_FILE);
490 if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE))
491 goto done;
492
493 err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE);
494 if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE))
495 goto done;
496
497 err = bpf_program__pin(skel->progs.tc_dst, DST_PROG_PIN_FILE);
498 if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE))
499 goto done;
500
501 if (netns_load_bpf())
502 goto done;
503
504 if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
505 goto done;
506
507 test_connectivity();
508
509 done:
510 if (skel)
511 test_tc_neigh__destroy(skel);
512 close_netns(nstoken);
513 }
514
test_tc_redirect_peer(struct netns_setup_result * setup_result)515 static void test_tc_redirect_peer(struct netns_setup_result *setup_result)
516 {
517 struct nstoken *nstoken;
518 struct test_tc_peer *skel;
519 int err;
520
521 nstoken = open_netns(NS_FWD);
522 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
523 return;
524
525 skel = test_tc_peer__open();
526 if (!ASSERT_OK_PTR(skel, "test_tc_peer__open"))
527 goto done;
528
529 skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd;
530 skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
531
532 err = test_tc_peer__load(skel);
533 if (!ASSERT_OK(err, "test_tc_peer__load"))
534 goto done;
535
536 err = bpf_program__pin(skel->progs.tc_src, SRC_PROG_PIN_FILE);
537 if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE))
538 goto done;
539
540 err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE);
541 if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE))
542 goto done;
543
544 err = bpf_program__pin(skel->progs.tc_dst, DST_PROG_PIN_FILE);
545 if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE))
546 goto done;
547
548 if (netns_load_bpf())
549 goto done;
550
551 if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
552 goto done;
553
554 test_connectivity();
555
556 done:
557 if (skel)
558 test_tc_peer__destroy(skel);
559 close_netns(nstoken);
560 }
561
tun_open(char * name)562 static int tun_open(char *name)
563 {
564 struct ifreq ifr;
565 int fd, err;
566
567 fd = open("/dev/net/tun", O_RDWR);
568 if (!ASSERT_GE(fd, 0, "open /dev/net/tun"))
569 return -1;
570
571 memset(&ifr, 0, sizeof(ifr));
572
573 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
574 if (*name)
575 strncpy(ifr.ifr_name, name, IFNAMSIZ);
576
577 err = ioctl(fd, TUNSETIFF, &ifr);
578 if (!ASSERT_OK(err, "ioctl TUNSETIFF"))
579 goto fail;
580
581 SYS("ip link set dev %s up", name);
582
583 return fd;
584 fail:
585 close(fd);
586 return -1;
587 }
588
589 #define MAX(a, b) ((a) > (b) ? (a) : (b))
590 enum {
591 SRC_TO_TARGET = 0,
592 TARGET_TO_SRC = 1,
593 };
594
tun_relay_loop(int src_fd,int target_fd)595 static int tun_relay_loop(int src_fd, int target_fd)
596 {
597 fd_set rfds, wfds;
598
599 FD_ZERO(&rfds);
600 FD_ZERO(&wfds);
601
602 for (;;) {
603 char buf[1500];
604 int direction, nread, nwrite;
605
606 FD_SET(src_fd, &rfds);
607 FD_SET(target_fd, &rfds);
608
609 if (select(1 + MAX(src_fd, target_fd), &rfds, NULL, NULL, NULL) < 0) {
610 log_err("select failed");
611 return 1;
612 }
613
614 direction = FD_ISSET(src_fd, &rfds) ? SRC_TO_TARGET : TARGET_TO_SRC;
615
616 nread = read(direction == SRC_TO_TARGET ? src_fd : target_fd, buf, sizeof(buf));
617 if (nread < 0) {
618 log_err("read failed");
619 return 1;
620 }
621
622 nwrite = write(direction == SRC_TO_TARGET ? target_fd : src_fd, buf, nread);
623 if (nwrite != nread) {
624 log_err("write failed");
625 return 1;
626 }
627 }
628 }
629
test_tc_redirect_peer_l3(struct netns_setup_result * setup_result)630 static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result)
631 {
632 struct test_tc_peer *skel = NULL;
633 struct nstoken *nstoken = NULL;
634 int err;
635 int tunnel_pid = -1;
636 int src_fd, target_fd;
637 int ifindex;
638
639 /* Start a L3 TUN/TAP tunnel between the src and dst namespaces.
640 * This test is using TUN/TAP instead of e.g. IPIP or GRE tunnel as those
641 * expose the L2 headers encapsulating the IP packet to BPF and hence
642 * don't have skb in suitable state for this test. Alternative to TUN/TAP
643 * would be e.g. Wireguard which would appear as a pure L3 device to BPF,
644 * but that requires much more complicated setup.
645 */
646 nstoken = open_netns(NS_SRC);
647 if (!ASSERT_OK_PTR(nstoken, "setns " NS_SRC))
648 return;
649
650 src_fd = tun_open("tun_src");
651 if (!ASSERT_GE(src_fd, 0, "tun_open tun_src"))
652 goto fail;
653
654 close_netns(nstoken);
655
656 nstoken = open_netns(NS_FWD);
657 if (!ASSERT_OK_PTR(nstoken, "setns " NS_FWD))
658 goto fail;
659
660 target_fd = tun_open("tun_fwd");
661 if (!ASSERT_GE(target_fd, 0, "tun_open tun_fwd"))
662 goto fail;
663
664 tunnel_pid = fork();
665 if (!ASSERT_GE(tunnel_pid, 0, "fork tun_relay_loop"))
666 goto fail;
667
668 if (tunnel_pid == 0)
669 exit(tun_relay_loop(src_fd, target_fd));
670
671 skel = test_tc_peer__open();
672 if (!ASSERT_OK_PTR(skel, "test_tc_peer__open"))
673 goto fail;
674
675 ifindex = get_ifindex("tun_fwd");
676 if (!ASSERT_GE(ifindex, 0, "get_ifindex tun_fwd"))
677 goto fail;
678
679 skel->rodata->IFINDEX_SRC = ifindex;
680 skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
681
682 err = test_tc_peer__load(skel);
683 if (!ASSERT_OK(err, "test_tc_peer__load"))
684 goto fail;
685
686 err = bpf_program__pin(skel->progs.tc_src_l3, SRC_PROG_PIN_FILE);
687 if (!ASSERT_OK(err, "pin " SRC_PROG_PIN_FILE))
688 goto fail;
689
690 err = bpf_program__pin(skel->progs.tc_dst_l3, DST_PROG_PIN_FILE);
691 if (!ASSERT_OK(err, "pin " DST_PROG_PIN_FILE))
692 goto fail;
693
694 err = bpf_program__pin(skel->progs.tc_chk, CHK_PROG_PIN_FILE);
695 if (!ASSERT_OK(err, "pin " CHK_PROG_PIN_FILE))
696 goto fail;
697
698 /* Load "tc_src_l3" to the tun_fwd interface to redirect packets
699 * towards dst, and "tc_dst" to redirect packets
700 * and "tc_chk" on veth_dst_fwd to drop non-redirected packets.
701 */
702 SYS("tc qdisc add dev tun_fwd clsact");
703 SYS("tc filter add dev tun_fwd ingress bpf da object-pinned "
704 SRC_PROG_PIN_FILE);
705
706 SYS("tc qdisc add dev veth_dst_fwd clsact");
707 SYS("tc filter add dev veth_dst_fwd ingress bpf da object-pinned "
708 DST_PROG_PIN_FILE);
709 SYS("tc filter add dev veth_dst_fwd egress bpf da object-pinned "
710 CHK_PROG_PIN_FILE);
711
712 /* Setup route and neigh tables */
713 SYS("ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24");
714 SYS("ip -netns " NS_FWD " addr add dev tun_fwd " IP4_TUN_FWD "/24");
715
716 SYS("ip -netns " NS_SRC " addr add dev tun_src " IP6_TUN_SRC "/64 nodad");
717 SYS("ip -netns " NS_FWD " addr add dev tun_fwd " IP6_TUN_FWD "/64 nodad");
718
719 SYS("ip -netns " NS_SRC " route del " IP4_DST "/32 dev veth_src scope global");
720 SYS("ip -netns " NS_SRC " route add " IP4_DST "/32 via " IP4_TUN_FWD
721 " dev tun_src scope global");
722 SYS("ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev veth_dst scope global");
723 SYS("ip -netns " NS_SRC " route del " IP6_DST "/128 dev veth_src scope global");
724 SYS("ip -netns " NS_SRC " route add " IP6_DST "/128 via " IP6_TUN_FWD
725 " dev tun_src scope global");
726 SYS("ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev veth_dst scope global");
727
728 SYS("ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
729 SYS("ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
730
731 if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
732 goto fail;
733
734 test_connectivity();
735
736 fail:
737 if (tunnel_pid > 0) {
738 kill(tunnel_pid, SIGTERM);
739 waitpid(tunnel_pid, NULL, 0);
740 }
741 if (src_fd >= 0)
742 close(src_fd);
743 if (target_fd >= 0)
744 close(target_fd);
745 if (skel)
746 test_tc_peer__destroy(skel);
747 if (nstoken)
748 close_netns(nstoken);
749 }
750
751 #define RUN_TEST(name) \
752 ({ \
753 struct netns_setup_result setup_result; \
754 if (test__start_subtest(#name)) \
755 if (ASSERT_OK(netns_setup_namespaces("add"), "setup namespaces")) { \
756 if (ASSERT_OK(netns_setup_links_and_routes(&setup_result), \
757 "setup links and routes")) \
758 test_ ## name(&setup_result); \
759 netns_setup_namespaces("delete"); \
760 } \
761 })
762
test_tc_redirect_run_tests(void * arg)763 static void *test_tc_redirect_run_tests(void *arg)
764 {
765 RUN_TEST(tc_redirect_peer);
766 RUN_TEST(tc_redirect_peer_l3);
767 RUN_TEST(tc_redirect_neigh);
768 RUN_TEST(tc_redirect_neigh_fib);
769 return NULL;
770 }
771
test_tc_redirect(void)772 void test_tc_redirect(void)
773 {
774 pthread_t test_thread;
775 int err;
776
777 /* Run the tests in their own thread to isolate the namespace changes
778 * so they do not affect the environment of other tests.
779 * (specifically needed because of unshare(CLONE_NEWNS) in open_netns())
780 */
781 err = pthread_create(&test_thread, NULL, &test_tc_redirect_run_tests, NULL);
782 if (ASSERT_OK(err, "pthread_create"))
783 ASSERT_OK(pthread_join(test_thread, NULL), "pthread_join");
784 }
785