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 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 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 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 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 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 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 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 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 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 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 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 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