github.com/cilium/cilium@v1.16.2/bpf/tests/session_affinity_test.c (about) 1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright Authors of Cilium */ 3 4 #include "bpf/ctx/xdp.h" 5 #include "common.h" 6 #include "pktgen.h" 7 8 #define ENABLE_IPV4 9 #define ENABLE_NODEPORT 10 #define ENABLE_NODEPORT_ACCELERATION 11 #define ENABLE_SESSION_AFFINITY 12 13 /* Make sure we always pick backend slot 1 if we end up in backend selection. */ 14 #define LB_SELECTION LB_SELECTION_FIRST 15 16 #define fib_lookup mock_fib_lookup 17 18 static const char fib_smac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02}; 19 static const char fib_dmac[6] = {0x13, 0x37, 0x13, 0x37, 0x13, 0x37}; 20 21 long mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params, 22 __maybe_unused int plen, __maybe_unused __u32 flags) 23 { 24 __bpf_memcpy_builtin(params->smac, fib_smac, ETH_ALEN); 25 __bpf_memcpy_builtin(params->dmac, fib_dmac, ETH_ALEN); 26 return 0; 27 } 28 29 #include "bpf_xdp.c" 30 #include "lib/nodeport.h" 31 32 struct { 33 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 34 __uint(key_size, sizeof(__u32)); 35 __uint(max_entries, 2); 36 __array(values, int()); 37 } entry_call_map __section(".maps") = { 38 .values = { 39 [0] = &cil_xdp_entry, 40 }, 41 }; 42 43 #define CLIENT_IP IPV4(10, 0, 0, 1) 44 #define FRONTEND_IP IPV4(10, 0, 1, 1) 45 #define BACKEND_IP1 IPV4(10, 0, 2, 1) 46 #define BACKEND_IP2 IPV4(10, 0, 3, 1) 47 #define FRONTEND_PORT bpf_htons(80) 48 #define BACKEND_PORT bpf_htons(8080) 49 #define REV_NAT_INDEX 123 50 #define BACKEND_ID1 7 51 #define BACKEND_ID2 42 52 53 static __always_inline int craft_packet(struct __ctx_buff *ctx) 54 { 55 struct pktgen builder; 56 struct ethhdr *eh; 57 struct iphdr *iph; 58 struct tcphdr *tcph; 59 60 pktgen__init(&builder, ctx); 61 62 eh = pktgen__push_ethhdr(&builder); 63 if (!eh) 64 return TEST_ERROR; 65 *eh = (struct ethhdr){.h_source = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, 66 .h_dest = {0x12, 0x23, 0x34, 0x45, 0x56, 0x67}, 67 .h_proto = bpf_htons(ETH_P_IP)}; 68 69 iph = pktgen__push_default_iphdr(&builder); 70 71 if (!iph) 72 return TEST_ERROR; 73 74 iph->saddr = CLIENT_IP; 75 iph->daddr = FRONTEND_IP; 76 77 tcph = pktgen__push_default_tcphdr(&builder); 78 if (!tcph) 79 return TEST_ERROR; 80 81 tcph->source = bpf_htons(23445); 82 tcph->dest = FRONTEND_PORT; 83 84 if (!pktgen__push_data(&builder, default_data, sizeof(default_data))) 85 return TEST_ERROR; 86 87 pktgen__finish(&builder); 88 89 return 0; 90 } 91 92 #define SVC_KEY_VALUE(_beslot, _beid, _scope) \ 93 { \ 94 .key = {.address = FRONTEND_IP, \ 95 .dport = FRONTEND_PORT, \ 96 .scope = (_scope), \ 97 .backend_slot = (_beslot)}, \ 98 .value = { \ 99 .flags = SVC_FLAG_ROUTABLE | SVC_FLAG_AFFINITY, \ 100 .count = 2, \ 101 .rev_nat_index = REV_NAT_INDEX, \ 102 .backend_id = (_beid) \ 103 } \ 104 } 105 106 #define BE_KEY_VALUE(_beid, _beip) \ 107 { \ 108 .key = (_beid), \ 109 .value = {.address = (_beip), \ 110 .port = BACKEND_PORT, \ 111 .proto = IPPROTO_TCP},\ 112 } 113 114 SETUP("xdp", "session_affinity") 115 int test1_setup(struct __ctx_buff *ctx) 116 { 117 struct { 118 struct lb4_key key; 119 struct lb4_service value; 120 } services[] = { 121 SVC_KEY_VALUE(0, 100 /* affinity timeout */, LB_LOOKUP_SCOPE_INT), 122 SVC_KEY_VALUE(0, 100 /* affinity timeout */, LB_LOOKUP_SCOPE_EXT), 123 124 SVC_KEY_VALUE(1, BACKEND_ID1, LB_LOOKUP_SCOPE_EXT), 125 SVC_KEY_VALUE(2, BACKEND_ID2, LB_LOOKUP_SCOPE_EXT), 126 }; 127 struct { 128 __u32 key; 129 struct lb4_backend value; 130 } backends[] = { 131 BE_KEY_VALUE(BACKEND_ID1, BACKEND_IP1), 132 BE_KEY_VALUE(BACKEND_ID2, BACKEND_IP2), 133 }; 134 struct lb4_affinity_key aff_key = { 135 .client_id = {.client_ip = CLIENT_IP}, 136 .rev_nat_id = REV_NAT_INDEX, 137 .netns_cookie = 0x0, 138 }; 139 struct lb_affinity_val aff_value = { 140 .last_used = bpf_mono_now(), 141 .backend_id = BACKEND_ID2, 142 }; 143 struct lb_affinity_match match_key = { 144 .backend_id = BACKEND_ID2, 145 .rev_nat_id = REV_NAT_INDEX, 146 }; 147 int ret; 148 int zero = 0; 149 150 /* Insert the service and backend map values */ 151 for (unsigned long i = 0; i < ARRAY_SIZE(services); i++) { 152 map_update_elem(&LB4_SERVICES_MAP_V2, &services[i].key, 153 &services[i].value, BPF_ANY); 154 } 155 156 for (unsigned long i = 0; i < ARRAY_SIZE(backends); i++) { 157 map_update_elem(&LB4_BACKEND_MAP, &backends[i].key, 158 &backends[i].value, BPF_ANY); 159 } 160 161 /* Create the session affinity entry for the client */ 162 map_update_elem(&LB4_AFFINITY_MAP, &aff_key, &aff_value, BPF_ANY); 163 164 /* Add the affinity match entry to mark the backend as alive */ 165 map_update_elem(&LB_AFFINITY_MATCH_MAP, &match_key, &zero, BPF_ANY); 166 167 ret = craft_packet(ctx); 168 if (ret) 169 return ret; 170 171 /* Jump into the entrypoint */ 172 tail_call_static(ctx, entry_call_map, 0); 173 /* Fail if we didn't jump */ 174 return TEST_ERROR; 175 } 176 177 CHECK("xdp", "session_affinity") 178 int test1_check(__maybe_unused const struct __ctx_buff *ctx) 179 { 180 test_init(); 181 182 void *data = (void *)(long)ctx->data; 183 void *data_end = (void *)(long)ctx->data_end; 184 185 if (data + sizeof(__u32) > data_end) 186 test_fatal("status code out of bounds"); 187 188 __u32 *status_code = data; 189 190 if (*status_code != XDP_TX) test_fatal("status code != XDP_TX %u", *status_code); 191 192 data += sizeof(__u32); 193 194 if (data + sizeof(struct ethhdr) > data_end) 195 test_fatal("ctx doesn't fit ethhdr"); 196 197 struct ethhdr *l2 = data; 198 199 data += sizeof(struct ethhdr); 200 201 if (memcmp(l2->h_source, fib_smac, sizeof(fib_smac)) != 0) 202 test_fatal("l2->h_source != fib_smac"); 203 204 if (memcmp(l2->h_dest, fib_dmac, sizeof(fib_dmac)) != 0) 205 test_fatal("l2->h_dest != fib_dmac"); 206 207 if (data + sizeof(struct iphdr) > data_end) 208 test_fatal("ctx doesn't fit iphdr"); 209 210 struct iphdr *l3 = data; 211 212 data += sizeof(struct iphdr); 213 214 if (l3->daddr != BACKEND_IP2) test_fatal("dst ip != backend IP"); 215 216 if (data + sizeof(struct tcphdr) > data_end) 217 test_fatal("ctx doesn't fit tcphdr"); 218 219 struct tcphdr *l4 = data; 220 221 data += sizeof(struct tcphdr); 222 223 if (l4->dest != BACKEND_PORT) 224 test_fatal("dst port changed"); 225 226 test_finish(); 227 }