1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 // Copyright (c) 2020 Cloudflare
3 /*
4 * Test BPF attach point for INET socket lookup (BPF_SK_LOOKUP).
5 *
6 * Tests exercise:
7 * - attaching/detaching/querying programs to BPF_SK_LOOKUP hook,
8 * - redirecting socket lookup to a socket selected by BPF program,
9 * - failing a socket lookup on BPF program's request,
10 * - error scenarios for selecting a socket from BPF program,
11 * - accessing BPF program context,
12 * - attaching and running multiple BPF programs.
13 *
14 * Tests run in a dedicated network namespace.
15 */
16
17 #define _GNU_SOURCE
18 #include <arpa/inet.h>
19 #include <assert.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <sched.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27
28 #include <bpf/libbpf.h>
29 #include <bpf/bpf.h>
30
31 #include "test_progs.h"
32 #include "bpf_util.h"
33 #include "cgroup_helpers.h"
34 #include "network_helpers.h"
35 #include "testing_helpers.h"
36 #include "test_sk_lookup.skel.h"
37
38 /* External (address, port) pairs the client sends packets to. */
39 #define EXT_IP4 "127.0.0.1"
40 #define EXT_IP6 "fd00::1"
41 #define EXT_PORT 7007
42
43 /* Internal (address, port) pairs the server listens/receives at. */
44 #define INT_IP4 "127.0.0.2"
45 #define INT_IP4_V6 "::ffff:127.0.0.2"
46 #define INT_IP6 "fd00::2"
47 #define INT_PORT 8008
48
49 #define IO_TIMEOUT_SEC 3
50
51 enum server {
52 SERVER_A = 0,
53 SERVER_B = 1,
54 MAX_SERVERS,
55 };
56
57 enum {
58 PROG1 = 0,
59 PROG2,
60 };
61
62 struct inet_addr {
63 const char *ip;
64 unsigned short port;
65 };
66
67 struct test {
68 const char *desc;
69 struct bpf_program *lookup_prog;
70 struct bpf_program *reuseport_prog;
71 struct bpf_map *sock_map;
72 int sotype;
73 struct inet_addr connect_to;
74 struct inet_addr listen_at;
75 enum server accept_on;
76 bool reuseport_has_conns; /* Add a connected socket to reuseport group */
77 };
78
79 static __u32 duration; /* for CHECK macro */
80
is_ipv6(const char * ip)81 static bool is_ipv6(const char *ip)
82 {
83 return !!strchr(ip, ':');
84 }
85
attach_reuseport(int sock_fd,struct bpf_program * reuseport_prog)86 static int attach_reuseport(int sock_fd, struct bpf_program *reuseport_prog)
87 {
88 int err, prog_fd;
89
90 prog_fd = bpf_program__fd(reuseport_prog);
91 if (prog_fd < 0) {
92 errno = -prog_fd;
93 return -1;
94 }
95
96 err = setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF,
97 &prog_fd, sizeof(prog_fd));
98 if (err)
99 return -1;
100
101 return 0;
102 }
103
inetaddr_len(const struct sockaddr_storage * addr)104 static socklen_t inetaddr_len(const struct sockaddr_storage *addr)
105 {
106 return (addr->ss_family == AF_INET ? sizeof(struct sockaddr_in) :
107 addr->ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : 0);
108 }
109
make_socket(int sotype,const char * ip,int port,struct sockaddr_storage * addr)110 static int make_socket(int sotype, const char *ip, int port,
111 struct sockaddr_storage *addr)
112 {
113 struct timeval timeo = { .tv_sec = IO_TIMEOUT_SEC };
114 int err, family, fd;
115
116 family = is_ipv6(ip) ? AF_INET6 : AF_INET;
117 err = make_sockaddr(family, ip, port, addr, NULL);
118 if (CHECK(err, "make_address", "failed\n"))
119 return -1;
120
121 fd = socket(addr->ss_family, sotype, 0);
122 if (CHECK(fd < 0, "socket", "failed\n")) {
123 log_err("failed to make socket");
124 return -1;
125 }
126
127 err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
128 if (CHECK(err, "setsockopt(SO_SNDTIMEO)", "failed\n")) {
129 log_err("failed to set SNDTIMEO");
130 close(fd);
131 return -1;
132 }
133
134 err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
135 if (CHECK(err, "setsockopt(SO_RCVTIMEO)", "failed\n")) {
136 log_err("failed to set RCVTIMEO");
137 close(fd);
138 return -1;
139 }
140
141 return fd;
142 }
143
make_server(int sotype,const char * ip,int port,struct bpf_program * reuseport_prog)144 static int make_server(int sotype, const char *ip, int port,
145 struct bpf_program *reuseport_prog)
146 {
147 struct sockaddr_storage addr = {0};
148 const int one = 1;
149 int err, fd = -1;
150
151 fd = make_socket(sotype, ip, port, &addr);
152 if (fd < 0)
153 return -1;
154
155 /* Enabled for UDPv6 sockets for IPv4-mapped IPv6 to work. */
156 if (sotype == SOCK_DGRAM) {
157 err = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &one,
158 sizeof(one));
159 if (CHECK(err, "setsockopt(IP_RECVORIGDSTADDR)", "failed\n")) {
160 log_err("failed to enable IP_RECVORIGDSTADDR");
161 goto fail;
162 }
163 }
164
165 if (sotype == SOCK_DGRAM && addr.ss_family == AF_INET6) {
166 err = setsockopt(fd, SOL_IPV6, IPV6_RECVORIGDSTADDR, &one,
167 sizeof(one));
168 if (CHECK(err, "setsockopt(IPV6_RECVORIGDSTADDR)", "failed\n")) {
169 log_err("failed to enable IPV6_RECVORIGDSTADDR");
170 goto fail;
171 }
172 }
173
174 if (sotype == SOCK_STREAM) {
175 err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one,
176 sizeof(one));
177 if (CHECK(err, "setsockopt(SO_REUSEADDR)", "failed\n")) {
178 log_err("failed to enable SO_REUSEADDR");
179 goto fail;
180 }
181 }
182
183 if (reuseport_prog) {
184 err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one,
185 sizeof(one));
186 if (CHECK(err, "setsockopt(SO_REUSEPORT)", "failed\n")) {
187 log_err("failed to enable SO_REUSEPORT");
188 goto fail;
189 }
190 }
191
192 err = bind(fd, (void *)&addr, inetaddr_len(&addr));
193 if (CHECK(err, "bind", "failed\n")) {
194 log_err("failed to bind listen socket");
195 goto fail;
196 }
197
198 if (sotype == SOCK_STREAM) {
199 err = listen(fd, SOMAXCONN);
200 if (CHECK(err, "make_server", "listen")) {
201 log_err("failed to listen on port %d", port);
202 goto fail;
203 }
204 }
205
206 /* Late attach reuseport prog so we can have one init path */
207 if (reuseport_prog) {
208 err = attach_reuseport(fd, reuseport_prog);
209 if (CHECK(err, "attach_reuseport", "failed\n")) {
210 log_err("failed to attach reuseport prog");
211 goto fail;
212 }
213 }
214
215 return fd;
216 fail:
217 close(fd);
218 return -1;
219 }
220
make_client(int sotype,const char * ip,int port)221 static int make_client(int sotype, const char *ip, int port)
222 {
223 struct sockaddr_storage addr = {0};
224 int err, fd;
225
226 fd = make_socket(sotype, ip, port, &addr);
227 if (fd < 0)
228 return -1;
229
230 err = connect(fd, (void *)&addr, inetaddr_len(&addr));
231 if (CHECK(err, "make_client", "connect")) {
232 log_err("failed to connect client socket");
233 goto fail;
234 }
235
236 return fd;
237 fail:
238 close(fd);
239 return -1;
240 }
241
socket_cookie(int fd)242 static __u64 socket_cookie(int fd)
243 {
244 __u64 cookie;
245 socklen_t cookie_len = sizeof(cookie);
246
247 if (CHECK(getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0,
248 "getsockopt(SO_COOKIE)", "%s\n", strerror(errno)))
249 return 0;
250 return cookie;
251 }
252
fill_sk_lookup_ctx(struct bpf_sk_lookup * ctx,const char * local_ip,__u16 local_port,const char * remote_ip,__u16 remote_port)253 static int fill_sk_lookup_ctx(struct bpf_sk_lookup *ctx, const char *local_ip, __u16 local_port,
254 const char *remote_ip, __u16 remote_port)
255 {
256 void *local, *remote;
257 int err;
258
259 memset(ctx, 0, sizeof(*ctx));
260 ctx->local_port = local_port;
261 ctx->remote_port = htons(remote_port);
262
263 if (is_ipv6(local_ip)) {
264 ctx->family = AF_INET6;
265 local = &ctx->local_ip6[0];
266 remote = &ctx->remote_ip6[0];
267 } else {
268 ctx->family = AF_INET;
269 local = &ctx->local_ip4;
270 remote = &ctx->remote_ip4;
271 }
272
273 err = inet_pton(ctx->family, local_ip, local);
274 if (CHECK(err != 1, "inet_pton", "local_ip failed\n"))
275 return 1;
276
277 err = inet_pton(ctx->family, remote_ip, remote);
278 if (CHECK(err != 1, "inet_pton", "remote_ip failed\n"))
279 return 1;
280
281 return 0;
282 }
283
send_byte(int fd)284 static int send_byte(int fd)
285 {
286 ssize_t n;
287
288 errno = 0;
289 n = send(fd, "a", 1, 0);
290 if (CHECK(n <= 0, "send_byte", "send")) {
291 log_err("failed/partial send");
292 return -1;
293 }
294 return 0;
295 }
296
recv_byte(int fd)297 static int recv_byte(int fd)
298 {
299 char buf[1];
300 ssize_t n;
301
302 n = recv(fd, buf, sizeof(buf), 0);
303 if (CHECK(n <= 0, "recv_byte", "recv")) {
304 log_err("failed/partial recv");
305 return -1;
306 }
307 return 0;
308 }
309
tcp_recv_send(int server_fd)310 static int tcp_recv_send(int server_fd)
311 {
312 char buf[1];
313 int ret, fd;
314 ssize_t n;
315
316 fd = accept(server_fd, NULL, NULL);
317 if (CHECK(fd < 0, "accept", "failed\n")) {
318 log_err("failed to accept");
319 return -1;
320 }
321
322 n = recv(fd, buf, sizeof(buf), 0);
323 if (CHECK(n <= 0, "recv", "failed\n")) {
324 log_err("failed/partial recv");
325 ret = -1;
326 goto close;
327 }
328
329 n = send(fd, buf, n, 0);
330 if (CHECK(n <= 0, "send", "failed\n")) {
331 log_err("failed/partial send");
332 ret = -1;
333 goto close;
334 }
335
336 ret = 0;
337 close:
338 close(fd);
339 return ret;
340 }
341
v4_to_v6(struct sockaddr_storage * ss)342 static void v4_to_v6(struct sockaddr_storage *ss)
343 {
344 struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)ss;
345 struct sockaddr_in v4 = *(struct sockaddr_in *)ss;
346
347 v6->sin6_family = AF_INET6;
348 v6->sin6_port = v4.sin_port;
349 v6->sin6_addr.s6_addr[10] = 0xff;
350 v6->sin6_addr.s6_addr[11] = 0xff;
351 memcpy(&v6->sin6_addr.s6_addr[12], &v4.sin_addr.s_addr, 4);
352 memset(&v6->sin6_addr.s6_addr[0], 0, 10);
353 }
354
udp_recv_send(int server_fd)355 static int udp_recv_send(int server_fd)
356 {
357 char cmsg_buf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
358 struct sockaddr_storage _src_addr = { 0 };
359 struct sockaddr_storage *src_addr = &_src_addr;
360 struct sockaddr_storage *dst_addr = NULL;
361 struct msghdr msg = { 0 };
362 struct iovec iov = { 0 };
363 struct cmsghdr *cm;
364 char buf[1];
365 int ret, fd;
366 ssize_t n;
367
368 iov.iov_base = buf;
369 iov.iov_len = sizeof(buf);
370
371 msg.msg_name = src_addr;
372 msg.msg_namelen = sizeof(*src_addr);
373 msg.msg_iov = &iov;
374 msg.msg_iovlen = 1;
375 msg.msg_control = cmsg_buf;
376 msg.msg_controllen = sizeof(cmsg_buf);
377
378 errno = 0;
379 n = recvmsg(server_fd, &msg, 0);
380 if (CHECK(n <= 0, "recvmsg", "failed\n")) {
381 log_err("failed to receive");
382 return -1;
383 }
384 if (CHECK(msg.msg_flags & MSG_CTRUNC, "recvmsg", "truncated cmsg\n"))
385 return -1;
386
387 for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) {
388 if ((cm->cmsg_level == SOL_IP &&
389 cm->cmsg_type == IP_ORIGDSTADDR) ||
390 (cm->cmsg_level == SOL_IPV6 &&
391 cm->cmsg_type == IPV6_ORIGDSTADDR)) {
392 dst_addr = (struct sockaddr_storage *)CMSG_DATA(cm);
393 break;
394 }
395 log_err("warning: ignored cmsg at level %d type %d",
396 cm->cmsg_level, cm->cmsg_type);
397 }
398 if (CHECK(!dst_addr, "recvmsg", "missing ORIGDSTADDR\n"))
399 return -1;
400
401 /* Server socket bound to IPv4-mapped IPv6 address */
402 if (src_addr->ss_family == AF_INET6 &&
403 dst_addr->ss_family == AF_INET) {
404 v4_to_v6(dst_addr);
405 }
406
407 /* Reply from original destination address. */
408 fd = socket(dst_addr->ss_family, SOCK_DGRAM, 0);
409 if (CHECK(fd < 0, "socket", "failed\n")) {
410 log_err("failed to create tx socket");
411 return -1;
412 }
413
414 ret = bind(fd, (struct sockaddr *)dst_addr, sizeof(*dst_addr));
415 if (CHECK(ret, "bind", "failed\n")) {
416 log_err("failed to bind tx socket");
417 goto out;
418 }
419
420 msg.msg_control = NULL;
421 msg.msg_controllen = 0;
422 n = sendmsg(fd, &msg, 0);
423 if (CHECK(n <= 0, "sendmsg", "failed\n")) {
424 log_err("failed to send echo reply");
425 ret = -1;
426 goto out;
427 }
428
429 ret = 0;
430 out:
431 close(fd);
432 return ret;
433 }
434
tcp_echo_test(int client_fd,int server_fd)435 static int tcp_echo_test(int client_fd, int server_fd)
436 {
437 int err;
438
439 err = send_byte(client_fd);
440 if (err)
441 return -1;
442 err = tcp_recv_send(server_fd);
443 if (err)
444 return -1;
445 err = recv_byte(client_fd);
446 if (err)
447 return -1;
448
449 return 0;
450 }
451
udp_echo_test(int client_fd,int server_fd)452 static int udp_echo_test(int client_fd, int server_fd)
453 {
454 int err;
455
456 err = send_byte(client_fd);
457 if (err)
458 return -1;
459 err = udp_recv_send(server_fd);
460 if (err)
461 return -1;
462 err = recv_byte(client_fd);
463 if (err)
464 return -1;
465
466 return 0;
467 }
468
attach_lookup_prog(struct bpf_program * prog)469 static struct bpf_link *attach_lookup_prog(struct bpf_program *prog)
470 {
471 struct bpf_link *link;
472 int net_fd;
473
474 net_fd = open("/proc/self/ns/net", O_RDONLY);
475 if (CHECK(net_fd < 0, "open", "failed\n")) {
476 log_err("failed to open /proc/self/ns/net");
477 return NULL;
478 }
479
480 link = bpf_program__attach_netns(prog, net_fd);
481 if (!ASSERT_OK_PTR(link, "bpf_program__attach_netns")) {
482 errno = -PTR_ERR(link);
483 log_err("failed to attach program '%s' to netns",
484 bpf_program__name(prog));
485 link = NULL;
486 }
487
488 close(net_fd);
489 return link;
490 }
491
update_lookup_map(struct bpf_map * map,int index,int sock_fd)492 static int update_lookup_map(struct bpf_map *map, int index, int sock_fd)
493 {
494 int err, map_fd;
495 uint64_t value;
496
497 map_fd = bpf_map__fd(map);
498 if (CHECK(map_fd < 0, "bpf_map__fd", "failed\n")) {
499 errno = -map_fd;
500 log_err("failed to get map FD");
501 return -1;
502 }
503
504 value = (uint64_t)sock_fd;
505 err = bpf_map_update_elem(map_fd, &index, &value, BPF_NOEXIST);
506 if (CHECK(err, "bpf_map_update_elem", "failed\n")) {
507 log_err("failed to update redir_map @ %d", index);
508 return -1;
509 }
510
511 return 0;
512 }
513
query_lookup_prog(struct test_sk_lookup * skel)514 static void query_lookup_prog(struct test_sk_lookup *skel)
515 {
516 struct bpf_link *link[3] = {};
517 struct bpf_link_info info;
518 __u32 attach_flags = 0;
519 __u32 prog_ids[3] = {};
520 __u32 prog_cnt = 3;
521 __u32 prog_id;
522 int net_fd;
523 int err;
524
525 net_fd = open("/proc/self/ns/net", O_RDONLY);
526 if (CHECK(net_fd < 0, "open", "failed\n")) {
527 log_err("failed to open /proc/self/ns/net");
528 return;
529 }
530
531 link[0] = attach_lookup_prog(skel->progs.lookup_pass);
532 if (!link[0])
533 goto close;
534 link[1] = attach_lookup_prog(skel->progs.lookup_pass);
535 if (!link[1])
536 goto detach;
537 link[2] = attach_lookup_prog(skel->progs.lookup_drop);
538 if (!link[2])
539 goto detach;
540
541 err = bpf_prog_query(net_fd, BPF_SK_LOOKUP, 0 /* query flags */,
542 &attach_flags, prog_ids, &prog_cnt);
543 if (CHECK(err, "bpf_prog_query", "failed\n")) {
544 log_err("failed to query lookup prog");
545 goto detach;
546 }
547
548 errno = 0;
549 if (CHECK(attach_flags != 0, "bpf_prog_query",
550 "wrong attach_flags on query: %u", attach_flags))
551 goto detach;
552 if (CHECK(prog_cnt != 3, "bpf_prog_query",
553 "wrong program count on query: %u", prog_cnt))
554 goto detach;
555 prog_id = link_info_prog_id(link[0], &info);
556 CHECK(prog_ids[0] != prog_id, "bpf_prog_query",
557 "invalid program #0 id on query: %u != %u\n",
558 prog_ids[0], prog_id);
559 CHECK(info.netns.netns_ino == 0, "netns_ino",
560 "unexpected netns_ino: %u\n", info.netns.netns_ino);
561 prog_id = link_info_prog_id(link[1], &info);
562 CHECK(prog_ids[1] != prog_id, "bpf_prog_query",
563 "invalid program #1 id on query: %u != %u\n",
564 prog_ids[1], prog_id);
565 CHECK(info.netns.netns_ino == 0, "netns_ino",
566 "unexpected netns_ino: %u\n", info.netns.netns_ino);
567 prog_id = link_info_prog_id(link[2], &info);
568 CHECK(prog_ids[2] != prog_id, "bpf_prog_query",
569 "invalid program #2 id on query: %u != %u\n",
570 prog_ids[2], prog_id);
571 CHECK(info.netns.netns_ino == 0, "netns_ino",
572 "unexpected netns_ino: %u\n", info.netns.netns_ino);
573
574 err = bpf_link__detach(link[0]);
575 if (CHECK(err, "link_detach", "failed %d\n", err))
576 goto detach;
577
578 /* prog id is still there, but netns_ino is zeroed out */
579 prog_id = link_info_prog_id(link[0], &info);
580 CHECK(prog_ids[0] != prog_id, "bpf_prog_query",
581 "invalid program #0 id on query: %u != %u\n",
582 prog_ids[0], prog_id);
583 CHECK(info.netns.netns_ino != 0, "netns_ino",
584 "unexpected netns_ino: %u\n", info.netns.netns_ino);
585
586 detach:
587 if (link[2])
588 bpf_link__destroy(link[2]);
589 if (link[1])
590 bpf_link__destroy(link[1]);
591 if (link[0])
592 bpf_link__destroy(link[0]);
593 close:
594 close(net_fd);
595 }
596
run_lookup_prog(const struct test * t)597 static void run_lookup_prog(const struct test *t)
598 {
599 int server_fds[] = { [0 ... MAX_SERVERS - 1] = -1 };
600 int client_fd, reuse_conn_fd = -1;
601 struct bpf_link *lookup_link;
602 int i, err;
603
604 lookup_link = attach_lookup_prog(t->lookup_prog);
605 if (!lookup_link)
606 return;
607
608 for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
609 server_fds[i] = make_server(t->sotype, t->listen_at.ip,
610 t->listen_at.port,
611 t->reuseport_prog);
612 if (server_fds[i] < 0)
613 goto close;
614
615 err = update_lookup_map(t->sock_map, i, server_fds[i]);
616 if (err)
617 goto close;
618
619 /* want just one server for non-reuseport test */
620 if (!t->reuseport_prog)
621 break;
622 }
623
624 /* Regular UDP socket lookup with reuseport behaves
625 * differently when reuseport group contains connected
626 * sockets. Check that adding a connected UDP socket to the
627 * reuseport group does not affect how reuseport works with
628 * BPF socket lookup.
629 */
630 if (t->reuseport_has_conns) {
631 struct sockaddr_storage addr = {};
632 socklen_t len = sizeof(addr);
633
634 /* Add an extra socket to reuseport group */
635 reuse_conn_fd = make_server(t->sotype, t->listen_at.ip,
636 t->listen_at.port,
637 t->reuseport_prog);
638 if (reuse_conn_fd < 0)
639 goto close;
640
641 /* Connect the extra socket to itself */
642 err = getsockname(reuse_conn_fd, (void *)&addr, &len);
643 if (CHECK(err, "getsockname", "errno %d\n", errno))
644 goto close;
645 err = connect(reuse_conn_fd, (void *)&addr, len);
646 if (CHECK(err, "connect", "errno %d\n", errno))
647 goto close;
648 }
649
650 client_fd = make_client(t->sotype, t->connect_to.ip, t->connect_to.port);
651 if (client_fd < 0)
652 goto close;
653
654 if (t->sotype == SOCK_STREAM)
655 tcp_echo_test(client_fd, server_fds[t->accept_on]);
656 else
657 udp_echo_test(client_fd, server_fds[t->accept_on]);
658
659 close(client_fd);
660 close:
661 if (reuse_conn_fd != -1)
662 close(reuse_conn_fd);
663 for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
664 if (server_fds[i] != -1)
665 close(server_fds[i]);
666 }
667 bpf_link__destroy(lookup_link);
668 }
669
test_redirect_lookup(struct test_sk_lookup * skel)670 static void test_redirect_lookup(struct test_sk_lookup *skel)
671 {
672 const struct test tests[] = {
673 {
674 .desc = "TCP IPv4 redir port",
675 .lookup_prog = skel->progs.redir_port,
676 .sock_map = skel->maps.redir_map,
677 .sotype = SOCK_STREAM,
678 .connect_to = { EXT_IP4, EXT_PORT },
679 .listen_at = { EXT_IP4, INT_PORT },
680 },
681 {
682 .desc = "TCP IPv4 redir addr",
683 .lookup_prog = skel->progs.redir_ip4,
684 .sock_map = skel->maps.redir_map,
685 .sotype = SOCK_STREAM,
686 .connect_to = { EXT_IP4, EXT_PORT },
687 .listen_at = { INT_IP4, EXT_PORT },
688 },
689 {
690 .desc = "TCP IPv4 redir with reuseport",
691 .lookup_prog = skel->progs.select_sock_a,
692 .reuseport_prog = skel->progs.select_sock_b,
693 .sock_map = skel->maps.redir_map,
694 .sotype = SOCK_STREAM,
695 .connect_to = { EXT_IP4, EXT_PORT },
696 .listen_at = { INT_IP4, INT_PORT },
697 .accept_on = SERVER_B,
698 },
699 {
700 .desc = "TCP IPv4 redir skip reuseport",
701 .lookup_prog = skel->progs.select_sock_a_no_reuseport,
702 .reuseport_prog = skel->progs.select_sock_b,
703 .sock_map = skel->maps.redir_map,
704 .sotype = SOCK_STREAM,
705 .connect_to = { EXT_IP4, EXT_PORT },
706 .listen_at = { INT_IP4, INT_PORT },
707 .accept_on = SERVER_A,
708 },
709 {
710 .desc = "TCP IPv6 redir port",
711 .lookup_prog = skel->progs.redir_port,
712 .sock_map = skel->maps.redir_map,
713 .sotype = SOCK_STREAM,
714 .connect_to = { EXT_IP6, EXT_PORT },
715 .listen_at = { EXT_IP6, INT_PORT },
716 },
717 {
718 .desc = "TCP IPv6 redir addr",
719 .lookup_prog = skel->progs.redir_ip6,
720 .sock_map = skel->maps.redir_map,
721 .sotype = SOCK_STREAM,
722 .connect_to = { EXT_IP6, EXT_PORT },
723 .listen_at = { INT_IP6, EXT_PORT },
724 },
725 {
726 .desc = "TCP IPv4->IPv6 redir port",
727 .lookup_prog = skel->progs.redir_port,
728 .sock_map = skel->maps.redir_map,
729 .sotype = SOCK_STREAM,
730 .connect_to = { EXT_IP4, EXT_PORT },
731 .listen_at = { INT_IP4_V6, INT_PORT },
732 },
733 {
734 .desc = "TCP IPv6 redir with reuseport",
735 .lookup_prog = skel->progs.select_sock_a,
736 .reuseport_prog = skel->progs.select_sock_b,
737 .sock_map = skel->maps.redir_map,
738 .sotype = SOCK_STREAM,
739 .connect_to = { EXT_IP6, EXT_PORT },
740 .listen_at = { INT_IP6, INT_PORT },
741 .accept_on = SERVER_B,
742 },
743 {
744 .desc = "TCP IPv6 redir skip reuseport",
745 .lookup_prog = skel->progs.select_sock_a_no_reuseport,
746 .reuseport_prog = skel->progs.select_sock_b,
747 .sock_map = skel->maps.redir_map,
748 .sotype = SOCK_STREAM,
749 .connect_to = { EXT_IP6, EXT_PORT },
750 .listen_at = { INT_IP6, INT_PORT },
751 .accept_on = SERVER_A,
752 },
753 {
754 .desc = "UDP IPv4 redir port",
755 .lookup_prog = skel->progs.redir_port,
756 .sock_map = skel->maps.redir_map,
757 .sotype = SOCK_DGRAM,
758 .connect_to = { EXT_IP4, EXT_PORT },
759 .listen_at = { EXT_IP4, INT_PORT },
760 },
761 {
762 .desc = "UDP IPv4 redir addr",
763 .lookup_prog = skel->progs.redir_ip4,
764 .sock_map = skel->maps.redir_map,
765 .sotype = SOCK_DGRAM,
766 .connect_to = { EXT_IP4, EXT_PORT },
767 .listen_at = { INT_IP4, EXT_PORT },
768 },
769 {
770 .desc = "UDP IPv4 redir with reuseport",
771 .lookup_prog = skel->progs.select_sock_a,
772 .reuseport_prog = skel->progs.select_sock_b,
773 .sock_map = skel->maps.redir_map,
774 .sotype = SOCK_DGRAM,
775 .connect_to = { EXT_IP4, EXT_PORT },
776 .listen_at = { INT_IP4, INT_PORT },
777 .accept_on = SERVER_B,
778 },
779 {
780 .desc = "UDP IPv4 redir and reuseport with conns",
781 .lookup_prog = skel->progs.select_sock_a,
782 .reuseport_prog = skel->progs.select_sock_b,
783 .sock_map = skel->maps.redir_map,
784 .sotype = SOCK_DGRAM,
785 .connect_to = { EXT_IP4, EXT_PORT },
786 .listen_at = { INT_IP4, INT_PORT },
787 .accept_on = SERVER_B,
788 .reuseport_has_conns = true,
789 },
790 {
791 .desc = "UDP IPv4 redir skip reuseport",
792 .lookup_prog = skel->progs.select_sock_a_no_reuseport,
793 .reuseport_prog = skel->progs.select_sock_b,
794 .sock_map = skel->maps.redir_map,
795 .sotype = SOCK_DGRAM,
796 .connect_to = { EXT_IP4, EXT_PORT },
797 .listen_at = { INT_IP4, INT_PORT },
798 .accept_on = SERVER_A,
799 },
800 {
801 .desc = "UDP IPv6 redir port",
802 .lookup_prog = skel->progs.redir_port,
803 .sock_map = skel->maps.redir_map,
804 .sotype = SOCK_DGRAM,
805 .connect_to = { EXT_IP6, EXT_PORT },
806 .listen_at = { EXT_IP6, INT_PORT },
807 },
808 {
809 .desc = "UDP IPv6 redir addr",
810 .lookup_prog = skel->progs.redir_ip6,
811 .sock_map = skel->maps.redir_map,
812 .sotype = SOCK_DGRAM,
813 .connect_to = { EXT_IP6, EXT_PORT },
814 .listen_at = { INT_IP6, EXT_PORT },
815 },
816 {
817 .desc = "UDP IPv4->IPv6 redir port",
818 .lookup_prog = skel->progs.redir_port,
819 .sock_map = skel->maps.redir_map,
820 .sotype = SOCK_DGRAM,
821 .listen_at = { INT_IP4_V6, INT_PORT },
822 .connect_to = { EXT_IP4, EXT_PORT },
823 },
824 {
825 .desc = "UDP IPv6 redir and reuseport",
826 .lookup_prog = skel->progs.select_sock_a,
827 .reuseport_prog = skel->progs.select_sock_b,
828 .sock_map = skel->maps.redir_map,
829 .sotype = SOCK_DGRAM,
830 .connect_to = { EXT_IP6, EXT_PORT },
831 .listen_at = { INT_IP6, INT_PORT },
832 .accept_on = SERVER_B,
833 },
834 {
835 .desc = "UDP IPv6 redir and reuseport with conns",
836 .lookup_prog = skel->progs.select_sock_a,
837 .reuseport_prog = skel->progs.select_sock_b,
838 .sock_map = skel->maps.redir_map,
839 .sotype = SOCK_DGRAM,
840 .connect_to = { EXT_IP6, EXT_PORT },
841 .listen_at = { INT_IP6, INT_PORT },
842 .accept_on = SERVER_B,
843 .reuseport_has_conns = true,
844 },
845 {
846 .desc = "UDP IPv6 redir skip reuseport",
847 .lookup_prog = skel->progs.select_sock_a_no_reuseport,
848 .reuseport_prog = skel->progs.select_sock_b,
849 .sock_map = skel->maps.redir_map,
850 .sotype = SOCK_DGRAM,
851 .connect_to = { EXT_IP6, EXT_PORT },
852 .listen_at = { INT_IP6, INT_PORT },
853 .accept_on = SERVER_A,
854 },
855 };
856 const struct test *t;
857
858 for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
859 if (test__start_subtest(t->desc))
860 run_lookup_prog(t);
861 }
862 }
863
drop_on_lookup(const struct test * t)864 static void drop_on_lookup(const struct test *t)
865 {
866 struct sockaddr_storage dst = {};
867 int client_fd, server_fd, err;
868 struct bpf_link *lookup_link;
869 ssize_t n;
870
871 lookup_link = attach_lookup_prog(t->lookup_prog);
872 if (!lookup_link)
873 return;
874
875 server_fd = make_server(t->sotype, t->listen_at.ip, t->listen_at.port,
876 t->reuseport_prog);
877 if (server_fd < 0)
878 goto detach;
879
880 client_fd = make_socket(t->sotype, t->connect_to.ip,
881 t->connect_to.port, &dst);
882 if (client_fd < 0)
883 goto close_srv;
884
885 err = connect(client_fd, (void *)&dst, inetaddr_len(&dst));
886 if (t->sotype == SOCK_DGRAM) {
887 err = send_byte(client_fd);
888 if (err)
889 goto close_all;
890
891 /* Read out asynchronous error */
892 n = recv(client_fd, NULL, 0, 0);
893 err = n == -1;
894 }
895 if (CHECK(!err || errno != ECONNREFUSED, "connect",
896 "unexpected success or error\n"))
897 log_err("expected ECONNREFUSED on connect");
898
899 close_all:
900 close(client_fd);
901 close_srv:
902 close(server_fd);
903 detach:
904 bpf_link__destroy(lookup_link);
905 }
906
test_drop_on_lookup(struct test_sk_lookup * skel)907 static void test_drop_on_lookup(struct test_sk_lookup *skel)
908 {
909 const struct test tests[] = {
910 {
911 .desc = "TCP IPv4 drop on lookup",
912 .lookup_prog = skel->progs.lookup_drop,
913 .sotype = SOCK_STREAM,
914 .connect_to = { EXT_IP4, EXT_PORT },
915 .listen_at = { EXT_IP4, EXT_PORT },
916 },
917 {
918 .desc = "TCP IPv6 drop on lookup",
919 .lookup_prog = skel->progs.lookup_drop,
920 .sotype = SOCK_STREAM,
921 .connect_to = { EXT_IP6, EXT_PORT },
922 .listen_at = { EXT_IP6, EXT_PORT },
923 },
924 {
925 .desc = "UDP IPv4 drop on lookup",
926 .lookup_prog = skel->progs.lookup_drop,
927 .sotype = SOCK_DGRAM,
928 .connect_to = { EXT_IP4, EXT_PORT },
929 .listen_at = { EXT_IP4, EXT_PORT },
930 },
931 {
932 .desc = "UDP IPv6 drop on lookup",
933 .lookup_prog = skel->progs.lookup_drop,
934 .sotype = SOCK_DGRAM,
935 .connect_to = { EXT_IP6, EXT_PORT },
936 .listen_at = { EXT_IP6, INT_PORT },
937 },
938 /* The program will drop on success, meaning that the ifindex
939 * was 1.
940 */
941 {
942 .desc = "TCP IPv4 drop on valid ifindex",
943 .lookup_prog = skel->progs.check_ifindex,
944 .sotype = SOCK_STREAM,
945 .connect_to = { EXT_IP4, EXT_PORT },
946 .listen_at = { EXT_IP4, EXT_PORT },
947 },
948 {
949 .desc = "TCP IPv6 drop on valid ifindex",
950 .lookup_prog = skel->progs.check_ifindex,
951 .sotype = SOCK_STREAM,
952 .connect_to = { EXT_IP6, EXT_PORT },
953 .listen_at = { EXT_IP6, EXT_PORT },
954 },
955 {
956 .desc = "UDP IPv4 drop on valid ifindex",
957 .lookup_prog = skel->progs.check_ifindex,
958 .sotype = SOCK_DGRAM,
959 .connect_to = { EXT_IP4, EXT_PORT },
960 .listen_at = { EXT_IP4, EXT_PORT },
961 },
962 {
963 .desc = "UDP IPv6 drop on valid ifindex",
964 .lookup_prog = skel->progs.check_ifindex,
965 .sotype = SOCK_DGRAM,
966 .connect_to = { EXT_IP6, EXT_PORT },
967 .listen_at = { EXT_IP6, EXT_PORT },
968 },
969 };
970 const struct test *t;
971
972 for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
973 if (test__start_subtest(t->desc))
974 drop_on_lookup(t);
975 }
976 }
977
drop_on_reuseport(const struct test * t)978 static void drop_on_reuseport(const struct test *t)
979 {
980 struct sockaddr_storage dst = { 0 };
981 int client, server1, server2, err;
982 struct bpf_link *lookup_link;
983 ssize_t n;
984
985 lookup_link = attach_lookup_prog(t->lookup_prog);
986 if (!lookup_link)
987 return;
988
989 server1 = make_server(t->sotype, t->listen_at.ip, t->listen_at.port,
990 t->reuseport_prog);
991 if (server1 < 0)
992 goto detach;
993
994 err = update_lookup_map(t->sock_map, SERVER_A, server1);
995 if (err)
996 goto close_srv1;
997
998 /* second server on destination address we should never reach */
999 server2 = make_server(t->sotype, t->connect_to.ip, t->connect_to.port,
1000 NULL /* reuseport prog */);
1001 if (server2 < 0)
1002 goto close_srv1;
1003
1004 client = make_socket(t->sotype, t->connect_to.ip,
1005 t->connect_to.port, &dst);
1006 if (client < 0)
1007 goto close_srv2;
1008
1009 err = connect(client, (void *)&dst, inetaddr_len(&dst));
1010 if (t->sotype == SOCK_DGRAM) {
1011 err = send_byte(client);
1012 if (err)
1013 goto close_all;
1014
1015 /* Read out asynchronous error */
1016 n = recv(client, NULL, 0, 0);
1017 err = n == -1;
1018 }
1019 if (CHECK(!err || errno != ECONNREFUSED, "connect",
1020 "unexpected success or error\n"))
1021 log_err("expected ECONNREFUSED on connect");
1022
1023 close_all:
1024 close(client);
1025 close_srv2:
1026 close(server2);
1027 close_srv1:
1028 close(server1);
1029 detach:
1030 bpf_link__destroy(lookup_link);
1031 }
1032
test_drop_on_reuseport(struct test_sk_lookup * skel)1033 static void test_drop_on_reuseport(struct test_sk_lookup *skel)
1034 {
1035 const struct test tests[] = {
1036 {
1037 .desc = "TCP IPv4 drop on reuseport",
1038 .lookup_prog = skel->progs.select_sock_a,
1039 .reuseport_prog = skel->progs.reuseport_drop,
1040 .sock_map = skel->maps.redir_map,
1041 .sotype = SOCK_STREAM,
1042 .connect_to = { EXT_IP4, EXT_PORT },
1043 .listen_at = { INT_IP4, INT_PORT },
1044 },
1045 {
1046 .desc = "TCP IPv6 drop on reuseport",
1047 .lookup_prog = skel->progs.select_sock_a,
1048 .reuseport_prog = skel->progs.reuseport_drop,
1049 .sock_map = skel->maps.redir_map,
1050 .sotype = SOCK_STREAM,
1051 .connect_to = { EXT_IP6, EXT_PORT },
1052 .listen_at = { INT_IP6, INT_PORT },
1053 },
1054 {
1055 .desc = "UDP IPv4 drop on reuseport",
1056 .lookup_prog = skel->progs.select_sock_a,
1057 .reuseport_prog = skel->progs.reuseport_drop,
1058 .sock_map = skel->maps.redir_map,
1059 .sotype = SOCK_DGRAM,
1060 .connect_to = { EXT_IP4, EXT_PORT },
1061 .listen_at = { INT_IP4, INT_PORT },
1062 },
1063 {
1064 .desc = "TCP IPv6 drop on reuseport",
1065 .lookup_prog = skel->progs.select_sock_a,
1066 .reuseport_prog = skel->progs.reuseport_drop,
1067 .sock_map = skel->maps.redir_map,
1068 .sotype = SOCK_STREAM,
1069 .connect_to = { EXT_IP6, EXT_PORT },
1070 .listen_at = { INT_IP6, INT_PORT },
1071 },
1072 };
1073 const struct test *t;
1074
1075 for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
1076 if (test__start_subtest(t->desc))
1077 drop_on_reuseport(t);
1078 }
1079 }
1080
run_sk_assign(struct test_sk_lookup * skel,struct bpf_program * lookup_prog,const char * remote_ip,const char * local_ip)1081 static void run_sk_assign(struct test_sk_lookup *skel,
1082 struct bpf_program *lookup_prog,
1083 const char *remote_ip, const char *local_ip)
1084 {
1085 int server_fds[] = { [0 ... MAX_SERVERS - 1] = -1 };
1086 struct bpf_sk_lookup ctx;
1087 __u64 server_cookie;
1088 int i, err;
1089
1090 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
1091 .ctx_in = &ctx,
1092 .ctx_size_in = sizeof(ctx),
1093 .ctx_out = &ctx,
1094 .ctx_size_out = sizeof(ctx),
1095 );
1096
1097 if (fill_sk_lookup_ctx(&ctx, local_ip, EXT_PORT, remote_ip, INT_PORT))
1098 return;
1099
1100 ctx.protocol = IPPROTO_TCP;
1101
1102 for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
1103 server_fds[i] = make_server(SOCK_STREAM, local_ip, 0, NULL);
1104 if (server_fds[i] < 0)
1105 goto close_servers;
1106
1107 err = update_lookup_map(skel->maps.redir_map, i,
1108 server_fds[i]);
1109 if (err)
1110 goto close_servers;
1111 }
1112
1113 server_cookie = socket_cookie(server_fds[SERVER_B]);
1114 if (!server_cookie)
1115 return;
1116
1117 err = bpf_prog_test_run_opts(bpf_program__fd(lookup_prog), &opts);
1118 if (CHECK(err, "test_run", "failed with error %d\n", errno))
1119 goto close_servers;
1120
1121 if (CHECK(ctx.cookie == 0, "ctx.cookie", "no socket selected\n"))
1122 goto close_servers;
1123
1124 CHECK(ctx.cookie != server_cookie, "ctx.cookie",
1125 "selected sk %llu instead of %llu\n", ctx.cookie, server_cookie);
1126
1127 close_servers:
1128 for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
1129 if (server_fds[i] != -1)
1130 close(server_fds[i]);
1131 }
1132 }
1133
run_sk_assign_v4(struct test_sk_lookup * skel,struct bpf_program * lookup_prog)1134 static void run_sk_assign_v4(struct test_sk_lookup *skel,
1135 struct bpf_program *lookup_prog)
1136 {
1137 run_sk_assign(skel, lookup_prog, INT_IP4, EXT_IP4);
1138 }
1139
run_sk_assign_v6(struct test_sk_lookup * skel,struct bpf_program * lookup_prog)1140 static void run_sk_assign_v6(struct test_sk_lookup *skel,
1141 struct bpf_program *lookup_prog)
1142 {
1143 run_sk_assign(skel, lookup_prog, INT_IP6, EXT_IP6);
1144 }
1145
run_sk_assign_connected(struct test_sk_lookup * skel,int sotype)1146 static void run_sk_assign_connected(struct test_sk_lookup *skel,
1147 int sotype)
1148 {
1149 int err, client_fd, connected_fd, server_fd;
1150 struct bpf_link *lookup_link;
1151
1152 server_fd = make_server(sotype, EXT_IP4, EXT_PORT, NULL);
1153 if (server_fd < 0)
1154 return;
1155
1156 connected_fd = make_client(sotype, EXT_IP4, EXT_PORT);
1157 if (connected_fd < 0)
1158 goto out_close_server;
1159
1160 /* Put a connected socket in redirect map */
1161 err = update_lookup_map(skel->maps.redir_map, SERVER_A, connected_fd);
1162 if (err)
1163 goto out_close_connected;
1164
1165 lookup_link = attach_lookup_prog(skel->progs.sk_assign_esocknosupport);
1166 if (!lookup_link)
1167 goto out_close_connected;
1168
1169 /* Try to redirect TCP SYN / UDP packet to a connected socket */
1170 client_fd = make_client(sotype, EXT_IP4, EXT_PORT);
1171 if (client_fd < 0)
1172 goto out_unlink_prog;
1173 if (sotype == SOCK_DGRAM) {
1174 send_byte(client_fd);
1175 recv_byte(server_fd);
1176 }
1177
1178 close(client_fd);
1179 out_unlink_prog:
1180 bpf_link__destroy(lookup_link);
1181 out_close_connected:
1182 close(connected_fd);
1183 out_close_server:
1184 close(server_fd);
1185 }
1186
test_sk_assign_helper(struct test_sk_lookup * skel)1187 static void test_sk_assign_helper(struct test_sk_lookup *skel)
1188 {
1189 if (test__start_subtest("sk_assign returns EEXIST"))
1190 run_sk_assign_v4(skel, skel->progs.sk_assign_eexist);
1191 if (test__start_subtest("sk_assign honors F_REPLACE"))
1192 run_sk_assign_v4(skel, skel->progs.sk_assign_replace_flag);
1193 if (test__start_subtest("sk_assign accepts NULL socket"))
1194 run_sk_assign_v4(skel, skel->progs.sk_assign_null);
1195 if (test__start_subtest("access ctx->sk"))
1196 run_sk_assign_v4(skel, skel->progs.access_ctx_sk);
1197 if (test__start_subtest("narrow access to ctx v4"))
1198 run_sk_assign_v4(skel, skel->progs.ctx_narrow_access);
1199 if (test__start_subtest("narrow access to ctx v6"))
1200 run_sk_assign_v6(skel, skel->progs.ctx_narrow_access);
1201 if (test__start_subtest("sk_assign rejects TCP established"))
1202 run_sk_assign_connected(skel, SOCK_STREAM);
1203 if (test__start_subtest("sk_assign rejects UDP connected"))
1204 run_sk_assign_connected(skel, SOCK_DGRAM);
1205 }
1206
1207 struct test_multi_prog {
1208 const char *desc;
1209 struct bpf_program *prog1;
1210 struct bpf_program *prog2;
1211 struct bpf_map *redir_map;
1212 struct bpf_map *run_map;
1213 int expect_errno;
1214 struct inet_addr listen_at;
1215 };
1216
run_multi_prog_lookup(const struct test_multi_prog * t)1217 static void run_multi_prog_lookup(const struct test_multi_prog *t)
1218 {
1219 struct sockaddr_storage dst = {};
1220 int map_fd, server_fd, client_fd;
1221 struct bpf_link *link1, *link2;
1222 int prog_idx, done, err;
1223
1224 map_fd = bpf_map__fd(t->run_map);
1225
1226 done = 0;
1227 prog_idx = PROG1;
1228 err = bpf_map_update_elem(map_fd, &prog_idx, &done, BPF_ANY);
1229 if (CHECK(err, "bpf_map_update_elem", "failed\n"))
1230 return;
1231 prog_idx = PROG2;
1232 err = bpf_map_update_elem(map_fd, &prog_idx, &done, BPF_ANY);
1233 if (CHECK(err, "bpf_map_update_elem", "failed\n"))
1234 return;
1235
1236 link1 = attach_lookup_prog(t->prog1);
1237 if (!link1)
1238 return;
1239 link2 = attach_lookup_prog(t->prog2);
1240 if (!link2)
1241 goto out_unlink1;
1242
1243 server_fd = make_server(SOCK_STREAM, t->listen_at.ip,
1244 t->listen_at.port, NULL);
1245 if (server_fd < 0)
1246 goto out_unlink2;
1247
1248 err = update_lookup_map(t->redir_map, SERVER_A, server_fd);
1249 if (err)
1250 goto out_close_server;
1251
1252 client_fd = make_socket(SOCK_STREAM, EXT_IP4, EXT_PORT, &dst);
1253 if (client_fd < 0)
1254 goto out_close_server;
1255
1256 err = connect(client_fd, (void *)&dst, inetaddr_len(&dst));
1257 if (CHECK(err && !t->expect_errno, "connect",
1258 "unexpected error %d\n", errno))
1259 goto out_close_client;
1260 if (CHECK(err && t->expect_errno && errno != t->expect_errno,
1261 "connect", "unexpected error %d\n", errno))
1262 goto out_close_client;
1263
1264 done = 0;
1265 prog_idx = PROG1;
1266 err = bpf_map_lookup_elem(map_fd, &prog_idx, &done);
1267 CHECK(err, "bpf_map_lookup_elem", "failed\n");
1268 CHECK(!done, "bpf_map_lookup_elem", "PROG1 !done\n");
1269
1270 done = 0;
1271 prog_idx = PROG2;
1272 err = bpf_map_lookup_elem(map_fd, &prog_idx, &done);
1273 CHECK(err, "bpf_map_lookup_elem", "failed\n");
1274 CHECK(!done, "bpf_map_lookup_elem", "PROG2 !done\n");
1275
1276 out_close_client:
1277 close(client_fd);
1278 out_close_server:
1279 close(server_fd);
1280 out_unlink2:
1281 bpf_link__destroy(link2);
1282 out_unlink1:
1283 bpf_link__destroy(link1);
1284 }
1285
test_multi_prog_lookup(struct test_sk_lookup * skel)1286 static void test_multi_prog_lookup(struct test_sk_lookup *skel)
1287 {
1288 struct test_multi_prog tests[] = {
1289 {
1290 .desc = "multi prog - pass, pass",
1291 .prog1 = skel->progs.multi_prog_pass1,
1292 .prog2 = skel->progs.multi_prog_pass2,
1293 .listen_at = { EXT_IP4, EXT_PORT },
1294 },
1295 {
1296 .desc = "multi prog - drop, drop",
1297 .prog1 = skel->progs.multi_prog_drop1,
1298 .prog2 = skel->progs.multi_prog_drop2,
1299 .listen_at = { EXT_IP4, EXT_PORT },
1300 .expect_errno = ECONNREFUSED,
1301 },
1302 {
1303 .desc = "multi prog - pass, drop",
1304 .prog1 = skel->progs.multi_prog_pass1,
1305 .prog2 = skel->progs.multi_prog_drop2,
1306 .listen_at = { EXT_IP4, EXT_PORT },
1307 .expect_errno = ECONNREFUSED,
1308 },
1309 {
1310 .desc = "multi prog - drop, pass",
1311 .prog1 = skel->progs.multi_prog_drop1,
1312 .prog2 = skel->progs.multi_prog_pass2,
1313 .listen_at = { EXT_IP4, EXT_PORT },
1314 .expect_errno = ECONNREFUSED,
1315 },
1316 {
1317 .desc = "multi prog - pass, redir",
1318 .prog1 = skel->progs.multi_prog_pass1,
1319 .prog2 = skel->progs.multi_prog_redir2,
1320 .listen_at = { INT_IP4, INT_PORT },
1321 },
1322 {
1323 .desc = "multi prog - redir, pass",
1324 .prog1 = skel->progs.multi_prog_redir1,
1325 .prog2 = skel->progs.multi_prog_pass2,
1326 .listen_at = { INT_IP4, INT_PORT },
1327 },
1328 {
1329 .desc = "multi prog - drop, redir",
1330 .prog1 = skel->progs.multi_prog_drop1,
1331 .prog2 = skel->progs.multi_prog_redir2,
1332 .listen_at = { INT_IP4, INT_PORT },
1333 },
1334 {
1335 .desc = "multi prog - redir, drop",
1336 .prog1 = skel->progs.multi_prog_redir1,
1337 .prog2 = skel->progs.multi_prog_drop2,
1338 .listen_at = { INT_IP4, INT_PORT },
1339 },
1340 {
1341 .desc = "multi prog - redir, redir",
1342 .prog1 = skel->progs.multi_prog_redir1,
1343 .prog2 = skel->progs.multi_prog_redir2,
1344 .listen_at = { INT_IP4, INT_PORT },
1345 },
1346 };
1347 struct test_multi_prog *t;
1348
1349 for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
1350 t->redir_map = skel->maps.redir_map;
1351 t->run_map = skel->maps.run_map;
1352 if (test__start_subtest(t->desc))
1353 run_multi_prog_lookup(t);
1354 }
1355 }
1356
run_tests(struct test_sk_lookup * skel)1357 static void run_tests(struct test_sk_lookup *skel)
1358 {
1359 if (test__start_subtest("query lookup prog"))
1360 query_lookup_prog(skel);
1361 test_redirect_lookup(skel);
1362 test_drop_on_lookup(skel);
1363 test_drop_on_reuseport(skel);
1364 test_sk_assign_helper(skel);
1365 test_multi_prog_lookup(skel);
1366 }
1367
switch_netns(void)1368 static int switch_netns(void)
1369 {
1370 static const char * const setup_script[] = {
1371 "ip -6 addr add dev lo " EXT_IP6 "/128",
1372 "ip -6 addr add dev lo " INT_IP6 "/128",
1373 "ip link set dev lo up",
1374 NULL,
1375 };
1376 const char * const *cmd;
1377 int err;
1378
1379 err = unshare(CLONE_NEWNET);
1380 if (CHECK(err, "unshare", "failed\n")) {
1381 log_err("unshare(CLONE_NEWNET)");
1382 return -1;
1383 }
1384
1385 for (cmd = setup_script; *cmd; cmd++) {
1386 err = system(*cmd);
1387 if (CHECK(err, "system", "failed\n")) {
1388 log_err("system(%s)", *cmd);
1389 return -1;
1390 }
1391 }
1392
1393 return 0;
1394 }
1395
test_sk_lookup(void)1396 void test_sk_lookup(void)
1397 {
1398 struct test_sk_lookup *skel;
1399 int err;
1400
1401 err = switch_netns();
1402 if (err)
1403 return;
1404
1405 skel = test_sk_lookup__open_and_load();
1406 if (CHECK(!skel, "skel open_and_load", "failed\n"))
1407 return;
1408
1409 run_tests(skel);
1410
1411 test_sk_lookup__destroy(skel);
1412 }
1413