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