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