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