nat: reduce number of hash tables for EI NAT
[vpp.git] / src / plugins / nat / nat44-ei / nat44_ei.c
1 /*
2  * nat44_ei.c - nat44 endpoint dependent plugin
3  *
4  * Copyright (c) 2020 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14  * License for the specific language governing permissions and limitations
15  * under the License.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/ip/ip.h>
20 #include <vnet/ip/ip4.h>
21 #include <vnet/plugin/plugin.h>
22 #include <nat/nat.h>
23 #include <nat/nat_dpo.h>
24 #include <nat/lib/ipfix_logging.h>
25 #include <nat/lib/nat_syslog.h>
26 #include <nat/nat_inlines.h>
27 #include <nat/nat44/inlines.h>
28 #include <nat/nat_affinity.h>
29 #include <vnet/fib/fib_table.h>
30 #include <vnet/fib/ip4_fib.h>
31 #include <vnet/ip/reass/ip4_sv_reass.h>
32 #include <vppinfra/bihash_16_8.h>
33 #include <nat/nat44/ed_inlines.h>
34 #include <vnet/ip/ip_table.h>
35
36 #include <nat/nat44-ei/nat44_ei_inlines.h>
37 #include <nat/nat44-ei/nat44_ei.h>
38
39 nat44_ei_main_t nat44_ei_main;
40
41 static void nat44_ei_db_free ();
42
43 static void nat44_ei_db_init (u32 translations, u32 translation_buckets,
44                               u32 user_buckets);
45
46 int
47 nat44_ei_plugin_enable (nat44_ei_config_t c)
48 {
49   nat44_ei_main_t *nm = &nat44_ei_main;
50   snat_main_t *sm = &snat_main;
51
52   clib_memset (nm, 0, sizeof (*nm));
53
54   if (!c.users)
55     c.users = 1024;
56
57   if (!c.sessions)
58     c.sessions = 10 * 1024;
59
60   nm->rconfig = c;
61
62   nm->translations = c.sessions;
63   nm->translation_buckets = nat_calc_bihash_buckets (c.sessions);
64   nm->user_buckets = nat_calc_bihash_buckets (c.users);
65
66   // OBSOLETE
67
68   sm->static_mapping_only = c.static_mapping_only;
69   sm->static_mapping_connection_tracking = c.connection_tracking;
70   sm->out2in_dpo = c.out2in_dpo;
71   sm->forwarding_enabled = 0;
72   sm->mss_clamping = 0;
73   sm->pat = (!c.static_mapping_only ||
74              (c.static_mapping_only && c.connection_tracking));
75
76   sm->max_users_per_thread = c.users;
77   sm->max_translations_per_thread = c.sessions;
78   sm->translation_buckets = nat_calc_bihash_buckets (c.sessions);
79   sm->max_translations_per_user =
80     c.user_sessions ? c.user_sessions : sm->max_translations_per_thread;
81
82   sm->inside_vrf_id = c.inside_vrf;
83   sm->inside_fib_index = fib_table_find_or_create_and_lock (
84     FIB_PROTOCOL_IP4, c.inside_vrf, sm->fib_src_hi);
85
86   sm->outside_vrf_id = c.outside_vrf;
87   sm->outside_fib_index = fib_table_find_or_create_and_lock (
88     FIB_PROTOCOL_IP4, c.outside_vrf, sm->fib_src_hi);
89
90   sm->worker_in2out_cb = nat44_ei_get_in2out_worker_index;
91   sm->worker_out2in_cb = nat44_ei_get_out2in_worker_index;
92
93   sm->in2out_node_index = sm->ei_in2out_node_index;
94   sm->out2in_node_index = sm->ei_out2in_node_index;
95
96   sm->in2out_output_node_index = sm->ei_in2out_output_node_index;
97
98   if (sm->pat)
99     {
100       sm->icmp_match_in2out_cb = icmp_match_in2out_slow;
101       sm->icmp_match_out2in_cb = icmp_match_out2in_slow;
102     }
103   else
104     {
105       sm->icmp_match_in2out_cb = icmp_match_in2out_fast;
106       sm->icmp_match_out2in_cb = icmp_match_out2in_fast;
107     }
108
109   nat_reset_timeouts (&sm->timeouts);
110   nat44_ei_db_init (nm->translations, nm->translation_buckets,
111                     nm->user_buckets);
112   nat44_ei_set_alloc_default ();
113   nat_ha_enable ();
114
115   // TODO: function for reset counters
116   vlib_zero_simple_counter (&sm->total_users, 0);
117   vlib_zero_simple_counter (&sm->total_sessions, 0);
118   vlib_zero_simple_counter (&sm->user_limit_reached, 0);
119
120   sm->enabled = 1;
121
122   return 0;
123 }
124
125 int
126 nat44_ei_plugin_disable ()
127 {
128   nat44_ei_main_t *nm = &nat44_ei_main;
129   snat_main_t *sm = &snat_main;
130   snat_interface_t *i, *vec;
131   int error = 0;
132
133   // first unregister all nodes from interfaces
134   vec = vec_dup (sm->interfaces);
135   vec_foreach (i, vec)
136     {
137       if (nat_interface_is_inside (i))
138         error = snat_interface_add_del (i->sw_if_index, 1, 1);
139       if (nat_interface_is_outside (i))
140         error = snat_interface_add_del (i->sw_if_index, 0, 1);
141
142       if (error)
143         {
144           nat_log_err ("error occurred while removing interface %u",
145                        i->sw_if_index);
146         }
147     }
148   vec_free (vec);
149   sm->interfaces = 0;
150
151   vec = vec_dup (sm->output_feature_interfaces);
152   vec_foreach (i, vec)
153     {
154       if (nat_interface_is_inside (i))
155         error = snat_interface_add_del_output_feature (i->sw_if_index, 1, 1);
156       if (nat_interface_is_outside (i))
157         error = snat_interface_add_del_output_feature (i->sw_if_index, 0, 1);
158
159       if (error)
160         {
161           nat_log_err ("error occurred while removing interface %u",
162                        i->sw_if_index);
163         }
164     }
165   vec_free (vec);
166   sm->output_feature_interfaces = 0;
167
168   nat_ha_disable ();
169   nat44_ei_db_free ();
170
171   nat44_addresses_free (&sm->addresses);
172   nat44_addresses_free (&sm->twice_nat_addresses);
173
174   vec_free (sm->to_resolve);
175   vec_free (sm->auto_add_sw_if_indices);
176   vec_free (sm->auto_add_sw_if_indices_twice_nat);
177
178   sm->to_resolve = 0;
179   sm->auto_add_sw_if_indices = 0;
180   sm->auto_add_sw_if_indices_twice_nat = 0;
181
182   sm->forwarding_enabled = 0;
183
184   sm->enabled = 0;
185   clib_memset (&nm->rconfig, 0, sizeof (nm->rconfig));
186   clib_memset (&sm->rconfig, 0, sizeof (sm->rconfig));
187
188   return error;
189 }
190
191 void
192 nat44_ei_free_session_data (snat_main_t *sm, snat_session_t *s,
193                             u32 thread_index, u8 is_ha)
194 {
195   clib_bihash_kv_8_8_t kv;
196
197   init_nat_i2o_k (&kv, s);
198   if (clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0))
199     nat_elog_warn ("in2out key del failed");
200
201   init_nat_o2i_k (&kv, s);
202   if (clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0))
203     nat_elog_warn ("out2in key del failed");
204
205   if (!is_ha)
206     {
207       nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
208                                &s->in2out.addr, s->in2out.port,
209                                &s->out2in.addr, s->out2in.port, s->nat_proto);
210
211       nat_ipfix_logging_nat44_ses_delete (
212         thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
213         s->nat_proto, s->in2out.port, s->out2in.port, s->in2out.fib_index);
214
215       nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
216                    s->ext_host_port, s->nat_proto, s->out2in.fib_index,
217                    thread_index);
218     }
219
220   if (snat_is_session_static (s))
221     return;
222
223   snat_free_outside_address_and_port (sm->addresses, thread_index,
224                                       &s->out2in.addr, s->out2in.port,
225                                       s->nat_proto);
226 }
227
228 static_always_inline void
229 nat44_ei_user_del_sessions (snat_user_t *u, u32 thread_index)
230 {
231   dlist_elt_t *elt;
232   snat_session_t *s;
233
234   snat_main_t *sm = &snat_main;
235   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
236
237   // get head
238   elt =
239     pool_elt_at_index (tsm->list_pool, u->sessions_per_user_list_head_index);
240   // get first element
241   elt = pool_elt_at_index (tsm->list_pool, elt->next);
242
243   while (elt->value != ~0)
244     {
245       s = pool_elt_at_index (tsm->sessions, elt->value);
246       elt = pool_elt_at_index (tsm->list_pool, elt->next);
247
248       nat44_ei_free_session_data (sm, s, thread_index, 0);
249       nat44_delete_session (sm, s, thread_index);
250     }
251 }
252
253 int
254 nat44_ei_user_del (ip4_address_t *addr, u32 fib_index)
255 {
256   int rv = 1;
257
258   snat_main_t *sm = &snat_main;
259   snat_main_per_thread_data_t *tsm;
260
261   snat_user_key_t user_key;
262   clib_bihash_kv_8_8_t kv, value;
263
264   if (sm->endpoint_dependent)
265     return rv;
266
267   user_key.addr.as_u32 = addr->as_u32;
268   user_key.fib_index = fib_index;
269   kv.key = user_key.as_u64;
270
271   if (sm->num_workers > 1)
272     {
273       vec_foreach (tsm, sm->per_thread_data)
274         {
275           if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
276             {
277               nat44_ei_user_del_sessions (
278                 pool_elt_at_index (tsm->users, value.value),
279                 tsm->thread_index);
280               rv = 0;
281               break;
282             }
283         }
284     }
285   else
286     {
287       tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
288       if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
289         {
290           nat44_ei_user_del_sessions (
291             pool_elt_at_index (tsm->users, value.value), tsm->thread_index);
292           rv = 0;
293         }
294     }
295   return rv;
296 }
297
298 void
299 nat44_ei_static_mapping_del_sessions (snat_main_t *sm,
300                                       snat_main_per_thread_data_t *tsm,
301                                       snat_user_key_t u_key, int addr_only,
302                                       ip4_address_t e_addr, u16 e_port)
303 {
304   clib_bihash_kv_8_8_t kv, value;
305   kv.key = u_key.as_u64;
306   u64 user_index;
307   dlist_elt_t *head, *elt;
308   snat_user_t *u;
309   snat_session_t *s;
310   u32 elt_index, head_index, ses_index;
311
312   if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
313     {
314       user_index = value.value;
315       u = pool_elt_at_index (tsm->users, user_index);
316       if (u->nstaticsessions)
317         {
318           head_index = u->sessions_per_user_list_head_index;
319           head = pool_elt_at_index (tsm->list_pool, head_index);
320           elt_index = head->next;
321           elt = pool_elt_at_index (tsm->list_pool, elt_index);
322           ses_index = elt->value;
323           while (ses_index != ~0)
324             {
325               s = pool_elt_at_index (tsm->sessions, ses_index);
326               elt = pool_elt_at_index (tsm->list_pool, elt->next);
327               ses_index = elt->value;
328
329               if (!addr_only)
330                 {
331                   if ((s->out2in.addr.as_u32 != e_addr.as_u32) ||
332                       (s->out2in.port != e_port))
333                     continue;
334                 }
335
336               if (is_lb_session (s))
337                 continue;
338
339               if (!snat_is_session_static (s))
340                 continue;
341
342               nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
343               nat44_delete_session (sm, s, tsm - sm->per_thread_data);
344
345               if (!addr_only)
346                 break;
347             }
348         }
349     }
350 }
351
352 u32
353 nat44_ei_get_in2out_worker_index (ip4_header_t *ip0, u32 rx_fib_index0,
354                                   u8 is_output)
355 {
356   snat_main_t *sm = &snat_main;
357   u32 next_worker_index = 0;
358   u32 hash;
359
360   next_worker_index = sm->first_worker_index;
361   hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) +
362          (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >> 24);
363
364   if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
365     next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
366   else
367     next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
368
369   return next_worker_index;
370 }
371
372 u32
373 nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0,
374                                   u32 rx_fib_index0, u8 is_output)
375 {
376   snat_main_t *sm = &snat_main;
377   udp_header_t *udp;
378   u16 port;
379   clib_bihash_kv_8_8_t kv, value;
380   snat_static_mapping_t *m;
381   u32 proto;
382   u32 next_worker_index = 0;
383
384   /* first try static mappings without port */
385   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
386     {
387       init_nat_k (&kv, ip0->dst_address, 0, rx_fib_index0, 0);
388       if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
389                                    &value))
390         {
391           m = pool_elt_at_index (sm->static_mappings, value.value);
392           return m->workers[0];
393         }
394     }
395
396   proto = ip_proto_to_nat_proto (ip0->protocol);
397   udp = ip4_next_header (ip0);
398   port = udp->dst_port;
399
400   /* unknown protocol */
401   if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER))
402     {
403       /* use current thread */
404       return vlib_get_thread_index ();
405     }
406
407   if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_ICMP))
408     {
409       icmp46_header_t *icmp = (icmp46_header_t *) udp;
410       icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
411       if (!icmp_type_is_error_message (
412             vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
413         port = vnet_buffer (b)->ip.reass.l4_src_port;
414       else
415         {
416           /* if error message, then it's not fragmented and we can access it */
417           ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
418           proto = ip_proto_to_nat_proto (inner_ip->protocol);
419           void *l4_header = ip4_next_header (inner_ip);
420           switch (proto)
421             {
422             case NAT_PROTOCOL_ICMP:
423               icmp = (icmp46_header_t *) l4_header;
424               echo = (icmp_echo_header_t *) (icmp + 1);
425               port = echo->identifier;
426               break;
427             case NAT_PROTOCOL_UDP:
428             case NAT_PROTOCOL_TCP:
429               port = ((tcp_udp_header_t *) l4_header)->src_port;
430               break;
431             default:
432               return vlib_get_thread_index ();
433             }
434         }
435     }
436
437   /* try static mappings with port */
438   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
439     {
440       init_nat_k (&kv, ip0->dst_address, port, rx_fib_index0, proto);
441       if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
442                                    &value))
443         {
444           m = pool_elt_at_index (sm->static_mappings, value.value);
445           return m->workers[0];
446         }
447     }
448
449   /* worker by outside port */
450   next_worker_index = sm->first_worker_index;
451   next_worker_index +=
452     sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
453   return next_worker_index;
454 }
455
456 static int
457 nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index,
458                            u32 thread_index, nat_protocol_t proto,
459                            ip4_address_t *addr, u16 *port, u16 port_per_thread,
460                            u32 snat_thread_index)
461 {
462   int i;
463   snat_address_t *a, *ga = 0;
464   u32 portnum;
465
466   for (i = 0; i < vec_len (addresses); i++)
467     {
468       a = addresses + i;
469       switch (proto)
470         {
471 #define _(N, j, n, s)                                                         \
472   case NAT_PROTOCOL_##N:                                                      \
473     if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread)       \
474       {                                                                       \
475         if (a->fib_index == fib_index)                                        \
476           {                                                                   \
477             while (1)                                                         \
478               {                                                               \
479                 portnum = (port_per_thread * snat_thread_index) +             \
480                           snat_random_port (0, port_per_thread - 1) + 1024;   \
481                 if (a->busy_##n##_port_refcounts[portnum])                    \
482                   continue;                                                   \
483                 --a->busy_##n##_port_refcounts[portnum];                      \
484                 a->busy_##n##_ports_per_thread[thread_index]++;               \
485                 a->busy_##n##_ports++;                                        \
486                 *addr = a->addr;                                              \
487                 *port = clib_host_to_net_u16 (portnum);                       \
488                 return 0;                                                     \
489               }                                                               \
490           }                                                                   \
491         else if (a->fib_index == ~0)                                          \
492           {                                                                   \
493             ga = a;                                                           \
494           }                                                                   \
495       }                                                                       \
496     break;
497           foreach_nat_protocol
498 #undef _
499             default : nat_elog_info ("unknown protocol");
500           return 1;
501         }
502     }
503
504   if (ga)
505     {
506       a = ga;
507       switch (proto)
508         {
509 #define _(N, j, n, s)                                                         \
510   case NAT_PROTOCOL_##N:                                                      \
511     while (1)                                                                 \
512       {                                                                       \
513         portnum = (port_per_thread * snat_thread_index) +                     \
514                   snat_random_port (0, port_per_thread - 1) + 1024;           \
515         if (a->busy_##n##_port_refcounts[portnum])                            \
516           continue;                                                           \
517         ++a->busy_##n##_port_refcounts[portnum];                              \
518         a->busy_##n##_ports_per_thread[thread_index]++;                       \
519         a->busy_##n##_ports++;                                                \
520         *addr = a->addr;                                                      \
521         *port = clib_host_to_net_u16 (portnum);                               \
522         return 0;                                                             \
523       }
524           break;
525           foreach_nat_protocol
526 #undef _
527             default : nat_elog_info ("unknown protocol");
528           return 1;
529         }
530     }
531
532   /* Totally out of translations to use... */
533   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
534   return 1;
535 }
536
537 static int
538 nat44_ei_alloc_range_cb (snat_address_t *addresses, u32 fib_index,
539                          u32 thread_index, nat_protocol_t proto,
540                          ip4_address_t *addr, u16 *port, u16 port_per_thread,
541                          u32 snat_thread_index)
542 {
543   snat_main_t *sm = &snat_main;
544   snat_address_t *a = addresses;
545   u16 portnum, ports;
546
547   ports = sm->end_port - sm->start_port + 1;
548
549   if (!vec_len (addresses))
550     goto exhausted;
551
552   switch (proto)
553     {
554 #define _(N, i, n, s)                                                         \
555   case NAT_PROTOCOL_##N:                                                      \
556     if (a->busy_##n##_ports < ports)                                          \
557       {                                                                       \
558         while (1)                                                             \
559           {                                                                   \
560             portnum = snat_random_port (sm->start_port, sm->end_port);        \
561             if (a->busy_##n##_port_refcounts[portnum])                        \
562               continue;                                                       \
563             ++a->busy_##n##_port_refcounts[portnum];                          \
564             a->busy_##n##_ports++;                                            \
565             *addr = a->addr;                                                  \
566             *port = clib_host_to_net_u16 (portnum);                           \
567             return 0;                                                         \
568           }                                                                   \
569       }                                                                       \
570     break;
571       foreach_nat_protocol
572 #undef _
573         default : nat_elog_info ("unknown protocol");
574       return 1;
575     }
576
577 exhausted:
578   /* Totally out of translations to use... */
579   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
580   return 1;
581 }
582
583 static int
584 nat44_ei_alloc_mape_cb (snat_address_t *addresses, u32 fib_index,
585                         u32 thread_index, nat_protocol_t proto,
586                         ip4_address_t *addr, u16 *port, u16 port_per_thread,
587                         u32 snat_thread_index)
588 {
589   snat_main_t *sm = &snat_main;
590   snat_address_t *a = addresses;
591   u16 m, ports, portnum, A, j;
592   m = 16 - (sm->psid_offset + sm->psid_length);
593   ports = (1 << (16 - sm->psid_length)) - (1 << m);
594
595   if (!vec_len (addresses))
596     goto exhausted;
597
598   switch (proto)
599     {
600 #define _(N, i, n, s)                                                         \
601   case NAT_PROTOCOL_##N:                                                      \
602     if (a->busy_##n##_ports < ports)                                          \
603       {                                                                       \
604         while (1)                                                             \
605           {                                                                   \
606             A = snat_random_port (1, pow2_mask (sm->psid_offset));            \
607             j = snat_random_port (0, pow2_mask (m));                          \
608             portnum = A | (sm->psid << sm->psid_offset) | (j << (16 - m));    \
609             if (a->busy_##n##_port_refcounts[portnum])                        \
610               continue;                                                       \
611             ++a->busy_##n##_port_refcounts[portnum];                          \
612             a->busy_##n##_ports++;                                            \
613             *addr = a->addr;                                                  \
614             *port = clib_host_to_net_u16 (portnum);                           \
615             return 0;                                                         \
616           }                                                                   \
617       }                                                                       \
618     break;
619       foreach_nat_protocol
620 #undef _
621         default : nat_elog_info ("unknown protocol");
622       return 1;
623     }
624
625 exhausted:
626   /* Totally out of translations to use... */
627   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
628   return 1;
629 }
630
631 void
632 nat44_ei_set_alloc_default ()
633 {
634   snat_main_t *sm = &snat_main;
635
636   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
637   sm->alloc_addr_and_port = nat44_ei_alloc_default_cb;
638 }
639
640 void
641 nat44_ei_set_alloc_range (u16 start_port, u16 end_port)
642 {
643   snat_main_t *sm = &snat_main;
644
645   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE;
646   sm->alloc_addr_and_port = nat44_ei_alloc_range_cb;
647   sm->start_port = start_port;
648   sm->end_port = end_port;
649 }
650
651 void
652 nat44_ei_set_alloc_mape (u16 psid, u16 psid_offset, u16 psid_length)
653 {
654   snat_main_t *sm = &snat_main;
655
656   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE;
657   sm->alloc_addr_and_port = nat44_ei_alloc_mape_cb;
658   sm->psid = psid;
659   sm->psid_offset = psid_offset;
660   sm->psid_length = psid_length;
661 }
662
663 static void
664 nat44_ei_add_static_mapping_when_resolved (ip4_address_t l_addr, u16 l_port,
665                                            u16 e_port, nat_protocol_t proto,
666                                            u32 sw_if_index, u32 vrf_id,
667                                            int addr_only, int identity_nat,
668                                            u8 *tag)
669 {
670   snat_main_t *sm = &snat_main;
671   snat_static_map_resolve_t *rp;
672
673   vec_add2 (sm->to_resolve, rp, 1);
674   clib_memset (rp, 0, sizeof (*rp));
675
676   rp->l_addr.as_u32 = l_addr.as_u32;
677   rp->l_port = l_port;
678   rp->e_port = e_port;
679   rp->sw_if_index = sw_if_index;
680   rp->vrf_id = vrf_id;
681   rp->proto = proto;
682   rp->addr_only = addr_only;
683   rp->identity_nat = identity_nat;
684   rp->tag = vec_dup (tag);
685 }
686
687 int
688 nat44_ei_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
689                       nat_protocol_t proto, u32 vrf_id, int is_in)
690 {
691   snat_main_per_thread_data_t *tsm;
692   clib_bihash_kv_8_8_t kv, value;
693   ip4_header_t ip;
694   u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
695   snat_session_t *s;
696   clib_bihash_8_8_t *t;
697
698   if (sm->endpoint_dependent)
699     return VNET_API_ERROR_UNSUPPORTED;
700
701   ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32;
702   if (sm->num_workers > 1)
703     tsm = vec_elt_at_index (sm->per_thread_data,
704                             sm->worker_in2out_cb (&ip, fib_index, 0));
705   else
706     tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
707
708   init_nat_k (&kv, *addr, port, fib_index, proto);
709   t = is_in ? &sm->in2out : &sm->out2in;
710   if (!clib_bihash_search_8_8 (t, &kv, &value))
711     {
712       if (pool_is_free_index (tsm->sessions, value.value))
713         return VNET_API_ERROR_UNSPECIFIED;
714
715       s = pool_elt_at_index (tsm->sessions, value.value);
716       nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
717       nat44_delete_session (sm, s, tsm - sm->per_thread_data);
718       return 0;
719     }
720
721   return VNET_API_ERROR_NO_SUCH_ENTRY;
722 }
723
724 int
725 nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
726                                  u16 l_port, u16 e_port, nat_protocol_t proto,
727                                  u32 sw_if_index, u32 vrf_id, u8 addr_only,
728                                  u8 identity_nat, u8 *tag, u8 is_add)
729 {
730   snat_main_t *sm = &snat_main;
731   snat_static_mapping_t *m = 0;
732   clib_bihash_kv_8_8_t kv, value;
733   snat_address_t *a = 0;
734   u32 fib_index = ~0;
735   snat_interface_t *interface;
736   snat_main_per_thread_data_t *tsm;
737   snat_user_key_t u_key;
738   snat_user_t *u;
739   dlist_elt_t *head, *elt;
740   u32 elt_index, head_index;
741   u32 ses_index;
742   u64 user_index;
743   snat_session_t *s;
744   snat_static_map_resolve_t *rp, *rp_match = 0;
745   nat44_lb_addr_port_t *local;
746   u32 find = ~0;
747   int i;
748
749   if (sw_if_index != ~0)
750     {
751       ip4_address_t *first_int_addr;
752
753       for (i = 0; i < vec_len (sm->to_resolve); i++)
754         {
755           rp = sm->to_resolve + i;
756           if (rp->sw_if_index != sw_if_index ||
757               rp->l_addr.as_u32 != l_addr.as_u32 || rp->vrf_id != vrf_id ||
758               rp->addr_only != addr_only)
759             continue;
760
761           if (!addr_only)
762             {
763               if ((rp->l_port != l_port && rp->e_port != e_port) ||
764                   rp->proto != proto)
765                 continue;
766             }
767
768           rp_match = rp;
769           break;
770         }
771
772       /* Might be already set... */
773       first_int_addr = ip4_interface_first_address (
774         sm->ip4_main, sw_if_index, 0 /* just want the address */);
775
776       if (is_add)
777         {
778           if (rp_match)
779             return VNET_API_ERROR_VALUE_EXIST;
780
781           nat44_ei_add_static_mapping_when_resolved (
782             l_addr, l_port, e_port, proto, sw_if_index, vrf_id, addr_only,
783             identity_nat, tag);
784
785           /* DHCP resolution required? */
786           if (!first_int_addr)
787             return 0;
788
789           e_addr.as_u32 = first_int_addr->as_u32;
790           /* Identity mapping? */
791           if (l_addr.as_u32 == 0)
792             l_addr.as_u32 = e_addr.as_u32;
793         }
794       else
795         {
796           if (!rp_match)
797             return VNET_API_ERROR_NO_SUCH_ENTRY;
798
799           vec_del1 (sm->to_resolve, i);
800
801           if (!first_int_addr)
802             return 0;
803
804           e_addr.as_u32 = first_int_addr->as_u32;
805           /* Identity mapping? */
806           if (l_addr.as_u32 == 0)
807             l_addr.as_u32 = e_addr.as_u32;
808         }
809     }
810
811   init_nat_k (&kv, e_addr, addr_only ? 0 : e_port, 0, addr_only ? 0 : proto);
812   if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
813     m = pool_elt_at_index (sm->static_mappings, value.value);
814
815   if (is_add)
816     {
817       if (m)
818         {
819           // identity mapping for second vrf
820           if (is_identity_static_mapping (m))
821             {
822               pool_foreach (local, m->locals)
823                 {
824                   if (local->vrf_id == vrf_id)
825                     return VNET_API_ERROR_VALUE_EXIST;
826                 }
827               pool_get (m->locals, local);
828               local->vrf_id = vrf_id;
829               local->fib_index = fib_table_find_or_create_and_lock (
830                 FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
831               init_nat_kv (&kv, m->local_addr, m->local_port, local->fib_index,
832                            m->proto, 0, m - sm->static_mappings);
833               clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
834               return 0;
835             }
836           return VNET_API_ERROR_VALUE_EXIST;
837         }
838
839       /* Convert VRF id to FIB index */
840       if (vrf_id != ~0)
841         {
842           fib_index = fib_table_find_or_create_and_lock (
843             FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
844         }
845       /* If not specified use inside VRF id from NAT44 plugin config */
846       else
847         {
848           fib_index = sm->inside_fib_index;
849           vrf_id = sm->inside_vrf_id;
850           fib_table_lock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
851         }
852
853       if (!identity_nat)
854         {
855           init_nat_k (&kv, l_addr, addr_only ? 0 : l_port, fib_index,
856                       addr_only ? 0 : proto);
857           if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv,
858                                        &value))
859             return VNET_API_ERROR_VALUE_EXIST;
860         }
861
862       /* Find external address in allocated addresses and reserve port for
863          address and port pair mapping when dynamic translations enabled */
864       if (!(addr_only || sm->static_mapping_only))
865         {
866           for (i = 0; i < vec_len (sm->addresses); i++)
867             {
868               if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
869                 {
870                   a = sm->addresses + i;
871                   /* External port must be unused */
872                   switch (proto)
873                     {
874 #define _(N, j, n, s)                                                         \
875   case NAT_PROTOCOL_##N:                                                      \
876     if (a->busy_##n##_port_refcounts[e_port])                                 \
877       return VNET_API_ERROR_INVALID_VALUE;                                    \
878     ++a->busy_##n##_port_refcounts[e_port];                                   \
879     if (e_port > 1024)                                                        \
880       {                                                                       \
881         a->busy_##n##_ports++;                                                \
882         a->busy_##n##_ports_per_thread[get_thread_idx_by_port (e_port)]++;    \
883       }                                                                       \
884     break;
885                       foreach_nat_protocol
886 #undef _
887                         default : nat_elog_info ("unknown protocol");
888                       return VNET_API_ERROR_INVALID_VALUE_2;
889                     }
890                   break;
891                 }
892             }
893           /* External address must be allocated */
894           if (!a && (l_addr.as_u32 != e_addr.as_u32))
895             {
896               if (sw_if_index != ~0)
897                 {
898                   for (i = 0; i < vec_len (sm->to_resolve); i++)
899                     {
900                       rp = sm->to_resolve + i;
901                       if (rp->addr_only)
902                         continue;
903                       if (rp->sw_if_index != sw_if_index &&
904                           rp->l_addr.as_u32 != l_addr.as_u32 &&
905                           rp->vrf_id != vrf_id && rp->l_port != l_port &&
906                           rp->e_port != e_port && rp->proto != proto)
907                         continue;
908
909                       vec_del1 (sm->to_resolve, i);
910                       break;
911                     }
912                 }
913               return VNET_API_ERROR_NO_SUCH_ENTRY;
914             }
915         }
916
917       pool_get (sm->static_mappings, m);
918       clib_memset (m, 0, sizeof (*m));
919       m->tag = vec_dup (tag);
920       m->local_addr = l_addr;
921       m->external_addr = e_addr;
922
923       if (addr_only)
924         m->flags |= NAT_STATIC_MAPPING_FLAG_ADDR_ONLY;
925       else
926         {
927           m->local_port = l_port;
928           m->external_port = e_port;
929           m->proto = proto;
930         }
931
932       if (identity_nat)
933         {
934           m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT;
935           pool_get (m->locals, local);
936           local->vrf_id = vrf_id;
937           local->fib_index = fib_index;
938         }
939       else
940         {
941           m->vrf_id = vrf_id;
942           m->fib_index = fib_index;
943         }
944
945       if (sm->num_workers > 1)
946         {
947           ip4_header_t ip = {
948             .src_address = m->local_addr,
949           };
950           vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index, 0));
951           tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]);
952         }
953       else
954         tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
955
956       init_nat_kv (&kv, m->local_addr, m->local_port, fib_index, m->proto, 0,
957                    m - sm->static_mappings);
958       clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
959
960       init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0,
961                    m - sm->static_mappings);
962       clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1);
963
964       /* Delete dynamic sessions matching local address (+ local port) */
965       // TODO: based on type of NAT EI/ED
966       if (!(sm->static_mapping_only))
967         {
968           u_key.addr = m->local_addr;
969           u_key.fib_index = m->fib_index;
970           kv.key = u_key.as_u64;
971           if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
972             {
973               user_index = value.value;
974               u = pool_elt_at_index (tsm->users, user_index);
975               if (u->nsessions)
976                 {
977                   head_index = u->sessions_per_user_list_head_index;
978                   head = pool_elt_at_index (tsm->list_pool, head_index);
979                   elt_index = head->next;
980                   elt = pool_elt_at_index (tsm->list_pool, elt_index);
981                   ses_index = elt->value;
982                   while (ses_index != ~0)
983                     {
984                       s = pool_elt_at_index (tsm->sessions, ses_index);
985                       elt = pool_elt_at_index (tsm->list_pool, elt->next);
986                       ses_index = elt->value;
987
988                       if (snat_is_session_static (s))
989                         continue;
990
991                       if (!addr_only && s->in2out.port != m->local_port)
992                         continue;
993
994                       nat_free_session_data (sm, s, tsm - sm->per_thread_data,
995                                              0);
996                       nat44_delete_session (sm, s, tsm - sm->per_thread_data);
997
998                       if (!addr_only)
999                         break;
1000                     }
1001                 }
1002             }
1003         }
1004     }
1005   else
1006     {
1007       if (!m)
1008         {
1009           if (sw_if_index != ~0)
1010             return 0;
1011           else
1012             return VNET_API_ERROR_NO_SUCH_ENTRY;
1013         }
1014
1015       if (identity_nat)
1016         {
1017           if (vrf_id == ~0)
1018             vrf_id = sm->inside_vrf_id;
1019
1020           pool_foreach (local, m->locals)
1021             {
1022               if (local->vrf_id == vrf_id)
1023                 find = local - m->locals;
1024             }
1025           if (find == ~0)
1026             return VNET_API_ERROR_NO_SUCH_ENTRY;
1027
1028           local = pool_elt_at_index (m->locals, find);
1029           fib_index = local->fib_index;
1030           pool_put (m->locals, local);
1031         }
1032       else
1033         fib_index = m->fib_index;
1034
1035       /* Free external address port */
1036       if (!(addr_only || sm->static_mapping_only))
1037         {
1038           for (i = 0; i < vec_len (sm->addresses); i++)
1039             {
1040               if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
1041                 {
1042                   a = sm->addresses + i;
1043                   switch (proto)
1044                     {
1045 #define _(N, j, n, s)                                                         \
1046   case NAT_PROTOCOL_##N:                                                      \
1047     --a->busy_##n##_port_refcounts[e_port];                                   \
1048     if (e_port > 1024)                                                        \
1049       {                                                                       \
1050         a->busy_##n##_ports--;                                                \
1051         a->busy_##n##_ports_per_thread[get_thread_idx_by_port (e_port)]--;    \
1052       }                                                                       \
1053     break;
1054                       foreach_nat_protocol
1055 #undef _
1056                         default : return VNET_API_ERROR_INVALID_VALUE_2;
1057                     }
1058                   break;
1059                 }
1060             }
1061         }
1062
1063       if (sm->num_workers > 1)
1064         tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]);
1065       else
1066         tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
1067
1068       init_nat_k (&kv, m->local_addr, m->local_port, fib_index, m->proto);
1069       clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0);
1070
1071       /* Delete session(s) for static mapping if exist */
1072       if (!(sm->static_mapping_only) ||
1073           (sm->static_mapping_only && sm->static_mapping_connection_tracking))
1074         {
1075           u_key.addr = m->local_addr;
1076           u_key.fib_index = fib_index;
1077           kv.key = u_key.as_u64;
1078           nat44_ei_static_mapping_del_sessions (sm, tsm, u_key, addr_only,
1079                                                 e_addr, e_port);
1080         }
1081
1082       fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
1083       if (pool_elts (m->locals))
1084         return 0;
1085
1086       init_nat_k (&kv, m->external_addr, m->external_port, 0, m->proto);
1087       clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0);
1088
1089       vec_free (m->tag);
1090       vec_free (m->workers);
1091       /* Delete static mapping from pool */
1092       pool_put (sm->static_mappings, m);
1093     }
1094
1095   if (!addr_only || (l_addr.as_u32 == e_addr.as_u32))
1096     return 0;
1097
1098   /* Add/delete external address to FIB */
1099   pool_foreach (interface, sm->interfaces)
1100     {
1101       if (nat_interface_is_inside (interface) || sm->out2in_dpo)
1102         continue;
1103
1104       snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, is_add);
1105       break;
1106     }
1107   pool_foreach (interface, sm->output_feature_interfaces)
1108     {
1109       if (nat_interface_is_inside (interface) || sm->out2in_dpo)
1110         continue;
1111
1112       snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, is_add);
1113       break;
1114     }
1115   return 0;
1116 }
1117
1118 int
1119 nat44_ei_static_mapping_match (ip4_address_t match_addr, u16 match_port,
1120                                u32 match_fib_index,
1121                                nat_protocol_t match_protocol,
1122                                ip4_address_t *mapping_addr, u16 *mapping_port,
1123                                u32 *mapping_fib_index, u8 by_external,
1124                                u8 *is_addr_only, u8 *is_identity_nat)
1125 {
1126   snat_main_t *sm = &snat_main;
1127   clib_bihash_kv_8_8_t kv, value;
1128   snat_static_mapping_t *m;
1129   u16 port;
1130
1131   if (by_external)
1132     {
1133       init_nat_k (&kv, match_addr, match_port, 0, match_protocol);
1134       if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
1135                                   &value))
1136         {
1137           /* Try address only mapping */
1138           init_nat_k (&kv, match_addr, 0, 0, 0);
1139           if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
1140                                       &value))
1141             return 1;
1142         }
1143       m = pool_elt_at_index (sm->static_mappings, value.value);
1144
1145       *mapping_fib_index = m->fib_index;
1146       *mapping_addr = m->local_addr;
1147       port = m->local_port;
1148     }
1149   else
1150     {
1151       init_nat_k (&kv, match_addr, match_port, match_fib_index,
1152                   match_protocol);
1153       if (clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value))
1154         {
1155           /* Try address only mapping */
1156           init_nat_k (&kv, match_addr, 0, match_fib_index, 0);
1157           if (clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv,
1158                                       &value))
1159             return 1;
1160         }
1161       m = pool_elt_at_index (sm->static_mappings, value.value);
1162
1163       *mapping_fib_index = sm->outside_fib_index;
1164       *mapping_addr = m->external_addr;
1165       port = m->external_port;
1166     }
1167
1168   /* Address only mapping doesn't change port */
1169   if (is_addr_only_static_mapping (m))
1170     *mapping_port = match_port;
1171   else
1172     *mapping_port = port;
1173
1174   if (PREDICT_FALSE (is_addr_only != 0))
1175     *is_addr_only = is_addr_only_static_mapping (m);
1176
1177   if (PREDICT_FALSE (is_identity_nat != 0))
1178     *is_identity_nat = is_identity_static_mapping (m);
1179
1180   return 0;
1181 }
1182
1183 static void
1184 nat44_ei_worker_db_free (snat_main_per_thread_data_t *tsm)
1185 {
1186   pool_free (tsm->list_pool);
1187   pool_free (tsm->lru_pool);
1188   pool_free (tsm->sessions);
1189   pool_free (tsm->users);
1190
1191   clib_bihash_free_8_8 (&tsm->user_hash);
1192 }
1193
1194 static void
1195 nat44_ei_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations,
1196                          u32 translation_buckets, u32 user_buckets)
1197 {
1198   dlist_elt_t *head;
1199
1200   pool_alloc (tsm->list_pool, translations);
1201   pool_alloc (tsm->lru_pool, translations);
1202   pool_alloc (tsm->sessions, translations);
1203
1204   clib_bihash_init_8_8 (&tsm->user_hash, "users", user_buckets, 0);
1205
1206   clib_bihash_set_kvp_format_fn_8_8 (&tsm->user_hash, format_user_kvp);
1207
1208   pool_get (tsm->lru_pool, head);
1209   tsm->tcp_trans_lru_head_index = head - tsm->lru_pool;
1210   clib_dlist_init (tsm->lru_pool, tsm->tcp_trans_lru_head_index);
1211
1212   pool_get (tsm->lru_pool, head);
1213   tsm->tcp_estab_lru_head_index = head - tsm->lru_pool;
1214   clib_dlist_init (tsm->lru_pool, tsm->tcp_estab_lru_head_index);
1215
1216   pool_get (tsm->lru_pool, head);
1217   tsm->udp_lru_head_index = head - tsm->lru_pool;
1218   clib_dlist_init (tsm->lru_pool, tsm->udp_lru_head_index);
1219
1220   pool_get (tsm->lru_pool, head);
1221   tsm->icmp_lru_head_index = head - tsm->lru_pool;
1222   clib_dlist_init (tsm->lru_pool, tsm->icmp_lru_head_index);
1223
1224   pool_get (tsm->lru_pool, head);
1225   tsm->unk_proto_lru_head_index = head - tsm->lru_pool;
1226   clib_dlist_init (tsm->lru_pool, tsm->unk_proto_lru_head_index);
1227 }
1228
1229 static void
1230 nat44_ei_db_free ()
1231 {
1232   snat_main_t *sm = &snat_main;
1233   snat_main_per_thread_data_t *tsm;
1234
1235   pool_free (sm->static_mappings);
1236   clib_bihash_free_8_8 (&sm->static_mapping_by_local);
1237   clib_bihash_free_8_8 (&sm->static_mapping_by_external);
1238
1239   if (sm->pat)
1240     {
1241       clib_bihash_free_8_8 (&sm->in2out);
1242       clib_bihash_free_8_8 (&sm->out2in);
1243       vec_foreach (tsm, sm->per_thread_data)
1244         {
1245           nat44_ei_worker_db_free (tsm);
1246         }
1247     }
1248 }
1249
1250 static void
1251 nat44_ei_db_init (u32 translations, u32 translation_buckets, u32 user_buckets)
1252 {
1253   snat_main_t *sm = &snat_main;
1254   snat_main_per_thread_data_t *tsm;
1255
1256   u32 static_mapping_buckets = 1024;
1257   u32 static_mapping_memory_size = 64 << 20;
1258
1259   clib_bihash_init_8_8 (&sm->static_mapping_by_local,
1260                         "static_mapping_by_local", static_mapping_buckets,
1261                         static_mapping_memory_size);
1262   clib_bihash_init_8_8 (&sm->static_mapping_by_external,
1263                         "static_mapping_by_external", static_mapping_buckets,
1264                         static_mapping_memory_size);
1265   clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_local,
1266                                      format_static_mapping_kvp);
1267   clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_external,
1268                                      format_static_mapping_kvp);
1269
1270   if (sm->pat)
1271     {
1272       clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets, 0);
1273       clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets, 0);
1274       clib_bihash_set_kvp_format_fn_8_8 (&sm->in2out, format_session_kvp);
1275       clib_bihash_set_kvp_format_fn_8_8 (&sm->out2in, format_session_kvp);
1276       vec_foreach (tsm, sm->per_thread_data)
1277         {
1278           nat44_ei_worker_db_init (tsm, translations, translation_buckets,
1279                                    user_buckets);
1280         }
1281     }
1282 }
1283
1284 void
1285 nat44_ei_sessions_clear ()
1286 {
1287   nat44_ei_main_t *nm = &nat44_ei_main;
1288
1289   snat_main_per_thread_data_t *tsm;
1290   snat_main_t *sm = &snat_main;
1291
1292   if (sm->pat)
1293     {
1294       clib_bihash_free_8_8 (&sm->in2out);
1295       clib_bihash_free_8_8 (&sm->out2in);
1296       clib_bihash_init_8_8 (&sm->in2out, "in2out", nm->translation_buckets, 0);
1297       clib_bihash_init_8_8 (&sm->out2in, "out2in", nm->translation_buckets, 0);
1298       clib_bihash_set_kvp_format_fn_8_8 (&sm->in2out, format_session_kvp);
1299       clib_bihash_set_kvp_format_fn_8_8 (&sm->out2in, format_session_kvp);
1300       vec_foreach (tsm, sm->per_thread_data)
1301         {
1302           nat44_ei_worker_db_free (tsm);
1303           nat44_ei_worker_db_init (tsm, nm->translations,
1304                                    nm->translation_buckets, nm->user_buckets);
1305         }
1306     }
1307
1308   // TODO: function for reset counters
1309   vlib_zero_simple_counter (&sm->total_users, 0);
1310   vlib_zero_simple_counter (&sm->total_sessions, 0);
1311   vlib_zero_simple_counter (&sm->user_limit_reached, 0);
1312 }
1313
1314 /*
1315  * fd.io coding-style-patch-verification: ON
1316  *
1317  * Local Variables:
1318  * eval: (c-set-style "gnu")
1319  * End:
1320  */