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,
41 format_ip4_address, &k.service_addr,
42 clib_net_to_host_u16 (k.service_port),
43 format_nat_protocol, k.proto);
49 nat_affinity_enable ()
51 nat_affinity_main_t *nam = &nat_affinity_main;
52 vlib_thread_main_t *tm = vlib_get_thread_main ();
54 if (tm->n_vlib_mains > 1)
55 clib_spinlock_init (&nam->affinity_lock);
56 clib_bihash_init_16_8 (&nam->affinity_hash, "nat-affinity",
57 AFFINITY_HASH_BUCKETS, AFFINITY_HASH_MEMORY);
58 clib_bihash_set_kvp_format_fn_16_8 (&nam->affinity_hash,
63 nat_affinity_disable ()
65 nat_affinity_main_t *nam = &nat_affinity_main;
66 vlib_thread_main_t *tm = vlib_get_thread_main ();
68 if (tm->n_vlib_mains > 1)
69 clib_spinlock_free (&nam->affinity_lock);
70 clib_bihash_free_16_8 (&nam->affinity_hash);
74 nat_affinity_init (vlib_main_t * vm)
76 nat_affinity_main_t *nam = &nat_affinity_main;
81 static_always_inline void
82 make_affinity_kv (clib_bihash_kv_16_8_t * kv, ip4_address_t client_addr,
83 ip4_address_t service_addr, u8 proto, u16 service_port)
85 nat_affinity_key_t *key = (nat_affinity_key_t *) kv->key;
87 key->client_addr = client_addr;
88 key->service_addr = service_addr;
90 key->service_port = service_port;
96 nat_affinity_get_per_service_list_head_index (void)
98 nat_affinity_main_t *nam = &nat_affinity_main;
99 dlist_elt_t *head_elt;
101 clib_spinlock_lock_if_init (&nam->affinity_lock);
103 pool_get (nam->list_pool, head_elt);
104 clib_dlist_init (nam->list_pool, head_elt - nam->list_pool);
106 clib_spinlock_unlock_if_init (&nam->affinity_lock);
108 return head_elt - nam->list_pool;
112 nat_affinity_flush_service (u32 affinity_per_service_list_head_index)
114 snat_main_t *sm = &snat_main;
115 nat_affinity_main_t *nam = &nat_affinity_main;
119 clib_bihash_kv_16_8_t kv;
121 clib_spinlock_lock_if_init (&nam->affinity_lock);
124 clib_dlist_remove_head (nam->list_pool,
125 affinity_per_service_list_head_index)) !=
128 elt = pool_elt_at_index (nam->list_pool, elt_index);
129 a = pool_elt_at_index (nam->affinity_pool, elt->value);
130 kv.key[0] = a->key.as_u64[0];
131 kv.key[1] = a->key.as_u64[1];
132 pool_put_index (nam->affinity_pool, elt->value);
133 if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0))
134 nat_elog_warn (sm, "affinity key del failed");
135 pool_put_index (nam->list_pool, elt_index);
137 pool_put_index (nam->list_pool, affinity_per_service_list_head_index);
139 clib_spinlock_unlock_if_init (&nam->affinity_lock);
143 nat_affinity_find_and_lock (ip4_address_t client_addr,
144 ip4_address_t service_addr, u8 proto,
145 u16 service_port, u8 * backend_index)
147 snat_main_t *sm = &snat_main;
148 nat_affinity_main_t *nam = &nat_affinity_main;
149 clib_bihash_kv_16_8_t kv, value;
153 make_affinity_kv (&kv, client_addr, service_addr, proto, service_port);
154 clib_spinlock_lock_if_init (&nam->affinity_lock);
155 if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value))
161 a = pool_elt_at_index (nam->affinity_pool, value.value);
162 /* if already expired delete */
165 if (a->expire < vlib_time_now (nam->vlib_main))
167 clib_dlist_remove (nam->list_pool, a->per_service_index);
168 pool_put_index (nam->list_pool, a->per_service_index);
169 pool_put_index (nam->affinity_pool, value.value);
170 if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0))
171 nat_elog_warn (sm, "affinity key del failed");
177 *backend_index = a->backend_index;
180 clib_spinlock_unlock_if_init (&nam->affinity_lock);
185 affinity_is_expired_cb (clib_bihash_kv_16_8_t * kv, void *arg)
187 snat_main_t *sm = &snat_main;
188 nat_affinity_main_t *nam = &nat_affinity_main;
191 a = pool_elt_at_index (nam->affinity_pool, kv->value);
194 if (a->expire < vlib_time_now (nam->vlib_main))
196 clib_dlist_remove (nam->list_pool, a->per_service_index);
197 pool_put_index (nam->list_pool, a->per_service_index);
198 pool_put_index (nam->affinity_pool, kv->value);
199 if (clib_bihash_add_del_16_8 (&nam->affinity_hash, kv, 0))
200 nat_elog_warn (sm, "affinity key del failed");
209 nat_affinity_create_and_lock (ip4_address_t client_addr,
210 ip4_address_t service_addr, u8 proto,
211 u16 service_port, u8 backend_index,
213 u32 affinity_per_service_list_head_index)
215 snat_main_t *sm = &snat_main;
216 nat_affinity_main_t *nam = &nat_affinity_main;
217 clib_bihash_kv_16_8_t kv, value;
219 dlist_elt_t *list_elt;
222 make_affinity_kv (&kv, client_addr, service_addr, proto, service_port);
223 clib_spinlock_lock_if_init (&nam->affinity_lock);
224 if (!clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value))
227 nat_elog_notice (sm, "affinity key already exist");
231 pool_get (nam->affinity_pool, a);
232 kv.value = a - nam->affinity_pool;
234 clib_bihash_add_or_overwrite_stale_16_8 (&nam->affinity_hash, &kv,
235 affinity_is_expired_cb, NULL);
238 nat_elog_notice (sm, "affinity key add failed");
239 pool_put (nam->affinity_pool, a);
243 pool_get (nam->list_pool, list_elt);
244 clib_dlist_init (nam->list_pool, list_elt - nam->list_pool);
245 list_elt->value = a - nam->affinity_pool;
246 a->per_service_index = list_elt - nam->list_pool;
247 a->backend_index = backend_index;
249 a->sticky_time = sticky_time;
250 a->key.as_u64[0] = kv.key[0];
251 a->key.as_u64[1] = kv.key[1];
252 clib_dlist_addtail (nam->list_pool, affinity_per_service_list_head_index,
253 a->per_service_index);
256 clib_spinlock_unlock_if_init (&nam->affinity_lock);
261 nat_affinity_unlock (ip4_address_t client_addr, ip4_address_t service_addr,
262 u8 proto, u16 service_port)
264 nat_affinity_main_t *nam = &nat_affinity_main;
265 clib_bihash_kv_16_8_t kv, value;
268 make_affinity_kv (&kv, client_addr, service_addr, proto, service_port);
269 clib_spinlock_lock_if_init (&nam->affinity_lock);
270 if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value))
273 a = pool_elt_at_index (nam->affinity_pool, value.value);
276 a->expire = (u64) a->sticky_time + vlib_time_now (nam->vlib_main);
279 clib_spinlock_unlock_if_init (&nam->affinity_lock);
283 * fd.io coding-style-patch-verification: ON
286 * eval: (c-set-style "gnu")