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 
25 static const struct timeval timeo_sec = { .tv_sec = 3 };
26 static const size_t timeo_optlen = sizeof(timeo_sec);
27 static int stop, duration;
28 
29 static bool
configure_stack(void)30 configure_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 
76 static int
start_server(const struct sockaddr * addr,socklen_t len,int type)77 start_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;
93 close_out:
94 	close(fd);
95 	fd = -1;
96 out:
97 	return fd;
98 }
99 
100 static int
connect_to_server(const struct sockaddr * addr,socklen_t len,int type)101 connect_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;
115 close_out:
116 	close(fd);
117 	fd = -1;
118 out:
119 	return fd;
120 }
121 
122 static in_port_t
get_port(int fd)123 get_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 
145 static ssize_t
rcv_msg(int srv_client,int type)146 rcv_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 
156 static int
run_test(int server_fd,const struct sockaddr * addr,socklen_t len,int type)157 run_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;
207 out:
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 
216 static void
prepare_addr(struct sockaddr * addr,int family,__u16 port,bool rewrite_addr)217 prepare_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 
247 struct 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 
test_sk_assign(void)267 void 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 
333 close:
334 	close(server);
335 	close(server_map);
336 cleanup:
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 }
343