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