github.com/cilium/cilium@v1.16.2/bpf/tests/xdp_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/xdp.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_NODEPORT_ACCELERATION 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 26 #define FRONTEND_IP_LOCAL v4_svc_one 27 #define FRONTEND_IP_REMOTE v4_svc_two 28 #define FRONTEND_PORT tcp_svc_one 29 30 #define LB_IP v4_node_one 31 #define IPV4_DIRECT_ROUTING LB_IP 32 33 #define BACKEND_IP_LOCAL v4_pod_one 34 #define BACKEND_IP_REMOTE v4_pod_two 35 #define BACKEND_PORT __bpf_htons(8080) 36 37 #define fib_lookup mock_fib_lookup 38 39 static volatile const __u8 *client_mac = mac_one; 40 /* this matches the default node_config.h: */ 41 static volatile const __u8 lb_mac[ETH_ALEN] = { 0xce, 0x72, 0xa7, 0x03, 0x88, 0x56 }; 42 static volatile const __u8 *node_mac = mac_three; 43 static volatile const __u8 *local_backend_mac = mac_four; 44 static volatile const __u8 *remote_backend_mac = mac_five; 45 46 #include <bpf/compiler.h> 47 #include <bpf/helpers.h> 48 #include <bpf/loader.h> 49 #include <bpf/section.h> 50 #include <linux/bpf.h> 51 #include <linux/types.h> 52 53 struct mock_settings { 54 __be16 nat_source_port; 55 bool fail_fib; 56 }; 57 58 struct { 59 __uint(type, BPF_MAP_TYPE_ARRAY); 60 __uint(key_size, sizeof(__u32)); 61 __uint(value_size, sizeof(struct mock_settings)); 62 __uint(max_entries, 1); 63 } settings_map __section_maps_btf; 64 65 long mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params, 66 __maybe_unused int plen, __maybe_unused __u32 flags) 67 { 68 __u32 key = 0; 69 struct mock_settings *settings = map_lookup_elem(&settings_map, &key); 70 71 if (settings && settings->fail_fib) 72 return BPF_FIB_LKUP_RET_NO_NEIGH; 73 74 params->ifindex = 0; 75 76 if (params->ipv4_dst == BACKEND_IP_REMOTE) { 77 __bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN); 78 __bpf_memcpy_builtin(params->dmac, (__u8 *)remote_backend_mac, ETH_ALEN); 79 } else { 80 __bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN); 81 __bpf_memcpy_builtin(params->dmac, (__u8 *)client_mac, ETH_ALEN); 82 } 83 84 return 0; 85 } 86 87 #include <bpf_xdp.c> 88 89 #include "lib/endpoint.h" 90 #include "lib/ipcache.h" 91 #include "lib/lb.h" 92 93 #define FROM_NETDEV 0 94 95 struct { 96 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 97 __uint(key_size, sizeof(__u32)); 98 __uint(max_entries, 1); 99 __array(values, int()); 100 } entry_call_map __section(".maps") = { 101 .values = { 102 [FROM_NETDEV] = &cil_xdp_entry, 103 }, 104 }; 105 106 /* Test that a SVC request to a local backend 107 * - gets DNATed (but not SNATed) 108 * - gets passed up from XDP to TC 109 */ 110 PKTGEN("xdp", "xdp_nodeport_local_backend") 111 int nodeport_local_backend_pktgen(struct __ctx_buff *ctx) 112 { 113 struct pktgen builder; 114 struct tcphdr *l4; 115 void *data; 116 117 /* Init packet builder */ 118 pktgen__init(&builder, ctx); 119 120 l4 = pktgen__push_ipv4_tcp_packet(&builder, 121 (__u8 *)client_mac, (__u8 *)lb_mac, 122 CLIENT_IP, FRONTEND_IP_LOCAL, 123 CLIENT_PORT, FRONTEND_PORT); 124 if (!l4) 125 return TEST_ERROR; 126 127 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 128 if (!data) 129 return TEST_ERROR; 130 131 /* Calc lengths, set protocol fields and calc checksums */ 132 pktgen__finish(&builder); 133 134 return 0; 135 } 136 137 SETUP("xdp", "xdp_nodeport_local_backend") 138 int nodeport_local_backend_setup(struct __ctx_buff *ctx) 139 { 140 __u16 revnat_id = 1; 141 142 lb_v4_add_service(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, revnat_id); 143 lb_v4_add_backend(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, 124, 144 BACKEND_IP_LOCAL, BACKEND_PORT, IPPROTO_TCP, 0); 145 146 /* add local backend */ 147 endpoint_v4_add_entry(BACKEND_IP_LOCAL, 0, 0, 0, 0, 148 (__u8 *)local_backend_mac, (__u8 *)node_mac); 149 ipcache_v4_add_entry(BACKEND_IP_LOCAL, 0, 112233, 0, 0); 150 151 /* Jump into the entrypoint */ 152 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 153 /* Fail if we didn't jump */ 154 return TEST_ERROR; 155 } 156 157 CHECK("xdp", "xdp_nodeport_local_backend") 158 int nodeport_local_backend_check(const struct __ctx_buff *ctx) 159 { 160 void *data, *data_end; 161 __u32 *status_code; 162 struct tcphdr *l4; 163 struct ethhdr *l2; 164 struct iphdr *l3; 165 __u32 *meta; 166 167 test_init(); 168 169 data = (void *)(long)ctx_data(ctx); 170 data_end = (void *)(long)ctx->data_end; 171 172 status_code = data; 173 if (data + sizeof(__u32) > data_end) 174 test_fatal("status code out of bounds"); 175 176 meta = (void *)status_code + sizeof(__u32); 177 if ((void *)meta + sizeof(__u32) > data_end) 178 test_fatal("meta out of bounds"); 179 180 l2 = (void *)meta + sizeof(__u32); 181 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 182 test_fatal("l2 out of bounds"); 183 184 l3 = (void *)l2 + sizeof(struct ethhdr); 185 if ((void *)l3 + sizeof(struct iphdr) > data_end) 186 test_fatal("l3 out of bounds"); 187 188 l4 = (void *)l3 + sizeof(struct iphdr); 189 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 190 test_fatal("l4 out of bounds"); 191 192 assert(*status_code == CTX_ACT_OK); 193 194 assert((*meta & XFER_PKT_NO_SVC) == XFER_PKT_NO_SVC); 195 196 if (memcmp(l2->h_source, (__u8 *)client_mac, ETH_ALEN) != 0) 197 test_fatal("src MAC is not the client MAC") 198 if (memcmp(l2->h_dest, (__u8 *)lb_mac, ETH_ALEN) != 0) 199 test_fatal("dst MAC is not the LB MAC") 200 201 if (l3->saddr != CLIENT_IP) 202 test_fatal("src IP has changed"); 203 204 if (l3->daddr != BACKEND_IP_LOCAL) 205 test_fatal("dst IP hasn't been NATed to local backend IP"); 206 207 if (l3->check != bpf_htons(0x4112)) 208 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 209 210 if (l4->source != CLIENT_PORT) 211 test_fatal("src port has changed"); 212 213 if (l4->dest != BACKEND_PORT) 214 test_fatal("dst TCP port hasn't been NATed to backend port"); 215 216 test_finish(); 217 } 218 219 /* Test that a SVC request that is LBed to a NAT remote backend 220 * - gets DNATed and SNATed, 221 * - gets redirected back out by XDP 222 */ 223 PKTGEN("xdp", "xdp_nodeport_nat_fwd") 224 int nodeport_nat_fwd_pktgen(struct __ctx_buff *ctx) 225 { 226 struct pktgen builder; 227 struct tcphdr *l4; 228 void *data; 229 230 /* Init packet builder */ 231 pktgen__init(&builder, ctx); 232 233 l4 = pktgen__push_ipv4_tcp_packet(&builder, 234 (__u8 *)client_mac, (__u8 *)lb_mac, 235 CLIENT_IP, FRONTEND_IP_REMOTE, 236 CLIENT_PORT, FRONTEND_PORT); 237 if (!l4) 238 return TEST_ERROR; 239 240 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 241 if (!data) 242 return TEST_ERROR; 243 244 /* Calc lengths, set protocol fields and calc checksums */ 245 pktgen__finish(&builder); 246 247 return 0; 248 } 249 250 SETUP("xdp", "xdp_nodeport_nat_fwd") 251 int nodeport_nat_fwd_setup(struct __ctx_buff *ctx) 252 { 253 __u16 revnat_id = 1; 254 255 lb_v4_add_service(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, revnat_id); 256 lb_v4_add_backend(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, 124, 257 BACKEND_IP_REMOTE, BACKEND_PORT, IPPROTO_TCP, 0); 258 259 ipcache_v4_add_entry(BACKEND_IP_REMOTE, 0, 112233, 0, 0); 260 261 /* Jump into the entrypoint */ 262 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 263 /* Fail if we didn't jump */ 264 return TEST_ERROR; 265 } 266 267 CHECK("xdp", "xdp_nodeport_nat_fwd") 268 int nodeport_nat_fwd_check(__maybe_unused const struct __ctx_buff *ctx) 269 { 270 void *data, *data_end; 271 __u32 *status_code; 272 struct tcphdr *l4; 273 struct ethhdr *l2; 274 struct iphdr *l3; 275 __u32 key = 0; 276 277 test_init(); 278 279 data = (void *)(long)ctx_data(ctx); 280 data_end = (void *)(long)ctx->data_end; 281 282 if (data + sizeof(__u32) > data_end) 283 test_fatal("status code out of bounds"); 284 285 status_code = data; 286 287 assert(fib_ok(*status_code)); 288 289 l2 = data + sizeof(__u32); 290 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 291 test_fatal("l2 out of bounds"); 292 293 l3 = (void *)l2 + sizeof(struct ethhdr); 294 if ((void *)l3 + sizeof(struct iphdr) > data_end) 295 test_fatal("l3 out of bounds"); 296 297 l4 = (void *)l3 + sizeof(struct iphdr); 298 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 299 test_fatal("l4 out of bounds"); 300 301 if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0) 302 test_fatal("src MAC is not the LB MAC") 303 if (memcmp(l2->h_dest, (__u8 *)remote_backend_mac, ETH_ALEN) != 0) 304 test_fatal("dst MAC is not the backend MAC") 305 306 if (l3->saddr != LB_IP) 307 test_fatal("src IP hasn't been NATed to LB IP"); 308 309 if (l3->daddr != BACKEND_IP_REMOTE) 310 test_fatal("dst IP hasn't been NATed to remote backend IP"); 311 312 if (l3->check != bpf_htons(0xa711)) 313 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 314 315 if (l4->source == CLIENT_PORT) 316 test_fatal("src port hasn't been NATed"); 317 318 if (l4->dest != BACKEND_PORT) 319 test_fatal("dst port hasn't been NATed to backend port"); 320 321 struct mock_settings *settings = map_lookup_elem(&settings_map, &key); 322 323 if (settings) 324 settings->nat_source_port = l4->source; 325 326 test_finish(); 327 } 328 329 static __always_inline int build_reply(struct __ctx_buff *ctx) 330 { 331 struct pktgen builder; 332 struct tcphdr *l4; 333 void *data; 334 __u16 nat_source_port = 0; 335 __u32 key = 0; 336 337 struct mock_settings *settings = map_lookup_elem(&settings_map, &key); 338 339 if (settings && settings->nat_source_port) 340 nat_source_port = settings->nat_source_port; 341 342 /* Init packet builder */ 343 pktgen__init(&builder, ctx); 344 345 l4 = pktgen__push_ipv4_tcp_packet(&builder, 346 (__u8 *)remote_backend_mac, (__u8 *)lb_mac, 347 BACKEND_IP_REMOTE, LB_IP, 348 BACKEND_PORT, nat_source_port); 349 if (!l4) 350 return TEST_ERROR; 351 352 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 353 if (!data) 354 return TEST_ERROR; 355 356 /* Calc lengths, set protocol fields and calc checksums */ 357 pktgen__finish(&builder); 358 359 return 0; 360 } 361 362 static __always_inline int check_reply(const struct __ctx_buff *ctx) 363 { 364 void *data, *data_end; 365 __u32 *status_code; 366 struct tcphdr *l4; 367 struct ethhdr *l2; 368 struct iphdr *l3; 369 370 test_init(); 371 372 data = (void *)(long)ctx_data(ctx); 373 data_end = (void *)(long)ctx->data_end; 374 375 if (data + sizeof(__u32) > data_end) 376 test_fatal("status code out of bounds"); 377 378 status_code = data; 379 380 assert(fib_ok(*status_code)); 381 382 l2 = data + sizeof(__u32); 383 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 384 test_fatal("l2 out of bounds"); 385 386 l3 = (void *)l2 + sizeof(struct ethhdr); 387 if ((void *)l3 + sizeof(struct iphdr) > data_end) 388 test_fatal("l3 out of bounds"); 389 390 l4 = (void *)l3 + sizeof(struct iphdr); 391 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 392 test_fatal("l4 out of bounds"); 393 394 if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0) 395 test_fatal("src MAC is not the LB MAC") 396 if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0) 397 test_fatal("dst MAC is not the client MAC") 398 399 if (l3->saddr != FRONTEND_IP_REMOTE) 400 test_fatal("src IP hasn't been RevNATed to frontend IP"); 401 402 if (l3->daddr != CLIENT_IP) 403 test_fatal("dst IP hasn't been RevNATed to client IP"); 404 405 if (l3->check != bpf_htons(0x4ca9)) 406 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 407 408 if (l4->source != FRONTEND_PORT) 409 test_fatal("src port hasn't been RevNATed to frontend port"); 410 411 if (l4->dest != CLIENT_PORT) 412 test_fatal("dst port hasn't been RevNATed to client port"); 413 414 test_finish(); 415 } 416 417 /* Test that the LB RevDNATs and RevSNATs a reply from the 418 * NAT remote backend, and sends it back to the client. 419 */ 420 PKTGEN("xdp", "xdp_nodeport_nat_fwd_reply") 421 int nodeport_nat_fwd_reply_pktgen(struct __ctx_buff *ctx) 422 { 423 return build_reply(ctx); 424 } 425 426 SETUP("xdp", "xdp_nodeport_nat_fwd_reply") 427 int nodeport_nat_fwd_reply_setup(struct __ctx_buff *ctx) 428 { 429 /* Jump into the entrypoint */ 430 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 431 /* Fail if we didn't jump */ 432 return TEST_ERROR; 433 } 434 435 CHECK("xdp", "xdp_nodeport_nat_fwd_reply") 436 int nodeport_nat_fwd_reply_check(const struct __ctx_buff *ctx) 437 { 438 return check_reply(ctx); 439 } 440 441 /* Test that the LB RevDNATs and RevSNATs a reply from the 442 * NAT remote backend, and sends it back to the client. 443 * Even if the FIB lookup fails. 444 */ 445 PKTGEN("xdp", "xdp_nodeport_nat_fwd_reply_no_fib") 446 int nodepoirt_nat_fwd_reply_no_fib_pktgen(struct __ctx_buff *ctx) 447 { 448 return build_reply(ctx); 449 } 450 451 SETUP("xdp", "xdp_nodeport_nat_fwd_reply_no_fib") 452 int nodeport_nat_fwd_reply_no_fib_setup(struct __ctx_buff *ctx) 453 { 454 __u32 key = 0; 455 struct mock_settings *settings = map_lookup_elem(&settings_map, &key); 456 457 if (settings) 458 settings->fail_fib = true; 459 460 /* Jump into the entrypoint */ 461 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 462 /* Fail if we didn't jump */ 463 return TEST_ERROR; 464 } 465 466 CHECK("xdp", "xdp_nodeport_nat_fwd_reply_no_fib") 467 int nodeport_nat_fwd_reply_no_fib_check(__maybe_unused const struct __ctx_buff *ctx) 468 { 469 return check_reply(ctx); 470 }