LCOV - code coverage report
Current view: top level - plugins/cnat - cnat_client.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 140 158 88.6 %
Date: 2023-10-26 01:39:38 Functions: 20 22 90.9 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2020 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             : #include <vnet/fib/fib_table.h>
      17             : #include <vnet/dpo/drop_dpo.h>
      18             : 
      19             : #include <cnat/cnat_client.h>
      20             : #include <cnat/cnat_translation.h>
      21             : 
      22             : cnat_client_t *cnat_client_pool;
      23             : cnat_client_db_t cnat_client_db;
      24             : dpo_type_t cnat_client_dpo;
      25             : fib_source_t cnat_fib_source;
      26             : 
      27             : static_always_inline u8
      28          70 : cnat_client_is_clone (cnat_client_t * cc)
      29             : {
      30          70 :   return (FIB_NODE_INDEX_INVALID == cc->cc_fei);
      31             : }
      32             : 
      33             : static void
      34          37 : cnat_client_db_remove (cnat_client_t * cc)
      35             : {
      36             :   clib_bihash_kv_16_8_t bkey;
      37          37 :   if (ip_addr_version (&cc->cc_ip) == AF_IP4)
      38             :     {
      39          21 :       bkey.key[0] = ip_addr_v4 (&cc->cc_ip).as_u32;
      40          21 :       bkey.key[1] = 0;
      41             :     }
      42             :   else
      43             :     {
      44          16 :       bkey.key[0] = ip_addr_v6 (&cc->cc_ip).as_u64[0];
      45          16 :       bkey.key[1] = ip_addr_v6 (&cc->cc_ip).as_u64[1];
      46             :     }
      47             : 
      48          37 :   clib_bihash_add_del_16_8 (&cnat_client_db.cc_ip_id_hash, &bkey, 0 /* del */);
      49          37 : }
      50             : 
      51             : static void
      52          41 : cnat_client_db_add (cnat_client_t *cc)
      53             : {
      54             :   index_t cci;
      55             : 
      56          41 :   cci = cc - cnat_client_pool;
      57             : 
      58             :   clib_bihash_kv_16_8_t bkey;
      59          41 :   bkey.value = cci;
      60          41 :   if (ip_addr_version (&cc->cc_ip) == AF_IP4)
      61             :     {
      62          23 :       bkey.key[0] = ip_addr_v4 (&cc->cc_ip).as_u32;
      63          23 :       bkey.key[1] = 0;
      64             :     }
      65             :   else
      66             :     {
      67          18 :       bkey.key[0] = ip_addr_v6 (&cc->cc_ip).as_u64[0];
      68          18 :       bkey.key[1] = ip_addr_v6 (&cc->cc_ip).as_u64[1];
      69             :     }
      70             : 
      71          41 :   clib_bihash_add_del_16_8 (&cnat_client_db.cc_ip_id_hash, &bkey, 1 /* add */);
      72          41 : }
      73             : 
      74             : static void
      75          37 : cnat_client_destroy (cnat_client_t * cc)
      76             : {
      77          37 :   ASSERT (!cnat_client_is_clone (cc));
      78             : 
      79          37 :   ASSERT (fib_entry_is_sourced (cc->cc_fei, cnat_fib_source));
      80          37 :   fib_table_entry_delete_index (cc->cc_fei, cnat_fib_source);
      81             : 
      82          37 :   cnat_client_db_remove (cc);
      83          37 :   dpo_reset (&cc->cc_parent);
      84          37 :   pool_put (cnat_client_pool, cc);
      85          37 : }
      86             : 
      87             : void
      88         242 : cnat_client_free_by_ip (ip46_address_t * ip, u8 af)
      89             : {
      90             :   cnat_client_t *cc;
      91         242 :   cc = (AF_IP4 == af ?
      92         242 :         cnat_client_ip4_find (&ip->ip4) : cnat_client_ip6_find (&ip->ip6));
      93         242 :   ASSERT (NULL != cc);
      94             : 
      95         242 :   if (0 == cnat_client_uncnt_session (cc) && 0 == cc->tr_refcnt)
      96          35 :     cnat_client_destroy (cc);
      97         242 : }
      98             : 
      99             : void
     100          88 : cnat_client_throttle_pool_process ()
     101             : {
     102             :   /* This processes ips stored in the throttle pool
     103             :      to update session refcounts
     104             :      and should be called before cnat_client_free_by_ip */
     105             :   cnat_client_t *cc;
     106          88 :   ip_address_t *addr, *del_vec = NULL;
     107             :   u32 refcnt;
     108             : 
     109          88 :   vec_reset_length (del_vec);
     110          88 :   clib_spinlock_lock (&cnat_client_db.throttle_lock);
     111        5747 :   hash_foreach_mem (addr, refcnt, cnat_client_db.throttle_mem, {
     112             :     cc = (AF_IP4 == addr->version ? cnat_client_ip4_find (&ip_addr_v4 (addr)) :
     113             :                                     cnat_client_ip6_find (&ip_addr_v6 (addr)));
     114             :     /* Client might not already be created */
     115             :     if (NULL != cc)
     116             :       {
     117             :         cnat_client_t *ccp = cnat_client_get (cc->parent_cci);
     118             :         clib_atomic_add_fetch (&ccp->session_refcnt, refcnt);
     119             :         vec_add1 (del_vec, *addr);
     120             :       }
     121             :   });
     122         115 :   vec_foreach (addr, del_vec)
     123          27 :     hash_unset_mem_free (&cnat_client_db.throttle_mem, addr);
     124          88 :   clib_spinlock_unlock (&cnat_client_db.throttle_lock);
     125          88 : }
     126             : 
     127             : void
     128          20 : cnat_client_translation_added (index_t cci)
     129             : {
     130             :   cnat_client_t *cc;
     131          20 :   if (INDEX_INVALID == cci)
     132           2 :     return;
     133             : 
     134          18 :   cc = cnat_client_get (cci);
     135          18 :   cc->tr_refcnt++;
     136             : }
     137             : 
     138             : void
     139          20 : cnat_client_translation_deleted (index_t cci)
     140             : {
     141             :   cnat_client_t *cc;
     142          20 :   if (INDEX_INVALID == cci)
     143           6 :     return;
     144             : 
     145          14 :   cc = cnat_client_get (cci);
     146          14 :   cc->tr_refcnt--;
     147             : 
     148          14 :   if (0 == cc->tr_refcnt && 0 == cc->session_refcnt)
     149           2 :     cnat_client_destroy (cc);
     150             : }
     151             : 
     152             : index_t
     153          51 : cnat_client_add (const ip_address_t * ip, u8 flags)
     154             : {
     155             :   cnat_client_t *cc;
     156          51 :   dpo_id_t tmp = DPO_INVALID;
     157             :   fib_node_index_t fei;
     158             :   dpo_proto_t dproto;
     159             :   fib_prefix_t pfx;
     160             :   index_t cci;
     161             :   u32 fib_flags;
     162             : 
     163             :   /* check again if we need this client */
     164         102 :   cc = (AF_IP4 == ip->version ?
     165          51 :         cnat_client_ip4_find (&ip->ip.ip4) :
     166          23 :         cnat_client_ip6_find (&ip->ip.ip6));
     167             : 
     168          51 :   if (NULL != cc)
     169          10 :     return (cc - cnat_client_pool);
     170             : 
     171             : 
     172          41 :   pool_get_aligned (cnat_client_pool, cc, CLIB_CACHE_LINE_BYTES);
     173          41 :   cc->cc_locks = 1;
     174          41 :   cci = cc - cnat_client_pool;
     175          41 :   cc->parent_cci = cci;
     176          41 :   cc->flags = flags;
     177          41 :   cc->tr_refcnt = 0;
     178          41 :   cc->session_refcnt = 0;
     179             : 
     180          41 :   ip_address_copy (&cc->cc_ip, ip);
     181          41 :   cnat_client_db_add (cc);
     182             : 
     183          41 :   ip_address_to_fib_prefix (&cc->cc_ip, &pfx);
     184             : 
     185          41 :   dproto = fib_proto_to_dpo (pfx.fp_proto);
     186          41 :   dpo_set (&tmp, cnat_client_dpo, dproto, cci);
     187          41 :   dpo_stack (cnat_client_dpo, dproto, &cc->cc_parent, drop_dpo_get (dproto));
     188             : 
     189          41 :   fib_flags = FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT;
     190          82 :   fib_flags |= (flags & CNAT_FLAG_EXCLUSIVE) ?
     191          41 :     FIB_ENTRY_FLAG_EXCLUSIVE : FIB_ENTRY_FLAG_INTERPOSE;
     192             : 
     193          41 :   fei = fib_table_entry_special_dpo_add (CNAT_FIB_TABLE,
     194             :                                          &pfx, cnat_fib_source, fib_flags,
     195             :                                          &tmp);
     196             : 
     197          41 :   cc = pool_elt_at_index (cnat_client_pool, cci);
     198          41 :   cc->cc_fei = fei;
     199             : 
     200          41 :   return (cci);
     201             : }
     202             : 
     203             : void
     204          27 : cnat_client_learn (const ip_address_t *addr)
     205             : {
     206             :   /* RPC call to add a client from the dataplane */
     207             :   index_t cci;
     208             :   cnat_client_t *cc;
     209          27 :   cci = cnat_client_add (addr, 0 /* flags */);
     210          27 :   cc = pool_elt_at_index (cnat_client_pool, cci);
     211          27 :   cnat_client_cnt_session (cc);
     212             :   /* Process throttled calls if any */
     213          27 :   cnat_client_throttle_pool_process ();
     214          27 : }
     215             : 
     216             : /**
     217             :  * Interpose a policy DPO
     218             :  */
     219             : static void
     220          27 : cnat_client_dpo_interpose (const dpo_id_t * original,
     221             :                            const dpo_id_t * parent, dpo_id_t * clone)
     222             : {
     223             :   cnat_client_t *cc, *cc_clone;
     224             : 
     225          27 :   pool_get_zero (cnat_client_pool, cc_clone);
     226          27 :   cc = cnat_client_get (original->dpoi_index);
     227             : 
     228          27 :   cc_clone->cc_fei = FIB_NODE_INDEX_INVALID;
     229          27 :   cc_clone->parent_cci = cc->parent_cci;
     230          27 :   cc_clone->flags = cc->flags;
     231          27 :   ip_address_copy (&cc_clone->cc_ip, &cc->cc_ip);
     232             : 
     233             :   /* stack the clone on the FIB provided parent */
     234          27 :   dpo_stack (cnat_client_dpo, original->dpoi_proto, &cc_clone->cc_parent,
     235             :              parent);
     236             : 
     237             :   /* return the clone */
     238          27 :   dpo_set (clone,
     239             :            cnat_client_dpo,
     240          27 :            original->dpoi_proto, cc_clone - cnat_client_pool);
     241          27 : }
     242             : 
     243             : int
     244           0 : cnat_client_purge (void)
     245             : {
     246           0 :   int rv = 0, rrv = 0;
     247           0 :   if ((rv = pool_elts (cnat_client_pool)))
     248           0 :     clib_warning ("len(cnat_client_pool) isnt 0 but %d", rv);
     249           0 :   rrv |= rv;
     250           0 :   if ((rv = hash_elts (cnat_client_db.throttle_mem)))
     251           0 :     clib_warning ("len(throttle_mem) isnt 0 but %d", rv);
     252           0 :   rrv |= rv;
     253           0 :   return (rrv);
     254             : }
     255             : 
     256             : u8 *
     257           6 : format_cnat_client (u8 * s, va_list * args)
     258             : {
     259           6 :   index_t cci = va_arg (*args, index_t);
     260           6 :   u32 indent = va_arg (*args, u32);
     261             : 
     262           6 :   cnat_client_t *cc = pool_elt_at_index (cnat_client_pool, cci);
     263             : 
     264           6 :   s = format (s, "[%d] cnat-client:[%U] tr:%d sess:%d", cci,
     265             :               format_ip_address, &cc->cc_ip,
     266             :               cc->tr_refcnt, cc->session_refcnt);
     267             : 
     268           6 :   if (cc->flags & CNAT_FLAG_EXCLUSIVE)
     269           6 :     s = format (s, " exclusive");
     270             : 
     271           6 :   if (cnat_client_is_clone (cc))
     272           0 :     s = format (s, "\n%Uclone of [%d]\n%U%U",
     273             :                 format_white_space, indent + 2, cc->parent_cci,
     274             :                 format_white_space, indent + 2,
     275             :                 format_dpo_id, &cc->cc_parent, indent + 4);
     276             : 
     277           6 :   return (s);
     278             : }
     279             : 
     280             : 
     281             : static clib_error_t *
     282           3 : cnat_client_show (vlib_main_t * vm,
     283             :                   unformat_input_t * input, vlib_cli_command_t * cmd)
     284             : {
     285             :   index_t cci;
     286             : 
     287           3 :   cci = INDEX_INVALID;
     288             : 
     289           3 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     290             :     {
     291           0 :       if (unformat (input, "%d", &cci))
     292             :         ;
     293             :       else
     294           0 :         return (clib_error_return (0, "unknown input '%U'",
     295             :                                    format_unformat_error, input));
     296             :     }
     297             : 
     298           3 :   if (INDEX_INVALID == cci)
     299             :     {
     300           9 :       pool_foreach_index (cci, cnat_client_pool)
     301           6 :         vlib_cli_output(vm, "%U", format_cnat_client, cci, 0);
     302             : 
     303           3 :       vlib_cli_output (vm, "%d clients", pool_elts (cnat_client_pool));
     304             :     }
     305             :   else
     306             :     {
     307           0 :       vlib_cli_output (vm, "Invalid policy ID:%d", cci);
     308             :     }
     309             : 
     310           3 :   return (NULL);
     311             : }
     312             : 
     313      257065 : VLIB_CLI_COMMAND (cnat_client_show_cmd_node, static) = {
     314             :   .path = "show cnat client",
     315             :   .function = cnat_client_show,
     316             :   .short_help = "show cnat client",
     317             :   .is_mp_safe = 1,
     318             : };
     319             : 
     320             : const static char *const cnat_client_dpo_ip4_nodes[] = {
     321             :   "ip4-cnat-tx",
     322             :   NULL,
     323             : };
     324             : 
     325             : const static char *const cnat_client_dpo_ip6_nodes[] = {
     326             :   "ip6-cnat-tx",
     327             :   NULL,
     328             : };
     329             : 
     330             : const static char *const *const cnat_client_dpo_nodes[DPO_PROTO_NUM] = {
     331             :   [DPO_PROTO_IP4] = cnat_client_dpo_ip4_nodes,
     332             :   [DPO_PROTO_IP6] = cnat_client_dpo_ip6_nodes,
     333             : };
     334             : 
     335             : static void
     336         270 : cnat_client_dpo_lock (dpo_id_t * dpo)
     337             : {
     338             :   cnat_client_t *cc;
     339             : 
     340         270 :   cc = cnat_client_get (dpo->dpoi_index);
     341             : 
     342         270 :   cc->cc_locks++;
     343         270 : }
     344             : 
     345             : static void
     346         217 : cnat_client_dpo_unlock (dpo_id_t * dpo)
     347             : {
     348             :   cnat_client_t *cc;
     349             : 
     350         217 :   cc = cnat_client_get (dpo->dpoi_index);
     351             : 
     352         217 :   cc->cc_locks--;
     353             : 
     354         217 :   if (0 == cc->cc_locks)
     355             :     {
     356          27 :       ASSERT (cnat_client_is_clone (cc));
     357          27 :       dpo_reset (&cc->cc_parent);
     358          27 :       pool_put (cnat_client_pool, cc);
     359             :     }
     360         217 : }
     361             : 
     362             : u8 *
     363           0 : format_cnat_client_dpo (u8 * s, va_list * ap)
     364             : {
     365           0 :   index_t cci = va_arg (*ap, index_t);
     366           0 :   u32 indent = va_arg (*ap, u32);
     367             : 
     368           0 :   s = format (s, "%U", format_cnat_client, cci, indent);
     369             : 
     370           0 :   return (s);
     371             : }
     372             : 
     373             : const static dpo_vft_t cnat_client_dpo_vft = {
     374             :   .dv_lock = cnat_client_dpo_lock,
     375             :   .dv_unlock = cnat_client_dpo_unlock,
     376             :   .dv_format = format_cnat_client_dpo,
     377             :   .dv_mk_interpose = cnat_client_dpo_interpose,
     378             : };
     379             : 
     380             : static clib_error_t *
     381         575 : cnat_client_init (vlib_main_t * vm)
     382             : {
     383         575 :   cnat_main_t *cm = &cnat_main;
     384         575 :   cnat_client_dpo = dpo_register_new_type (&cnat_client_dpo_vft,
     385             :                                            cnat_client_dpo_nodes);
     386             : 
     387         575 :   clib_bihash_init_16_8 (&cnat_client_db.cc_ip_id_hash, "CNat client DB",
     388             :                          cm->client_hash_buckets, cm->client_hash_memory);
     389             : 
     390         575 :   cnat_fib_source = fib_source_allocate ("cnat", CNAT_FIB_SOURCE_PRIORITY,
     391             :                                          FIB_SOURCE_BH_SIMPLE);
     392             : 
     393         575 :   clib_spinlock_init (&cnat_client_db.throttle_lock);
     394         575 :   cnat_client_db.throttle_mem =
     395         575 :     hash_create_mem (0, sizeof (ip_address_t), sizeof (uword));
     396             : 
     397         575 :   return (NULL);
     398             : }
     399             : 
     400        1151 : VLIB_INIT_FUNCTION (cnat_client_init);
     401             : 
     402             : /*
     403             :  * fd.io coding-style-patch-verification: ON
     404             :  *
     405             :  * Local Variables:
     406             :  * eval: (c-set-style "gnu")
     407             :  * End:
     408             :  */

Generated by: LCOV version 1.14