xref: /linux/tools/testing/selftests/bpf/prog_tests/sk_assign.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
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 
26 static int stop, duration;
27 
28 static bool
configure_stack(void)29 configure_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 	else
45 		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 
75 static in_port_t
get_port(int fd)76 get_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 
98 static ssize_t
rcv_msg(int srv_client,int type)99 rcv_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 	else
106 		return recvfrom(srv_client, &buf, sizeof(buf), 0, NULL, NULL);
107 }
108 
109 static int
run_test(int server_fd,const struct sockaddr * addr,socklen_t len,int type)110 run_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 address
145 	 * will be the CONNECT_PORT rather than the BIND port that corresponds
146 	 * to the listen socket. SOCK_DGRAM on the other hand is connectionless
147 	 * so we can't really do the same check there; the server doesn't ever
148 	 * 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;
160 out:
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 
169 static void
prepare_addr(struct sockaddr * addr,int family,__u16 port,bool rewrite_addr)170 prepare_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 		else
184 			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 
200 struct 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 
test_sk_assign(void)220 void 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 
288 close:
289 	close(server);
290 	close(server_map);
291 cleanup:
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