github.com/cilium/cilium@v1.16.2/bpf/tests/tc_nodeport_lb4_nat_lb.c (about) 1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright Authors of Cilium */ 3 4 #include "common.h" 5 6 #include <bpf/ctx/skb.h> 7 #include "pktgen.h" 8 9 /* Set ETH_HLEN to 14 to indicate that the packet has a 14 byte ethernet header */ 10 #define ETH_HLEN 14 11 12 /* Enable code paths under test */ 13 #define ENABLE_IPV4 14 #define ENABLE_NODEPORT 15 #define ENABLE_HOST_ROUTING 16 17 #define DISABLE_LOOPBACK_LB 18 19 /* Skip ingress policy checks, not needed to validate hairpin flow */ 20 #define USE_BPF_PROG_FOR_INGRESS_POLICY 21 #undef FORCE_LOCAL_POLICY_EVAL_AT_SOURCE 22 23 #define CLIENT_IP v4_ext_one 24 #define CLIENT_PORT __bpf_htons(111) 25 #define CLIENT_IP_2 v4_ext_two 26 27 #define FRONTEND_IP_LOCAL v4_svc_one 28 #define FRONTEND_IP_REMOTE v4_svc_two 29 #define FRONTEND_PORT tcp_svc_one 30 31 #define LB_IP v4_node_one 32 #define IPV4_DIRECT_ROUTING LB_IP 33 34 #define BACKEND_IP_LOCAL v4_pod_one 35 #define BACKEND_IP_REMOTE v4_pod_two 36 #define BACKEND_PORT __bpf_htons(8080) 37 38 #define NATIVE_DEV_IFINDEX 24 39 #define DEFAULT_IFACE NATIVE_DEV_IFINDEX 40 #define BACKEND_IFACE 25 41 #define SVC_EGRESS_IFACE 26 42 43 #define fib_lookup mock_fib_lookup 44 45 static volatile const __u8 *client_mac = mac_one; 46 /* this matches the default node_config.h: */ 47 static volatile const __u8 lb_mac[ETH_ALEN] = { 0xce, 0x72, 0xa7, 0x03, 0x88, 0x56 }; 48 static volatile const __u8 *node_mac = mac_three; 49 static volatile const __u8 *local_backend_mac = mac_four; 50 static volatile const __u8 *remote_backend_mac = mac_five; 51 52 struct mock_settings { 53 __be16 nat_source_port; 54 bool fail_fib; 55 }; 56 57 struct { 58 __uint(type, BPF_MAP_TYPE_ARRAY); 59 __uint(key_size, sizeof(__u32)); 60 __uint(value_size, sizeof(struct mock_settings)); 61 __uint(max_entries, 1); 62 } settings_map __section_maps_btf; 63 64 #define fib_lookup mock_fib_lookup 65 66 long mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params, 67 __maybe_unused int plen, __maybe_unused __u32 flags) 68 { 69 __u32 key = 0; 70 struct mock_settings *settings = map_lookup_elem(&settings_map, &key); 71 72 if (settings && settings->fail_fib) 73 return BPF_FIB_LKUP_RET_NO_NEIGH; 74 75 params->ifindex = DEFAULT_IFACE; 76 77 if (params->ipv4_dst == BACKEND_IP_REMOTE) { 78 __bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN); 79 __bpf_memcpy_builtin(params->dmac, (__u8 *)remote_backend_mac, ETH_ALEN); 80 } else if (params->ipv4_src == FRONTEND_IP_LOCAL && 81 params->ipv4_dst == CLIENT_IP_2) { 82 __bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN); 83 __bpf_memcpy_builtin(params->dmac, (__u8 *)client_mac, ETH_ALEN); 84 params->ifindex = SVC_EGRESS_IFACE; 85 } else { 86 __bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN); 87 __bpf_memcpy_builtin(params->dmac, (__u8 *)client_mac, ETH_ALEN); 88 } 89 90 return 0; 91 } 92 93 #define ctx_redirect mock_ctx_redirect 94 95 static __always_inline __maybe_unused int 96 mock_ctx_redirect(const struct __sk_buff *ctx __maybe_unused, 97 int ifindex __maybe_unused, __u32 flags __maybe_unused) 98 { 99 void *data = (void *)(long)ctx_data(ctx); 100 void *data_end = (void *)(long)ctx->data_end; 101 struct iphdr *ip4; 102 103 ip4 = data + sizeof(struct ethhdr); 104 if ((void *)ip4 + sizeof(*ip4) > data_end) 105 return CTX_ACT_DROP; 106 107 /* Forward to backend: */ 108 if (ip4->saddr == CLIENT_IP && ifindex == BACKEND_IFACE) 109 return CTX_ACT_REDIRECT; 110 if (ip4->saddr == CLIENT_IP_2 && ifindex == BACKEND_IFACE) 111 return CTX_ACT_REDIRECT; 112 if (ip4->saddr == LB_IP && ifindex == DEFAULT_IFACE) 113 return CTX_ACT_REDIRECT; 114 115 /* Redirected reply: */ 116 if (ip4->daddr == CLIENT_IP_2 && ifindex == SVC_EGRESS_IFACE) 117 return CTX_ACT_REDIRECT; 118 if (ip4->saddr == FRONTEND_IP_REMOTE && ifindex == DEFAULT_IFACE) 119 return CTX_ACT_REDIRECT; 120 121 return CTX_ACT_DROP; 122 } 123 124 #define SECCTX_FROM_IPCACHE 1 125 126 #include "bpf_host.c" 127 128 #include "lib/endpoint.h" 129 #include "lib/ipcache.h" 130 #include "lib/lb.h" 131 132 #define FROM_NETDEV 0 133 #define TO_NETDEV 1 134 135 struct { 136 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 137 __uint(key_size, sizeof(__u32)); 138 __uint(max_entries, 2); 139 __array(values, int()); 140 } entry_call_map __section(".maps") = { 141 .values = { 142 [FROM_NETDEV] = &cil_from_netdev, 143 [TO_NETDEV] = &cil_to_netdev, 144 }, 145 }; 146 147 /* Test that a SVC request to a local backend 148 * - gets DNATed (but not SNATed) 149 * - gets redirected by TC (as ENABLE_HOST_ROUTING is set) 150 */ 151 PKTGEN("tc", "tc_nodeport_local_backend") 152 int nodeport_local_backend_pktgen(struct __ctx_buff *ctx) 153 { 154 struct pktgen builder; 155 struct tcphdr *l4; 156 void *data; 157 158 /* Init packet builder */ 159 pktgen__init(&builder, ctx); 160 161 l4 = pktgen__push_ipv4_tcp_packet(&builder, 162 (__u8 *)client_mac, (__u8 *)lb_mac, 163 CLIENT_IP, FRONTEND_IP_LOCAL, 164 CLIENT_PORT, FRONTEND_PORT); 165 if (!l4) 166 return TEST_ERROR; 167 168 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 169 if (!data) 170 return TEST_ERROR; 171 172 /* Calc lengths, set protocol fields and calc checksums */ 173 pktgen__finish(&builder); 174 175 return 0; 176 } 177 178 SETUP("tc", "tc_nodeport_local_backend") 179 int nodeport_local_backend_setup(struct __ctx_buff *ctx) 180 { 181 __u16 revnat_id = 1; 182 183 lb_v4_add_service(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, revnat_id); 184 lb_v4_add_backend(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, 124, 185 BACKEND_IP_LOCAL, BACKEND_PORT, IPPROTO_TCP, 0); 186 187 /* add local backend */ 188 endpoint_v4_add_entry(BACKEND_IP_LOCAL, BACKEND_IFACE, 0, 0, 0, 189 (__u8 *)local_backend_mac, (__u8 *)node_mac); 190 191 ipcache_v4_add_entry(BACKEND_IP_LOCAL, 0, 112233, 0, 0); 192 193 /* Jump into the entrypoint */ 194 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 195 /* Fail if we didn't jump */ 196 return TEST_ERROR; 197 } 198 199 CHECK("tc", "tc_nodeport_local_backend") 200 int nodeport_local_backend_check(const struct __ctx_buff *ctx) 201 { 202 void *data, *data_end; 203 __u32 *status_code; 204 struct tcphdr *l4; 205 struct ethhdr *l2; 206 struct iphdr *l3; 207 208 test_init(); 209 210 data = (void *)(long)ctx_data(ctx); 211 data_end = (void *)(long)ctx->data_end; 212 213 if (data + sizeof(__u32) > data_end) 214 test_fatal("status code out of bounds"); 215 216 status_code = data; 217 218 assert(*status_code == CTX_ACT_REDIRECT); 219 220 l2 = data + sizeof(__u32); 221 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 222 test_fatal("l2 out of bounds"); 223 224 l3 = (void *)l2 + sizeof(struct ethhdr); 225 if ((void *)l3 + sizeof(struct iphdr) > data_end) 226 test_fatal("l3 out of bounds"); 227 228 l4 = (void *)l3 + sizeof(struct iphdr); 229 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 230 test_fatal("l4 out of bounds"); 231 232 if (memcmp(l2->h_source, (__u8 *)node_mac, ETH_ALEN) != 0) 233 test_fatal("src MAC is not the node MAC") 234 if (memcmp(l2->h_dest, (__u8 *)local_backend_mac, ETH_ALEN) != 0) 235 test_fatal("dst MAC is not the endpoint MAC") 236 237 if (l3->saddr != CLIENT_IP) 238 test_fatal("src IP has changed"); 239 240 if (l3->daddr != BACKEND_IP_LOCAL) 241 test_fatal("dst IP hasn't been NATed to local backend IP"); 242 243 if (l3->check != bpf_htons(0x4212)) 244 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 245 246 if (l4->source != CLIENT_PORT) 247 test_fatal("src port has changed"); 248 249 if (l4->dest != BACKEND_PORT) 250 test_fatal("dst TCP port hasn't been NATed to backend port"); 251 252 test_finish(); 253 } 254 255 /* Test that a reply by the local backend gets revDNATed at to-netdev. */ 256 PKTGEN("tc", "tc_nodeport_local_backend_reply") 257 int nodeport_local_backend_reply_pktgen(struct __ctx_buff *ctx) 258 { 259 struct pktgen builder; 260 struct tcphdr *l4; 261 void *data; 262 263 /* Init packet builder */ 264 pktgen__init(&builder, ctx); 265 266 l4 = pktgen__push_ipv4_tcp_packet(&builder, 267 (__u8 *)lb_mac, (__u8 *)client_mac, 268 BACKEND_IP_LOCAL, CLIENT_IP, 269 BACKEND_PORT, CLIENT_PORT); 270 if (!l4) 271 return TEST_ERROR; 272 273 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 274 if (!data) 275 return TEST_ERROR; 276 277 /* Calc lengths, set protocol fields and calc checksums */ 278 pktgen__finish(&builder); 279 280 return 0; 281 } 282 283 SETUP("tc", "tc_nodeport_local_backend_reply") 284 int nodeport_local_backend_reply_setup(struct __ctx_buff *ctx) 285 { 286 /* Jump into the entrypoint */ 287 tail_call_static(ctx, entry_call_map, TO_NETDEV); 288 /* Fail if we didn't jump */ 289 return TEST_ERROR; 290 } 291 292 CHECK("tc", "tc_nodeport_local_backend_reply") 293 int nodeport_local_backend_reply_check(const struct __ctx_buff *ctx) 294 { 295 void *data, *data_end; 296 __u32 *status_code; 297 struct tcphdr *l4; 298 struct ethhdr *l2; 299 struct iphdr *l3; 300 301 test_init(); 302 303 data = (void *)(long)ctx_data(ctx); 304 data_end = (void *)(long)ctx->data_end; 305 306 if (data + sizeof(__u32) > data_end) 307 test_fatal("status code out of bounds"); 308 309 status_code = data; 310 311 assert(*status_code == CTX_ACT_OK); 312 313 l2 = data + sizeof(__u32); 314 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 315 test_fatal("l2 out of bounds"); 316 317 l3 = (void *)l2 + sizeof(struct ethhdr); 318 if ((void *)l3 + sizeof(struct iphdr) > data_end) 319 test_fatal("l3 out of bounds"); 320 321 l4 = (void *)l3 + sizeof(struct iphdr); 322 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 323 test_fatal("l4 out of bounds"); 324 325 if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0) 326 test_fatal("src MAC is not the LB MAC") 327 if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0) 328 test_fatal("dst MAC is not the client MAC") 329 330 if (l3->saddr != FRONTEND_IP_LOCAL) 331 test_fatal("src IP hasn't been revNATed to frontend IP"); 332 333 if (l3->daddr != CLIENT_IP) 334 test_fatal("dst IP has changed"); 335 336 if (l3->check != bpf_htons(0x4baa)) 337 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 338 339 if (l4->source != FRONTEND_PORT) 340 test_fatal("src port hasn't been revNATed to frontend port"); 341 342 if (l4->dest != CLIENT_PORT) 343 test_fatal("dst port has changed"); 344 345 test_finish(); 346 } 347 348 /* Same scenario as above, but for a different CLIENT_IP_2. Here replies 349 * should leave via a non-default interface. 350 */ 351 PKTGEN("tc", "tc_nodeport_local_backend_redirect") 352 int nodeport_local_backend_redirect_pktgen(struct __ctx_buff *ctx) 353 { 354 struct pktgen builder; 355 struct tcphdr *l4; 356 void *data; 357 358 /* Init packet builder */ 359 pktgen__init(&builder, ctx); 360 361 l4 = pktgen__push_ipv4_tcp_packet(&builder, 362 (__u8 *)client_mac, (__u8 *)lb_mac, 363 CLIENT_IP_2, FRONTEND_IP_LOCAL, 364 CLIENT_PORT, FRONTEND_PORT); 365 if (!l4) 366 return TEST_ERROR; 367 368 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 369 if (!data) 370 return TEST_ERROR; 371 372 /* Calc lengths, set protocol fields and calc checksums */ 373 pktgen__finish(&builder); 374 375 return 0; 376 } 377 378 SETUP("tc", "tc_nodeport_local_backend_redirect") 379 int nodeport_local_backend_redirect_setup(struct __ctx_buff *ctx) 380 { 381 /* Jump into the entrypoint */ 382 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 383 /* Fail if we didn't jump */ 384 return TEST_ERROR; 385 } 386 387 CHECK("tc", "tc_nodeport_local_backend_redirect") 388 int nodeport_local_backend_redirect_check(const struct __ctx_buff *ctx) 389 { 390 void *data, *data_end; 391 __u32 *status_code; 392 struct tcphdr *l4; 393 struct ethhdr *l2; 394 struct iphdr *l3; 395 396 test_init(); 397 398 data = (void *)(long)ctx_data(ctx); 399 data_end = (void *)(long)ctx->data_end; 400 401 if (data + sizeof(__u32) > data_end) 402 test_fatal("status code out of bounds"); 403 404 status_code = data; 405 406 assert(*status_code == CTX_ACT_REDIRECT); 407 408 l2 = data + sizeof(__u32); 409 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 410 test_fatal("l2 out of bounds"); 411 412 l3 = (void *)l2 + sizeof(struct ethhdr); 413 if ((void *)l3 + sizeof(struct iphdr) > data_end) 414 test_fatal("l3 out of bounds"); 415 416 l4 = (void *)l3 + sizeof(struct iphdr); 417 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 418 test_fatal("l4 out of bounds"); 419 420 if (memcmp(l2->h_source, (__u8 *)node_mac, ETH_ALEN) != 0) 421 test_fatal("src MAC is not the node MAC") 422 if (memcmp(l2->h_dest, (__u8 *)local_backend_mac, ETH_ALEN) != 0) 423 test_fatal("dst MAC is not the endpoint MAC") 424 425 if (l3->saddr != CLIENT_IP_2) 426 test_fatal("src IP has changed"); 427 428 if (l3->daddr != BACKEND_IP_LOCAL) 429 test_fatal("dst IP hasn't been NATed to local backend IP"); 430 431 if (l3->check != bpf_htons(0x3711)) 432 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 433 434 if (l4->source != CLIENT_PORT) 435 test_fatal("src port has changed"); 436 437 if (l4->dest != BACKEND_PORT) 438 test_fatal("dst TCP port hasn't been NATed to backend port"); 439 440 test_finish(); 441 } 442 443 /* Test that to-netdev respects the routing needed for CLIENT_IP_2, 444 * and redirects the packet to the correct egress interface. 445 */ 446 PKTGEN("tc", "tc_nodeport_local_backend_redirect_reply") 447 int nodeport_local_backend_redirect_reply_pktgen(struct __ctx_buff *ctx) 448 { 449 struct pktgen builder; 450 struct tcphdr *l4; 451 void *data; 452 453 /* Init packet builder */ 454 pktgen__init(&builder, ctx); 455 456 l4 = pktgen__push_ipv4_tcp_packet(&builder, 457 (__u8 *)lb_mac, (__u8 *)client_mac, 458 BACKEND_IP_LOCAL, CLIENT_IP_2, 459 BACKEND_PORT, CLIENT_PORT); 460 if (!l4) 461 return TEST_ERROR; 462 463 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 464 if (!data) 465 return TEST_ERROR; 466 467 /* Calc lengths, set protocol fields and calc checksums */ 468 pktgen__finish(&builder); 469 470 return 0; 471 } 472 473 SETUP("tc", "tc_nodeport_local_backend_redirect_reply") 474 int nodeport_local_backend_redirect_reply_setup(struct __ctx_buff *ctx) 475 { 476 /* Jump into the entrypoint */ 477 tail_call_static(ctx, entry_call_map, TO_NETDEV); 478 /* Fail if we didn't jump */ 479 return TEST_ERROR; 480 } 481 482 CHECK("tc", "tc_nodeport_local_backend_redirect_reply") 483 int nodeport_local_backend_redirect_reply_check(const struct __ctx_buff *ctx) 484 { 485 void *data, *data_end; 486 __u32 *status_code; 487 struct tcphdr *l4; 488 struct ethhdr *l2; 489 struct iphdr *l3; 490 491 test_init(); 492 493 data = (void *)(long)ctx_data(ctx); 494 data_end = (void *)(long)ctx->data_end; 495 496 if (data + sizeof(__u32) > data_end) 497 test_fatal("status code out of bounds"); 498 499 status_code = data; 500 501 assert(*status_code == CTX_ACT_REDIRECT); 502 503 l2 = data + sizeof(__u32); 504 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 505 test_fatal("l2 out of bounds"); 506 507 l3 = (void *)l2 + sizeof(struct ethhdr); 508 if ((void *)l3 + sizeof(struct iphdr) > data_end) 509 test_fatal("l3 out of bounds"); 510 511 l4 = (void *)l3 + sizeof(struct iphdr); 512 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 513 test_fatal("l4 out of bounds"); 514 515 if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0) 516 test_fatal("src MAC is not the LB MAC") 517 if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0) 518 test_fatal("dst MAC is not the client MAC") 519 520 if (l3->saddr != BACKEND_IP_LOCAL) 521 test_fatal("src IP has changed"); 522 523 if (l3->daddr != CLIENT_IP_2) 524 test_fatal("dst IP has changed"); 525 526 if (l3->check != bpf_htons(0x3611)) 527 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 528 529 if (l4->source != BACKEND_PORT) 530 test_fatal("src port has changed"); 531 532 if (l4->dest != CLIENT_PORT) 533 test_fatal("dst port has changed"); 534 535 test_finish(); 536 } 537 538 /* Test that a SVC request (UDP) to a local backend 539 * - gets DNATed (but not SNATed) 540 * - gets redirected by TC (as ENABLE_HOST_ROUTING is set) 541 */ 542 PKTGEN("tc", "tc_nodeport_udp_local_backend") 543 int nodeport_udp_local_backend_pktgen(struct __ctx_buff *ctx) 544 { 545 struct pktgen builder; 546 struct udphdr *l4; 547 void *data; 548 549 /* Init packet builder */ 550 pktgen__init(&builder, ctx); 551 552 l4 = pktgen__push_ipv4_udp_packet(&builder, 553 (__u8 *)client_mac, (__u8 *)lb_mac, 554 CLIENT_IP, FRONTEND_IP_LOCAL, 555 CLIENT_PORT, FRONTEND_PORT); 556 if (!l4) 557 return TEST_ERROR; 558 559 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 560 if (!data) 561 return TEST_ERROR; 562 563 /* Calc lengths, set protocol fields and calc checksums */ 564 pktgen__finish(&builder); 565 566 return 0; 567 } 568 569 SETUP("tc", "tc_nodeport_udp_local_backend") 570 int nodeport_udp_local_backend_setup(struct __ctx_buff *ctx) 571 { 572 __u16 revnat_id = 2; 573 574 lb_v4_add_service(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, revnat_id); 575 lb_v4_add_backend(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, 125, 576 BACKEND_IP_LOCAL, BACKEND_PORT, IPPROTO_UDP, 0); 577 578 /* Jump into the entrypoint */ 579 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 580 /* Fail if we didn't jump */ 581 return TEST_ERROR; 582 } 583 584 CHECK("tc", "tc_nodeport_udp_local_backend") 585 int nodeport_udp_local_backend_check(const struct __ctx_buff *ctx) 586 { 587 void *data, *data_end; 588 __u32 *status_code; 589 struct udphdr *l4; 590 struct ethhdr *l2; 591 struct iphdr *l3; 592 593 test_init(); 594 595 data = (void *)(long)ctx_data(ctx); 596 data_end = (void *)(long)ctx->data_end; 597 598 if (data + sizeof(__u32) > data_end) 599 test_fatal("status code out of bounds"); 600 601 status_code = data; 602 603 assert(*status_code == CTX_ACT_REDIRECT); 604 605 l2 = data + sizeof(__u32); 606 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 607 test_fatal("l2 out of bounds"); 608 609 l3 = (void *)l2 + sizeof(struct ethhdr); 610 if ((void *)l3 + sizeof(struct iphdr) > data_end) 611 test_fatal("l3 out of bounds"); 612 613 l4 = (void *)l3 + sizeof(struct iphdr); 614 if ((void *)l4 + sizeof(*l4) > data_end) 615 test_fatal("l4 out of bounds"); 616 617 if (memcmp(l2->h_source, (__u8 *)node_mac, ETH_ALEN) != 0) 618 test_fatal("src MAC is not the node MAC") 619 if (memcmp(l2->h_dest, (__u8 *)local_backend_mac, ETH_ALEN) != 0) 620 test_fatal("dst MAC is not the endpoint MAC") 621 622 if (l3->saddr != CLIENT_IP) 623 test_fatal("src IP has changed"); 624 625 if (l3->daddr != BACKEND_IP_LOCAL) 626 test_fatal("dst IP hasn't been NATed to local backend IP"); 627 628 if (l3->check != bpf_htons(0x4213)) 629 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 630 631 if (l4->source != CLIENT_PORT) 632 test_fatal("src port has changed"); 633 634 if (l4->dest != BACKEND_PORT) 635 test_fatal("dst port hasn't been NATed to backend port"); 636 637 test_finish(); 638 } 639 640 /* Test that a SVC request that is LBed to a NAT remote backend 641 * - gets DNATed and SNATed, 642 * - gets redirected back out by TC 643 */ 644 PKTGEN("tc", "tc_nodeport_nat_fwd") 645 int nodeport_nat_fwd_pktgen(struct __ctx_buff *ctx) 646 { 647 struct pktgen builder; 648 struct tcphdr *l4; 649 void *data; 650 651 /* Init packet builder */ 652 pktgen__init(&builder, ctx); 653 654 l4 = pktgen__push_ipv4_tcp_packet(&builder, 655 (__u8 *)client_mac, (__u8 *)lb_mac, 656 CLIENT_IP, FRONTEND_IP_REMOTE, 657 CLIENT_PORT, FRONTEND_PORT); 658 if (!l4) 659 return TEST_ERROR; 660 661 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 662 if (!data) 663 return TEST_ERROR; 664 665 /* Calc lengths, set protocol fields and calc checksums */ 666 pktgen__finish(&builder); 667 668 return 0; 669 } 670 671 SETUP("tc", "tc_nodeport_nat_fwd") 672 int nodeport_nat_fwd_setup(struct __ctx_buff *ctx) 673 { 674 __u16 revnat_id = 1; 675 676 lb_v4_add_service(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, revnat_id); 677 lb_v4_add_backend(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, 124, 678 BACKEND_IP_REMOTE, BACKEND_PORT, IPPROTO_TCP, 0); 679 680 ipcache_v4_add_entry(BACKEND_IP_REMOTE, 0, 112233, 0, 0); 681 682 /* Jump into the entrypoint */ 683 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 684 /* Fail if we didn't jump */ 685 return TEST_ERROR; 686 } 687 688 CHECK("tc", "tc_nodeport_nat_fwd") 689 int nodeport_nat_fwd_check(__maybe_unused const struct __ctx_buff *ctx) 690 { 691 void *data, *data_end; 692 __u32 *status_code; 693 struct tcphdr *l4; 694 struct ethhdr *l2; 695 struct iphdr *l3; 696 __u32 key = 0; 697 698 test_init(); 699 700 data = (void *)(long)ctx_data(ctx); 701 data_end = (void *)(long)ctx->data_end; 702 703 if (data + sizeof(__u32) > data_end) 704 test_fatal("status code out of bounds"); 705 706 status_code = data; 707 708 assert(*status_code == CTX_ACT_REDIRECT); 709 710 l2 = data + sizeof(__u32); 711 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 712 test_fatal("l2 out of bounds"); 713 714 l3 = (void *)l2 + sizeof(struct ethhdr); 715 if ((void *)l3 + sizeof(struct iphdr) > data_end) 716 test_fatal("l3 out of bounds"); 717 718 l4 = (void *)l3 + sizeof(struct iphdr); 719 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 720 test_fatal("l4 out of bounds"); 721 722 if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0) 723 test_fatal("src MAC is not the LB MAC") 724 if (memcmp(l2->h_dest, (__u8 *)remote_backend_mac, ETH_ALEN) != 0) 725 test_fatal("dst MAC is not the remote backend MAC") 726 727 if (l3->saddr != LB_IP) 728 test_fatal("src IP hasn't been NATed to LB IP"); 729 730 if (l3->daddr != BACKEND_IP_REMOTE) 731 test_fatal("dst IP hasn't been NATed to remote backend IP"); 732 733 if (l3->check != bpf_htons(0xa711)) 734 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 735 736 if (l4->source == CLIENT_PORT) 737 test_fatal("src port hasn't been NATed"); 738 739 if (l4->dest != BACKEND_PORT) 740 test_fatal("dst port hasn't been NATed to backend port"); 741 742 struct mock_settings *settings = map_lookup_elem(&settings_map, &key); 743 744 if (settings) 745 settings->nat_source_port = l4->source; 746 747 test_finish(); 748 } 749 750 static __always_inline int build_reply(struct __ctx_buff *ctx) 751 { 752 struct pktgen builder; 753 struct tcphdr *l4; 754 void *data; 755 __u16 nat_source_port = 0; 756 __u32 key = 0; 757 758 struct mock_settings *settings = map_lookup_elem(&settings_map, &key); 759 760 if (settings) 761 nat_source_port = settings->nat_source_port; 762 763 /* Init packet builder */ 764 pktgen__init(&builder, ctx); 765 766 l4 = pktgen__push_ipv4_tcp_packet(&builder, 767 (__u8 *)remote_backend_mac, (__u8 *)lb_mac, 768 BACKEND_IP_REMOTE, LB_IP, 769 BACKEND_PORT, nat_source_port); 770 if (!l4) 771 return TEST_ERROR; 772 773 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 774 if (!data) 775 return TEST_ERROR; 776 777 /* Calc lengths, set protocol fields and calc checksums */ 778 pktgen__finish(&builder); 779 780 return 0; 781 } 782 783 static __always_inline int check_reply(const struct __ctx_buff *ctx) 784 { 785 void *data, *data_end; 786 __u32 *status_code; 787 struct tcphdr *l4; 788 struct ethhdr *l2; 789 struct iphdr *l3; 790 791 test_init(); 792 793 data = (void *)(long)ctx_data(ctx); 794 data_end = (void *)(long)ctx->data_end; 795 796 if (data + sizeof(__u32) > data_end) 797 test_fatal("status code out of bounds"); 798 799 status_code = data; 800 801 assert(*status_code == CTX_ACT_REDIRECT); 802 803 l2 = data + sizeof(__u32); 804 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 805 test_fatal("l2 out of bounds"); 806 807 l3 = (void *)l2 + sizeof(struct ethhdr); 808 if ((void *)l3 + sizeof(struct iphdr) > data_end) 809 test_fatal("l3 out of bounds"); 810 811 l4 = (void *)l3 + sizeof(struct iphdr); 812 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 813 test_fatal("l4 out of bounds"); 814 815 if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0) 816 test_fatal("src MAC is not the LB MAC") 817 if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0) 818 test_fatal("dst MAC is not the client MAC") 819 820 if (l3->saddr != FRONTEND_IP_REMOTE) 821 test_fatal("src IP hasn't been RevNATed to frontend IP"); 822 823 if (l3->daddr != CLIENT_IP) 824 test_fatal("dst IP hasn't been RevNATed to client IP"); 825 826 if (l4->source != FRONTEND_PORT) 827 test_fatal("src port hasn't been RevNATed to frontend port"); 828 829 if (l4->dest != CLIENT_PORT) 830 test_fatal("dst port hasn't been RevNATed to client port"); 831 832 test_finish(); 833 } 834 835 /* Test that the LB RevDNATs and RevSNATs a reply from the 836 * NAT remote backend, and sends it back to the client. 837 */ 838 PKTGEN("tc", "tc_nodeport_nat_fwd_reply") 839 int nodeport_nat_fwd_reply_pktgen(struct __ctx_buff *ctx) 840 { 841 return build_reply(ctx); 842 } 843 844 SETUP("tc", "tc_nodeport_nat_fwd_reply") 845 int nodeport_nat_fwd_reply_setup(struct __ctx_buff *ctx) 846 { 847 /* Jump into the entrypoint */ 848 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 849 /* Fail if we didn't jump */ 850 return TEST_ERROR; 851 } 852 853 CHECK("tc", "tc_nodeport_nat_fwd_reply") 854 int nodeport_nat_fwd_reply_check(const struct __ctx_buff *ctx) 855 { 856 return check_reply(ctx); 857 } 858 859 /* Test that the LB RevDNATs and RevSNATs a reply from the 860 * NAT remote backend, and sends it back to the client. 861 * Even if the FIB lookup fails. 862 */ 863 PKTGEN("tc", "tc_nodeport_nat_fwd_reply_no_fib") 864 int nodepoirt_nat_fwd_reply_no_fib_pktgen(struct __ctx_buff *ctx) 865 { 866 return build_reply(ctx); 867 } 868 869 SETUP("tc", "tc_nodeport_nat_fwd_reply_no_fib") 870 int nodeport_nat_fwd_reply_no_fib_setup(struct __ctx_buff *ctx) 871 { 872 __u32 key = 0; 873 struct mock_settings *settings = map_lookup_elem(&settings_map, &key); 874 875 if (settings) 876 settings->fail_fib = true; 877 878 /* Jump into the entrypoint */ 879 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 880 /* Fail if we didn't jump */ 881 return TEST_ERROR; 882 } 883 884 CHECK("tc", "tc_nodeport_nat_fwd_reply_no_fib") 885 int nodeport_nat_fwd_reply_no_fib_check(__maybe_unused const struct __ctx_buff *ctx) 886 { 887 return check_reply(ctx); 888 }