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