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

Generated by: LCOV version 1.14