1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 // Copyright (c) 2020 Cloudflare
3 
4 #define _GNU_SOURCE
5 
6 #include <arpa/inet.h>
7 #include <string.h>
8 
9 #include <linux/pkt_cls.h>
10 #include <netinet/tcp.h>
11 
12 #include <test_progs.h>
13 
14 #include "progs/test_cls_redirect.h"
15 #include "test_cls_redirect.skel.h"
16 #include "test_cls_redirect_dynptr.skel.h"
17 #include "test_cls_redirect_subprogs.skel.h"
18 
19 #define ENCAP_IP INADDR_LOOPBACK
20 #define ENCAP_PORT (1234)
21 
22 static int duration = 0;
23 
24 struct addr_port {
25 	in_port_t port;
26 	union {
27 		struct in_addr in_addr;
28 		struct in6_addr in6_addr;
29 	};
30 };
31 
32 struct tuple {
33 	int family;
34 	struct addr_port src;
35 	struct addr_port dst;
36 };
37 
start_server(const struct sockaddr * addr,socklen_t len,int type)38 static int start_server(const struct sockaddr *addr, socklen_t len, int type)
39 {
40 	int fd = socket(addr->sa_family, type, 0);
41 	if (CHECK_FAIL(fd == -1))
42 		return -1;
43 	if (CHECK_FAIL(bind(fd, addr, len) == -1))
44 		goto err;
45 	if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
46 		goto err;
47 
48 	return fd;
49 
50 err:
51 	close(fd);
52 	return -1;
53 }
54 
connect_to_server(const struct sockaddr * addr,socklen_t len,int type)55 static int connect_to_server(const struct sockaddr *addr, socklen_t len,
56 			     int type)
57 {
58 	int fd = socket(addr->sa_family, type, 0);
59 	if (CHECK_FAIL(fd == -1))
60 		return -1;
61 	if (CHECK_FAIL(connect(fd, addr, len)))
62 		goto err;
63 
64 	return fd;
65 
66 err:
67 	close(fd);
68 	return -1;
69 }
70 
fill_addr_port(const struct sockaddr * sa,struct addr_port * ap)71 static bool fill_addr_port(const struct sockaddr *sa, struct addr_port *ap)
72 {
73 	const struct sockaddr_in6 *in6;
74 	const struct sockaddr_in *in;
75 
76 	switch (sa->sa_family) {
77 	case AF_INET:
78 		in = (const struct sockaddr_in *)sa;
79 		ap->in_addr = in->sin_addr;
80 		ap->port = in->sin_port;
81 		return true;
82 
83 	case AF_INET6:
84 		in6 = (const struct sockaddr_in6 *)sa;
85 		ap->in6_addr = in6->sin6_addr;
86 		ap->port = in6->sin6_port;
87 		return true;
88 
89 	default:
90 		return false;
91 	}
92 }
93 
set_up_conn(const struct sockaddr * addr,socklen_t len,int type,int * server,int * conn,struct tuple * tuple)94 static bool set_up_conn(const struct sockaddr *addr, socklen_t len, int type,
95 			int *server, int *conn, struct tuple *tuple)
96 {
97 	struct sockaddr_storage ss;
98 	socklen_t slen = sizeof(ss);
99 	struct sockaddr *sa = (struct sockaddr *)&ss;
100 
101 	*server = start_server(addr, len, type);
102 	if (*server < 0)
103 		return false;
104 
105 	if (CHECK_FAIL(getsockname(*server, sa, &slen)))
106 		goto close_server;
107 
108 	*conn = connect_to_server(sa, slen, type);
109 	if (*conn < 0)
110 		goto close_server;
111 
112 	/* We want to simulate packets arriving at conn, so we have to
113 	 * swap src and dst.
114 	 */
115 	slen = sizeof(ss);
116 	if (CHECK_FAIL(getsockname(*conn, sa, &slen)))
117 		goto close_conn;
118 
119 	if (CHECK_FAIL(!fill_addr_port(sa, &tuple->dst)))
120 		goto close_conn;
121 
122 	slen = sizeof(ss);
123 	if (CHECK_FAIL(getpeername(*conn, sa, &slen)))
124 		goto close_conn;
125 
126 	if (CHECK_FAIL(!fill_addr_port(sa, &tuple->src)))
127 		goto close_conn;
128 
129 	tuple->family = ss.ss_family;
130 	return true;
131 
132 close_conn:
133 	close(*conn);
134 	*conn = -1;
135 close_server:
136 	close(*server);
137 	*server = -1;
138 	return false;
139 }
140 
prepare_addr(struct sockaddr_storage * addr,int family)141 static socklen_t prepare_addr(struct sockaddr_storage *addr, int family)
142 {
143 	struct sockaddr_in *addr4;
144 	struct sockaddr_in6 *addr6;
145 
146 	switch (family) {
147 	case AF_INET:
148 		addr4 = (struct sockaddr_in *)addr;
149 		memset(addr4, 0, sizeof(*addr4));
150 		addr4->sin_family = family;
151 		addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
152 		return sizeof(*addr4);
153 	case AF_INET6:
154 		addr6 = (struct sockaddr_in6 *)addr;
155 		memset(addr6, 0, sizeof(*addr6));
156 		addr6->sin6_family = family;
157 		addr6->sin6_addr = in6addr_loopback;
158 		return sizeof(*addr6);
159 	default:
160 		fprintf(stderr, "Invalid family %d", family);
161 		return 0;
162 	}
163 }
164 
was_decapsulated(struct bpf_test_run_opts * tattr)165 static bool was_decapsulated(struct bpf_test_run_opts *tattr)
166 {
167 	return tattr->data_size_out < tattr->data_size_in;
168 }
169 
170 enum type {
171 	UDP,
172 	TCP,
173 	__NR_KIND,
174 };
175 
176 enum hops {
177 	NO_HOPS,
178 	ONE_HOP,
179 };
180 
181 enum flags {
182 	NONE,
183 	SYN,
184 	ACK,
185 };
186 
187 enum conn {
188 	KNOWN_CONN,
189 	UNKNOWN_CONN,
190 };
191 
192 enum result {
193 	ACCEPT,
194 	FORWARD,
195 };
196 
197 struct test_cfg {
198 	enum type type;
199 	enum result result;
200 	enum conn conn;
201 	enum hops hops;
202 	enum flags flags;
203 };
204 
test_str(void * buf,size_t len,const struct test_cfg * test,int family)205 static int test_str(void *buf, size_t len, const struct test_cfg *test,
206 		    int family)
207 {
208 	const char *family_str, *type, *conn, *hops, *result, *flags;
209 
210 	family_str = "IPv4";
211 	if (family == AF_INET6)
212 		family_str = "IPv6";
213 
214 	type = "TCP";
215 	if (test->type == UDP)
216 		type = "UDP";
217 
218 	conn = "known";
219 	if (test->conn == UNKNOWN_CONN)
220 		conn = "unknown";
221 
222 	hops = "no hops";
223 	if (test->hops == ONE_HOP)
224 		hops = "one hop";
225 
226 	result = "accept";
227 	if (test->result == FORWARD)
228 		result = "forward";
229 
230 	flags = "none";
231 	if (test->flags == SYN)
232 		flags = "SYN";
233 	else if (test->flags == ACK)
234 		flags = "ACK";
235 
236 	return snprintf(buf, len, "%s %s %s %s (%s, flags: %s)", family_str,
237 			type, result, conn, hops, flags);
238 }
239 
240 static struct test_cfg tests[] = {
241 	{ TCP, ACCEPT, UNKNOWN_CONN, NO_HOPS, SYN },
242 	{ TCP, ACCEPT, UNKNOWN_CONN, NO_HOPS, ACK },
243 	{ TCP, FORWARD, UNKNOWN_CONN, ONE_HOP, ACK },
244 	{ TCP, ACCEPT, KNOWN_CONN, ONE_HOP, ACK },
245 	{ UDP, ACCEPT, UNKNOWN_CONN, NO_HOPS, NONE },
246 	{ UDP, FORWARD, UNKNOWN_CONN, ONE_HOP, NONE },
247 	{ UDP, ACCEPT, KNOWN_CONN, ONE_HOP, NONE },
248 };
249 
encap_init(encap_headers_t * encap,uint8_t hop_count,uint8_t proto)250 static void encap_init(encap_headers_t *encap, uint8_t hop_count, uint8_t proto)
251 {
252 	const uint8_t hlen =
253 		(sizeof(struct guehdr) / sizeof(uint32_t)) + hop_count;
254 	*encap = (encap_headers_t){
255 		.eth = { .h_proto = htons(ETH_P_IP) },
256 		.ip = {
257 			.ihl = 5,
258 			.version = 4,
259 			.ttl = IPDEFTTL,
260 			.protocol = IPPROTO_UDP,
261 			.daddr = htonl(ENCAP_IP)
262 		},
263 		.udp = {
264 			.dest = htons(ENCAP_PORT),
265 		},
266 		.gue = {
267 			.hlen = hlen,
268 			.proto_ctype = proto
269 		},
270 		.unigue = {
271 			.hop_count = hop_count
272 		},
273 	};
274 }
275 
build_input(const struct test_cfg * test,void * const buf,const struct tuple * tuple)276 static size_t build_input(const struct test_cfg *test, void *const buf,
277 			  const struct tuple *tuple)
278 {
279 	in_port_t sport = tuple->src.port;
280 	encap_headers_t encap;
281 	struct iphdr ip;
282 	struct ipv6hdr ipv6;
283 	struct tcphdr tcp;
284 	struct udphdr udp;
285 	struct in_addr next_hop;
286 	uint8_t *p = buf;
287 	int proto;
288 
289 	proto = IPPROTO_IPIP;
290 	if (tuple->family == AF_INET6)
291 		proto = IPPROTO_IPV6;
292 
293 	encap_init(&encap, test->hops == ONE_HOP ? 1 : 0, proto);
294 	p = mempcpy(p, &encap, sizeof(encap));
295 
296 	if (test->hops == ONE_HOP) {
297 		next_hop = (struct in_addr){ .s_addr = htonl(0x7f000002) };
298 		p = mempcpy(p, &next_hop, sizeof(next_hop));
299 	}
300 
301 	proto = IPPROTO_TCP;
302 	if (test->type == UDP)
303 		proto = IPPROTO_UDP;
304 
305 	switch (tuple->family) {
306 	case AF_INET:
307 		ip = (struct iphdr){
308 			.ihl = 5,
309 			.version = 4,
310 			.ttl = IPDEFTTL,
311 			.protocol = proto,
312 			.saddr = tuple->src.in_addr.s_addr,
313 			.daddr = tuple->dst.in_addr.s_addr,
314 		};
315 		p = mempcpy(p, &ip, sizeof(ip));
316 		break;
317 	case AF_INET6:
318 		ipv6 = (struct ipv6hdr){
319 			.version = 6,
320 			.hop_limit = IPDEFTTL,
321 			.nexthdr = proto,
322 			.saddr = tuple->src.in6_addr,
323 			.daddr = tuple->dst.in6_addr,
324 		};
325 		p = mempcpy(p, &ipv6, sizeof(ipv6));
326 		break;
327 	default:
328 		return 0;
329 	}
330 
331 	if (test->conn == UNKNOWN_CONN)
332 		sport--;
333 
334 	switch (test->type) {
335 	case TCP:
336 		tcp = (struct tcphdr){
337 			.source = sport,
338 			.dest = tuple->dst.port,
339 		};
340 		if (test->flags == SYN)
341 			tcp.syn = true;
342 		if (test->flags == ACK)
343 			tcp.ack = true;
344 		p = mempcpy(p, &tcp, sizeof(tcp));
345 		break;
346 	case UDP:
347 		udp = (struct udphdr){
348 			.source = sport,
349 			.dest = tuple->dst.port,
350 		};
351 		p = mempcpy(p, &udp, sizeof(udp));
352 		break;
353 	default:
354 		return 0;
355 	}
356 
357 	return (void *)p - buf;
358 }
359 
close_fds(int * fds,int n)360 static void close_fds(int *fds, int n)
361 {
362 	int i;
363 
364 	for (i = 0; i < n; i++)
365 		if (fds[i] > 0)
366 			close(fds[i]);
367 }
368 
test_cls_redirect_common(struct bpf_program * prog)369 static void test_cls_redirect_common(struct bpf_program *prog)
370 {
371 	LIBBPF_OPTS(bpf_test_run_opts, tattr);
372 	int families[] = { AF_INET, AF_INET6 };
373 	struct sockaddr_storage ss;
374 	struct sockaddr *addr;
375 	socklen_t slen;
376 	int i, j, err, prog_fd;
377 	int servers[__NR_KIND][ARRAY_SIZE(families)] = {};
378 	int conns[__NR_KIND][ARRAY_SIZE(families)] = {};
379 	struct tuple tuples[__NR_KIND][ARRAY_SIZE(families)];
380 
381 	addr = (struct sockaddr *)&ss;
382 	for (i = 0; i < ARRAY_SIZE(families); i++) {
383 		slen = prepare_addr(&ss, families[i]);
384 		if (CHECK_FAIL(!slen))
385 			goto cleanup;
386 
387 		if (CHECK_FAIL(!set_up_conn(addr, slen, SOCK_DGRAM,
388 					    &servers[UDP][i], &conns[UDP][i],
389 					    &tuples[UDP][i])))
390 			goto cleanup;
391 
392 		if (CHECK_FAIL(!set_up_conn(addr, slen, SOCK_STREAM,
393 					    &servers[TCP][i], &conns[TCP][i],
394 					    &tuples[TCP][i])))
395 			goto cleanup;
396 	}
397 
398 	prog_fd = bpf_program__fd(prog);
399 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
400 		struct test_cfg *test = &tests[i];
401 
402 		for (j = 0; j < ARRAY_SIZE(families); j++) {
403 			struct tuple *tuple = &tuples[test->type][j];
404 			char input[256];
405 			char tmp[256];
406 
407 			test_str(tmp, sizeof(tmp), test, tuple->family);
408 			if (!test__start_subtest(tmp))
409 				continue;
410 
411 			tattr.data_out = tmp;
412 			tattr.data_size_out = sizeof(tmp);
413 
414 			tattr.data_in = input;
415 			tattr.data_size_in = build_input(test, input, tuple);
416 			if (CHECK_FAIL(!tattr.data_size_in))
417 				continue;
418 
419 			err = bpf_prog_test_run_opts(prog_fd, &tattr);
420 			if (CHECK_FAIL(err))
421 				continue;
422 
423 			if (tattr.retval != TC_ACT_REDIRECT) {
424 				PRINT_FAIL("expected TC_ACT_REDIRECT, got %d\n",
425 					   tattr.retval);
426 				continue;
427 			}
428 
429 			switch (test->result) {
430 			case ACCEPT:
431 				if (CHECK_FAIL(!was_decapsulated(&tattr)))
432 					continue;
433 				break;
434 			case FORWARD:
435 				if (CHECK_FAIL(was_decapsulated(&tattr)))
436 					continue;
437 				break;
438 			default:
439 				PRINT_FAIL("unknown result %d\n", test->result);
440 				continue;
441 			}
442 		}
443 	}
444 
445 cleanup:
446 	close_fds((int *)servers, sizeof(servers) / sizeof(servers[0][0]));
447 	close_fds((int *)conns, sizeof(conns) / sizeof(conns[0][0]));
448 }
449 
test_cls_redirect_dynptr(void)450 static void test_cls_redirect_dynptr(void)
451 {
452 	struct test_cls_redirect_dynptr *skel;
453 	int err;
454 
455 	skel = test_cls_redirect_dynptr__open();
456 	if (!ASSERT_OK_PTR(skel, "skel_open"))
457 		return;
458 
459 	skel->rodata->ENCAPSULATION_IP = htonl(ENCAP_IP);
460 	skel->rodata->ENCAPSULATION_PORT = htons(ENCAP_PORT);
461 
462 	err = test_cls_redirect_dynptr__load(skel);
463 	if (!ASSERT_OK(err, "skel_load"))
464 		goto cleanup;
465 
466 	test_cls_redirect_common(skel->progs.cls_redirect);
467 
468 cleanup:
469 	test_cls_redirect_dynptr__destroy(skel);
470 }
471 
test_cls_redirect_inlined(void)472 static void test_cls_redirect_inlined(void)
473 {
474 	struct test_cls_redirect *skel;
475 	int err;
476 
477 	skel = test_cls_redirect__open();
478 	if (CHECK(!skel, "skel_open", "failed\n"))
479 		return;
480 
481 	skel->rodata->ENCAPSULATION_IP = htonl(ENCAP_IP);
482 	skel->rodata->ENCAPSULATION_PORT = htons(ENCAP_PORT);
483 
484 	err = test_cls_redirect__load(skel);
485 	if (CHECK(err, "skel_load", "failed: %d\n", err))
486 		goto cleanup;
487 
488 	test_cls_redirect_common(skel->progs.cls_redirect);
489 
490 cleanup:
491 	test_cls_redirect__destroy(skel);
492 }
493 
test_cls_redirect_subprogs(void)494 static void test_cls_redirect_subprogs(void)
495 {
496 	struct test_cls_redirect_subprogs *skel;
497 	int err;
498 
499 	skel = test_cls_redirect_subprogs__open();
500 	if (CHECK(!skel, "skel_open", "failed\n"))
501 		return;
502 
503 	skel->rodata->ENCAPSULATION_IP = htonl(ENCAP_IP);
504 	skel->rodata->ENCAPSULATION_PORT = htons(ENCAP_PORT);
505 
506 	err = test_cls_redirect_subprogs__load(skel);
507 	if (CHECK(err, "skel_load", "failed: %d\n", err))
508 		goto cleanup;
509 
510 	test_cls_redirect_common(skel->progs.cls_redirect);
511 
512 cleanup:
513 	test_cls_redirect_subprogs__destroy(skel);
514 }
515 
test_cls_redirect(void)516 void test_cls_redirect(void)
517 {
518 	if (test__start_subtest("cls_redirect_inlined"))
519 		test_cls_redirect_inlined();
520 	if (test__start_subtest("cls_redirect_subprogs"))
521 		test_cls_redirect_subprogs();
522 	if (test__start_subtest("cls_redirect_dynptr"))
523 		test_cls_redirect_dynptr();
524 }
525