github.com/cilium/cilium@v1.16.2/bpf/tests/nodeport_geneve_dsr_lb_xdp.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 #define ENABLE_DSR 1 17 18 #define DSR_ENCAP_IPIP 2 19 #define DSR_ENCAP_GENEVE 3 20 #define DSR_ENCAP_MODE DSR_ENCAP_GENEVE 21 22 #define TUNNEL_PROTOCOL TUNNEL_PROTOCOL_GENEVE 23 #define ENCAP_IFINDEX 42 24 #define TUNNEL_MODE 25 26 #define DISABLE_LOOPBACK_LB 27 28 /* Skip ingress policy checks, not needed to validate hairpin flow */ 29 #define USE_BPF_PROG_FOR_INGRESS_POLICY 30 #undef FORCE_LOCAL_POLICY_EVAL_AT_SOURCE 31 32 #define CLIENT_IP v4_ext_one 33 #define CLIENT_PORT __bpf_htons(111) 34 35 #define FRONTEND_IP_LOCAL v4_svc_one 36 #define FRONTEND_IP_REMOTE v4_svc_two 37 #define FRONTEND_PORT tcp_svc_one 38 39 #define LB_IP v4_node_one 40 #define IPV4_DIRECT_ROUTING LB_IP 41 #define BACKEND_NODE_IP v4_node_two 42 43 #define DIRECT_ROUTING_IFINDEX 25 44 45 #define BACKEND_IP_LOCAL v4_pod_one 46 #define BACKEND_IP_REMOTE v4_pod_two 47 #define BACKEND_PORT __bpf_htons(8080) 48 49 #define fib_lookup mock_fib_lookup 50 51 static volatile const __u8 *client_mac = mac_one; 52 /* this matches the default node_config.h: */ 53 static volatile const __u8 lb_mac[ETH_ALEN] = { 0xce, 0x72, 0xa7, 0x03, 0x88, 0x56 }; 54 static volatile const __u8 *node_mac = mac_three; 55 static volatile const __u8 *local_backend_mac = mac_four; 56 static volatile const __u8 *backend_node_mac = mac_six; 57 58 static bool fail_fib; 59 60 #define ctx_redirect mock_ctx_redirect 61 static __always_inline __maybe_unused int 62 mock_ctx_redirect(const struct __ctx_buff *ctx __maybe_unused, int ifindex __maybe_unused, 63 __u32 flags __maybe_unused) 64 { 65 if (ifindex != DIRECT_ROUTING_IFINDEX) 66 return CTX_ACT_DROP; 67 68 return CTX_ACT_REDIRECT; 69 } 70 71 long mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params, 72 __maybe_unused int plen, __maybe_unused __u32 flags) 73 { 74 if (fail_fib) 75 return BPF_FIB_LKUP_RET_NO_NEIGH; 76 77 params->ifindex = DIRECT_ROUTING_IFINDEX; 78 79 if (params->ipv4_dst == BACKEND_NODE_IP) { 80 __bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN); 81 __bpf_memcpy_builtin(params->dmac, (__u8 *)backend_node_mac, ETH_ALEN); 82 } else { 83 return CTX_ACT_DROP; 84 } 85 86 return 0; 87 } 88 89 #include <bpf_xdp.c> 90 91 #include "lib/endpoint.h" 92 #include "lib/ipcache.h" 93 #include "lib/lb.h" 94 95 #define FROM_NETDEV 0 96 97 struct { 98 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 99 __uint(key_size, sizeof(__u32)); 100 __uint(max_entries, 1); 101 __array(values, int()); 102 } entry_call_map __section(".maps") = { 103 .values = { 104 [FROM_NETDEV] = &cil_xdp_entry, 105 }, 106 }; 107 108 /* Test that a SVC request to a local backend 109 * - gets DNATed (but not SNATed) 110 * - gets passed up from XDP to TC 111 */ 112 PKTGEN("xdp", "nodeport_geneve_dsr_lb_xdp1_local_backend") 113 int nodeport_geneve_dsr_lb_xdp1_local_backend_pktgen(struct __ctx_buff *ctx) 114 { 115 struct pktgen builder; 116 struct tcphdr *l4; 117 void *data; 118 119 /* Init packet builder */ 120 pktgen__init(&builder, ctx); 121 122 l4 = pktgen__push_ipv4_tcp_packet(&builder, 123 (__u8 *)client_mac, (__u8 *)lb_mac, 124 CLIENT_IP, FRONTEND_IP_LOCAL, 125 CLIENT_PORT, FRONTEND_PORT); 126 if (!l4) 127 return TEST_ERROR; 128 129 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 130 if (!data) 131 return TEST_ERROR; 132 133 /* Calc lengths, set protocol fields and calc checksums */ 134 pktgen__finish(&builder); 135 136 return 0; 137 } 138 139 SETUP("xdp", "nodeport_geneve_dsr_lb_xdp1_local_backend") 140 int nodeport_geneve_dsr_lb_xdp1_local_backend_setup(struct __ctx_buff *ctx) 141 { 142 __u16 revnat_id = 1; 143 144 lb_v4_add_service(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, revnat_id); 145 lb_v4_add_backend(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, 124, 146 BACKEND_IP_LOCAL, BACKEND_PORT, IPPROTO_TCP, 0); 147 148 /* add local backend */ 149 endpoint_v4_add_entry(BACKEND_IP_LOCAL, 0, 0, 0, 0, 150 (__u8 *)local_backend_mac, (__u8 *)node_mac); 151 152 ipcache_v4_add_entry(BACKEND_IP_LOCAL, 0, 112233, 0, 0); 153 154 /* Jump into the entrypoint */ 155 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 156 /* Fail if we didn't jump */ 157 return TEST_ERROR; 158 } 159 160 CHECK("xdp", "nodeport_geneve_dsr_lb_xdp1_local_backend") 161 int nodeport_geneve_dsr_lb_xdp1_local_backend_check(const struct __ctx_buff *ctx) 162 { 163 void *data, *data_end; 164 __u32 *status_code; 165 struct tcphdr *l4; 166 struct ethhdr *l2; 167 struct iphdr *l3; 168 __u32 *meta; 169 170 test_init(); 171 172 data = (void *)(long)ctx_data(ctx); 173 data_end = (void *)(long)ctx->data_end; 174 175 status_code = data; 176 if (data + sizeof(__u32) > data_end) 177 test_fatal("status code out of bounds"); 178 179 meta = (void *)status_code + sizeof(__u32); 180 if ((void *)meta + sizeof(__u32) > data_end) 181 test_fatal("meta out of bounds"); 182 183 l2 = (void *)meta + sizeof(__u32); 184 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 185 test_fatal("l2 out of bounds"); 186 187 l3 = (void *)l2 + sizeof(struct ethhdr); 188 if ((void *)l3 + sizeof(struct iphdr) > data_end) 189 test_fatal("l3 out of bounds"); 190 191 l4 = (void *)l3 + sizeof(struct iphdr); 192 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 193 test_fatal("l4 out of bounds"); 194 195 assert(*status_code == CTX_ACT_OK); 196 197 assert((*meta & XFER_PKT_NO_SVC) == XFER_PKT_NO_SVC); 198 199 if (memcmp(l2->h_source, (__u8 *)client_mac, ETH_ALEN) != 0) 200 test_fatal("src MAC is not the client MAC"); 201 if (memcmp(l2->h_dest, (__u8 *)lb_mac, ETH_ALEN) != 0) 202 test_fatal("dst MAC is not the LB MAC"); 203 204 if (l3->saddr != CLIENT_IP) 205 test_fatal("src IP has changed"); 206 207 if (l3->daddr != BACKEND_IP_LOCAL) 208 test_fatal("dst IP hasn't been NATed to local backend IP"); 209 210 if (l3->check != bpf_htons(0x4112)) 211 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 212 213 if (l4->source != CLIENT_PORT) 214 test_fatal("src port has changed"); 215 216 if (l4->dest != BACKEND_PORT) 217 test_fatal("dst TCP port hasn't been NATed to backend port"); 218 219 test_finish(); 220 } 221 222 /* Test that a SVC request that is LBed to a DSR remote backend 223 * - gets DNATed, 224 * - has tunnel encapsulation header added, 225 * - has DSR option inserted 226 */ 227 PKTGEN("xdp", "nodeport_geneve_dsr_lb_xdp2_fwd") 228 int nodeport_geneve_dsr_lb_xdp2_fwd_pktgen(struct __ctx_buff *ctx) 229 { 230 struct pktgen builder; 231 struct tcphdr *l4; 232 void *data; 233 234 /* Init packet builder */ 235 pktgen__init(&builder, ctx); 236 237 l4 = pktgen__push_ipv4_tcp_packet(&builder, 238 (__u8 *)client_mac, (__u8 *)lb_mac, 239 CLIENT_IP, FRONTEND_IP_REMOTE, 240 CLIENT_PORT, FRONTEND_PORT); 241 if (!l4) 242 return TEST_ERROR; 243 244 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 245 if (!data) 246 return TEST_ERROR; 247 248 /* Calc lengths, set protocol fields and calc checksums */ 249 pktgen__finish(&builder); 250 251 return 0; 252 } 253 254 SETUP("xdp", "nodeport_geneve_dsr_lb_xdp2_fwd") 255 int nodeport_geneve_dsr_lb_xdp2_fwd_setup(struct __ctx_buff *ctx) 256 { 257 __u32 backend_id = 125; 258 __u16 revnat_id = 2; 259 260 lb_v4_add_service(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, revnat_id); 261 lb_v4_add_backend(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, backend_id, 262 BACKEND_IP_REMOTE, BACKEND_PORT, IPPROTO_TCP, 0); 263 264 ipcache_v4_add_entry(BACKEND_IP_REMOTE, 0, 112233, BACKEND_NODE_IP, 0); 265 266 /* Jump into the entrypoint */ 267 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 268 /* Fail if we didn't jump */ 269 return TEST_ERROR; 270 } 271 272 CHECK("xdp", "nodeport_geneve_dsr_lb_xdp2_fwd") 273 int nodeport_geneve_dsr_lb_xdp_fwd_check(__maybe_unused const struct __ctx_buff *ctx) 274 { 275 struct geneve_dsr_opt4 *dsr_opt; 276 struct ethhdr *l2, *inner_l2; 277 struct iphdr *l3, *inner_l3; 278 struct tcphdr *tcp_inner; 279 struct genevehdr *geneve; 280 void *data, *data_end; 281 __u32 *status_code; 282 struct udphdr *udp; 283 284 test_init(); 285 286 data = (void *)(long)ctx_data(ctx); 287 data_end = (void *)(long)ctx->data_end; 288 289 if (data + sizeof(__u32) > data_end) 290 test_fatal("status code out of bounds"); 291 292 status_code = data; 293 294 assert(*status_code == CTX_ACT_REDIRECT); 295 296 l2 = data + sizeof(__u32); 297 if ((void *)l2 + sizeof(*l2) > data_end) 298 test_fatal("l2 out of bounds"); 299 300 l3 = (void *)l2 + sizeof(*l2); 301 if ((void *)l3 + sizeof(*l3) > data_end) 302 test_fatal("l3 out of bounds"); 303 304 udp = (void *)l3 + sizeof(*l3); 305 if ((void *)udp + sizeof(*udp) > data_end) 306 test_fatal("udp out of bounds"); 307 308 geneve = (void *)udp + sizeof(*udp); 309 if ((void *)geneve + sizeof(*geneve) > data_end) 310 test_fatal("geneve out of bounds"); 311 312 dsr_opt = (void *)geneve + sizeof(*geneve); 313 if ((void *)dsr_opt + sizeof(*dsr_opt) > data_end) 314 test_fatal("dsr opt out of bounds"); 315 if ((void *)dsr_opt + geneve->opt_len * 4 > data_end) 316 test_fatal("geneve opts out of bounds"); 317 318 inner_l2 = (void *)dsr_opt + geneve->opt_len * 4; 319 if ((void *)inner_l2 + sizeof(*inner_l2) > data_end) 320 test_fatal("l2 out of bounds"); 321 322 inner_l3 = (void *)inner_l2 + sizeof(*inner_l2); 323 if ((void *)inner_l3 + sizeof(*inner_l3) > data_end) 324 test_fatal("l3 out of bounds"); 325 326 tcp_inner = (void *)inner_l3 + sizeof(*inner_l3); 327 if ((void *)tcp_inner + sizeof(*tcp_inner) > data_end) 328 test_fatal("tcp out of bounds"); 329 330 if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0) 331 test_fatal("src MAC is not the LB MAC"); 332 if (memcmp(l2->h_dest, (__u8 *)backend_node_mac, ETH_ALEN) != 0) 333 test_fatal("dst MAC is not the backend node MAC"); 334 335 if (l2->h_proto != bpf_htons(ETH_P_IP)) 336 test_fatal("l2 doesn't have correct proto type"); 337 338 if (l3->protocol != IPPROTO_UDP) 339 test_fatal("outer IP doesn't have correct L4 protocol"); 340 341 if (l3->saddr != IPV4_DIRECT_ROUTING) 342 test_fatal("outerSrcIP is not correct"); 343 344 if (l3->daddr != BACKEND_NODE_IP) 345 test_fatal("outerDstIP is not correct"); 346 347 if (l3->check != bpf_htons(0x5371)) 348 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 349 350 if (udp->dest != bpf_htons(TUNNEL_PORT)) 351 test_fatal("outerDstPort is not tunnel port"); 352 353 __be32 sec_id; 354 355 memcpy(&sec_id, &geneve->vni, 4); 356 if (tunnel_vni_to_sec_identity(sec_id) != WORLD_ID) 357 test_fatal("geneve has unexpected SrcSecID"); 358 359 if (geneve->opt_len * 4 != sizeof(*dsr_opt)) 360 test_fatal("geneve has unexpected opt length"); 361 362 if (dsr_opt->hdr.opt_class != bpf_htons(DSR_GENEVE_OPT_CLASS)) 363 test_fatal("geneve opt has unexpected class"); 364 if (dsr_opt->hdr.type != DSR_GENEVE_OPT_TYPE) 365 test_fatal("geneve opt has unexpected type"); 366 if (dsr_opt->hdr.length != DSR_IPV4_GENEVE_OPT_LEN) 367 test_fatal("geneve opt has unexpected length"); 368 if (dsr_opt->addr != FRONTEND_IP_REMOTE) 369 test_fatal("geneve opt has unexpected svc IP"); 370 if (dsr_opt->port != FRONTEND_PORT) 371 test_fatal("geneve opt has unexpected svc port"); 372 373 if (inner_l2->h_proto != bpf_htons(ETH_P_IP)) 374 test_fatal("inner l2 doesn't have correct proto type"); 375 376 if (inner_l3->protocol != IPPROTO_TCP) 377 test_fatal("inner IP doesn't have correct L4 protocol"); 378 379 if (inner_l3->saddr != CLIENT_IP) 380 test_fatal("innerSrcIP has changed"); 381 382 if (inner_l3->daddr != BACKEND_IP_REMOTE) 383 test_fatal("innerDstIP hasn't been NATed to remote backend IP"); 384 385 if (inner_l3->check != bpf_htons(0x4111)) 386 test_fatal("L3 checksum is invalid: %d", bpf_htons(inner_l3->check)); 387 388 if (tcp_inner->source != CLIENT_PORT) 389 test_fatal("innerSrcPort has changed"); 390 391 if (tcp_inner->dest != BACKEND_PORT) 392 test_fatal("innerDstPort hasn't been NATed to backend port"); 393 394 test_finish(); 395 }