LCOV - code coverage report
Current view: top level - plugins/cnat - cnat_session.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 108 133 81.2 %
Date: 2023-07-05 22:20:52 Functions: 16 18 88.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/ip/ip.h>
      17             : #include <cnat/cnat_session.h>
      18             : #include <cnat/cnat_inline.h>
      19             : 
      20             : #include <vppinfra/bihash_template.h>
      21             : #include <vppinfra/bihash_template.c>
      22             : 
      23             : cnat_bihash_t cnat_session_db;
      24             : void (*cnat_free_port_cb) (u16 port, ip_protocol_t iproto);
      25             : 
      26             : typedef struct cnat_session_walk_ctx_t_
      27             : {
      28             :   cnat_session_walk_cb_t cb;
      29             :   void *ctx;
      30             : } cnat_session_walk_ctx_t;
      31             : 
      32             : static int
      33           0 : cnat_session_walk_cb (BVT (clib_bihash_kv) * kv, void *arg)
      34             : {
      35           0 :   cnat_session_t *session = (cnat_session_t *) kv;
      36           0 :   cnat_session_walk_ctx_t *ctx = arg;
      37             : 
      38           0 :   ctx->cb (session, ctx->ctx);
      39             : 
      40           0 :   return (BIHASH_WALK_CONTINUE);
      41             : }
      42             : 
      43             : void
      44           6 : cnat_session_walk (cnat_session_walk_cb_t cb, void *ctx)
      45             : {
      46           6 :   cnat_session_walk_ctx_t wctx = {
      47             :     .cb = cb,
      48             :     .ctx = ctx,
      49             :   };
      50           6 :   BV (clib_bihash_foreach_key_value_pair) (&cnat_session_db,
      51             :                                            cnat_session_walk_cb, &wctx);
      52           6 : }
      53             : 
      54             : typedef struct cnat_session_purge_walk_t_
      55             : {
      56             :   cnat_bihash_kv_t *keys;
      57             : } cnat_session_purge_walk_ctx_t;
      58             : 
      59             : static int
      60         100 : cnat_session_purge_walk (BVT (clib_bihash_kv) * key, void *arg)
      61             : {
      62         100 :   cnat_session_purge_walk_ctx_t *ctx = arg;
      63             : 
      64         100 :   vec_add1 (ctx->keys, *key);
      65             : 
      66         100 :   return (BIHASH_WALK_CONTINUE);
      67             : }
      68             : 
      69             : u8 *
      70        4512 : format_cnat_session_location (u8 *s, va_list *args)
      71             : {
      72        4512 :   u8 location = va_arg (*args, int);
      73        4512 :   switch (location)
      74             :     {
      75           0 :     case CNAT_LOCATION_INPUT:
      76           0 :       s = format (s, "input");
      77           0 :       break;
      78           0 :     case CNAT_LOCATION_OUTPUT:
      79           0 :       s = format (s, "output");
      80           0 :       break;
      81        4512 :     case CNAT_LOCATION_FIB:
      82        4512 :       s = format (s, "fib");
      83        4512 :       break;
      84           0 :     default:
      85           0 :       s = format (s, "unknown");
      86           0 :       break;
      87             :     }
      88        4512 :   return (s);
      89             : }
      90             : 
      91             : u8 *
      92        4512 : format_cnat_session (u8 * s, va_list * args)
      93             : {
      94        4512 :   cnat_session_t *sess = va_arg (*args, cnat_session_t *);
      95        4512 :   CLIB_UNUSED (int verbose) = va_arg (*args, int);
      96        4512 :   f64 ts = 0;
      97        4512 :   if (!pool_is_free_index (cnat_timestamps, sess->value.cs_ts_index))
      98        4422 :     ts = cnat_timestamp_exp (sess->value.cs_ts_index);
      99             : 
     100        4512 :   s = format (
     101             :     s, "session:[%U;%d -> %U;%d, %U] => %U;%d -> %U;%d %U lb:%d age:%f",
     102             :     format_ip46_address, &sess->key.cs_ip[VLIB_RX], IP46_TYPE_ANY,
     103        4512 :     clib_host_to_net_u16 (sess->key.cs_port[VLIB_RX]), format_ip46_address,
     104             :     &sess->key.cs_ip[VLIB_TX], IP46_TYPE_ANY,
     105        4512 :     clib_host_to_net_u16 (sess->key.cs_port[VLIB_TX]), format_ip_protocol,
     106        4512 :     sess->key.cs_proto, format_ip46_address, &sess->value.cs_ip[VLIB_RX],
     107        4512 :     IP46_TYPE_ANY, clib_host_to_net_u16 (sess->value.cs_port[VLIB_RX]),
     108             :     format_ip46_address, &sess->value.cs_ip[VLIB_TX], IP46_TYPE_ANY,
     109        4512 :     clib_host_to_net_u16 (sess->value.cs_port[VLIB_TX]),
     110        4512 :     format_cnat_session_location, sess->key.cs_loc, sess->value.cs_lbi, ts);
     111             : 
     112        4512 :   return (s);
     113             : }
     114             : 
     115             : static clib_error_t *
     116           6 : cnat_session_show (vlib_main_t * vm,
     117             :                    unformat_input_t * input, vlib_cli_command_t * cmd)
     118             : {
     119           6 :   u8 verbose = 0;
     120          12 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     121             :     {
     122           6 :       if (unformat (input, "verbose"))
     123           6 :         verbose = 1;
     124             :       else
     125           0 :         return (clib_error_return (0, "unknown input '%U'",
     126             :                                    format_unformat_error, input));
     127             :     }
     128             : 
     129           6 :   vlib_cli_output (vm, "CNat Sessions: now:%f\n%U\n",
     130             :                    vlib_time_now (vm),
     131             :                    BV (format_bihash), &cnat_session_db, verbose);
     132             : 
     133           6 :   return (NULL);
     134             : }
     135             : 
     136      245447 : VLIB_CLI_COMMAND (cnat_session_show_cmd_node, static) = {
     137             :   .path = "show cnat session",
     138             :   .function = cnat_session_show,
     139             :   .short_help = "show cnat session",
     140             :   .is_mp_safe = 1,
     141             : };
     142             : 
     143             : void
     144         196 : cnat_session_free (cnat_session_t * session)
     145             : {
     146         196 :   cnat_bihash_kv_t *bkey = (cnat_bihash_kv_t *) session;
     147             :   /* age it */
     148         196 :   if (session->value.flags & CNAT_SESSION_FLAG_ALLOC_PORT)
     149          20 :     cnat_free_port_cb (session->value.cs_port[VLIB_RX],
     150          20 :                        session->key.cs_proto);
     151         196 :   if (!(session->value.flags & CNAT_SESSION_FLAG_NO_CLIENT))
     152         176 :     cnat_client_free_by_ip (&session->key.cs_ip[VLIB_TX], session->key.cs_af);
     153         196 :   cnat_timestamp_free (session->value.cs_ts_index);
     154             : 
     155         196 :   cnat_bihash_add_del (&cnat_session_db, bkey, 0 /* is_add */);
     156         196 : }
     157             : 
     158             : int
     159          14 : cnat_session_purge (void)
     160             : {
     161             :   /* flush all the session from the DB */
     162          14 :   cnat_session_purge_walk_ctx_t ctx = { };
     163             :   cnat_bihash_kv_t *key;
     164             : 
     165          14 :   BV (clib_bihash_foreach_key_value_pair) (&cnat_session_db,
     166             :                                            cnat_session_purge_walk, &ctx);
     167             : 
     168         114 :   vec_foreach (key, ctx.keys) cnat_session_free ((cnat_session_t *) key);
     169             : 
     170          14 :   vec_free (ctx.keys);
     171             : 
     172          14 :   return (0);
     173             : }
     174             : 
     175             : void
     176          96 : cnat_reverse_session_free (cnat_session_t *session)
     177             : {
     178             :   cnat_bihash_kv_t bkey, bvalue;
     179          96 :   cnat_session_t *rsession = (cnat_session_t *) &bkey;
     180             :   int rv;
     181             : 
     182          96 :   ip46_address_copy (&rsession->key.cs_ip[VLIB_RX],
     183          96 :                      &session->value.cs_ip[VLIB_TX]);
     184          96 :   ip46_address_copy (&rsession->key.cs_ip[VLIB_TX],
     185          96 :                      &session->value.cs_ip[VLIB_RX]);
     186          96 :   rsession->key.cs_proto = session->key.cs_proto;
     187          96 :   rsession->key.cs_loc = session->key.cs_loc == CNAT_LOCATION_OUTPUT ?
     188          96 :                            CNAT_LOCATION_INPUT :
     189             :                            CNAT_LOCATION_OUTPUT;
     190          96 :   rsession->key.__cs_pad = 0;
     191          96 :   rsession->key.cs_af = session->key.cs_af;
     192          96 :   rsession->key.cs_port[VLIB_RX] = session->value.cs_port[VLIB_TX];
     193          96 :   rsession->key.cs_port[VLIB_TX] = session->value.cs_port[VLIB_RX];
     194             : 
     195          96 :   rv = cnat_bihash_search_i2 (&cnat_session_db, &bkey, &bvalue);
     196          96 :   if (!rv)
     197             :     {
     198             :       /* other session is in bihash */
     199           0 :       cnat_session_t *rsession = (cnat_session_t *) &bvalue;
     200           0 :       cnat_session_free (rsession);
     201             :     }
     202          96 : }
     203             : 
     204             : u64
     205          46 : cnat_session_scan (vlib_main_t * vm, f64 start_time, int i)
     206             : {
     207          46 :   BVT (clib_bihash) * h = &cnat_session_db;
     208             :   int j, k;
     209             : 
     210          46 :   if (alloc_arena (h) == 0)
     211           3 :     return 0;
     212             : 
     213        2667 :   for ( /* caller saves starting point */ ; i < h->nbuckets; i++)
     214             :     {
     215             :       /* allow no more than 100us without a pause */
     216        2626 :       if ((vlib_time_now (vm) - start_time) > 10e-5)
     217           2 :         return (i);
     218             : 
     219        2624 :       if (i < (h->nbuckets - 3))
     220             :         {
     221             :           BVT (clib_bihash_bucket) * b =
     222        2501 :             BV (clib_bihash_get_bucket) (h, i + 3);
     223        2501 :           clib_prefetch_load (b);
     224        2501 :           b = BV (clib_bihash_get_bucket) (h, i + 1);
     225        2501 :           if (!BV (clib_bihash_bucket_is_empty) (b))
     226             :             {
     227             :               BVT (clib_bihash_value) * v =
     228         293 :                 BV (clib_bihash_get_value) (h, b->offset);
     229         293 :               clib_prefetch_load (v);
     230             :             }
     231             :         }
     232             : 
     233        2624 :       BVT (clib_bihash_bucket) * b = BV (clib_bihash_get_bucket) (h, i);
     234        2624 :       if (BV (clib_bihash_bucket_is_empty) (b))
     235        2302 :         continue;
     236         322 :       BVT (clib_bihash_value) * v = BV (clib_bihash_get_value) (h, b->offset);
     237         588 :       for (j = 0; j < (1 << b->log2_pages); j++)
     238             :         {
     239         888 :           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
     240             :             {
     241         622 :               if (BV (clib_bihash_is_free) (&v->kvp[k]))
     242         244 :                 continue;
     243             : 
     244         378 :               cnat_session_t *session = (cnat_session_t *) & v->kvp[k];
     245             : 
     246         378 :               if (start_time >
     247         378 :                   cnat_timestamp_exp (session->value.cs_ts_index))
     248             :                 {
     249             :                   /* age it */
     250          96 :                   cnat_reverse_session_free (session);
     251             :                   /* this should be last as deleting the session memset it to
     252             :                    * 0xff */
     253          96 :                   cnat_session_free (session);
     254             : 
     255             :                   /*
     256             :                    * Note: we may have just freed the bucket's backing
     257             :                    * storage, so check right here...
     258             :                    */
     259          96 :                   if (BV (clib_bihash_bucket_is_empty) (b))
     260          68 :                     goto doublebreak;
     261             :                 }
     262             :             }
     263         266 :           v++;
     264             :         }
     265        2624 :     doublebreak:
     266             :       ;
     267             :     }
     268             : 
     269             :   /* start again */
     270          41 :   return (0);
     271             : }
     272             : 
     273             : static clib_error_t *
     274         559 : cnat_session_init (vlib_main_t * vm)
     275             : {
     276         559 :   cnat_main_t *cm = &cnat_main;
     277         559 :   BV (clib_bihash_init) (&cnat_session_db,
     278             :                          "CNat Session DB", cm->session_hash_buckets,
     279             :                          cm->session_hash_memory);
     280         559 :   BV (clib_bihash_set_kvp_format_fn) (&cnat_session_db, format_cnat_session);
     281             : 
     282         559 :   return (NULL);
     283             : }
     284             : 
     285        2239 : VLIB_INIT_FUNCTION (cnat_session_init);
     286             : 
     287             : static clib_error_t *
     288           0 : cnat_timestamp_show (vlib_main_t * vm,
     289             :                      unformat_input_t * input, vlib_cli_command_t * cmd)
     290             : {
     291             :   cnat_timestamp_t *ts;
     292           0 :   clib_rwlock_reader_lock (&cnat_main.ts_lock);
     293           0 :   pool_foreach (ts, cnat_timestamps)
     294             :     {
     295           0 :       vlib_cli_output (vm, "[%d] last_seen:%f lifetime:%u ref:%u",
     296           0 :                        ts - cnat_timestamps, ts->last_seen, ts->lifetime,
     297           0 :                        ts->refcnt);
     298             :     }
     299           0 :   clib_rwlock_reader_unlock (&cnat_main.ts_lock);
     300           0 :   return (NULL);
     301             : }
     302             : 
     303      245447 : VLIB_CLI_COMMAND (cnat_timestamp_show_cmd, static) = {
     304             :   .path = "show cnat timestamp",
     305             :   .function = cnat_timestamp_show,
     306             :   .short_help = "show cnat timestamp",
     307             :   .is_mp_safe = 1,
     308             : };
     309             : 
     310             : /*
     311             :  * fd.io coding-style-patch-verification: ON
     312             :  *
     313             :  * Local Variables:
     314             :  * eval: (c-set-style "gnu")
     315             :  * End:
     316             :  */

Generated by: LCOV version 1.14