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