2 * Copyright (c) 2018 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 * @brief NAT plugin client-IP based session affinity for load-balancing
20 #include <nat/lib/log.h>
22 #include <nat/nat44-ed/nat44_ed.h>
23 #include <nat/nat44-ed/nat44_ed_affinity.h>
25 nat_affinity_main_t nat_affinity_main;
27 #define AFFINITY_HASH_BUCKETS 65536
28 #define AFFINITY_HASH_MEMORY (2 << 25)
31 format_affinity_kvp (u8 * s, va_list * args)
33 clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *);
36 k.as_u64[0] = v->key[0];
37 k.as_u64[1] = v->key[1];
39 s = format (s, "client %U backend %U:%d proto %U index %llu",
40 format_ip4_address, &k.client_addr, format_ip4_address,
41 &k.service_addr, clib_net_to_host_u16 (k.service_port),
42 format_ip_protocol, k.proto);
48 nat_affinity_enable ()
50 nat_affinity_main_t *nam = &nat_affinity_main;
51 vlib_thread_main_t *tm = vlib_get_thread_main ();
53 if (tm->n_vlib_mains > 1)
54 clib_spinlock_init (&nam->affinity_lock);
55 clib_bihash_init_16_8 (&nam->affinity_hash, "nat-affinity",
56 AFFINITY_HASH_BUCKETS, AFFINITY_HASH_MEMORY);
57 clib_bihash_set_kvp_format_fn_16_8 (&nam->affinity_hash,
62 nat_affinity_disable ()
64 nat_affinity_main_t *nam = &nat_affinity_main;
65 vlib_thread_main_t *tm = vlib_get_thread_main ();
67 if (tm->n_vlib_mains > 1)
68 clib_spinlock_free (&nam->affinity_lock);
69 clib_bihash_free_16_8 (&nam->affinity_hash);
73 nat_affinity_init (vlib_main_t * vm)
75 nat_affinity_main_t *nam = &nat_affinity_main;
80 static_always_inline void
81 make_affinity_kv (clib_bihash_kv_16_8_t * kv, ip4_address_t client_addr,
82 ip4_address_t service_addr, u8 proto, u16 service_port)
84 nat_affinity_key_t *key = (nat_affinity_key_t *) kv->key;
86 key->client_addr = client_addr;
87 key->service_addr = service_addr;
89 key->service_port = service_port;
95 nat_affinity_get_per_service_list_head_index (void)
97 nat_affinity_main_t *nam = &nat_affinity_main;
98 dlist_elt_t *head_elt;
100 clib_spinlock_lock_if_init (&nam->affinity_lock);
102 pool_get (nam->list_pool, head_elt);
103 clib_dlist_init (nam->list_pool, head_elt - nam->list_pool);
105 clib_spinlock_unlock_if_init (&nam->affinity_lock);
107 return head_elt - nam->list_pool;
111 nat_affinity_flush_service (u32 affinity_per_service_list_head_index)
113 snat_main_t *sm = &snat_main;
114 nat_affinity_main_t *nam = &nat_affinity_main;
118 clib_bihash_kv_16_8_t kv;
120 clib_spinlock_lock_if_init (&nam->affinity_lock);
123 clib_dlist_remove_head (nam->list_pool,
124 affinity_per_service_list_head_index)) !=
127 elt = pool_elt_at_index (nam->list_pool, elt_index);
128 a = pool_elt_at_index (nam->affinity_pool, elt->value);
129 kv.key[0] = a->key.as_u64[0];
130 kv.key[1] = a->key.as_u64[1];
131 pool_put_index (nam->affinity_pool, elt->value);
132 if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0))
133 nat_elog_warn (sm, "affinity key del failed");
134 pool_put_index (nam->list_pool, elt_index);
136 pool_put_index (nam->list_pool, affinity_per_service_list_head_index);
138 clib_spinlock_unlock_if_init (&nam->affinity_lock);
142 nat_affinity_find_and_lock (vlib_main_t *vm, ip4_address_t client_addr,
143 ip4_address_t service_addr, u8 proto,
144 u16 service_port, u8 *backend_index)
146 snat_main_t *sm = &snat_main;
147 nat_affinity_main_t *nam = &nat_affinity_main;
148 clib_bihash_kv_16_8_t kv, value;
152 make_affinity_kv (&kv, client_addr, service_addr, proto, service_port);
153 clib_spinlock_lock_if_init (&nam->affinity_lock);
154 if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value))
160 a = pool_elt_at_index (nam->affinity_pool, value.value);
161 /* if already expired delete */
164 if (a->expire < vlib_time_now (vm))
166 clib_dlist_remove (nam->list_pool, a->per_service_index);
167 pool_put_index (nam->list_pool, a->per_service_index);
168 pool_put_index (nam->affinity_pool, value.value);
169 if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0))
170 nat_elog_warn (sm, "affinity key del failed");
176 *backend_index = a->backend_index;
179 clib_spinlock_unlock_if_init (&nam->affinity_lock);
184 affinity_is_expired_cb (clib_bihash_kv_16_8_t * kv, void *arg)
186 snat_main_t *sm = &snat_main;
187 nat_affinity_main_t *nam = &nat_affinity_main;
190 a = pool_elt_at_index (nam->affinity_pool, kv->value);
193 if (a->expire < vlib_time_now (nam->vlib_main))
195 clib_dlist_remove (nam->list_pool, a->per_service_index);
196 pool_put_index (nam->list_pool, a->per_service_index);
197 pool_put_index (nam->affinity_pool, kv->value);
198 if (clib_bihash_add_del_16_8 (&nam->affinity_hash, kv, 0))
199 nat_elog_warn (sm, "affinity key del failed");
208 nat_affinity_create_and_lock (ip4_address_t client_addr,
209 ip4_address_t service_addr, u8 proto,
210 u16 service_port, u8 backend_index,
212 u32 affinity_per_service_list_head_index)
214 snat_main_t *sm = &snat_main;
215 nat_affinity_main_t *nam = &nat_affinity_main;
216 clib_bihash_kv_16_8_t kv, value;
218 dlist_elt_t *list_elt;
221 make_affinity_kv (&kv, client_addr, service_addr, proto, service_port);
222 clib_spinlock_lock_if_init (&nam->affinity_lock);
223 if (!clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value))
226 nat_elog_notice (sm, "affinity key already exist");
230 pool_get (nam->affinity_pool, a);
231 kv.value = a - nam->affinity_pool;
233 clib_bihash_add_or_overwrite_stale_16_8 (&nam->affinity_hash, &kv,
234 affinity_is_expired_cb, NULL);
237 nat_elog_notice (sm, "affinity key add failed");
238 pool_put (nam->affinity_pool, a);
242 pool_get (nam->list_pool, list_elt);
243 clib_dlist_init (nam->list_pool, list_elt - nam->list_pool);
244 list_elt->value = a - nam->affinity_pool;
245 a->per_service_index = list_elt - nam->list_pool;
246 a->backend_index = backend_index;
248 a->sticky_time = sticky_time;
249 a->key.as_u64[0] = kv.key[0];
250 a->key.as_u64[1] = kv.key[1];
251 clib_dlist_addtail (nam->list_pool, affinity_per_service_list_head_index,
252 a->per_service_index);
255 clib_spinlock_unlock_if_init (&nam->affinity_lock);
260 nat_affinity_unlock (ip4_address_t client_addr, ip4_address_t service_addr,
261 u8 proto, u16 service_port)
263 nat_affinity_main_t *nam = &nat_affinity_main;
264 clib_bihash_kv_16_8_t kv, value;
267 make_affinity_kv (&kv, client_addr, service_addr, proto, service_port);
268 clib_spinlock_lock_if_init (&nam->affinity_lock);
269 if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value))
272 a = pool_elt_at_index (nam->affinity_pool, value.value);
275 a->expire = (u64) a->sticky_time + vlib_time_now (nam->vlib_main);
278 clib_spinlock_unlock_if_init (&nam->affinity_lock);
282 * fd.io coding-style-patch-verification: ON
285 * eval: (c-set-style "gnu")