github.com/cilium/cilium@v1.16.2/bpf/tests/tc_nodeport_l3_dev_to_tunnel.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 #define ETH_HLEN 0 10 #define SECCTX_FROM_IPCACHE 1 11 #define ENABLE_IPV4 12 #define ENABLE_IPV6 13 #define TUNNEL_MODE 1 14 #define ENCAP_IFINDEX 42 15 #define ENABLE_NODEPORT 1 16 17 #define TEST_IP_LOCAL v4_pod_one 18 #define TEST_IP_REMOTE v4_pod_two 19 #define TEST_IPV6_LOCAL v6_pod_one 20 21 #define CLIENT_IP v4_ext_one 22 #define CLIENT_IPV6 { .addr = { 0x1, 0x0, 0x0, 0x0, 0x0, 0x0 } } 23 #define CLIENT_PORT __bpf_htons(111) 24 25 #define FRONTEND_IP v4_svc_one 26 #define FRONTEND_IPV6 { .addr = { 0x5, 0x0, 0x0, 0x0, 0x0, 0x0 } } 27 #define FRONTEND_PORT tcp_svc_one 28 29 #define BACKEND_IP v4_pod_one 30 #define BACKEND_IPV6 { .addr = { 0x3, 0x0, 0x0, 0x0, 0x0, 0x0 } } 31 #define BACKEND_PORT __bpf_htons(8080) 32 33 #include "bpf_host.c" 34 #include "lib/ipcache.h" 35 #include "lib/lb.h" 36 37 static volatile const __u8 *node_mac = mac_two; 38 39 struct { 40 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 41 __uint(key_size, sizeof(__u32)); 42 __uint(max_entries, 2); 43 __array(values, int()); 44 } entry_call_map __section(".maps") = { 45 .values = { 46 [0] = &cil_from_netdev, 47 }, 48 }; 49 50 PKTGEN("tc", "ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel") 51 int ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel(struct __ctx_buff *ctx) 52 { 53 struct pktgen builder; 54 struct tcphdr *l4; 55 void *data; 56 57 /* Init packet builder */ 58 pktgen__init(&builder, ctx); 59 60 /* We are building an L3 skb which doesn't have L2 header, so in theory 61 * we need to skip L2 header and set ctx->protocol = bpf_ntohs(ETH_P_IP), 62 * but bpf verifier doesn't allow us to do so, and kernel also doesn't 63 * handle an L3 skb properly (see https://elixir.bootlin.com/linux/v6.2.1/source/net/bpf/test_run.c#L1156). 64 * Therefore we workaround the issue by pushing L2 header in the PKTGEN 65 * and stripping it in the SETUP. 66 */ 67 68 l4 = pktgen__push_ipv4_tcp_packet(&builder, 69 (__u8 *)node_mac, (__u8 *)node_mac, 70 CLIENT_IP, FRONTEND_IP, 71 CLIENT_PORT, FRONTEND_PORT); 72 if (!l4) 73 return TEST_ERROR; 74 75 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 76 77 if (!data) 78 return TEST_ERROR; 79 80 pktgen__finish(&builder); 81 82 return 0; 83 } 84 85 SETUP("tc", "ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel") 86 int ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel_setup(struct __ctx_buff *ctx) 87 { 88 void *data = (void *)(long)ctx->data; 89 void *data_end = (void *)(long)ctx->data_end; 90 __u64 flags = BPF_F_ADJ_ROOM_FIXED_GSO; 91 92 lb_v4_add_service(FRONTEND_IP, FRONTEND_PORT, 1, 1); 93 lb_v4_add_backend(FRONTEND_IP, FRONTEND_PORT, 1, 124, 94 BACKEND_IP, BACKEND_PORT, IPPROTO_TCP, 0); 95 96 ipcache_v4_add_entry(BACKEND_IP, 0, 112233, TEST_IP_REMOTE, 0); 97 98 /* As commented in PKTGEN, now we strip the L2 header. Bpf helper 99 * skb_adjust_room will use L2 header to overwrite L3 header, so we play 100 * a trick to memcpy(ethhdr, iphdr, ETH_HLEN) ahead of skb_adjust_room 101 * so as to guarantee L3 header keeps intact. 102 */ 103 if ((void *)data + __ETH_HLEN + __ETH_HLEN <= data_end) 104 memcpy(data, data + __ETH_HLEN, __ETH_HLEN); 105 106 skb_adjust_room(ctx, -__ETH_HLEN, BPF_ADJ_ROOM_MAC, flags); 107 108 tail_call_static(ctx, entry_call_map, 0); 109 return TEST_ERROR; 110 } 111 112 CHECK("tc", "ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel") 113 int ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel_check(__maybe_unused 114 const struct __ctx_buff *ctx) 115 { 116 void *data; 117 void *data_end; 118 __u32 *status_code; 119 struct ethhdr *l2; 120 121 test_init(); 122 123 data = (void *)(long)ctx->data; 124 data_end = (void *)(long)ctx->data_end; 125 126 if (data + sizeof(__u32) > data_end) 127 test_fatal("status code out of bounds"); 128 129 status_code = data; 130 131 /* Check that LB request got redirected (to a tunnel iface) */ 132 assert(*status_code == TC_ACT_REDIRECT); 133 134 /* Check that L2 hdr was added */ 135 l2 = data + sizeof(__u32); 136 137 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 138 test_fatal("l2 out of bounds"); 139 140 if (l2->h_proto != bpf_htons(ETH_P_IP)) 141 test_fatal("l2 proto hasn't been set to ETH_P_IP"); 142 143 test_finish(); 144 } 145 146 PKTGEN("tc", "ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel") 147 int ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel(struct __ctx_buff *ctx) 148 { 149 union v6addr frontend_ip = FRONTEND_IPV6; 150 union v6addr client_ip = CLIENT_IPV6; 151 struct pktgen builder; 152 struct tcphdr *l4; 153 void *data; 154 155 /* Init packet builder */ 156 pktgen__init(&builder, ctx); 157 158 /* We are building an L3 skb which doesn't have L2 header, so in theory 159 * we need to skip L2 header and set ctx->protocol = bpf_ntohs(ETH_P_IP), 160 * but bpf verifier doesn't allow us to do so, and kernel also doesn't 161 * handle an L3 skb properly (see https://elixir.bootlin.com/linux/v6.2.1/source/net/bpf/test_run.c#L1156). 162 * Therefore we workaround the issue by pushing L2 header in the PKTGEN 163 * and stripping it in the SETUP. 164 */ 165 166 l4 = pktgen__push_ipv6_tcp_packet(&builder, 167 (__u8 *)node_mac, (__u8 *)node_mac, 168 (__u8 *)&client_ip, (__u8 *)&frontend_ip, 169 CLIENT_PORT, FRONTEND_PORT); 170 if (!l4) 171 return TEST_ERROR; 172 173 data = pktgen__push_data(&builder, default_data, sizeof(default_data)); 174 175 if (!data) 176 return TEST_ERROR; 177 178 pktgen__finish(&builder); 179 180 return 0; 181 } 182 183 SETUP("tc", "ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel") 184 int ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel_setup(struct __ctx_buff *ctx) 185 { 186 union v6addr frontend_ip = FRONTEND_IPV6; 187 union v6addr backend_ip = BACKEND_IPV6; 188 void *data = (void *)(long)ctx->data; 189 void *data_end = (void *)(long)ctx->data_end; 190 __u64 flags = BPF_F_ADJ_ROOM_FIXED_GSO; 191 192 lb_v6_add_service(&frontend_ip, FRONTEND_PORT, 1, 1); 193 lb_v6_add_backend(&frontend_ip, FRONTEND_PORT, 1, 124, 194 &backend_ip, BACKEND_PORT, IPPROTO_TCP, 0); 195 196 ipcache_v6_add_entry(&backend_ip, 0, 112233, TEST_IP_REMOTE, 0); 197 198 /* As commented in PKTGEN, now we strip the L2 header. Bpf helper 199 * skb_adjust_room will use L2 header to overwrite L3 header, so we play 200 * a trick to memcpy(ethhdr, iphdr, ETH_HLEN) ahead of skb_adjust_room 201 * so as to guarantee L3 header keeps intact. 202 */ 203 if ((void *)data + __ETH_HLEN + __ETH_HLEN <= data_end) 204 memcpy(data, data + __ETH_HLEN, __ETH_HLEN); 205 206 skb_adjust_room(ctx, -__ETH_HLEN, BPF_ADJ_ROOM_MAC, flags); 207 208 tail_call_static(ctx, entry_call_map, 0); 209 return TEST_ERROR; 210 } 211 212 CHECK("tc", "ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel") 213 int ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel_check(__maybe_unused 214 const struct __ctx_buff *ctx) 215 { 216 void *data; 217 void *data_end; 218 __u32 *status_code; 219 struct ethhdr *l2; 220 221 test_init(); 222 223 data = (void *)(long)ctx->data; 224 data_end = (void *)(long)ctx->data_end; 225 226 if (data + sizeof(__u32) > data_end) 227 test_fatal("status code out of bounds"); 228 229 status_code = data; 230 231 /* Check that LB request got redirected (to a tunnel iface) */ 232 assert(*status_code == TC_ACT_REDIRECT); 233 234 /* Check that L2 hdr was added */ 235 l2 = data + sizeof(__u32); 236 237 if ((void *)l2 + sizeof(struct ethhdr) > data_end) 238 test_fatal("l2 out of bounds"); 239 240 if (l2->h_proto != bpf_htons(ETH_P_IPV6)) 241 test_fatal("l2 proto hasn't been set to ETH_P_IPV6"); 242 243 test_finish(); 244 }