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

Generated by: LCOV version 1.14