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 #include <arpa/inet.h>
14 #include <linux/if_tun.h>
15 #include <linux/limits.h>
16 #include <linux/sysctl.h>
17 #include <linux/time_types.h>
18 #include <linux/net_tstamp.h>
19 #include <net/if.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24
25 #include "test_progs.h"
26 #include "network_helpers.h"
27 #include "test_tc_neigh_fib.skel.h"
28 #include "test_tc_neigh.skel.h"
29 #include "test_tc_peer.skel.h"
30 #include "test_tc_dtime.skel.h"
31
32 #ifndef TCP_TX_DELAY
33 #define TCP_TX_DELAY 37
34 #endif
35
36 #define NS_SRC "ns_src"
37 #define NS_FWD "ns_fwd"
38 #define NS_DST "ns_dst"
39
40 #define IP4_SRC "172.16.1.100"
41 #define IP4_DST "172.16.2.100"
42 #define IP4_TUN_SRC "172.17.1.100"
43 #define IP4_TUN_FWD "172.17.1.200"
44 #define IP4_PORT 9004
45
46 #define IP6_SRC "0::1:dead:beef:cafe"
47 #define IP6_DST "0::2:dead:beef:cafe"
48 #define IP6_TUN_SRC "1::1:dead:beef:cafe"
49 #define IP6_TUN_FWD "1::2:dead:beef:cafe"
50 #define IP6_PORT 9006
51
52 #define IP4_SLL "169.254.0.1"
53 #define IP4_DLL "169.254.0.2"
54 #define IP4_NET "169.254.0.0"
55
56 #define MAC_DST_FWD "00:11:22:33:44:55"
57 #define MAC_DST "00:22:33:44:55:66"
58
59 #define IFADDR_STR_LEN 18
60 #define PING_ARGS "-i 0.2 -c 3 -w 10 -q"
61
62 #define TIMEOUT_MILLIS 10000
63 #define NSEC_PER_SEC 1000000000ULL
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
netns_setup_namespaces(const char * verb)87 static int netns_setup_namespaces(const char *verb)
88 {
89 const char * const *ns = namespaces;
90 char cmd[128];
91
92 while (*ns) {
93 snprintf(cmd, sizeof(cmd), "ip netns %s %s", verb, *ns);
94 if (!ASSERT_OK(system(cmd), cmd))
95 return -1;
96 ns++;
97 }
98 return 0;
99 }
100
netns_setup_namespaces_nofail(const char * verb)101 static void netns_setup_namespaces_nofail(const char *verb)
102 {
103 const char * const *ns = namespaces;
104 char cmd[128];
105
106 while (*ns) {
107 snprintf(cmd, sizeof(cmd), "ip netns %s %s > /dev/null 2>&1", verb, *ns);
108 system(cmd);
109 ns++;
110 }
111 }
112
113 struct netns_setup_result {
114 int ifindex_veth_src;
115 int ifindex_veth_src_fwd;
116 int ifindex_veth_dst;
117 int ifindex_veth_dst_fwd;
118 };
119
get_ifaddr(const char * name,char * ifaddr)120 static int get_ifaddr(const char *name, char *ifaddr)
121 {
122 char path[PATH_MAX];
123 FILE *f;
124 int ret;
125
126 snprintf(path, PATH_MAX, "/sys/class/net/%s/address", name);
127 f = fopen(path, "r");
128 if (!ASSERT_OK_PTR(f, path))
129 return -1;
130
131 ret = fread(ifaddr, 1, IFADDR_STR_LEN, f);
132 if (!ASSERT_EQ(ret, IFADDR_STR_LEN, "fread ifaddr")) {
133 fclose(f);
134 return -1;
135 }
136 fclose(f);
137 return 0;
138 }
139
netns_setup_links_and_routes(struct netns_setup_result * result)140 static int netns_setup_links_and_routes(struct netns_setup_result *result)
141 {
142 struct nstoken *nstoken = NULL;
143 char veth_src_fwd_addr[IFADDR_STR_LEN+1] = {};
144
145 SYS(fail, "ip link add veth_src type veth peer name veth_src_fwd");
146 SYS(fail, "ip link add veth_dst type veth peer name veth_dst_fwd");
147
148 SYS(fail, "ip link set veth_dst_fwd address " MAC_DST_FWD);
149 SYS(fail, "ip link set veth_dst address " MAC_DST);
150
151 if (get_ifaddr("veth_src_fwd", veth_src_fwd_addr))
152 goto fail;
153
154 result->ifindex_veth_src = if_nametoindex("veth_src");
155 if (!ASSERT_GT(result->ifindex_veth_src, 0, "ifindex_veth_src"))
156 goto fail;
157
158 result->ifindex_veth_src_fwd = if_nametoindex("veth_src_fwd");
159 if (!ASSERT_GT(result->ifindex_veth_src_fwd, 0, "ifindex_veth_src_fwd"))
160 goto fail;
161
162 result->ifindex_veth_dst = if_nametoindex("veth_dst");
163 if (!ASSERT_GT(result->ifindex_veth_dst, 0, "ifindex_veth_dst"))
164 goto fail;
165
166 result->ifindex_veth_dst_fwd = if_nametoindex("veth_dst_fwd");
167 if (!ASSERT_GT(result->ifindex_veth_dst_fwd, 0, "ifindex_veth_dst_fwd"))
168 goto fail;
169
170 SYS(fail, "ip link set veth_src netns " NS_SRC);
171 SYS(fail, "ip link set veth_src_fwd netns " NS_FWD);
172 SYS(fail, "ip link set veth_dst_fwd netns " NS_FWD);
173 SYS(fail, "ip link set veth_dst netns " NS_DST);
174
175 /** setup in 'src' namespace */
176 nstoken = open_netns(NS_SRC);
177 if (!ASSERT_OK_PTR(nstoken, "setns src"))
178 goto fail;
179
180 SYS(fail, "ip addr add " IP4_SRC "/32 dev veth_src");
181 SYS(fail, "ip addr add " IP6_SRC "/128 dev veth_src nodad");
182 SYS(fail, "ip link set dev veth_src up");
183
184 SYS(fail, "ip route add " IP4_DST "/32 dev veth_src scope global");
185 SYS(fail, "ip route add " IP4_NET "/16 dev veth_src scope global");
186 SYS(fail, "ip route add " IP6_DST "/128 dev veth_src scope global");
187
188 SYS(fail, "ip neigh add " IP4_DST " dev veth_src lladdr %s",
189 veth_src_fwd_addr);
190 SYS(fail, "ip neigh add " IP6_DST " dev veth_src lladdr %s",
191 veth_src_fwd_addr);
192
193 close_netns(nstoken);
194
195 /** setup in 'fwd' namespace */
196 nstoken = open_netns(NS_FWD);
197 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
198 goto fail;
199
200 /* The fwd netns automatically gets a v6 LL address / routes, but also
201 * needs v4 one in order to start ARP probing. IP4_NET route is added
202 * to the endpoints so that the ARP processing will reply.
203 */
204 SYS(fail, "ip addr add " IP4_SLL "/32 dev veth_src_fwd");
205 SYS(fail, "ip addr add " IP4_DLL "/32 dev veth_dst_fwd");
206 SYS(fail, "ip link set dev veth_src_fwd up");
207 SYS(fail, "ip link set dev veth_dst_fwd up");
208
209 SYS(fail, "ip route add " IP4_SRC "/32 dev veth_src_fwd scope global");
210 SYS(fail, "ip route add " IP6_SRC "/128 dev veth_src_fwd scope global");
211 SYS(fail, "ip route add " IP4_DST "/32 dev veth_dst_fwd scope global");
212 SYS(fail, "ip route add " IP6_DST "/128 dev veth_dst_fwd scope global");
213
214 close_netns(nstoken);
215
216 /** setup in 'dst' namespace */
217 nstoken = open_netns(NS_DST);
218 if (!ASSERT_OK_PTR(nstoken, "setns dst"))
219 goto fail;
220
221 SYS(fail, "ip addr add " IP4_DST "/32 dev veth_dst");
222 SYS(fail, "ip addr add " IP6_DST "/128 dev veth_dst nodad");
223 SYS(fail, "ip link set dev veth_dst up");
224
225 SYS(fail, "ip route add " IP4_SRC "/32 dev veth_dst scope global");
226 SYS(fail, "ip route add " IP4_NET "/16 dev veth_dst scope global");
227 SYS(fail, "ip route add " IP6_SRC "/128 dev veth_dst scope global");
228
229 SYS(fail, "ip neigh add " IP4_SRC " dev veth_dst lladdr " MAC_DST_FWD);
230 SYS(fail, "ip neigh add " IP6_SRC " dev veth_dst lladdr " MAC_DST_FWD);
231
232 close_netns(nstoken);
233
234 return 0;
235 fail:
236 if (nstoken)
237 close_netns(nstoken);
238 return -1;
239 }
240
qdisc_clsact_create(struct bpf_tc_hook * qdisc_hook,int ifindex)241 static int qdisc_clsact_create(struct bpf_tc_hook *qdisc_hook, int ifindex)
242 {
243 char err_str[128], ifname[16];
244 int err;
245
246 qdisc_hook->ifindex = ifindex;
247 qdisc_hook->attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS;
248 err = bpf_tc_hook_create(qdisc_hook);
249 snprintf(err_str, sizeof(err_str),
250 "qdisc add dev %s clsact",
251 if_indextoname(qdisc_hook->ifindex, ifname) ? : "<unknown_iface>");
252 err_str[sizeof(err_str) - 1] = 0;
253 ASSERT_OK(err, err_str);
254
255 return err;
256 }
257
xgress_filter_add(struct bpf_tc_hook * qdisc_hook,enum bpf_tc_attach_point xgress,const struct bpf_program * prog,int priority)258 static int xgress_filter_add(struct bpf_tc_hook *qdisc_hook,
259 enum bpf_tc_attach_point xgress,
260 const struct bpf_program *prog, int priority)
261 {
262 LIBBPF_OPTS(bpf_tc_opts, tc_attach);
263 char err_str[128], ifname[16];
264 int err;
265
266 qdisc_hook->attach_point = xgress;
267 tc_attach.prog_fd = bpf_program__fd(prog);
268 tc_attach.priority = priority;
269 err = bpf_tc_attach(qdisc_hook, &tc_attach);
270 snprintf(err_str, sizeof(err_str),
271 "filter add dev %s %s prio %d bpf da %s",
272 if_indextoname(qdisc_hook->ifindex, ifname) ? : "<unknown_iface>",
273 xgress == BPF_TC_INGRESS ? "ingress" : "egress",
274 priority, bpf_program__name(prog));
275 err_str[sizeof(err_str) - 1] = 0;
276 ASSERT_OK(err, err_str);
277
278 return err;
279 }
280
281 #define QDISC_CLSACT_CREATE(qdisc_hook, ifindex) ({ \
282 if ((err = qdisc_clsact_create(qdisc_hook, ifindex))) \
283 goto fail; \
284 })
285
286 #define XGRESS_FILTER_ADD(qdisc_hook, xgress, prog, priority) ({ \
287 if ((err = xgress_filter_add(qdisc_hook, xgress, prog, priority))) \
288 goto fail; \
289 })
290
netns_load_bpf(const struct bpf_program * src_prog,const struct bpf_program * dst_prog,const struct bpf_program * chk_prog,const struct netns_setup_result * setup_result)291 static int netns_load_bpf(const struct bpf_program *src_prog,
292 const struct bpf_program *dst_prog,
293 const struct bpf_program *chk_prog,
294 const struct netns_setup_result *setup_result)
295 {
296 LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_src_fwd);
297 LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst_fwd);
298 int err;
299
300 /* tc qdisc add dev veth_src_fwd clsact */
301 QDISC_CLSACT_CREATE(&qdisc_veth_src_fwd, setup_result->ifindex_veth_src_fwd);
302 /* tc filter add dev veth_src_fwd ingress bpf da src_prog */
303 XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_INGRESS, src_prog, 0);
304 /* tc filter add dev veth_src_fwd egress bpf da chk_prog */
305 XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_EGRESS, chk_prog, 0);
306
307 /* tc qdisc add dev veth_dst_fwd clsact */
308 QDISC_CLSACT_CREATE(&qdisc_veth_dst_fwd, setup_result->ifindex_veth_dst_fwd);
309 /* tc filter add dev veth_dst_fwd ingress bpf da dst_prog */
310 XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, dst_prog, 0);
311 /* tc filter add dev veth_dst_fwd egress bpf da chk_prog */
312 XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, chk_prog, 0);
313
314 return 0;
315 fail:
316 return -1;
317 }
318
test_tcp(int family,const char * addr,__u16 port)319 static void test_tcp(int family, const char *addr, __u16 port)
320 {
321 int listen_fd = -1, accept_fd = -1, client_fd = -1;
322 char buf[] = "testing testing";
323 int n;
324 struct nstoken *nstoken;
325
326 nstoken = open_netns(NS_DST);
327 if (!ASSERT_OK_PTR(nstoken, "setns dst"))
328 return;
329
330 listen_fd = start_server(family, SOCK_STREAM, addr, port, 0);
331 if (!ASSERT_GE(listen_fd, 0, "listen"))
332 goto done;
333
334 close_netns(nstoken);
335 nstoken = open_netns(NS_SRC);
336 if (!ASSERT_OK_PTR(nstoken, "setns src"))
337 goto done;
338
339 client_fd = connect_to_fd(listen_fd, TIMEOUT_MILLIS);
340 if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
341 goto done;
342
343 accept_fd = accept(listen_fd, NULL, NULL);
344 if (!ASSERT_GE(accept_fd, 0, "accept"))
345 goto done;
346
347 if (!ASSERT_OK(settimeo(accept_fd, TIMEOUT_MILLIS), "settimeo"))
348 goto done;
349
350 n = write(client_fd, buf, sizeof(buf));
351 if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
352 goto done;
353
354 n = read(accept_fd, buf, sizeof(buf));
355 ASSERT_EQ(n, sizeof(buf), "recv from server");
356
357 done:
358 if (nstoken)
359 close_netns(nstoken);
360 if (listen_fd >= 0)
361 close(listen_fd);
362 if (accept_fd >= 0)
363 close(accept_fd);
364 if (client_fd >= 0)
365 close(client_fd);
366 }
367
test_ping(int family,const char * addr)368 static int test_ping(int family, const char *addr)
369 {
370 SYS(fail, "ip netns exec " NS_SRC " %s " PING_ARGS " %s > /dev/null", ping_command(family), addr);
371 return 0;
372 fail:
373 return -1;
374 }
375
test_connectivity(void)376 static void test_connectivity(void)
377 {
378 test_tcp(AF_INET, IP4_DST, IP4_PORT);
379 test_ping(AF_INET, IP4_DST);
380 test_tcp(AF_INET6, IP6_DST, IP6_PORT);
381 test_ping(AF_INET6, IP6_DST);
382 }
383
set_forwarding(bool enable)384 static int set_forwarding(bool enable)
385 {
386 int err;
387
388 err = write_file("/proc/sys/net/ipv4/ip_forward", enable ? "1" : "0");
389 if (!ASSERT_OK(err, "set ipv4.ip_forward=0"))
390 return err;
391
392 err = write_file("/proc/sys/net/ipv6/conf/all/forwarding", enable ? "1" : "0");
393 if (!ASSERT_OK(err, "set ipv6.forwarding=0"))
394 return err;
395
396 return 0;
397 }
398
rcv_tstamp(int fd,const char * expected,size_t s)399 static void rcv_tstamp(int fd, const char *expected, size_t s)
400 {
401 struct __kernel_timespec pkt_ts = {};
402 char ctl[CMSG_SPACE(sizeof(pkt_ts))];
403 struct timespec now_ts;
404 struct msghdr msg = {};
405 __u64 now_ns, pkt_ns;
406 struct cmsghdr *cmsg;
407 struct iovec iov;
408 char data[32];
409 int ret;
410
411 iov.iov_base = data;
412 iov.iov_len = sizeof(data);
413 msg.msg_iov = &iov;
414 msg.msg_iovlen = 1;
415 msg.msg_control = &ctl;
416 msg.msg_controllen = sizeof(ctl);
417
418 ret = recvmsg(fd, &msg, 0);
419 if (!ASSERT_EQ(ret, s, "recvmsg"))
420 return;
421 ASSERT_STRNEQ(data, expected, s, "expected rcv data");
422
423 cmsg = CMSG_FIRSTHDR(&msg);
424 if (cmsg && cmsg->cmsg_level == SOL_SOCKET &&
425 cmsg->cmsg_type == SO_TIMESTAMPNS_NEW)
426 memcpy(&pkt_ts, CMSG_DATA(cmsg), sizeof(pkt_ts));
427
428 pkt_ns = pkt_ts.tv_sec * NSEC_PER_SEC + pkt_ts.tv_nsec;
429 ASSERT_NEQ(pkt_ns, 0, "pkt rcv tstamp");
430
431 ret = clock_gettime(CLOCK_REALTIME, &now_ts);
432 ASSERT_OK(ret, "clock_gettime");
433 now_ns = now_ts.tv_sec * NSEC_PER_SEC + now_ts.tv_nsec;
434
435 if (ASSERT_GE(now_ns, pkt_ns, "check rcv tstamp"))
436 ASSERT_LT(now_ns - pkt_ns, 5 * NSEC_PER_SEC,
437 "check rcv tstamp");
438 }
439
snd_tstamp(int fd,char * b,size_t s)440 static void snd_tstamp(int fd, char *b, size_t s)
441 {
442 struct sock_txtime opt = { .clockid = CLOCK_TAI };
443 char ctl[CMSG_SPACE(sizeof(__u64))];
444 struct timespec now_ts;
445 struct msghdr msg = {};
446 struct cmsghdr *cmsg;
447 struct iovec iov;
448 __u64 now_ns;
449 int ret;
450
451 ret = clock_gettime(CLOCK_TAI, &now_ts);
452 ASSERT_OK(ret, "clock_get_time(CLOCK_TAI)");
453 now_ns = now_ts.tv_sec * NSEC_PER_SEC + now_ts.tv_nsec;
454
455 iov.iov_base = b;
456 iov.iov_len = s;
457 msg.msg_iov = &iov;
458 msg.msg_iovlen = 1;
459 msg.msg_control = &ctl;
460 msg.msg_controllen = sizeof(ctl);
461
462 cmsg = CMSG_FIRSTHDR(&msg);
463 cmsg->cmsg_level = SOL_SOCKET;
464 cmsg->cmsg_type = SCM_TXTIME;
465 cmsg->cmsg_len = CMSG_LEN(sizeof(now_ns));
466 *(__u64 *)CMSG_DATA(cmsg) = now_ns;
467
468 ret = setsockopt(fd, SOL_SOCKET, SO_TXTIME, &opt, sizeof(opt));
469 ASSERT_OK(ret, "setsockopt(SO_TXTIME)");
470
471 ret = sendmsg(fd, &msg, 0);
472 ASSERT_EQ(ret, s, "sendmsg");
473 }
474
test_inet_dtime(int family,int type,const char * addr,__u16 port)475 static void test_inet_dtime(int family, int type, const char *addr, __u16 port)
476 {
477 int opt = 1, accept_fd = -1, client_fd = -1, listen_fd, err;
478 char buf[] = "testing testing";
479 struct nstoken *nstoken;
480
481 nstoken = open_netns(NS_DST);
482 if (!ASSERT_OK_PTR(nstoken, "setns dst"))
483 return;
484 listen_fd = start_server(family, type, addr, port, 0);
485 close_netns(nstoken);
486
487 if (!ASSERT_GE(listen_fd, 0, "listen"))
488 return;
489
490 /* Ensure the kernel puts the (rcv) timestamp for all skb */
491 err = setsockopt(listen_fd, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
492 &opt, sizeof(opt));
493 if (!ASSERT_OK(err, "setsockopt(SO_TIMESTAMPNS_NEW)"))
494 goto done;
495
496 if (type == SOCK_STREAM) {
497 /* Ensure the kernel set EDT when sending out rst/ack
498 * from the kernel's ctl_sk.
499 */
500 err = setsockopt(listen_fd, SOL_TCP, TCP_TX_DELAY, &opt,
501 sizeof(opt));
502 if (!ASSERT_OK(err, "setsockopt(TCP_TX_DELAY)"))
503 goto done;
504 }
505
506 nstoken = open_netns(NS_SRC);
507 if (!ASSERT_OK_PTR(nstoken, "setns src"))
508 goto done;
509 client_fd = connect_to_fd(listen_fd, TIMEOUT_MILLIS);
510 close_netns(nstoken);
511
512 if (!ASSERT_GE(client_fd, 0, "connect_to_fd"))
513 goto done;
514
515 if (type == SOCK_STREAM) {
516 int n;
517
518 accept_fd = accept(listen_fd, NULL, NULL);
519 if (!ASSERT_GE(accept_fd, 0, "accept"))
520 goto done;
521
522 n = write(client_fd, buf, sizeof(buf));
523 if (!ASSERT_EQ(n, sizeof(buf), "send to server"))
524 goto done;
525 rcv_tstamp(accept_fd, buf, sizeof(buf));
526 } else {
527 snd_tstamp(client_fd, buf, sizeof(buf));
528 rcv_tstamp(listen_fd, buf, sizeof(buf));
529 }
530
531 done:
532 close(listen_fd);
533 if (accept_fd != -1)
534 close(accept_fd);
535 if (client_fd != -1)
536 close(client_fd);
537 }
538
netns_load_dtime_bpf(struct test_tc_dtime * skel,const struct netns_setup_result * setup_result)539 static int netns_load_dtime_bpf(struct test_tc_dtime *skel,
540 const struct netns_setup_result *setup_result)
541 {
542 LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_src_fwd);
543 LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst_fwd);
544 LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_src);
545 LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst);
546 struct nstoken *nstoken;
547 int err;
548
549 /* setup ns_src tc progs */
550 nstoken = open_netns(NS_SRC);
551 if (!ASSERT_OK_PTR(nstoken, "setns " NS_SRC))
552 return -1;
553 /* tc qdisc add dev veth_src clsact */
554 QDISC_CLSACT_CREATE(&qdisc_veth_src, setup_result->ifindex_veth_src);
555 /* tc filter add dev veth_src ingress bpf da ingress_host */
556 XGRESS_FILTER_ADD(&qdisc_veth_src, BPF_TC_INGRESS, skel->progs.ingress_host, 0);
557 /* tc filter add dev veth_src egress bpf da egress_host */
558 XGRESS_FILTER_ADD(&qdisc_veth_src, BPF_TC_EGRESS, skel->progs.egress_host, 0);
559 close_netns(nstoken);
560
561 /* setup ns_dst tc progs */
562 nstoken = open_netns(NS_DST);
563 if (!ASSERT_OK_PTR(nstoken, "setns " NS_DST))
564 return -1;
565 /* tc qdisc add dev veth_dst clsact */
566 QDISC_CLSACT_CREATE(&qdisc_veth_dst, setup_result->ifindex_veth_dst);
567 /* tc filter add dev veth_dst ingress bpf da ingress_host */
568 XGRESS_FILTER_ADD(&qdisc_veth_dst, BPF_TC_INGRESS, skel->progs.ingress_host, 0);
569 /* tc filter add dev veth_dst egress bpf da egress_host */
570 XGRESS_FILTER_ADD(&qdisc_veth_dst, BPF_TC_EGRESS, skel->progs.egress_host, 0);
571 close_netns(nstoken);
572
573 /* setup ns_fwd tc progs */
574 nstoken = open_netns(NS_FWD);
575 if (!ASSERT_OK_PTR(nstoken, "setns " NS_FWD))
576 return -1;
577 /* tc qdisc add dev veth_dst_fwd clsact */
578 QDISC_CLSACT_CREATE(&qdisc_veth_dst_fwd, setup_result->ifindex_veth_dst_fwd);
579 /* tc filter add dev veth_dst_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */
580 XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS,
581 skel->progs.ingress_fwdns_prio100, 100);
582 /* tc filter add dev veth_dst_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */
583 XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS,
584 skel->progs.ingress_fwdns_prio101, 101);
585 /* tc filter add dev veth_dst_fwd egress prio 100 bpf da egress_fwdns_prio100 */
586 XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS,
587 skel->progs.egress_fwdns_prio100, 100);
588 /* tc filter add dev veth_dst_fwd egress prio 101 bpf da egress_fwdns_prio101 */
589 XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS,
590 skel->progs.egress_fwdns_prio101, 101);
591
592 /* tc qdisc add dev veth_src_fwd clsact */
593 QDISC_CLSACT_CREATE(&qdisc_veth_src_fwd, setup_result->ifindex_veth_src_fwd);
594 /* tc filter add dev veth_src_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */
595 XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_INGRESS,
596 skel->progs.ingress_fwdns_prio100, 100);
597 /* tc filter add dev veth_src_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */
598 XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_INGRESS,
599 skel->progs.ingress_fwdns_prio101, 101);
600 /* tc filter add dev veth_src_fwd egress prio 100 bpf da egress_fwdns_prio100 */
601 XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_EGRESS,
602 skel->progs.egress_fwdns_prio100, 100);
603 /* tc filter add dev veth_src_fwd egress prio 101 bpf da egress_fwdns_prio101 */
604 XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_EGRESS,
605 skel->progs.egress_fwdns_prio101, 101);
606 close_netns(nstoken);
607 return 0;
608
609 fail:
610 close_netns(nstoken);
611 return err;
612 }
613
614 enum {
615 INGRESS_FWDNS_P100,
616 INGRESS_FWDNS_P101,
617 EGRESS_FWDNS_P100,
618 EGRESS_FWDNS_P101,
619 INGRESS_ENDHOST,
620 EGRESS_ENDHOST,
621 SET_DTIME,
622 __MAX_CNT,
623 };
624
625 const char *cnt_names[] = {
626 "ingress_fwdns_p100",
627 "ingress_fwdns_p101",
628 "egress_fwdns_p100",
629 "egress_fwdns_p101",
630 "ingress_endhost",
631 "egress_endhost",
632 "set_dtime",
633 };
634
635 enum {
636 TCP_IP6_CLEAR_DTIME,
637 TCP_IP4,
638 TCP_IP6,
639 UDP_IP4,
640 UDP_IP6,
641 TCP_IP4_RT_FWD,
642 TCP_IP6_RT_FWD,
643 UDP_IP4_RT_FWD,
644 UDP_IP6_RT_FWD,
645 UKN_TEST,
646 __NR_TESTS,
647 };
648
649 const char *test_names[] = {
650 "tcp ip6 clear dtime",
651 "tcp ip4",
652 "tcp ip6",
653 "udp ip4",
654 "udp ip6",
655 "tcp ip4 rt fwd",
656 "tcp ip6 rt fwd",
657 "udp ip4 rt fwd",
658 "udp ip6 rt fwd",
659 };
660
dtime_cnt_str(int test,int cnt)661 static const char *dtime_cnt_str(int test, int cnt)
662 {
663 static char name[64];
664
665 snprintf(name, sizeof(name), "%s %s", test_names[test], cnt_names[cnt]);
666
667 return name;
668 }
669
dtime_err_str(int test,int cnt)670 static const char *dtime_err_str(int test, int cnt)
671 {
672 static char name[64];
673
674 snprintf(name, sizeof(name), "%s %s errs", test_names[test],
675 cnt_names[cnt]);
676
677 return name;
678 }
679
test_tcp_clear_dtime(struct test_tc_dtime * skel)680 static void test_tcp_clear_dtime(struct test_tc_dtime *skel)
681 {
682 int i, t = TCP_IP6_CLEAR_DTIME;
683 __u32 *dtimes = skel->bss->dtimes[t];
684 __u32 *errs = skel->bss->errs[t];
685
686 skel->bss->test = t;
687 test_inet_dtime(AF_INET6, SOCK_STREAM, IP6_DST, 50000 + t);
688
689 ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0,
690 dtime_cnt_str(t, INGRESS_FWDNS_P100));
691 ASSERT_EQ(dtimes[INGRESS_FWDNS_P101], 0,
692 dtime_cnt_str(t, INGRESS_FWDNS_P101));
693 ASSERT_GT(dtimes[EGRESS_FWDNS_P100], 0,
694 dtime_cnt_str(t, EGRESS_FWDNS_P100));
695 ASSERT_EQ(dtimes[EGRESS_FWDNS_P101], 0,
696 dtime_cnt_str(t, EGRESS_FWDNS_P101));
697 ASSERT_GT(dtimes[EGRESS_ENDHOST], 0,
698 dtime_cnt_str(t, EGRESS_ENDHOST));
699 ASSERT_GT(dtimes[INGRESS_ENDHOST], 0,
700 dtime_cnt_str(t, INGRESS_ENDHOST));
701
702 for (i = INGRESS_FWDNS_P100; i < __MAX_CNT; i++)
703 ASSERT_EQ(errs[i], 0, dtime_err_str(t, i));
704 }
705
test_tcp_dtime(struct test_tc_dtime * skel,int family,bool bpf_fwd)706 static void test_tcp_dtime(struct test_tc_dtime *skel, int family, bool bpf_fwd)
707 {
708 __u32 *dtimes, *errs;
709 const char *addr;
710 int i, t;
711
712 if (family == AF_INET) {
713 t = bpf_fwd ? TCP_IP4 : TCP_IP4_RT_FWD;
714 addr = IP4_DST;
715 } else {
716 t = bpf_fwd ? TCP_IP6 : TCP_IP6_RT_FWD;
717 addr = IP6_DST;
718 }
719
720 dtimes = skel->bss->dtimes[t];
721 errs = skel->bss->errs[t];
722
723 skel->bss->test = t;
724 test_inet_dtime(family, SOCK_STREAM, addr, 50000 + t);
725
726 /* fwdns_prio100 prog does not read delivery_time_type, so
727 * kernel puts the (rcv) timetamp in __sk_buff->tstamp
728 */
729 ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0,
730 dtime_cnt_str(t, INGRESS_FWDNS_P100));
731 for (i = INGRESS_FWDNS_P101; i < SET_DTIME; i++)
732 ASSERT_GT(dtimes[i], 0, dtime_cnt_str(t, i));
733
734 for (i = INGRESS_FWDNS_P100; i < __MAX_CNT; i++)
735 ASSERT_EQ(errs[i], 0, dtime_err_str(t, i));
736 }
737
test_udp_dtime(struct test_tc_dtime * skel,int family,bool bpf_fwd)738 static void test_udp_dtime(struct test_tc_dtime *skel, int family, bool bpf_fwd)
739 {
740 __u32 *dtimes, *errs;
741 const char *addr;
742 int i, t;
743
744 if (family == AF_INET) {
745 t = bpf_fwd ? UDP_IP4 : UDP_IP4_RT_FWD;
746 addr = IP4_DST;
747 } else {
748 t = bpf_fwd ? UDP_IP6 : UDP_IP6_RT_FWD;
749 addr = IP6_DST;
750 }
751
752 dtimes = skel->bss->dtimes[t];
753 errs = skel->bss->errs[t];
754
755 skel->bss->test = t;
756 test_inet_dtime(family, SOCK_DGRAM, addr, 50000 + t);
757
758 ASSERT_EQ(dtimes[INGRESS_FWDNS_P100], 0,
759 dtime_cnt_str(t, INGRESS_FWDNS_P100));
760 /* non mono delivery time is not forwarded */
761 ASSERT_EQ(dtimes[INGRESS_FWDNS_P101], 0,
762 dtime_cnt_str(t, INGRESS_FWDNS_P101));
763 for (i = EGRESS_FWDNS_P100; i < SET_DTIME; i++)
764 ASSERT_GT(dtimes[i], 0, dtime_cnt_str(t, i));
765
766 for (i = INGRESS_FWDNS_P100; i < __MAX_CNT; i++)
767 ASSERT_EQ(errs[i], 0, dtime_err_str(t, i));
768 }
769
test_tc_redirect_dtime(struct netns_setup_result * setup_result)770 static void test_tc_redirect_dtime(struct netns_setup_result *setup_result)
771 {
772 struct test_tc_dtime *skel;
773 struct nstoken *nstoken;
774 int err;
775
776 skel = test_tc_dtime__open();
777 if (!ASSERT_OK_PTR(skel, "test_tc_dtime__open"))
778 return;
779
780 skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd;
781 skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
782
783 err = test_tc_dtime__load(skel);
784 if (!ASSERT_OK(err, "test_tc_dtime__load"))
785 goto done;
786
787 if (netns_load_dtime_bpf(skel, setup_result))
788 goto done;
789
790 nstoken = open_netns(NS_FWD);
791 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
792 goto done;
793 err = set_forwarding(false);
794 close_netns(nstoken);
795 if (!ASSERT_OK(err, "disable forwarding"))
796 goto done;
797
798 test_tcp_clear_dtime(skel);
799
800 test_tcp_dtime(skel, AF_INET, true);
801 test_tcp_dtime(skel, AF_INET6, true);
802 test_udp_dtime(skel, AF_INET, true);
803 test_udp_dtime(skel, AF_INET6, true);
804
805 /* Test the kernel ip[6]_forward path instead
806 * of bpf_redirect_neigh().
807 */
808 nstoken = open_netns(NS_FWD);
809 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
810 goto done;
811 err = set_forwarding(true);
812 close_netns(nstoken);
813 if (!ASSERT_OK(err, "enable forwarding"))
814 goto done;
815
816 test_tcp_dtime(skel, AF_INET, false);
817 test_tcp_dtime(skel, AF_INET6, false);
818 test_udp_dtime(skel, AF_INET, false);
819 test_udp_dtime(skel, AF_INET6, false);
820
821 done:
822 test_tc_dtime__destroy(skel);
823 }
824
test_tc_redirect_neigh_fib(struct netns_setup_result * setup_result)825 static void test_tc_redirect_neigh_fib(struct netns_setup_result *setup_result)
826 {
827 struct nstoken *nstoken = NULL;
828 struct test_tc_neigh_fib *skel = NULL;
829
830 nstoken = open_netns(NS_FWD);
831 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
832 return;
833
834 skel = test_tc_neigh_fib__open();
835 if (!ASSERT_OK_PTR(skel, "test_tc_neigh_fib__open"))
836 goto done;
837
838 if (!ASSERT_OK(test_tc_neigh_fib__load(skel), "test_tc_neigh_fib__load"))
839 goto done;
840
841 if (netns_load_bpf(skel->progs.tc_src, skel->progs.tc_dst,
842 skel->progs.tc_chk, setup_result))
843 goto done;
844
845 /* bpf_fib_lookup() checks if forwarding is enabled */
846 if (!ASSERT_OK(set_forwarding(true), "enable forwarding"))
847 goto done;
848
849 test_connectivity();
850
851 done:
852 if (skel)
853 test_tc_neigh_fib__destroy(skel);
854 close_netns(nstoken);
855 }
856
test_tc_redirect_neigh(struct netns_setup_result * setup_result)857 static void test_tc_redirect_neigh(struct netns_setup_result *setup_result)
858 {
859 struct nstoken *nstoken = NULL;
860 struct test_tc_neigh *skel = NULL;
861 int err;
862
863 nstoken = open_netns(NS_FWD);
864 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
865 return;
866
867 skel = test_tc_neigh__open();
868 if (!ASSERT_OK_PTR(skel, "test_tc_neigh__open"))
869 goto done;
870
871 skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd;
872 skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
873
874 err = test_tc_neigh__load(skel);
875 if (!ASSERT_OK(err, "test_tc_neigh__load"))
876 goto done;
877
878 if (netns_load_bpf(skel->progs.tc_src, skel->progs.tc_dst,
879 skel->progs.tc_chk, setup_result))
880 goto done;
881
882 if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
883 goto done;
884
885 test_connectivity();
886
887 done:
888 if (skel)
889 test_tc_neigh__destroy(skel);
890 close_netns(nstoken);
891 }
892
test_tc_redirect_peer(struct netns_setup_result * setup_result)893 static void test_tc_redirect_peer(struct netns_setup_result *setup_result)
894 {
895 struct nstoken *nstoken;
896 struct test_tc_peer *skel;
897 int err;
898
899 nstoken = open_netns(NS_FWD);
900 if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
901 return;
902
903 skel = test_tc_peer__open();
904 if (!ASSERT_OK_PTR(skel, "test_tc_peer__open"))
905 goto done;
906
907 skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd;
908 skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
909
910 err = test_tc_peer__load(skel);
911 if (!ASSERT_OK(err, "test_tc_peer__load"))
912 goto done;
913
914 if (netns_load_bpf(skel->progs.tc_src, skel->progs.tc_dst,
915 skel->progs.tc_chk, setup_result))
916 goto done;
917
918 if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
919 goto done;
920
921 test_connectivity();
922
923 done:
924 if (skel)
925 test_tc_peer__destroy(skel);
926 close_netns(nstoken);
927 }
928
tun_open(char * name)929 static int tun_open(char *name)
930 {
931 struct ifreq ifr;
932 int fd, err;
933
934 fd = open("/dev/net/tun", O_RDWR);
935 if (!ASSERT_GE(fd, 0, "open /dev/net/tun"))
936 return -1;
937
938 memset(&ifr, 0, sizeof(ifr));
939
940 ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
941 if (*name)
942 strncpy(ifr.ifr_name, name, IFNAMSIZ);
943
944 err = ioctl(fd, TUNSETIFF, &ifr);
945 if (!ASSERT_OK(err, "ioctl TUNSETIFF"))
946 goto fail;
947
948 SYS(fail, "ip link set dev %s up", name);
949
950 return fd;
951 fail:
952 close(fd);
953 return -1;
954 }
955
956 enum {
957 SRC_TO_TARGET = 0,
958 TARGET_TO_SRC = 1,
959 };
960
tun_relay_loop(int src_fd,int target_fd)961 static int tun_relay_loop(int src_fd, int target_fd)
962 {
963 fd_set rfds, wfds;
964
965 FD_ZERO(&rfds);
966 FD_ZERO(&wfds);
967
968 for (;;) {
969 char buf[1500];
970 int direction, nread, nwrite;
971
972 FD_SET(src_fd, &rfds);
973 FD_SET(target_fd, &rfds);
974
975 if (select(1 + MAX(src_fd, target_fd), &rfds, NULL, NULL, NULL) < 0) {
976 log_err("select failed");
977 return 1;
978 }
979
980 direction = FD_ISSET(src_fd, &rfds) ? SRC_TO_TARGET : TARGET_TO_SRC;
981
982 nread = read(direction == SRC_TO_TARGET ? src_fd : target_fd, buf, sizeof(buf));
983 if (nread < 0) {
984 log_err("read failed");
985 return 1;
986 }
987
988 nwrite = write(direction == SRC_TO_TARGET ? target_fd : src_fd, buf, nread);
989 if (nwrite != nread) {
990 log_err("write failed");
991 return 1;
992 }
993 }
994 }
995
test_tc_redirect_peer_l3(struct netns_setup_result * setup_result)996 static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result)
997 {
998 LIBBPF_OPTS(bpf_tc_hook, qdisc_tun_fwd);
999 LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst_fwd);
1000 struct test_tc_peer *skel = NULL;
1001 struct nstoken *nstoken = NULL;
1002 int err;
1003 int tunnel_pid = -1;
1004 int src_fd, target_fd = -1;
1005 int ifindex;
1006
1007 /* Start a L3 TUN/TAP tunnel between the src and dst namespaces.
1008 * This test is using TUN/TAP instead of e.g. IPIP or GRE tunnel as those
1009 * expose the L2 headers encapsulating the IP packet to BPF and hence
1010 * don't have skb in suitable state for this test. Alternative to TUN/TAP
1011 * would be e.g. Wireguard which would appear as a pure L3 device to BPF,
1012 * but that requires much more complicated setup.
1013 */
1014 nstoken = open_netns(NS_SRC);
1015 if (!ASSERT_OK_PTR(nstoken, "setns " NS_SRC))
1016 return;
1017
1018 src_fd = tun_open("tun_src");
1019 if (!ASSERT_GE(src_fd, 0, "tun_open tun_src"))
1020 goto fail;
1021
1022 close_netns(nstoken);
1023
1024 nstoken = open_netns(NS_FWD);
1025 if (!ASSERT_OK_PTR(nstoken, "setns " NS_FWD))
1026 goto fail;
1027
1028 target_fd = tun_open("tun_fwd");
1029 if (!ASSERT_GE(target_fd, 0, "tun_open tun_fwd"))
1030 goto fail;
1031
1032 tunnel_pid = fork();
1033 if (!ASSERT_GE(tunnel_pid, 0, "fork tun_relay_loop"))
1034 goto fail;
1035
1036 if (tunnel_pid == 0)
1037 exit(tun_relay_loop(src_fd, target_fd));
1038
1039 skel = test_tc_peer__open();
1040 if (!ASSERT_OK_PTR(skel, "test_tc_peer__open"))
1041 goto fail;
1042
1043 ifindex = if_nametoindex("tun_fwd");
1044 if (!ASSERT_GT(ifindex, 0, "if_indextoname tun_fwd"))
1045 goto fail;
1046
1047 skel->rodata->IFINDEX_SRC = ifindex;
1048 skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd;
1049
1050 err = test_tc_peer__load(skel);
1051 if (!ASSERT_OK(err, "test_tc_peer__load"))
1052 goto fail;
1053
1054 /* Load "tc_src_l3" to the tun_fwd interface to redirect packets
1055 * towards dst, and "tc_dst" to redirect packets
1056 * and "tc_chk" on veth_dst_fwd to drop non-redirected packets.
1057 */
1058 /* tc qdisc add dev tun_fwd clsact */
1059 QDISC_CLSACT_CREATE(&qdisc_tun_fwd, ifindex);
1060 /* tc filter add dev tun_fwd ingress bpf da tc_src_l3 */
1061 XGRESS_FILTER_ADD(&qdisc_tun_fwd, BPF_TC_INGRESS, skel->progs.tc_src_l3, 0);
1062
1063 /* tc qdisc add dev veth_dst_fwd clsact */
1064 QDISC_CLSACT_CREATE(&qdisc_veth_dst_fwd, setup_result->ifindex_veth_dst_fwd);
1065 /* tc filter add dev veth_dst_fwd ingress bpf da tc_dst_l3 */
1066 XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, skel->progs.tc_dst_l3, 0);
1067 /* tc filter add dev veth_dst_fwd egress bpf da tc_chk */
1068 XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, skel->progs.tc_chk, 0);
1069
1070 /* Setup route and neigh tables */
1071 SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24");
1072 SYS(fail, "ip -netns " NS_FWD " addr add dev tun_fwd " IP4_TUN_FWD "/24");
1073
1074 SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP6_TUN_SRC "/64 nodad");
1075 SYS(fail, "ip -netns " NS_FWD " addr add dev tun_fwd " IP6_TUN_FWD "/64 nodad");
1076
1077 SYS(fail, "ip -netns " NS_SRC " route del " IP4_DST "/32 dev veth_src scope global");
1078 SYS(fail, "ip -netns " NS_SRC " route add " IP4_DST "/32 via " IP4_TUN_FWD
1079 " dev tun_src scope global");
1080 SYS(fail, "ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev veth_dst scope global");
1081 SYS(fail, "ip -netns " NS_SRC " route del " IP6_DST "/128 dev veth_src scope global");
1082 SYS(fail, "ip -netns " NS_SRC " route add " IP6_DST "/128 via " IP6_TUN_FWD
1083 " dev tun_src scope global");
1084 SYS(fail, "ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev veth_dst scope global");
1085
1086 SYS(fail, "ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
1087 SYS(fail, "ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);
1088
1089 if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
1090 goto fail;
1091
1092 test_connectivity();
1093
1094 fail:
1095 if (tunnel_pid > 0) {
1096 kill(tunnel_pid, SIGTERM);
1097 waitpid(tunnel_pid, NULL, 0);
1098 }
1099 if (src_fd >= 0)
1100 close(src_fd);
1101 if (target_fd >= 0)
1102 close(target_fd);
1103 if (skel)
1104 test_tc_peer__destroy(skel);
1105 if (nstoken)
1106 close_netns(nstoken);
1107 }
1108
1109 #define RUN_TEST(name) \
1110 ({ \
1111 struct netns_setup_result setup_result; \
1112 if (test__start_subtest(#name)) \
1113 if (ASSERT_OK(netns_setup_namespaces("add"), "setup namespaces")) { \
1114 if (ASSERT_OK(netns_setup_links_and_routes(&setup_result), \
1115 "setup links and routes")) \
1116 test_ ## name(&setup_result); \
1117 netns_setup_namespaces("delete"); \
1118 } \
1119 })
1120
test_tc_redirect_run_tests(void * arg)1121 static void *test_tc_redirect_run_tests(void *arg)
1122 {
1123 netns_setup_namespaces_nofail("delete");
1124
1125 RUN_TEST(tc_redirect_peer);
1126 RUN_TEST(tc_redirect_peer_l3);
1127 RUN_TEST(tc_redirect_neigh);
1128 RUN_TEST(tc_redirect_neigh_fib);
1129 RUN_TEST(tc_redirect_dtime);
1130 return NULL;
1131 }
1132
test_tc_redirect(void)1133 void test_tc_redirect(void)
1134 {
1135 pthread_t test_thread;
1136 int err;
1137
1138 /* Run the tests in their own thread to isolate the namespace changes
1139 * so they do not affect the environment of other tests.
1140 * (specifically needed because of unshare(CLONE_NEWNS) in open_netns())
1141 */
1142 err = pthread_create(&test_thread, NULL, &test_tc_redirect_run_tests, NULL);
1143 if (ASSERT_OK(err, "pthread_create"))
1144 ASSERT_OK(pthread_join(test_thread, NULL), "pthread_join");
1145 }
1146