File Index
Files and directories
Sign in
FILE

sk_assign.c

File preview with syntax highlighting.

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