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
set_up_conn(const struct sockaddr_storage * addr,socklen_t len,int type,int * server,int * conn,struct sockaddr_storage * src,struct sockaddr_storage * dst)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
prepare_addr(struct sockaddr_storage * addr,int family)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
was_decapsulated(struct bpf_test_run_opts * tattr)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
test_str(void * buf,size_t len,const struct test_cfg * test,int family)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
encap_init(encap_headers_t * encap,uint8_t hop_count,uint8_t proto)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
build_input(const struct test_cfg * test,void * const buf,const struct sockaddr_storage * src,const struct sockaddr_storage * dst)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
close_fds(int * fds,int n)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
test_cls_redirect_common(struct bpf_program * prog)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
test_cls_redirect_dynptr(void)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
test_cls_redirect_inlined(void)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
test_cls_redirect_subprogs(void)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
test_cls_redirect(void)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