LCOV - code coverage report
Current view: top level - plugins/nat/nat44-ed - nat44_ed_affinity.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 87 139 62.6 %
Date: 2023-07-05 22:20:52 Functions: 8 11 72.7 %

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

Generated by: LCOV version 1.14