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