brintos

brintos / linux-shallow public Read only

0
0
Text · 7.4 KiB · 0b9bd1d Raw
298 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2018 Facebook3// Copyright (c) 2019 Cloudflare4// Copyright (c) 2020 Isovalent, Inc.5/*6 * Test that the socket assign program is able to redirect traffic towards a7 * socket, regardless of whether the port or address destination of the traffic8 * matches the port.9 */10 11#define _GNU_SOURCE12#include <fcntl.h>13#include <signal.h>14#include <stdlib.h>15#include <unistd.h>16 17#include "test_progs.h"18#include "network_helpers.h"19 20#define BIND_PORT 123421#define CONNECT_PORT 432122#define TEST_DADDR (0xC0A80203)23#define NS_SELF "/proc/self/ns/net"24#define SERVER_MAP_PATH "/sys/fs/bpf/tc/globals/server_map"25 26static int stop, duration;27 28static bool29configure_stack(void)30{31	char tc_version[128];32	char tc_cmd[BUFSIZ];33	char *prog;34	FILE *tc;35 36	/* Check whether tc is built with libbpf. */37	tc = popen("tc -V", "r");38	if (CHECK_FAIL(!tc))39		return false;40	if (CHECK_FAIL(!fgets(tc_version, sizeof(tc_version), tc)))41		return false;42	if (strstr(tc_version, ", libbpf "))43		prog = "test_sk_assign_libbpf.bpf.o";44	else45		prog = "test_sk_assign.bpf.o";46	if (CHECK_FAIL(pclose(tc)))47		return false;48 49	/* Move to a new networking namespace */50	if (CHECK_FAIL(unshare(CLONE_NEWNET)))51		return false;52 53	/* Configure necessary links, routes */54	if (CHECK_FAIL(system("ip link set dev lo up")))55		return false;56	if (CHECK_FAIL(system("ip route add local default dev lo")))57		return false;58	if (CHECK_FAIL(system("ip -6 route add local default dev lo")))59		return false;60 61	/* Load qdisc, BPF program */62	if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))63		return false;64	sprintf(tc_cmd, "%s %s %s %s %s", "tc filter add dev lo ingress bpf",65		       "direct-action object-file", prog,66		       "section tc",67		       (env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : "verbose");68	if (CHECK(system(tc_cmd), "BPF load failed;",69		  "run with -vv for more info\n"))70		return false;71 72	return true;73}74 75static in_port_t76get_port(int fd)77{78	struct sockaddr_storage ss;79	socklen_t slen = sizeof(ss);80	in_port_t port = 0;81 82	if (CHECK_FAIL(getsockname(fd, (struct sockaddr *)&ss, &slen)))83		return port;84 85	switch (ss.ss_family) {86	case AF_INET:87		port = ((struct sockaddr_in *)&ss)->sin_port;88		break;89	case AF_INET6:90		port = ((struct sockaddr_in6 *)&ss)->sin6_port;91		break;92	default:93		CHECK(1, "Invalid address family", "%d\n", ss.ss_family);94	}95	return port;96}97 98static ssize_t99rcv_msg(int srv_client, int type)100{101	char buf[BUFSIZ];102 103	if (type == SOCK_STREAM)104		return read(srv_client, &buf, sizeof(buf));105	else106		return recvfrom(srv_client, &buf, sizeof(buf), 0, NULL, NULL);107}108 109static int110run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)111{112	int client = -1, srv_client = -1;113	char buf[] = "testing";114	in_port_t port;115	int ret = 1;116 117	client = connect_to_addr(type, (struct sockaddr_storage *)addr, len, NULL);118	if (client == -1) {119		perror("Cannot connect to server");120		goto out;121	}122 123	if (type == SOCK_STREAM) {124		srv_client = accept(server_fd, NULL, NULL);125		if (CHECK_FAIL(srv_client == -1)) {126			perror("Can't accept connection");127			goto out;128		}129	} else {130		srv_client = server_fd;131	}132	if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {133		perror("Can't write on client");134		goto out;135	}136	if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {137		perror("Can't read on server");138		goto out;139	}140 141	port = get_port(srv_client);142	if (CHECK_FAIL(!port))143		goto out;144	/* SOCK_STREAM is connected via accept(), so the server's local address145	 * will be the CONNECT_PORT rather than the BIND port that corresponds146	 * to the listen socket. SOCK_DGRAM on the other hand is connectionless147	 * so we can't really do the same check there; the server doesn't ever148	 * create a socket with CONNECT_PORT.149	 */150	if (type == SOCK_STREAM &&151	    CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",152		  CONNECT_PORT, ntohs(port)))153		goto out;154	else if (type == SOCK_DGRAM &&155		 CHECK(port != htons(BIND_PORT), "Expected",156		       "port %u but got %u", BIND_PORT, ntohs(port)))157		goto out;158 159	ret = 0;160out:161	close(client);162	if (srv_client != server_fd)163		close(srv_client);164	if (ret)165		WRITE_ONCE(stop, 1);166	return ret;167}168 169static void170prepare_addr(struct sockaddr *addr, int family, __u16 port, bool rewrite_addr)171{172	struct sockaddr_in *addr4;173	struct sockaddr_in6 *addr6;174 175	switch (family) {176	case AF_INET:177		addr4 = (struct sockaddr_in *)addr;178		memset(addr4, 0, sizeof(*addr4));179		addr4->sin_family = family;180		addr4->sin_port = htons(port);181		if (rewrite_addr)182			addr4->sin_addr.s_addr = htonl(TEST_DADDR);183		else184			addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);185		break;186	case AF_INET6:187		addr6 = (struct sockaddr_in6 *)addr;188		memset(addr6, 0, sizeof(*addr6));189		addr6->sin6_family = family;190		addr6->sin6_port = htons(port);191		addr6->sin6_addr = in6addr_loopback;192		if (rewrite_addr)193			addr6->sin6_addr.s6_addr32[3] = htonl(TEST_DADDR);194		break;195	default:196		fprintf(stderr, "Invalid family %d", family);197	}198}199 200struct test_sk_cfg {201	const char *name;202	int family;203	struct sockaddr *addr;204	socklen_t len;205	int type;206	bool rewrite_addr;207};208 209#define TEST(NAME, FAMILY, TYPE, REWRITE)				\210{									\211	.name = NAME,							\212	.family = FAMILY,						\213	.addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4		\214				    : (struct sockaddr *)&addr6,	\215	.len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6),	\216	.type = TYPE,							\217	.rewrite_addr = REWRITE,					\218}219 220void test_sk_assign(void)221{222	struct sockaddr_in addr4;223	struct sockaddr_in6 addr6;224	struct test_sk_cfg tests[] = {225		TEST("ipv4 tcp port redir", AF_INET, SOCK_STREAM, false),226		TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),227		TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),228		TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),229		TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),230		TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),231		TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),232		TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),233	};234	__s64 server = -1;235	int server_map;236	int self_net;237	int i;238 239	self_net = open(NS_SELF, O_RDONLY);240	if (CHECK_FAIL(self_net < 0)) {241		perror("Unable to open "NS_SELF);242		return;243	}244 245	if (!configure_stack()) {246		perror("configure_stack");247		goto cleanup;248	}249 250	server_map = bpf_obj_get(SERVER_MAP_PATH);251	if (CHECK_FAIL(server_map < 0)) {252		perror("Unable to open " SERVER_MAP_PATH);253		goto cleanup;254	}255 256	for (i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {257		struct test_sk_cfg *test = &tests[i];258		const struct sockaddr *addr;259		const int zero = 0;260		int err;261 262		if (!test__start_subtest(test->name))263			continue;264		prepare_addr(test->addr, test->family, BIND_PORT, false);265		addr = (const struct sockaddr *)test->addr;266		server = start_server_addr(test->type,267					   (const struct sockaddr_storage *)addr,268					   test->len, NULL);269		if (server == -1)270			goto close;271 272		err = bpf_map_update_elem(server_map, &zero, &server, BPF_ANY);273		if (CHECK_FAIL(err)) {274			perror("Unable to update server_map");275			goto close;276		}277 278		/* connect to unbound ports */279		prepare_addr(test->addr, test->family, CONNECT_PORT,280			     test->rewrite_addr);281		if (run_test(server, addr, test->len, test->type))282			goto close;283 284		close(server);285		server = -1;286	}287 288close:289	close(server);290	close(server_map);291cleanup:292	if (CHECK_FAIL(unlink(SERVER_MAP_PATH)))293		perror("Unable to unlink " SERVER_MAP_PATH);294	if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))295		perror("Failed to setns("NS_SELF")");296	close(self_net);297}298