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