Linux Audio

Check our new training course

Loading...
v6.8
  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
 19#define BIND_PORT 1234
 20#define CONNECT_PORT 4321
 21#define TEST_DADDR (0xC0A80203)
 22#define NS_SELF "/proc/self/ns/net"
 23#define SERVER_MAP_PATH "/sys/fs/bpf/tc/globals/server_map"
 24
 25static const struct timeval timeo_sec = { .tv_sec = 3 };
 26static const size_t timeo_optlen = sizeof(timeo_sec);
 27static int stop, duration;
 28
 29static bool
 30configure_stack(void)
 31{
 32	char tc_version[128];
 33	char tc_cmd[BUFSIZ];
 34	char *prog;
 35	FILE *tc;
 36
 37	/* Check whether tc is built with libbpf. */
 38	tc = popen("tc -V", "r");
 39	if (CHECK_FAIL(!tc))
 40		return false;
 41	if (CHECK_FAIL(!fgets(tc_version, sizeof(tc_version), tc)))
 42		return false;
 43	if (strstr(tc_version, ", libbpf "))
 44		prog = "test_sk_assign_libbpf.bpf.o";
 45	else
 46		prog = "test_sk_assign.bpf.o";
 47	if (CHECK_FAIL(pclose(tc)))
 48		return false;
 49
 50	/* Move to a new networking namespace */
 51	if (CHECK_FAIL(unshare(CLONE_NEWNET)))
 52		return false;
 53
 54	/* Configure necessary links, routes */
 55	if (CHECK_FAIL(system("ip link set dev lo up")))
 56		return false;
 57	if (CHECK_FAIL(system("ip route add local default dev lo")))
 58		return false;
 59	if (CHECK_FAIL(system("ip -6 route add local default dev lo")))
 60		return false;
 61
 62	/* Load qdisc, BPF program */
 63	if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))
 64		return false;
 65	sprintf(tc_cmd, "%s %s %s %s %s", "tc filter add dev lo ingress bpf",
 66		       "direct-action object-file", prog,
 67		       "section tc",
 68		       (env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : "verbose");
 69	if (CHECK(system(tc_cmd), "BPF load failed;",
 70		  "run with -vv for more info\n"))
 71		return false;
 72
 73	return true;
 74}
 75
 76static int
 77start_server(const struct sockaddr *addr, socklen_t len, int type)
 78{
 79	int fd;
 80
 81	fd = socket(addr->sa_family, type, 0);
 82	if (CHECK_FAIL(fd == -1))
 83		goto out;
 84	if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec,
 85				  timeo_optlen)))
 86		goto close_out;
 87	if (CHECK_FAIL(bind(fd, addr, len) == -1))
 88		goto close_out;
 89	if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
 90		goto close_out;
 91
 92	goto out;
 93close_out:
 94	close(fd);
 95	fd = -1;
 96out:
 97	return fd;
 98}
 99
100static int
101connect_to_server(const struct sockaddr *addr, socklen_t len, int type)
102{
103	int fd = -1;
104
105	fd = socket(addr->sa_family, type, 0);
106	if (CHECK_FAIL(fd == -1))
107		goto out;
108	if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo_sec,
109				  timeo_optlen)))
110		goto close_out;
111	if (CHECK_FAIL(connect(fd, addr, len)))
112		goto close_out;
113
114	goto out;
115close_out:
116	close(fd);
117	fd = -1;
118out:
119	return fd;
120}
121
122static in_port_t
123get_port(int fd)
124{
125	struct sockaddr_storage ss;
126	socklen_t slen = sizeof(ss);
127	in_port_t port = 0;
128
129	if (CHECK_FAIL(getsockname(fd, (struct sockaddr *)&ss, &slen)))
130		return port;
131
132	switch (ss.ss_family) {
133	case AF_INET:
134		port = ((struct sockaddr_in *)&ss)->sin_port;
135		break;
136	case AF_INET6:
137		port = ((struct sockaddr_in6 *)&ss)->sin6_port;
138		break;
139	default:
140		CHECK(1, "Invalid address family", "%d\n", ss.ss_family);
141	}
142	return port;
143}
144
145static ssize_t
146rcv_msg(int srv_client, int type)
147{
 
148	char buf[BUFSIZ];
 
149
150	if (type == SOCK_STREAM)
151		return read(srv_client, &buf, sizeof(buf));
152	else
153		return recvfrom(srv_client, &buf, sizeof(buf), 0, NULL, NULL);
 
154}
155
156static int
157run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
158{
159	int client = -1, srv_client = -1;
160	char buf[] = "testing";
161	in_port_t port;
162	int ret = 1;
163
164	client = connect_to_server(addr, len, type);
165	if (client == -1) {
166		perror("Cannot connect to server");
167		goto out;
168	}
169
170	if (type == SOCK_STREAM) {
171		srv_client = accept(server_fd, NULL, NULL);
172		if (CHECK_FAIL(srv_client == -1)) {
173			perror("Can't accept connection");
174			goto out;
175		}
176	} else {
177		srv_client = server_fd;
178	}
179	if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
180		perror("Can't write on client");
181		goto out;
182	}
183	if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
184		perror("Can't read on server");
185		goto out;
186	}
187
188	port = get_port(srv_client);
189	if (CHECK_FAIL(!port))
190		goto out;
191	/* SOCK_STREAM is connected via accept(), so the server's local address
192	 * will be the CONNECT_PORT rather than the BIND port that corresponds
193	 * to the listen socket. SOCK_DGRAM on the other hand is connectionless
194	 * so we can't really do the same check there; the server doesn't ever
195	 * create a socket with CONNECT_PORT.
196	 */
197	if (type == SOCK_STREAM &&
198	    CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
199		  CONNECT_PORT, ntohs(port)))
200		goto out;
201	else if (type == SOCK_DGRAM &&
202		 CHECK(port != htons(BIND_PORT), "Expected",
203		       "port %u but got %u", BIND_PORT, ntohs(port)))
204		goto out;
205
206	ret = 0;
207out:
208	close(client);
209	if (srv_client != server_fd)
210		close(srv_client);
211	if (ret)
212		WRITE_ONCE(stop, 1);
213	return ret;
214}
215
216static void
217prepare_addr(struct sockaddr *addr, int family, __u16 port, bool rewrite_addr)
218{
219	struct sockaddr_in *addr4;
220	struct sockaddr_in6 *addr6;
221
222	switch (family) {
223	case AF_INET:
224		addr4 = (struct sockaddr_in *)addr;
225		memset(addr4, 0, sizeof(*addr4));
226		addr4->sin_family = family;
227		addr4->sin_port = htons(port);
228		if (rewrite_addr)
229			addr4->sin_addr.s_addr = htonl(TEST_DADDR);
230		else
231			addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
232		break;
233	case AF_INET6:
234		addr6 = (struct sockaddr_in6 *)addr;
235		memset(addr6, 0, sizeof(*addr6));
236		addr6->sin6_family = family;
237		addr6->sin6_port = htons(port);
238		addr6->sin6_addr = in6addr_loopback;
239		if (rewrite_addr)
240			addr6->sin6_addr.s6_addr32[3] = htonl(TEST_DADDR);
241		break;
242	default:
243		fprintf(stderr, "Invalid family %d", family);
244	}
245}
246
247struct test_sk_cfg {
248	const char *name;
249	int family;
250	struct sockaddr *addr;
251	socklen_t len;
252	int type;
253	bool rewrite_addr;
254};
255
256#define TEST(NAME, FAMILY, TYPE, REWRITE)				\
257{									\
258	.name = NAME,							\
259	.family = FAMILY,						\
260	.addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4		\
261				    : (struct sockaddr *)&addr6,	\
262	.len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6),	\
263	.type = TYPE,							\
264	.rewrite_addr = REWRITE,					\
265}
266
267void test_sk_assign(void)
268{
269	struct sockaddr_in addr4;
270	struct sockaddr_in6 addr6;
271	struct test_sk_cfg tests[] = {
272		TEST("ipv4 tcp port redir", AF_INET, SOCK_STREAM, false),
273		TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
274		TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
275		TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
276		TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
277		TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
278		TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
279		TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
280	};
281	__s64 server = -1;
282	int server_map;
283	int self_net;
284	int i;
285
286	self_net = open(NS_SELF, O_RDONLY);
287	if (CHECK_FAIL(self_net < 0)) {
288		perror("Unable to open "NS_SELF);
289		return;
290	}
291
292	if (!configure_stack()) {
293		perror("configure_stack");
294		goto cleanup;
295	}
296
297	server_map = bpf_obj_get(SERVER_MAP_PATH);
298	if (CHECK_FAIL(server_map < 0)) {
299		perror("Unable to open " SERVER_MAP_PATH);
300		goto cleanup;
301	}
302
303	for (i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {
304		struct test_sk_cfg *test = &tests[i];
305		const struct sockaddr *addr;
306		const int zero = 0;
307		int err;
308
309		if (!test__start_subtest(test->name))
310			continue;
311		prepare_addr(test->addr, test->family, BIND_PORT, false);
312		addr = (const struct sockaddr *)test->addr;
313		server = start_server(addr, test->len, test->type);
314		if (server == -1)
315			goto close;
316
317		err = bpf_map_update_elem(server_map, &zero, &server, BPF_ANY);
318		if (CHECK_FAIL(err)) {
319			perror("Unable to update server_map");
320			goto close;
321		}
322
323		/* connect to unbound ports */
324		prepare_addr(test->addr, test->family, CONNECT_PORT,
325			     test->rewrite_addr);
326		if (run_test(server, addr, test->len, test->type))
327			goto close;
328
329		close(server);
330		server = -1;
331	}
332
333close:
334	close(server);
335	close(server_map);
336cleanup:
337	if (CHECK_FAIL(unlink(SERVER_MAP_PATH)))
338		perror("Unable to unlink " SERVER_MAP_PATH);
339	if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))
340		perror("Failed to setns("NS_SELF")");
341	close(self_net);
342}
v6.2
  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
 19#define BIND_PORT 1234
 20#define CONNECT_PORT 4321
 21#define TEST_DADDR (0xC0A80203)
 22#define NS_SELF "/proc/self/ns/net"
 23#define SERVER_MAP_PATH "/sys/fs/bpf/tc/globals/server_map"
 24
 25static const struct timeval timeo_sec = { .tv_sec = 3 };
 26static const size_t timeo_optlen = sizeof(timeo_sec);
 27static int stop, duration;
 28
 29static bool
 30configure_stack(void)
 31{
 
 32	char tc_cmd[BUFSIZ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 33
 34	/* Move to a new networking namespace */
 35	if (CHECK_FAIL(unshare(CLONE_NEWNET)))
 36		return false;
 37
 38	/* Configure necessary links, routes */
 39	if (CHECK_FAIL(system("ip link set dev lo up")))
 40		return false;
 41	if (CHECK_FAIL(system("ip route add local default dev lo")))
 42		return false;
 43	if (CHECK_FAIL(system("ip -6 route add local default dev lo")))
 44		return false;
 45
 46	/* Load qdisc, BPF program */
 47	if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))
 48		return false;
 49	sprintf(tc_cmd, "%s %s %s %s", "tc filter add dev lo ingress bpf",
 50		       "direct-action object-file ./test_sk_assign.bpf.o",
 51		       "section tc",
 52		       (env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : "verbose");
 53	if (CHECK(system(tc_cmd), "BPF load failed;",
 54		  "run with -vv for more info\n"))
 55		return false;
 56
 57	return true;
 58}
 59
 60static int
 61start_server(const struct sockaddr *addr, socklen_t len, int type)
 62{
 63	int fd;
 64
 65	fd = socket(addr->sa_family, type, 0);
 66	if (CHECK_FAIL(fd == -1))
 67		goto out;
 68	if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec,
 69				  timeo_optlen)))
 70		goto close_out;
 71	if (CHECK_FAIL(bind(fd, addr, len) == -1))
 72		goto close_out;
 73	if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
 74		goto close_out;
 75
 76	goto out;
 77close_out:
 78	close(fd);
 79	fd = -1;
 80out:
 81	return fd;
 82}
 83
 84static int
 85connect_to_server(const struct sockaddr *addr, socklen_t len, int type)
 86{
 87	int fd = -1;
 88
 89	fd = socket(addr->sa_family, type, 0);
 90	if (CHECK_FAIL(fd == -1))
 91		goto out;
 92	if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo_sec,
 93				  timeo_optlen)))
 94		goto close_out;
 95	if (CHECK_FAIL(connect(fd, addr, len)))
 96		goto close_out;
 97
 98	goto out;
 99close_out:
100	close(fd);
101	fd = -1;
102out:
103	return fd;
104}
105
106static in_port_t
107get_port(int fd)
108{
109	struct sockaddr_storage ss;
110	socklen_t slen = sizeof(ss);
111	in_port_t port = 0;
112
113	if (CHECK_FAIL(getsockname(fd, (struct sockaddr *)&ss, &slen)))
114		return port;
115
116	switch (ss.ss_family) {
117	case AF_INET:
118		port = ((struct sockaddr_in *)&ss)->sin_port;
119		break;
120	case AF_INET6:
121		port = ((struct sockaddr_in6 *)&ss)->sin6_port;
122		break;
123	default:
124		CHECK(1, "Invalid address family", "%d\n", ss.ss_family);
125	}
126	return port;
127}
128
129static ssize_t
130rcv_msg(int srv_client, int type)
131{
132	struct sockaddr_storage ss;
133	char buf[BUFSIZ];
134	socklen_t slen;
135
136	if (type == SOCK_STREAM)
137		return read(srv_client, &buf, sizeof(buf));
138	else
139		return recvfrom(srv_client, &buf, sizeof(buf), 0,
140				(struct sockaddr *)&ss, &slen);
141}
142
143static int
144run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
145{
146	int client = -1, srv_client = -1;
147	char buf[] = "testing";
148	in_port_t port;
149	int ret = 1;
150
151	client = connect_to_server(addr, len, type);
152	if (client == -1) {
153		perror("Cannot connect to server");
154		goto out;
155	}
156
157	if (type == SOCK_STREAM) {
158		srv_client = accept(server_fd, NULL, NULL);
159		if (CHECK_FAIL(srv_client == -1)) {
160			perror("Can't accept connection");
161			goto out;
162		}
163	} else {
164		srv_client = server_fd;
165	}
166	if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
167		perror("Can't write on client");
168		goto out;
169	}
170	if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
171		perror("Can't read on server");
172		goto out;
173	}
174
175	port = get_port(srv_client);
176	if (CHECK_FAIL(!port))
177		goto out;
178	/* SOCK_STREAM is connected via accept(), so the server's local address
179	 * will be the CONNECT_PORT rather than the BIND port that corresponds
180	 * to the listen socket. SOCK_DGRAM on the other hand is connectionless
181	 * so we can't really do the same check there; the server doesn't ever
182	 * create a socket with CONNECT_PORT.
183	 */
184	if (type == SOCK_STREAM &&
185	    CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
186		  CONNECT_PORT, ntohs(port)))
187		goto out;
188	else if (type == SOCK_DGRAM &&
189		 CHECK(port != htons(BIND_PORT), "Expected",
190		       "port %u but got %u", BIND_PORT, ntohs(port)))
191		goto out;
192
193	ret = 0;
194out:
195	close(client);
196	if (srv_client != server_fd)
197		close(srv_client);
198	if (ret)
199		WRITE_ONCE(stop, 1);
200	return ret;
201}
202
203static void
204prepare_addr(struct sockaddr *addr, int family, __u16 port, bool rewrite_addr)
205{
206	struct sockaddr_in *addr4;
207	struct sockaddr_in6 *addr6;
208
209	switch (family) {
210	case AF_INET:
211		addr4 = (struct sockaddr_in *)addr;
212		memset(addr4, 0, sizeof(*addr4));
213		addr4->sin_family = family;
214		addr4->sin_port = htons(port);
215		if (rewrite_addr)
216			addr4->sin_addr.s_addr = htonl(TEST_DADDR);
217		else
218			addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
219		break;
220	case AF_INET6:
221		addr6 = (struct sockaddr_in6 *)addr;
222		memset(addr6, 0, sizeof(*addr6));
223		addr6->sin6_family = family;
224		addr6->sin6_port = htons(port);
225		addr6->sin6_addr = in6addr_loopback;
226		if (rewrite_addr)
227			addr6->sin6_addr.s6_addr32[3] = htonl(TEST_DADDR);
228		break;
229	default:
230		fprintf(stderr, "Invalid family %d", family);
231	}
232}
233
234struct test_sk_cfg {
235	const char *name;
236	int family;
237	struct sockaddr *addr;
238	socklen_t len;
239	int type;
240	bool rewrite_addr;
241};
242
243#define TEST(NAME, FAMILY, TYPE, REWRITE)				\
244{									\
245	.name = NAME,							\
246	.family = FAMILY,						\
247	.addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4		\
248				    : (struct sockaddr *)&addr6,	\
249	.len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6),	\
250	.type = TYPE,							\
251	.rewrite_addr = REWRITE,					\
252}
253
254void test_sk_assign(void)
255{
256	struct sockaddr_in addr4;
257	struct sockaddr_in6 addr6;
258	struct test_sk_cfg tests[] = {
259		TEST("ipv4 tcp port redir", AF_INET, SOCK_STREAM, false),
260		TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
261		TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
262		TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
263		TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
264		TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
265		TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
266		TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
267	};
268	__s64 server = -1;
269	int server_map;
270	int self_net;
271	int i;
272
273	self_net = open(NS_SELF, O_RDONLY);
274	if (CHECK_FAIL(self_net < 0)) {
275		perror("Unable to open "NS_SELF);
276		return;
277	}
278
279	if (!configure_stack()) {
280		perror("configure_stack");
281		goto cleanup;
282	}
283
284	server_map = bpf_obj_get(SERVER_MAP_PATH);
285	if (CHECK_FAIL(server_map < 0)) {
286		perror("Unable to open " SERVER_MAP_PATH);
287		goto cleanup;
288	}
289
290	for (i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {
291		struct test_sk_cfg *test = &tests[i];
292		const struct sockaddr *addr;
293		const int zero = 0;
294		int err;
295
296		if (!test__start_subtest(test->name))
297			continue;
298		prepare_addr(test->addr, test->family, BIND_PORT, false);
299		addr = (const struct sockaddr *)test->addr;
300		server = start_server(addr, test->len, test->type);
301		if (server == -1)
302			goto close;
303
304		err = bpf_map_update_elem(server_map, &zero, &server, BPF_ANY);
305		if (CHECK_FAIL(err)) {
306			perror("Unable to update server_map");
307			goto close;
308		}
309
310		/* connect to unbound ports */
311		prepare_addr(test->addr, test->family, CONNECT_PORT,
312			     test->rewrite_addr);
313		if (run_test(server, addr, test->len, test->type))
314			goto close;
315
316		close(server);
317		server = -1;
318	}
319
320close:
321	close(server);
322	close(server_map);
323cleanup:
324	if (CHECK_FAIL(unlink(SERVER_MAP_PATH)))
325		perror("Unable to unlink " SERVER_MAP_PATH);
326	if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))
327		perror("Failed to setns("NS_SELF")");
328	close(self_net);
329}