misc: move to new pool_foreach macros
[vpp.git] / src / plugins / cnat / cnat_session.c
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
24 clib_bihash_40_48_t cnat_session_db;
25 void (*cnat_free_port_cb) (u16 port, ip_protocol_t iproto);
26
27 typedef struct cnat_session_walk_ctx_t_
28 {
29   cnat_session_walk_cb_t cb;
30   void *ctx;
31 } cnat_session_walk_ctx_t;
32
33 static int
34 cnat_session_walk_cb (BVT (clib_bihash_kv) * kv, void *arg)
35 {
36   cnat_session_t *session = (cnat_session_t *) kv;
37   cnat_session_walk_ctx_t *ctx = arg;
38
39   ctx->cb (session, ctx->ctx);
40
41   return (BIHASH_WALK_CONTINUE);
42 }
43
44 void
45 cnat_session_walk (cnat_session_walk_cb_t cb, void *ctx)
46 {
47   cnat_session_walk_ctx_t wctx = {
48     .cb = cb,
49     .ctx = ctx,
50   };
51   BV (clib_bihash_foreach_key_value_pair) (&cnat_session_db,
52                                            cnat_session_walk_cb, &wctx);
53 }
54
55 typedef struct cnat_session_purge_walk_t_
56 {
57   clib_bihash_kv_40_48_t *keys;
58 } cnat_session_purge_walk_ctx_t;
59
60 static int
61 cnat_session_purge_walk (BVT (clib_bihash_kv) * key, void *arg)
62 {
63   cnat_session_purge_walk_ctx_t *ctx = arg;
64
65   vec_add1 (ctx->keys, *key);
66
67   return (BIHASH_WALK_CONTINUE);
68 }
69
70 u8 *
71 format_cnat_session (u8 * s, va_list * args)
72 {
73   cnat_session_t *sess = va_arg (*args, cnat_session_t *);
74   CLIB_UNUSED (int verbose) = va_arg (*args, int);
75   f64 ts = 0;
76   if (!pool_is_free_index (cnat_timestamps, sess->value.cs_ts_index))
77     ts = cnat_timestamp_exp (sess->value.cs_ts_index);
78
79   s =
80     format (s,
81             "session:[%U;%d -> %U;%d, %U] => %U;%d -> %U;%d lb:%d age:%f",
82             format_ip46_address, &sess->key.cs_ip[VLIB_RX], IP46_TYPE_ANY,
83             clib_host_to_net_u16 (sess->key.cs_port[VLIB_RX]),
84             format_ip46_address, &sess->key.cs_ip[VLIB_TX], IP46_TYPE_ANY,
85             clib_host_to_net_u16 (sess->key.cs_port[VLIB_TX]),
86             format_ip_protocol, sess->key.cs_proto, format_ip46_address,
87             &sess->value.cs_ip[VLIB_RX], IP46_TYPE_ANY,
88             clib_host_to_net_u16 (sess->value.cs_port[VLIB_RX]),
89             format_ip46_address, &sess->value.cs_ip[VLIB_TX], IP46_TYPE_ANY,
90             clib_host_to_net_u16 (sess->value.cs_port[VLIB_TX]),
91             sess->value.cs_lbi, ts);
92
93   return (s);
94 }
95
96 static clib_error_t *
97 cnat_session_show (vlib_main_t * vm,
98                    unformat_input_t * input, vlib_cli_command_t * cmd)
99 {
100   u8 verbose = 0;
101   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
102     {
103       if (unformat (input, "verbose"))
104         verbose = 1;
105       else
106         return (clib_error_return (0, "unknown input '%U'",
107                                    format_unformat_error, input));
108     }
109
110   vlib_cli_output (vm, "CNat Sessions: now:%f\n%U\n",
111                    vlib_time_now (vm),
112                    BV (format_bihash), &cnat_session_db, verbose);
113
114   return (NULL);
115 }
116
117 /* *INDENT-OFF* */
118 VLIB_CLI_COMMAND (cnat_session_show_cmd_node, static) = {
119   .path = "show cnat session",
120   .function = cnat_session_show,
121   .short_help = "show cnat session",
122   .is_mp_safe = 1,
123 };
124 /* *INDENT-ON* */
125
126 void
127 cnat_session_free (cnat_session_t * session)
128 {
129   clib_bihash_kv_40_48_t *bkey = (clib_bihash_kv_40_48_t *) session;
130   /* age it */
131   if (session->value.flags & CNAT_SESSION_FLAG_ALLOC_PORT)
132     cnat_free_port_cb (session->value.cs_port[VLIB_RX],
133                        session->key.cs_proto);
134   if (!(session->value.flags & CNAT_SESSION_FLAG_NO_CLIENT))
135     cnat_client_free_by_ip (&session->key.cs_ip[VLIB_TX], session->key.cs_af);
136   cnat_timestamp_free (session->value.cs_ts_index);
137
138   clib_bihash_add_del_40_48 (&cnat_session_db, bkey, 0 /* is_add */ );
139 }
140
141 int
142 cnat_session_purge (void)
143 {
144   /* flush all the session from the DB */
145   cnat_session_purge_walk_ctx_t ctx = { };
146   clib_bihash_kv_40_48_t *key;
147
148   BV (clib_bihash_foreach_key_value_pair) (&cnat_session_db,
149                                            cnat_session_purge_walk, &ctx);
150
151   vec_foreach (key, ctx.keys) cnat_session_free ((cnat_session_t *) key);
152
153   vec_free (ctx.keys);
154
155   return (0);
156 }
157
158 u64
159 cnat_session_scan (vlib_main_t * vm, f64 start_time, int i)
160 {
161   BVT (clib_bihash) * h = &cnat_session_db;
162   int j, k;
163
164   /* Don't scan the l2 fib if it hasn't been instantiated yet */
165   if (alloc_arena (h) == 0)
166     return 0.0;
167
168   for ( /* caller saves starting point */ ; i < h->nbuckets; i++)
169     {
170       /* allow no more than 100us without a pause */
171       if ((vlib_time_now (vm) - start_time) > 10e-5)
172         return (i);
173
174       if (i < (h->nbuckets - 3))
175         {
176           BVT (clib_bihash_bucket) * b =
177             BV (clib_bihash_get_bucket) (h, i + 3);
178           CLIB_PREFETCH (b, CLIB_CACHE_LINE_BYTES, LOAD);
179           b = BV (clib_bihash_get_bucket) (h, i + 1);
180           if (!BV (clib_bihash_bucket_is_empty) (b))
181             {
182               BVT (clib_bihash_value) * v =
183                 BV (clib_bihash_get_value) (h, b->offset);
184               CLIB_PREFETCH (v, CLIB_CACHE_LINE_BYTES, LOAD);
185             }
186         }
187
188       BVT (clib_bihash_bucket) * b = BV (clib_bihash_get_bucket) (h, i);
189       if (BV (clib_bihash_bucket_is_empty) (b))
190         continue;
191       BVT (clib_bihash_value) * v = BV (clib_bihash_get_value) (h, b->offset);
192       for (j = 0; j < (1 << b->log2_pages); j++)
193         {
194           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
195             {
196               if (v->kvp[k].key[0] == ~0ULL && v->kvp[k].value[0] == ~0ULL)
197                 continue;
198
199               cnat_session_t *session = (cnat_session_t *) & v->kvp[k];
200
201               if (start_time >
202                   cnat_timestamp_exp (session->value.cs_ts_index))
203                 {
204                   /* age it */
205                   cnat_session_free (session);
206
207                   /*
208                    * Note: we may have just freed the bucket's backing
209                    * storage, so check right here...
210                    */
211                   if (BV (clib_bihash_bucket_is_empty) (b))
212                     goto doublebreak;
213                 }
214             }
215           v++;
216         }
217     doublebreak:
218       ;
219     }
220
221   /* start again */
222   return (0);
223 }
224
225 static clib_error_t *
226 cnat_session_init (vlib_main_t * vm)
227 {
228   cnat_main_t *cm = &cnat_main;
229   BV (clib_bihash_init) (&cnat_session_db,
230                          "CNat Session DB", cm->session_hash_buckets,
231                          cm->session_hash_memory);
232   BV (clib_bihash_set_kvp_format_fn) (&cnat_session_db, format_cnat_session);
233
234   return (NULL);
235 }
236
237 VLIB_INIT_FUNCTION (cnat_session_init);
238
239 static clib_error_t *
240 cnat_timestamp_show (vlib_main_t * vm,
241                      unformat_input_t * input, vlib_cli_command_t * cmd)
242 {
243   cnat_timestamp_t *ts;
244   clib_rwlock_reader_lock (&cnat_main.ts_lock);
245     /* *INDENT-OFF* */
246   pool_foreach (ts, cnat_timestamps)  {
247     vlib_cli_output (vm, "[%d] last_seen:%f lifetime:%u ref:%u",
248                      ts - cnat_timestamps,
249                      ts->last_seen, ts->lifetime, ts->refcnt);
250   }
251   /* *INDENT-ON* */
252   clib_rwlock_reader_unlock (&cnat_main.ts_lock);
253   return (NULL);
254 }
255
256 /* *INDENT-OFF* */
257 VLIB_CLI_COMMAND (cnat_timestamp_show_cmd, static) = {
258   .path = "show cnat timestamp",
259   .function = cnat_timestamp_show,
260   .short_help = "show cnat timestamp",
261   .is_mp_safe = 1,
262 };
263 /* *INDENT-ON* */
264
265 /*
266  * fd.io coding-style-patch-verification: ON
267  *
268  * Local Variables:
269  * eval: (c-set-style "gnu")
270  * End:
271  */