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 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 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 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 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 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 164 static bool was_decapsulated(struct bpf_prog_test_run_attr *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 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 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 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 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 368 static void test_cls_redirect_common(struct bpf_program *prog) 369 { 370 struct bpf_prog_test_run_attr 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; 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 tattr.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_xattr(&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 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 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 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