brintos

brintos / linux-shallow public Read only

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