github.com/cilium/cilium@v1.16.2/bpf/tests/tc_egressgw_snat.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_EGRESS_GATEWAY 16 #define ENABLE_MASQUERADE_IPV4 17 #define ENCAP_IFINDEX 42 18 #define SECONDARY_IFACE_IFINDEX 44 19 20 #define SECCTX_FROM_IPCACHE 1 21 22 #define ctx_redirect mock_ctx_redirect 23 static __always_inline __maybe_unused int 24 mock_ctx_redirect(const struct __sk_buff *ctx __maybe_unused, 25 int ifindex __maybe_unused, __u32 flags __maybe_unused); 26 27 #define fib_lookup mock_fib_lookup 28 static __always_inline __maybe_unused long 29 mock_fib_lookup(void *ctx __maybe_unused, struct bpf_fib_lookup *params __maybe_unused, 30 int plen __maybe_unused, __u32 flags __maybe_unused); 31 32 #include "bpf_host.c" 33 34 #include "lib/egressgw.h" 35 #include "lib/ipcache.h" 36 37 static __always_inline __maybe_unused int 38 mock_ctx_redirect(const struct __sk_buff *ctx __maybe_unused, 39 int ifindex __maybe_unused, __u32 flags __maybe_unused) 40 { 41 if (ifindex == ENCAP_IFINDEX) 42 return CTX_ACT_REDIRECT; 43 if (ifindex == SECONDARY_IFACE_IFINDEX) 44 return CTX_ACT_REDIRECT; 45 46 return CTX_ACT_DROP; 47 } 48 49 static __always_inline __maybe_unused long 50 mock_fib_lookup(void *ctx __maybe_unused, struct bpf_fib_lookup *params __maybe_unused, 51 int plen __maybe_unused, __u32 flags __maybe_unused) 52 { 53 if (params && params->ipv4_src == EGRESS_IP2) 54 params->ifindex = SECONDARY_IFACE_IFINDEX; 55 56 return 0; 57 } 58 59 #define TO_NETDEV 0 60 #define FROM_NETDEV 1 61 62 struct { 63 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 64 __uint(key_size, sizeof(__u32)); 65 __uint(max_entries, 2); 66 __array(values, int()); 67 } entry_call_map __section(".maps") = { 68 .values = { 69 [TO_NETDEV] = &cil_to_netdev, 70 [FROM_NETDEV] = &cil_from_netdev, 71 }, 72 }; 73 74 /* Test that a packet matching an egress gateway policy on the to-netdev program 75 * gets correctly SNATed with the egress IP of the policy. 76 */ 77 PKTGEN("tc", "tc_egressgw_snat1") 78 int egressgw_snat1_pktgen(struct __ctx_buff *ctx) 79 { 80 return egressgw_pktgen(ctx, (struct egressgw_test_ctx) { 81 .test = TEST_SNAT1, 82 }); 83 } 84 85 SETUP("tc", "tc_egressgw_snat1") 86 int egressgw_snat1_setup(struct __ctx_buff *ctx) 87 { 88 add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24, 89 GATEWAY_NODE_IP, EGRESS_IP); 90 91 /* Jump into the entrypoint */ 92 ctx_egw_done_set(ctx); 93 tail_call_static(ctx, entry_call_map, TO_NETDEV); 94 /* Fail if we didn't jump */ 95 return TEST_ERROR; 96 } 97 98 CHECK("tc", "tc_egressgw_snat1") 99 int egressgw_snat1_check(const struct __ctx_buff *ctx) 100 { 101 return egressgw_snat_check(ctx, (struct egressgw_test_ctx) { 102 .test = TEST_SNAT1, 103 .packets = 1, 104 .status_code = CTX_ACT_OK 105 }); 106 } 107 108 /* Test that a packet matching an egress gateway policy on the from-netdev program 109 * gets correctly revSNATed and connection tracked. 110 */ 111 PKTGEN("tc", "tc_egressgw_snat1_2_reply") 112 int egressgw_snat1_2_reply_pktgen(struct __ctx_buff *ctx) 113 { 114 return egressgw_pktgen(ctx, (struct egressgw_test_ctx) { 115 .test = TEST_SNAT1, 116 .dir = CT_INGRESS, 117 }); 118 } 119 120 SETUP("tc", "tc_egressgw_snat1_2_reply") 121 int egressgw_snat1_2_reply_setup(struct __ctx_buff *ctx) 122 { 123 /* install ipcache entry for the CLIENT_IP: */ 124 ipcache_v4_add_entry(CLIENT_IP, 0, 0, CLIENT_NODE_IP, 0); 125 126 /* Jump into the entrypoint */ 127 tail_call_static(ctx, entry_call_map, FROM_NETDEV); 128 /* Fail if we didn't jump */ 129 return TEST_ERROR; 130 } 131 132 CHECK("tc", "tc_egressgw_snat1_2_reply") 133 int egressgw_snat1_2_reply_check(const struct __ctx_buff *ctx) 134 { 135 return egressgw_snat_check(ctx, (struct egressgw_test_ctx) { 136 .test = TEST_SNAT1, 137 .dir = CT_INGRESS, 138 .packets = 2, 139 .status_code = CTX_ACT_REDIRECT, 140 }); 141 } 142 143 PKTGEN("tc", "tc_egressgw_snat2") 144 int egressgw_snat2_pktgen(struct __ctx_buff *ctx) 145 { 146 return egressgw_pktgen(ctx, (struct egressgw_test_ctx) { 147 .test = TEST_SNAT2, 148 }); 149 } 150 151 SETUP("tc", "tc_egressgw_snat2") 152 int egressgw_snat2_setup(struct __ctx_buff *ctx) 153 { 154 /* Jump into the entrypoint */ 155 ctx_egw_done_set(ctx); 156 tail_call_static(ctx, entry_call_map, TO_NETDEV); 157 /* Fail if we didn't jump */ 158 return TEST_ERROR; 159 } 160 161 CHECK("tc", "tc_egressgw_snat2") 162 int egressgw_snat2_check(struct __ctx_buff *ctx) 163 { 164 int ret = egressgw_snat_check(ctx, (struct egressgw_test_ctx) { 165 .test = TEST_SNAT2, 166 .packets = 1, 167 .status_code = CTX_ACT_OK 168 }); 169 170 del_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0Xffffff, 24); 171 172 return ret; 173 } 174 175 /* Test that a packet matching an excluded CIDR egress gateway policy on the 176 * to-netdev program does not get SNATed with the egress IP of the policy. 177 */ 178 PKTGEN("tc", "tc_egressgw_skip_excluded_cidr_snat") 179 int egressgw_skip_excluded_cidr_snat_pktgen(struct __ctx_buff *ctx) 180 { 181 return egressgw_pktgen(ctx, (struct egressgw_test_ctx) { 182 .test = TEST_SNAT_EXCL_CIDR, 183 }); 184 } 185 186 SETUP("tc", "tc_egressgw_skip_excluded_cidr_snat") 187 int egressgw_skip_excluded_cidr_snat_setup(struct __ctx_buff *ctx) 188 { 189 190 add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24, GATEWAY_NODE_IP, 0); 191 add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP, 32, EGRESS_GATEWAY_EXCLUDED_CIDR, 0); 192 193 /* Jump into the entrypoint */ 194 ctx_egw_done_set(ctx); 195 tail_call_static(ctx, entry_call_map, TO_NETDEV); 196 /* Fail if we didn't jump */ 197 return TEST_ERROR; 198 } 199 200 CHECK("tc", "tc_egressgw_skip_excluded_cidr_snat") 201 int egressgw_skip_excluded_cidr_snat_check(const struct __ctx_buff *ctx) 202 { 203 void *data, *data_end; 204 __u32 *status_code; 205 struct tcphdr *l4; 206 struct ethhdr *l2; 207 struct iphdr *l3; 208 209 test_init(); 210 211 del_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP, 32); 212 213 data = (void *)(long)ctx_data(ctx); 214 data_end = (void *)(long)ctx->data_end; 215 216 if (data + sizeof(__u32) > data_end) 217 test_fatal("status code out of bounds"); 218 219 status_code = data; 220 assert(*status_code == CTX_ACT_OK); 221 222 l2 = data + sizeof(__u32); 223 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 224 test_fatal("l2 out of bounds"); 225 226 l3 = (void *)l2 + sizeof(struct ethhdr); 227 if ((void *)l3 + sizeof(struct iphdr) > data_end) 228 test_fatal("l3 out of bounds"); 229 230 if (l3->check != bpf_htons(0x4112)) 231 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 232 233 l4 = (void *)l3 + sizeof(struct iphdr); 234 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 235 test_fatal("l4 out of bounds"); 236 237 if (memcmp(l2->h_source, (__u8 *)client_mac, ETH_ALEN) != 0) 238 test_fatal("src MAC is not the client MAC") 239 240 if (memcmp(l2->h_dest, (__u8 *)ext_svc_mac, ETH_ALEN) != 0) 241 test_fatal("dst MAC is not the external svc MAC") 242 243 if (l3->saddr != CLIENT_IP) 244 test_fatal("src IP has changed"); 245 246 if (l3->daddr != EXTERNAL_SVC_IP) 247 test_fatal("dst IP has changed"); 248 249 if (l4->source != client_port(TEST_SNAT_EXCL_CIDR)) 250 test_fatal("src TCP port has changed"); 251 252 if (l4->dest != EXTERNAL_SVC_PORT) 253 test_fatal("dst port has changed"); 254 255 test_finish(); 256 } 257 258 PKTGEN("tc", "tc_egressgw_fib_redirect") 259 int egressgw_fib_redirect_pktgen(struct __ctx_buff *ctx) 260 { 261 return egressgw_pktgen(ctx, (struct egressgw_test_ctx) { 262 .test = TEST_FIB, 263 .redirect = true, 264 }); 265 } 266 267 SETUP("tc", "tc_egressgw_fib_redirect") 268 int egressgw_fib_redirect_setup(struct __ctx_buff *ctx) 269 { 270 add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24, 271 GATEWAY_NODE_IP, EGRESS_IP2); 272 273 /* Jump into the entrypoint */ 274 ctx_egw_done_set(ctx); 275 tail_call_static(ctx, entry_call_map, TO_NETDEV); 276 /* Fail if we didn't jump */ 277 return TEST_ERROR; 278 } 279 280 CHECK("tc", "tc_egressgw_fib_redirect") 281 int egressgw_fib_redirect_check(const struct __ctx_buff *ctx __maybe_unused) 282 { 283 int ret = egressgw_snat_check(ctx, (struct egressgw_test_ctx) { 284 .test = TEST_FIB, 285 .redirect = true, 286 .packets = 1, 287 .status_code = CTX_ACT_REDIRECT, 288 }); 289 290 del_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24); 291 292 return ret; 293 } 294