github.com/cilium/cilium@v1.16.2/bpf/tests/xdp_egressgw_reply.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 ENABLE_EGRESS_GATEWAY 18 #define ENABLE_MASQUERADE 19 20 #define TUNNEL_PROTOCOL TUNNEL_PROTOCOL_VXLAN 21 #define ENCAP_IFINDEX 42 22 23 #define DISABLE_LOOPBACK_LB 24 25 /* Skip ingress policy checks, not needed to validate hairpin flow */ 26 #define USE_BPF_PROG_FOR_INGRESS_POLICY 27 28 #define IPV4_DIRECT_ROUTING v4_node_one /* gateway node */ 29 #define MASQ_PORT __bpf_htons(NODEPORT_PORT_MIN_NAT + 1) 30 #define DIRECT_ROUTING_IFINDEX 25 31 32 #define ctx_redirect mock_ctx_redirect 33 static __always_inline __maybe_unused int 34 mock_ctx_redirect(const struct __ctx_buff *ctx __maybe_unused, int ifindex __maybe_unused, 35 __u32 flags __maybe_unused); 36 37 #define fib_lookup mock_fib_lookup 38 static __always_inline __maybe_unused long 39 mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params, 40 __maybe_unused int plen, __maybe_unused __u32 flags); 41 42 #include <bpf_xdp.c> 43 44 #include "lib/egressgw.h" 45 #include "lib/ipcache.h" 46 47 static __always_inline __maybe_unused int 48 mock_ctx_redirect(const struct __ctx_buff *ctx __maybe_unused, int ifindex __maybe_unused, 49 __u32 flags __maybe_unused) 50 { 51 if (ifindex != DIRECT_ROUTING_IFINDEX) 52 return CTX_ACT_DROP; 53 54 return CTX_ACT_REDIRECT; 55 } 56 57 static __always_inline __maybe_unused long 58 mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params, 59 __maybe_unused int plen, __maybe_unused __u32 flags) 60 { 61 params->ifindex = DIRECT_ROUTING_IFINDEX; 62 63 if (params->ipv4_dst == CLIENT_NODE_IP) { 64 __bpf_memcpy_builtin(params->smac, (__u8 *)gateway_mac, ETH_ALEN); 65 __bpf_memcpy_builtin(params->dmac, (__u8 *)client_mac, ETH_ALEN); 66 } else { 67 return CTX_ACT_DROP; 68 } 69 70 return 0; 71 } 72 73 #define FROM_NETDEV 0 74 75 struct { 76 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 77 __uint(key_size, sizeof(__u32)); 78 __uint(max_entries, 1); 79 __array(values, int()); 80 } entry_call_map __section(".maps") = { 81 .values = { 82 [FROM_NETDEV] = &cil_xdp_entry, 83 }, 84 }; 85 86 /* Test that a EgressGW reply gets RevSNATed, and forwarded to the 87 * worker node via tunnel. 88 */ 89 PKTGEN("xdp", "xdp_egressgw_reply") 90 int egressgw_reply_pktgen(struct __ctx_buff *ctx) 91 { 92 /* Add a new NAT entry so that pktgen can figure out the correct destination port */ 93 struct ipv4_ct_tuple tuple = { 94 .saddr = CLIENT_IP, 95 .daddr = EXTERNAL_SVC_IP, 96 .dport = EXTERNAL_SVC_PORT, 97 .sport = client_port(TEST_XDP_REPLY), 98 .nexthdr = IPPROTO_TCP, 99 }; 100 101 struct ipv4_nat_entry nat_entry = { 102 .to_saddr = EGRESS_IP, 103 .to_sport = MASQ_PORT, 104 }; 105 106 map_update_elem(&SNAT_MAPPING_IPV4, &tuple, &nat_entry, BPF_ANY); 107 108 return egressgw_pktgen(ctx, (struct egressgw_test_ctx) { 109 .test = TEST_XDP_REPLY, 110 .dir = CT_INGRESS, 111 }); 112 } 113 114 SETUP("xdp", "xdp_egressgw_reply") 115 int egressgw_reply_setup(struct __ctx_buff *ctx) 116 { 117 /* install EgressGW policy for the connection: */ 118 add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24, GATEWAY_NODE_IP, 0); 119 120 /* install RevSNAT entry */ 121 struct ipv4_ct_tuple snat_tuple = { 122 .daddr = EGRESS_IP, 123 .saddr = EXTERNAL_SVC_IP, 124 .dport = MASQ_PORT, 125 .sport = EXTERNAL_SVC_PORT, 126 .nexthdr = IPPROTO_TCP, 127 .flags = NAT_DIR_INGRESS, 128 }; 129 130 struct ipv4_nat_entry snat_entry = { 131 .to_daddr = CLIENT_IP, 132 .to_dport = client_port(TEST_XDP_REPLY), 133 }; 134 135 map_update_elem(&SNAT_MAPPING_IPV4, &snat_tuple, &snat_entry, BPF_ANY); 136 137 /* install ipcache entry for the CLIENT_IP: */ 138 ipcache_v4_add_entry(CLIENT_IP, 0, 0, CLIENT_NODE_IP, 0); 139 140 /* Jump into the entrypoint */ 141 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 142 /* Fail if we didn't jump */ 143 return TEST_ERROR; 144 } 145 146 CHECK("xdp", "xdp_egressgw_reply") 147 int egressgw_reply_check(__maybe_unused const struct __ctx_buff *ctx) 148 { 149 void *data, *data_end; 150 __u32 *status_code; 151 struct tcphdr *inner_l4; 152 struct udphdr *l4; 153 struct ethhdr *l2, *inner_l2; 154 struct iphdr *l3, *inner_l3; 155 struct vxlanhdr *vxlan; 156 157 test_init(); 158 159 data = (void *)(long)ctx_data(ctx); 160 data_end = (void *)(long)ctx->data_end; 161 162 if (data + sizeof(__u32) > data_end) 163 test_fatal("status code out of bounds"); 164 165 status_code = data; 166 167 assert(*status_code == CTX_ACT_REDIRECT); 168 169 l2 = data + sizeof(__u32); 170 if ((void *)l2 + sizeof(*l2) > data_end) 171 test_fatal("l2 out of bounds"); 172 173 l3 = (void *)l2 + sizeof(*l2); 174 if ((void *)l3 + sizeof(*l3) > data_end) 175 test_fatal("l3 out of bounds"); 176 177 l4 = (void *)l3 + sizeof(*l3); 178 if ((void *)l4 + sizeof(*l4) > data_end) 179 test_fatal("l4 out of bounds"); 180 181 vxlan = (void *)l4 + sizeof(*l4); 182 if ((void *)vxlan + sizeof(*vxlan) > data_end) 183 test_fatal("vxlan out of bounds"); 184 185 inner_l2 = (void *)vxlan + sizeof(*vxlan); 186 if ((void *)inner_l2 + sizeof(*inner_l2) > data_end) 187 test_fatal("inner l2 out of bounds"); 188 189 inner_l3 = (void *)inner_l2 + sizeof(*inner_l2); 190 if ((void *)inner_l3 + sizeof(*inner_l3) > data_end) 191 test_fatal("inner l3 out of bounds"); 192 193 inner_l4 = (void *)inner_l3 + sizeof(*inner_l3); 194 if ((void *)inner_l4 + sizeof(*inner_l4) > data_end) 195 test_fatal("inner l4 out of bounds"); 196 197 if (memcmp(l2->h_source, (__u8 *)gateway_mac, ETH_ALEN) != 0) 198 test_fatal("src MAC is not the gateway MAC") 199 if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0) 200 test_fatal("dst MAC is not the client node MAC") 201 202 if (l2->h_proto != bpf_htons(ETH_P_IP)) 203 test_fatal("l2 doesn't have correct proto type") 204 205 if (l3->protocol != IPPROTO_UDP) 206 test_fatal("outer IP doesn't have correct L4 protocol") 207 208 if (l3->check != bpf_htons(0x527e)) 209 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 210 211 if (l3->saddr != IPV4_DIRECT_ROUTING) 212 test_fatal("outerSrcIP is not correct") 213 214 if (l3->daddr != CLIENT_NODE_IP) 215 test_fatal("outerDstIP is not correct") 216 217 if (l4->dest != bpf_htons(TUNNEL_PORT)) 218 test_fatal("outerDstPort is not tunnel port") 219 220 if (inner_l2->h_proto != bpf_htons(ETH_P_IP)) 221 test_fatal("inner L2 doesn't have correct ethertype") 222 223 if (inner_l3->protocol != IPPROTO_TCP) 224 test_fatal("inner IP doesn't have correct L4 protocol") 225 226 if (inner_l3->saddr != EXTERNAL_SVC_IP) 227 test_fatal("innerSrcIP is not the external SVC IP"); 228 229 if (inner_l3->daddr != CLIENT_IP) 230 test_fatal("innerDstIP hasn't been revNATed to the client IP"); 231 232 if (inner_l3->check != bpf_htons(0x4212)) 233 test_fatal("inner L3 checksum is invalid: %d", bpf_htons(inner_l3->check)); 234 235 if (inner_l4->source != EXTERNAL_SVC_PORT) 236 test_fatal("innerSrcPort is not the external SVC port"); 237 238 if (inner_l4->dest != client_port(TEST_XDP_REPLY)) 239 test_fatal("innerDstPort hasn't been revNATed to client port"); 240 241 del_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24); 242 243 test_finish(); 244 }