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