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