virtio: enable the interrupt support for uio_pci_generic
[vpp.git] / src / plugins / nat / nat_affinity.c
1 /*
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:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15 /**
16  * @file
17  * @brief NAT plugin client-IP based session affinity for load-balancing
18  */
19
20 #include <nat/nat_affinity.h>
21 #include <nat/nat.h>
22
23 nat_affinity_main_t nat_affinity_main;
24
25 #define AFFINITY_HASH_BUCKETS 65536
26 #define AFFINITY_HASH_MEMORY (2 << 25)
27
28 u8 *
29 format_affinity_kvp (u8 * s, va_list * args)
30 {
31   clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *);
32   nat_affinity_key_t k;
33
34   k.as_u64[0] = v->key[0];
35   k.as_u64[1] = v->key[1];
36
37   s = format (s, "client %U backend %U:%d proto %U index %llu",
38               format_ip4_address, &k.client_addr,
39               format_ip4_address, &k.service_addr,
40               clib_net_to_host_u16 (k.service_port),
41               format_nat_protocol, k.proto);
42
43   return s;
44 }
45
46 void
47 nat_affinity_enable ()
48 {
49   nat_affinity_main_t *nam = &nat_affinity_main;
50   vlib_thread_main_t *tm = vlib_get_thread_main ();
51
52   if (tm->n_vlib_mains > 1)
53     clib_spinlock_init (&nam->affinity_lock);
54   clib_bihash_init_16_8 (&nam->affinity_hash, "nat-affinity",
55                          AFFINITY_HASH_BUCKETS, AFFINITY_HASH_MEMORY);
56   clib_bihash_set_kvp_format_fn_16_8 (&nam->affinity_hash,
57                                       format_affinity_kvp);
58 }
59
60 void
61 nat_affinity_disable ()
62 {
63   nat_affinity_main_t *nam = &nat_affinity_main;
64   vlib_thread_main_t *tm = vlib_get_thread_main ();
65
66   if (tm->n_vlib_mains > 1)
67     clib_spinlock_free (&nam->affinity_lock);
68   clib_bihash_free_16_8 (&nam->affinity_hash);
69 }
70
71 clib_error_t *
72 nat_affinity_init (vlib_main_t * vm)
73 {
74   nat_affinity_main_t *nam = &nat_affinity_main;
75   nam->vlib_main = vm;
76   return 0;
77 }
78
79 static_always_inline void
80 make_affinity_kv (clib_bihash_kv_16_8_t * kv, ip4_address_t client_addr,
81                   ip4_address_t service_addr, u8 proto, u16 service_port)
82 {
83   nat_affinity_key_t *key = (nat_affinity_key_t *) kv->key;
84
85   key->client_addr = client_addr;
86   key->service_addr = service_addr;
87   key->proto = proto;
88   key->service_port = service_port;
89
90   kv->value = ~0ULL;
91 }
92
93 u32
94 nat_affinity_get_per_service_list_head_index (void)
95 {
96   nat_affinity_main_t *nam = &nat_affinity_main;
97   dlist_elt_t *head_elt;
98
99   clib_spinlock_lock_if_init (&nam->affinity_lock);
100
101   pool_get (nam->list_pool, head_elt);
102   clib_dlist_init (nam->list_pool, head_elt - nam->list_pool);
103
104   clib_spinlock_unlock_if_init (&nam->affinity_lock);
105
106   return head_elt - nam->list_pool;
107 }
108
109 void
110 nat_affinity_flush_service (u32 affinity_per_service_list_head_index)
111 {
112   nat_affinity_main_t *nam = &nat_affinity_main;
113   u32 elt_index;
114   dlist_elt_t *elt;
115   nat_affinity_t *a;
116   clib_bihash_kv_16_8_t kv;
117
118   clib_spinlock_lock_if_init (&nam->affinity_lock);
119
120   while ((elt_index =
121           clib_dlist_remove_head (nam->list_pool,
122                                   affinity_per_service_list_head_index)) !=
123          ~0)
124     {
125       elt = pool_elt_at_index (nam->list_pool, elt_index);
126       a = pool_elt_at_index (nam->affinity_pool, elt->value);
127       kv.key[0] = a->key.as_u64[0];
128       kv.key[1] = a->key.as_u64[1];
129       pool_put_index (nam->affinity_pool, elt->value);
130       if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0))
131         nat_elog_warn ("affinity key del failed");
132       pool_put_index (nam->list_pool, elt_index);
133     }
134   pool_put_index (nam->list_pool, affinity_per_service_list_head_index);
135
136   clib_spinlock_unlock_if_init (&nam->affinity_lock);
137 }
138
139 int
140 nat_affinity_find_and_lock (ip4_address_t client_addr,
141                             ip4_address_t service_addr, u8 proto,
142                             u16 service_port, u8 * backend_index)
143 {
144   nat_affinity_main_t *nam = &nat_affinity_main;
145   clib_bihash_kv_16_8_t kv, value;
146   nat_affinity_t *a;
147   int rv = 0;
148
149   make_affinity_kv (&kv, client_addr, service_addr, proto, service_port);
150   clib_spinlock_lock_if_init (&nam->affinity_lock);
151   if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value))
152     {
153       rv = 1;
154       goto unlock;
155     }
156
157   a = pool_elt_at_index (nam->affinity_pool, value.value);
158   /* if already expired delete */
159   if (a->ref_cnt == 0)
160     {
161       if (a->expire < vlib_time_now (nam->vlib_main))
162         {
163           clib_dlist_remove (nam->list_pool, a->per_service_index);
164           pool_put_index (nam->list_pool, a->per_service_index);
165           pool_put_index (nam->affinity_pool, value.value);
166           if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0))
167             nat_elog_warn ("affinity key del failed");
168           rv = 1;
169           goto unlock;
170         }
171     }
172   a->ref_cnt++;
173   *backend_index = a->backend_index;
174
175 unlock:
176   clib_spinlock_unlock_if_init (&nam->affinity_lock);
177   return rv;
178 }
179
180 static int
181 affinity_is_expired_cb (clib_bihash_kv_16_8_t * kv, void *arg)
182 {
183   nat_affinity_main_t *nam = &nat_affinity_main;
184   nat_affinity_t *a;
185
186   a = pool_elt_at_index (nam->affinity_pool, kv->value);
187   if (a->ref_cnt == 0)
188     {
189       if (a->expire < vlib_time_now (nam->vlib_main))
190         {
191           clib_dlist_remove (nam->list_pool, a->per_service_index);
192           pool_put_index (nam->list_pool, a->per_service_index);
193           pool_put_index (nam->affinity_pool, kv->value);
194           if (clib_bihash_add_del_16_8 (&nam->affinity_hash, kv, 0))
195             nat_elog_warn ("affinity key del failed");
196           return 1;
197         }
198     }
199
200   return 0;
201 }
202
203 int
204 nat_affinity_create_and_lock (ip4_address_t client_addr,
205                               ip4_address_t service_addr, u8 proto,
206                               u16 service_port, u8 backend_index,
207                               u32 sticky_time,
208                               u32 affinity_per_service_list_head_index)
209 {
210   nat_affinity_main_t *nam = &nat_affinity_main;
211   clib_bihash_kv_16_8_t kv, value;
212   nat_affinity_t *a;
213   dlist_elt_t *list_elt;
214   int rv = 0;
215
216   make_affinity_kv (&kv, client_addr, service_addr, proto, service_port);
217   clib_spinlock_lock_if_init (&nam->affinity_lock);
218   if (!clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value))
219     {
220       rv = 1;
221       nat_elog_notice ("affinity key already exist");
222       goto unlock;
223     }
224
225   pool_get (nam->affinity_pool, a);
226   kv.value = a - nam->affinity_pool;
227   rv =
228     clib_bihash_add_or_overwrite_stale_16_8 (&nam->affinity_hash, &kv,
229                                              affinity_is_expired_cb, NULL);
230   if (rv)
231     {
232       nat_elog_notice ("affinity key add failed");
233       pool_put (nam->affinity_pool, a);
234       goto unlock;
235     }
236
237   pool_get (nam->list_pool, list_elt);
238   clib_dlist_init (nam->list_pool, list_elt - nam->list_pool);
239   list_elt->value = a - nam->affinity_pool;
240   a->per_service_index = list_elt - nam->list_pool;
241   a->backend_index = backend_index;
242   a->ref_cnt = 1;
243   a->sticky_time = sticky_time;
244   a->key.as_u64[0] = kv.key[0];
245   a->key.as_u64[1] = kv.key[1];
246   clib_dlist_addtail (nam->list_pool, affinity_per_service_list_head_index,
247                       a->per_service_index);
248
249 unlock:
250   clib_spinlock_unlock_if_init (&nam->affinity_lock);
251   return rv;
252 }
253
254 void
255 nat_affinity_unlock (ip4_address_t client_addr, ip4_address_t service_addr,
256                      u8 proto, u16 service_port)
257 {
258   nat_affinity_main_t *nam = &nat_affinity_main;
259   clib_bihash_kv_16_8_t kv, value;
260   nat_affinity_t *a;
261
262   make_affinity_kv (&kv, client_addr, service_addr, proto, service_port);
263   clib_spinlock_lock_if_init (&nam->affinity_lock);
264   if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value))
265     goto unlock;
266
267   a = pool_elt_at_index (nam->affinity_pool, value.value);
268   a->ref_cnt--;
269   if (a->ref_cnt == 0)
270     a->expire = (u64) a->sticky_time + vlib_time_now (nam->vlib_main);
271
272 unlock:
273   clib_spinlock_unlock_if_init (&nam->affinity_lock);
274 }
275
276 /*
277  * fd.io coding-style-patch-verification: ON
278  *
279  * Local Variables:
280  * eval: (c-set-style "gnu")
281  * End:
282  */