github.com/cilium/cilium@v1.16.2/bpf/tests/tc_nodeport_l3_dev.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 <bpf/helpers_skb.h> 8 #include "pktgen.h" 9 10 #define ETH_HLEN 0 11 #define SECCTX_FROM_IPCACHE 1 12 #define ENABLE_HOST_ROUTING 13 #define ENABLE_IPV4 14 #define ENABLE_IPV6 15 16 #define TEST_IP_LOCAL v4_pod_one 17 #define TEST_IP_REMOTE v4_pod_two 18 #define TEST_IPV6_LOCAL v6_pod_one 19 #define TEST_IPV6_REMOTE v6_pod_two 20 #define TEST_LXC_ID_LOCAL 233 21 22 /* We wanted to tail call handle_policy from bpf_lxc, but at present it's 23 * impossible to #include both bpf_host.c and bpf_lxc.c at the same time. 24 * Therefore, we created a stud, mock_hanle_policy, to simply check if the 25 * our skb reaches there. 26 */ 27 __section("mock-handle-policy") 28 int mock_handle_policy(struct __ctx_buff *ctx __maybe_unused) 29 { 30 return TC_ACT_REDIRECT; 31 } 32 33 struct { 34 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 35 __uint(key_size, sizeof(__u32)); 36 __uint(max_entries, 256); 37 __array(values, int()); 38 } mock_policy_call_map __section(".maps") = { 39 .values = { 40 [TEST_LXC_ID_LOCAL] = &mock_handle_policy, 41 }, 42 }; 43 44 #define tail_call_dynamic mock_tail_call_dynamic 45 static __always_inline __maybe_unused void 46 mock_tail_call_dynamic(struct __ctx_buff *ctx __maybe_unused, 47 const void *map __maybe_unused, __u32 slot __maybe_unused) 48 { 49 tail_call(ctx, &mock_policy_call_map, slot); 50 } 51 52 static volatile const __u8 *ep_mac = mac_one; 53 static volatile const __u8 *node_mac = mac_two; 54 55 #include "bpf_host.c" 56 57 #include "lib/endpoint.h" 58 59 struct { 60 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 61 __uint(key_size, sizeof(__u32)); 62 __uint(max_entries, 2); 63 __array(values, int()); 64 } entry_call_map __section(".maps") = { 65 .values = { 66 [0] = &cil_from_netdev, 67 }, 68 }; 69 70 PKTGEN("tc", "ipv4_l3_to_l2_fast_redirect") 71 int ipv4_l3_to_l2_fast_redirect_pktgen(struct __ctx_buff *ctx) 72 { 73 struct pktgen builder; 74 struct tcphdr *l4; 75 void *data; 76 77 /* Init packet builder */ 78 pktgen__init(&builder, ctx); 79 80 /* We are building an L3 skb which doesn't have L2 header, so in theory 81 * we need to skip L2 header and set ctx->protocol = bpf_ntohs(ETH_P_IP), 82 * but bpf verifier doesn't allow us to do so, and kernel also doesn't 83 * handle an L3 skb properly (see https://elixir.bootlin.com/linux/v6.2.1/source/net/bpf/test_run.c#L1156). 84 * Therefore we workaround the issue by pushing L2 header in the PKTGEN 85 * and stripping it in the SETUP. 86 */ 87 88 l4 = pktgen__push_ipv4_tcp_packet(&builder, 89 (__u8 *)node_mac, (__u8 *)ep_mac, 90 TEST_IP_REMOTE, TEST_IP_LOCAL, 91 tcp_src_one, tcp_svc_one); 92 if (!l4) 93 return TEST_ERROR; 94 95 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 96 97 if (!data) 98 return TEST_ERROR; 99 100 pktgen__finish(&builder); 101 102 return 0; 103 } 104 105 SETUP("tc", "ipv4_l3_to_l2_fast_redirect") 106 int ipv4_l3_to_l2_fast_redirect_setup(struct __ctx_buff *ctx) 107 { 108 void *data = (void *)(long)ctx->data; 109 void *data_end = (void *)(long)ctx->data_end; 110 __u64 flags = BPF_F_ADJ_ROOM_FIXED_GSO; 111 112 endpoint_v4_add_entry(TEST_IP_LOCAL, 0, TEST_LXC_ID_LOCAL, 0, 0, 113 (__u8 *)ep_mac, (__u8 *)node_mac); 114 115 /* As commented in PKTGEN, now we strip the L2 header. Bpf helper 116 * skb_adjust_room will use L2 header to overwrite L3 header, so we play 117 * a trick to memcpy(ethhdr, iphdr, ETH_HLEN) ahead of skb_adjust_room 118 * so as to guarantee L3 header keeps intact. 119 */ 120 if ((void *)data + __ETH_HLEN + __ETH_HLEN <= data_end) 121 memcpy(data, data + __ETH_HLEN, __ETH_HLEN); 122 123 skb_adjust_room(ctx, -__ETH_HLEN, BPF_ADJ_ROOM_MAC, flags); 124 125 tail_call_static(ctx, entry_call_map, 0); 126 return TEST_ERROR; 127 } 128 129 CHECK("tc", "ipv4_l3_to_l2_fast_redirect") 130 int ipv4_l3_to_l2_fast_redirect_check(__maybe_unused const struct __ctx_buff *ctx) 131 { 132 void *data; 133 void *data_end; 134 __u32 *status_code; 135 struct ethhdr *l2; 136 struct iphdr *l3; 137 struct tcphdr *l4; 138 __u8 *payload; 139 140 test_init(); 141 142 data = (void *)(long)ctx->data; 143 data_end = (void *)(long)ctx->data_end; 144 145 if (data + sizeof(__u32) > data_end) 146 test_fatal("status code out of bounds"); 147 148 status_code = data; 149 150 assert(*status_code == TC_ACT_REDIRECT); 151 152 l2 = data + sizeof(__u32); 153 154 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 155 test_fatal("l2 out of bounds"); 156 157 if (l2->h_proto != bpf_htons(ETH_P_IP)) 158 test_fatal("l2 proto hasn't been set to ETH_P_IP"); 159 160 if (memcmp(l2->h_source, (__u8 *)node_mac, ETH_ALEN) != 0) 161 test_fatal("src mac hasn't been set to router's mac"); 162 163 if (memcmp(l2->h_dest, (__u8 *)ep_mac, ETH_ALEN) != 0) 164 test_fatal("dest mac hasn't been set to ep's mac"); 165 166 l3 = data + sizeof(__u32) + sizeof(struct ethhdr); 167 168 if ((void *)l3 + sizeof(struct iphdr) > data_end) 169 test_fatal("l3 out of bounds"); 170 171 if (l3->saddr != TEST_IP_REMOTE) 172 test_fatal("src IP was changed"); 173 174 if (l3->daddr != TEST_IP_LOCAL) 175 test_fatal("dest IP was changed"); 176 177 if (l3->check != bpf_htons(0xfa68)) 178 test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check)); 179 180 l4 = (void *)l3 + sizeof(struct iphdr); 181 182 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 183 test_fatal("l4 out of bounds"); 184 185 if (l4->source != tcp_src_one) 186 test_fatal("src TCP port was changed"); 187 188 if (l4->dest != tcp_svc_one) 189 test_fatal("dst TCP port was changed"); 190 191 payload = (void *)l4 + sizeof(struct tcphdr); 192 if ((void *)payload + sizeof(default_data) > data_end) 193 test_fatal("paylaod out of bounds\n"); 194 195 if (memcmp(payload, default_data, sizeof(default_data)) != 0) 196 test_fatal("tcp payload was changed"); 197 198 test_finish(); 199 } 200 201 PKTGEN("tc", "ipv6_l3_to_l2_fast_redirect") 202 int ipv6_l3_to_l2_fast_redirect_pktgen(struct __ctx_buff *ctx) 203 { 204 struct pktgen builder; 205 struct tcphdr *l4; 206 void *data; 207 208 /* Init packet builder */ 209 pktgen__init(&builder, ctx); 210 211 /* We are building an L3 skb which doesn't have L2 header, so in theory 212 * we need to skip L2 header and set ctx->protocol = bpf_ntohs(ETH_P_IP), 213 * but bpf verifier doesn't allow us to do so, and kernel also doesn't 214 * handle an L3 skb properly (see https://elixir.bootlin.com/linux/v6.2.1/source/net/bpf/test_run.c#L1156). 215 * Therefore we workaround the issue by pushing L2 header in the PKTGEN 216 * and stripping it in the SETUP. 217 */ 218 l4 = pktgen__push_ipv6_tcp_packet(&builder, 219 (__u8 *)node_mac, (__u8 *)ep_mac, 220 (__u8 *)TEST_IPV6_REMOTE, 221 (__u8 *)TEST_IPV6_LOCAL, 222 tcp_src_one, tcp_svc_one); 223 if (!l4) 224 return TEST_ERROR; 225 226 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 227 228 if (!data) 229 return TEST_ERROR; 230 231 pktgen__finish(&builder); 232 233 return 0; 234 } 235 236 SETUP("tc", "ipv6_l3_to_l2_fast_redirect") 237 int ipv6_l3_to_l2_fast_redirect_setup(struct __ctx_buff *ctx) 238 { 239 void *data = (void *)(long)ctx->data; 240 void *data_end = (void *)(long)ctx->data_end; 241 __u64 flags = BPF_F_ADJ_ROOM_FIXED_GSO; 242 243 endpoint_v6_add_entry((union v6addr *)TEST_IPV6_LOCAL, 0, TEST_LXC_ID_LOCAL, 0, 0, 244 (__u8 *)ep_mac, (__u8 *)node_mac); 245 246 /* As commented in PKTGEN, now we strip the L2 header. Bpf helper 247 * skb_adjust_room will use L2 header to overwrite L3 header, so we play 248 * a trick to memcpy(ethhdr, ipv6hdr, ETH_HLEN) ahead of skb_adjust_room 249 * so as to guarantee L3 header keeps intact. 250 */ 251 if ((void *)data + __ETH_HLEN + __ETH_HLEN <= data_end) 252 memcpy(data, data + __ETH_HLEN, __ETH_HLEN); 253 254 skb_adjust_room(ctx, -__ETH_HLEN, BPF_ADJ_ROOM_MAC, flags); 255 256 tail_call_static(ctx, entry_call_map, 0); 257 return TEST_ERROR; 258 } 259 260 CHECK("tc", "ipv6_l3_to_l2_fast_redirect") 261 int ipv6_l3_to_l2_fast_redirect_check(__maybe_unused const struct __ctx_buff *ctx) 262 { 263 void *data; 264 void *data_end; 265 __u32 *status_code; 266 struct ethhdr *l2; 267 struct ipv6hdr *l3; 268 struct tcphdr *l4; 269 __u8 *payload; 270 271 test_init(); 272 273 data = (void *)(long)ctx->data; 274 data_end = (void *)(long)ctx->data_end; 275 276 if (data + sizeof(__u32) > data_end) 277 test_fatal("status code out of bounds"); 278 279 status_code = data; 280 281 assert(*status_code == TC_ACT_REDIRECT); 282 283 l2 = data + sizeof(__u32); 284 285 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 286 test_fatal("l2 out of bounds"); 287 288 if (l2->h_proto != bpf_htons(ETH_P_IPV6)) 289 test_fatal("l2 proto hasn't been set to ETH_P_IPV6"); 290 291 if (memcmp(l2->h_source, (__u8 *)node_mac, ETH_ALEN) != 0) 292 test_fatal("src mac hasn't been set to router's mac"); 293 294 if (memcmp(l2->h_dest, (__u8 *)ep_mac, ETH_ALEN) != 0) 295 test_fatal("dest mac hasn't been set to ep's mac"); 296 297 l3 = data + sizeof(__u32) + sizeof(struct ethhdr); 298 299 if ((void *)l3 + sizeof(struct ipv6hdr) > data_end) 300 test_fatal("l3 out of bounds"); 301 302 if (memcmp((__u8 *)&l3->saddr, (__u8 *)TEST_IPV6_REMOTE, 16) != 0) 303 test_fatal("src IP was changed"); 304 305 if (memcmp((__u8 *)&l3->daddr, (__u8 *)TEST_IPV6_LOCAL, 16) != 0) 306 test_fatal("dest IP was changed"); 307 308 l4 = (void *)l3 + sizeof(struct ipv6hdr); 309 310 if ((void *)l4 + sizeof(struct tcphdr) > data_end) 311 test_fatal("l4 out of bounds"); 312 313 if (l4->source != tcp_src_one) 314 test_fatal("src TCP port was changed"); 315 316 if (l4->dest != tcp_svc_one) 317 test_fatal("dst TCP port was changed"); 318 319 payload = (void *)l4 + sizeof(struct tcphdr); 320 if ((void *)payload + sizeof(default_data) > data_end) 321 test_fatal("paylaod out of bounds\n"); 322 323 if (memcmp(payload, default_data, sizeof(default_data)) != 0) 324 test_fatal("tcp payload was changed"); 325 326 test_finish(); 327 }