VPP-459: SNAT dump API for in and out interfaces
[vpp.git] / plugins / snat-plugin / snat / snat.c
1 /*
2  * snat.c - simple nat plugin
3  *
4  * Copyright (c) 2016 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,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <vlibapi/api.h>
21 #include <snat/snat.h>
22
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25 #include <vlibsocket/api.h>
26
27 snat_main_t snat_main;
28
29 /* define message IDs */
30 #include <snat/snat_msg_enum.h>
31
32 /* define message structures */
33 #define vl_typedefs
34 #include <snat/snat_all_api_h.h> 
35 #undef vl_typedefs
36
37 /* define generated endian-swappers */
38 #define vl_endianfun
39 #include <snat/snat_all_api_h.h> 
40 #undef vl_endianfun
41
42 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
43
44 /* Get the API version number */
45 #define vl_api_version(n,v) static u32 api_version=(v);
46 #include <snat/snat_all_api_h.h>
47 #undef vl_api_version
48
49 /* Macro to finish up custom dump fns */
50 #define FINISH                                  \
51     vec_add1 (s, 0);                            \
52     vl_print (handle, (char *)s);               \
53     vec_free (s);                               \
54     return handle;
55
56 /* 
57  * A handy macro to set up a message reply.
58  * Assumes that the following variables are available:
59  * mp - pointer to request message
60  * rmp - pointer to reply message type
61  * rv - return value
62  */
63
64 #define REPLY_MACRO(t)                                          \
65 do {                                                            \
66     unix_shared_memory_queue_t * q =                            \
67     vl_api_client_index_to_input_queue (mp->client_index);      \
68     if (!q)                                                     \
69         return;                                                 \
70                                                                 \
71     rmp = vl_msg_api_alloc (sizeof (*rmp));                     \
72     rmp->_vl_msg_id = ntohs((t)+sm->msg_id_base);               \
73     rmp->context = mp->context;                                 \
74     rmp->retval = ntohl(rv);                                    \
75                                                                 \
76     vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
77 } while(0);
78
79 #define REPLY_MACRO2(t, body)                                   \
80 do {                                                            \
81     unix_shared_memory_queue_t * q =                            \
82     vl_api_client_index_to_input_queue (mp->client_index);      \
83     if (!q)                                                     \
84         return;                                                 \
85                                                                 \
86     rmp = vl_msg_api_alloc (sizeof (*rmp));                     \
87     rmp->_vl_msg_id = ntohs((t)+sm->msg_id_base);               \
88     rmp->context = mp->context;                                 \
89     rmp->retval = ntohl(rv);                                    \
90     do {body;} while (0);                                       \
91     vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
92 } while(0);
93
94
95 /* Hook up input features */
96 VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_in2out, static) = {
97   .node_name = "snat-in2out",
98   .runs_before = (char *[]){"snat-out2in", 0},
99   .feature_index = &snat_main.rx_feature_in2out,
100 };
101 VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_out2in, static) = {
102   .node_name = "snat-out2in",
103   .runs_before = (char *[]){"ip4-lookup", 0},
104   .feature_index = &snat_main.rx_feature_out2in,
105 };
106 VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_in2out_fast, static) = {
107   .node_name = "snat-in2out-fast",
108   .runs_before = (char *[]){"snat-out2in-fast", 0},
109   .feature_index = &snat_main.rx_feature_in2out_fast,
110 };
111 VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
112   .node_name = "snat-out2in-fast",
113   .runs_before = (char *[]){"ip4-lookup", 0},
114   .feature_index = &snat_main.rx_feature_out2in_fast,
115 };
116
117
118 /* 
119  * This routine exists to convince the vlib plugin framework that
120  * we haven't accidentally copied a random .dll into the plugin directory.
121  *
122  * Also collects global variable pointers passed from the vpp engine
123  */
124
125 clib_error_t * 
126 vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
127                       int from_early_init)
128 {
129   snat_main_t * sm = &snat_main;
130   clib_error_t * error = 0;
131
132   sm->vlib_main = vm;
133   sm->vnet_main = h->vnet_main;
134   sm->ethernet_main = h->ethernet_main;
135
136   return error;
137 }
138
139 /*$$$$$ move to an installed header file */
140 #if (1 || CLIB_DEBUG > 0)       /* "trust, but verify" */
141
142 #define VALIDATE_SW_IF_INDEX(mp)                                \
143  do { u32 __sw_if_index = ntohl(mp->sw_if_index);               \
144     vnet_main_t *__vnm = vnet_get_main();                       \
145     if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
146                            __sw_if_index)) {                    \
147         rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;                \
148         goto bad_sw_if_index;                                   \
149     }                                                           \
150 } while(0);
151
152 #define BAD_SW_IF_INDEX_LABEL                   \
153 do {                                            \
154 bad_sw_if_index:                                \
155     ;                                           \
156 } while (0);
157
158 #define VALIDATE_RX_SW_IF_INDEX(mp)                             \
159  do { u32 __rx_sw_if_index = ntohl(mp->rx_sw_if_index);         \
160     vnet_main_t *__vnm = vnet_get_main();                       \
161     if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
162                            __rx_sw_if_index)) {                 \
163         rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;                \
164         goto bad_rx_sw_if_index;                                \
165     }                                                           \
166 } while(0);
167
168 #define BAD_RX_SW_IF_INDEX_LABEL                \
169 do {                                            \
170 bad_rx_sw_if_index:                             \
171     ;                                           \
172 } while (0);
173
174 #define VALIDATE_TX_SW_IF_INDEX(mp)                             \
175  do { u32 __tx_sw_if_index = ntohl(mp->tx_sw_if_index);         \
176     vnet_main_t *__vnm = vnet_get_main();                       \
177     if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
178                            __tx_sw_if_index)) {                 \
179         rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;                \
180         goto bad_tx_sw_if_index;                                \
181     }                                                           \
182 } while(0);
183
184 #define BAD_TX_SW_IF_INDEX_LABEL                \
185 do {                                            \
186 bad_tx_sw_if_index:                             \
187     ;                                           \
188 } while (0);
189
190 #else
191
192 #define VALIDATE_SW_IF_INDEX(mp)
193 #define BAD_SW_IF_INDEX_LABEL
194 #define VALIDATE_RX_SW_IF_INDEX(mp)
195 #define BAD_RX_SW_IF_INDEX_LABEL
196 #define VALIDATE_TX_SW_IF_INDEX(mp)
197 #define BAD_TX_SW_IF_INDEX_LABEL
198
199 #endif  /* CLIB_DEBUG > 0 */
200
201 void snat_add_address (snat_main_t *sm, ip4_address_t *addr)
202 {
203   snat_address_t * ap;
204
205   vec_add2 (sm->addresses, ap, 1);
206   ap->addr = *addr;
207
208 }
209
210 static int is_snat_address_used_in_static_mapping (snat_main_t *sm,
211                                                    ip4_address_t addr)
212 {
213   snat_static_mapping_t *m;
214   pool_foreach (m, sm->static_mappings,
215   ({
216       if (m->external_addr.as_u32 == addr.as_u32)
217         return 1;
218   }));
219
220   return 0;
221 }
222
223 int snat_del_address (snat_main_t *sm, ip4_address_t addr)
224 {
225   clib_warning("%U", format_ip4_address, &addr);
226   snat_address_t *a = 0;
227   snat_session_t *ses;
228   u32 *ses_to_be_removed = 0, *ses_index;
229   clib_bihash_kv_8_8_t kv, value;
230   snat_user_key_t user_key;
231   snat_user_t *u;
232
233   int i;
234
235   /* Find SNAT address */
236   for (i=0; i < vec_len (sm->addresses); i++)
237     {
238       if (sm->addresses[i].addr.as_u32 == addr.as_u32)
239         {
240           a = sm->addresses + i;
241           break;
242         }
243     }
244   if (!a)
245     return VNET_API_ERROR_NO_SUCH_ENTRY;
246
247   /* Check if address is used in some static mapping */
248   if (is_snat_address_used_in_static_mapping(sm, addr))
249     {
250       clib_warning ("address used in static mapping");
251       return VNET_API_ERROR_UNSPECIFIED;
252     }
253
254   /* Delete sessions using address */
255   if (a->busy_ports)
256     {
257       pool_foreach (ses, sm->sessions, ({
258         if (ses->out2in.addr.as_u32 == addr.as_u32)
259           {
260             vec_add1 (ses_to_be_removed, ses - sm->sessions);
261             kv.key = ses->in2out.as_u64;
262             clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0);
263             kv.key = ses->out2in.as_u64;
264             clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0);
265             clib_dlist_remove (sm->list_pool, ses->per_user_index);
266             user_key.addr = ses->in2out.addr;
267             user_key.fib_index = ses->in2out.fib_index;
268             kv.key = user_key.as_u64;
269             if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value))
270               {
271                 u = pool_elt_at_index (sm->users, value.value);
272                 u->nsessions--;
273               }
274           }
275       }));
276
277       vec_foreach (ses_index, ses_to_be_removed)
278         pool_put_index (sm->sessions, ses_index[0]);
279
280       vec_free (ses_to_be_removed);
281     }
282
283   vec_del1 (sm->addresses, i);
284
285   return 0;
286 }
287
288 static void increment_v4_address (ip4_address_t * a)
289 {
290   u32 v;
291   
292   v = clib_net_to_host_u32(a->as_u32) + 1;
293   a->as_u32 = clib_host_to_net_u32(v);
294 }
295
296 /**
297  * @brief Add static mapping.
298  *
299  * Create static mapping between local addr+port and external addr+port.
300  *
301  * @param l_addr Local IPv4 address.
302  * @param e_addr External IPv4 address.
303  * @param l_port Local port number.
304  * @param e_port External port number.
305  * @param vrf_id VRF ID.
306  * @param addr_only If 0 address port and pair mapping, otherwise address only.
307  * @param is_add If 0 delete static mapping, otherwise add.
308  *
309  * @returns
310  */
311 int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
312                             u16 l_port, u16 e_port, u32 vrf_id, int addr_only,
313                             int is_add)
314 {
315   snat_main_t * sm = &snat_main;
316   snat_static_mapping_t *m;
317   snat_static_mapping_key_t m_key;
318   clib_bihash_kv_8_8_t kv, value;
319   snat_address_t *a = 0;
320   u32 fib_index = ~0;
321   uword * p;
322   int i;
323
324   /* If outside FIB index is not resolved yet */
325   if (sm->outside_fib_index == ~0)
326     {
327       p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id);
328       if (!p)
329         return VNET_API_ERROR_NO_SUCH_FIB;
330       sm->outside_fib_index = p[0];
331     }
332
333   m_key.addr = e_addr;
334   m_key.port = addr_only ? 0 : e_port;
335   m_key.fib_index = sm->outside_fib_index;
336   kv.key = m_key.as_u64;
337   if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
338     m = 0;
339   else
340     m = pool_elt_at_index (sm->static_mappings, value.value);
341
342   if (is_add)
343     {
344       if (m)
345         return VNET_API_ERROR_VALUE_EXIST;
346
347       /* Convert VRF id to FIB index */
348       if (vrf_id != ~0)
349         {
350           p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id);
351           if (!p)
352             return VNET_API_ERROR_NO_SUCH_FIB;
353           fib_index = p[0];
354         }
355       /* If not specified use inside VRF id from SNAT plugin startup config */
356       else
357         {
358           if (sm->inside_fib_index == ~0)
359             {
360               p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->inside_vrf_id);
361               if (!p)
362                 return VNET_API_ERROR_NO_SUCH_FIB;
363               fib_index = p[0];
364               sm->inside_fib_index = fib_index;
365             }
366           else
367             fib_index = sm->inside_fib_index;
368
369           vrf_id = sm->inside_vrf_id;
370         }
371
372       /* Find external address in allocated addresses and reserve port for
373          address and port pair mapping when dynamic translations enabled */
374       if (!addr_only && !(sm->static_mapping_only))
375         {
376           for (i = 0; i < vec_len (sm->addresses); i++)
377             {
378               if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
379                 {
380                   a = sm->addresses + i;
381                   /* External port must be unused */
382                   if (clib_bitmap_get (a->busy_port_bitmap, e_port))
383                     return VNET_API_ERROR_INVALID_VALUE;
384                   a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
385                                                          e_port, 1);
386                   if (e_port > 1024)
387                     a->busy_ports++;
388
389                   break;
390                 }
391             }
392           /* External address must be allocated */
393           if (!a)
394             return VNET_API_ERROR_NO_SUCH_ENTRY;
395         }
396
397       pool_get (sm->static_mappings, m);
398       memset (m, 0, sizeof (*m));
399       m->local_addr = l_addr;
400       m->external_addr = e_addr;
401       m->addr_only = addr_only;
402       m->vrf_id = vrf_id;
403       m->fib_index = fib_index;
404       if (!addr_only)
405         {
406           m->local_port = l_port;
407           m->external_port = e_port;
408         }
409
410       m_key.addr = m->local_addr;
411       m_key.port = m->local_port;
412       m_key.fib_index = m->fib_index;
413       kv.key = m_key.as_u64;
414       kv.value = m - sm->static_mappings;
415       clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
416
417       m_key.addr = m->external_addr;
418       m_key.port = m->external_port;
419       m_key.fib_index = sm->outside_fib_index;
420       kv.key = m_key.as_u64;
421       kv.value = m - sm->static_mappings;
422       clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1);
423     }
424   else
425     {
426       if (!m)
427         return VNET_API_ERROR_NO_SUCH_ENTRY;
428
429       /* Free external address port */
430       if (!addr_only && !(sm->static_mapping_only))
431         {
432           for (i = 0; i < vec_len (sm->addresses); i++)
433             {
434               if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
435                 {
436                   a = sm->addresses + i;
437                   a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
438                                                          e_port, 0);
439                   a->busy_ports--;
440
441                   break;
442                 }
443             }
444         }
445
446       m_key.addr = m->local_addr;
447       m_key.port = m->local_port;
448       m_key.fib_index = m->fib_index;
449       kv.key = m_key.as_u64;
450       clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0);
451
452       m_key.addr = m->external_addr;
453       m_key.port = m->external_port;
454       m_key.fib_index = sm->outside_fib_index;
455       kv.key = m_key.as_u64;
456       clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0);
457
458       /* Delete session(s) for static mapping if exist */
459       if (!(sm->static_mapping_only) ||
460           (sm->static_mapping_only && sm->static_mapping_connection_tracking))
461         {
462           snat_user_key_t u_key;
463           snat_user_t *u;
464           dlist_elt_t * head, * elt;
465           u32 elt_index, head_index;
466           u32 ses_index;
467           snat_session_t * s;
468
469           u_key.addr = m->local_addr;
470           u_key.fib_index = m->fib_index;
471           kv.key = u_key.as_u64;
472           if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value))
473             {
474               u = pool_elt_at_index (sm->users, value.value);
475               if (u->nstaticsessions)
476                 {
477                   head_index = u->sessions_per_user_list_head_index;
478                   head = pool_elt_at_index (sm->list_pool, head_index);
479                   elt_index = head->next;
480                   elt = pool_elt_at_index (sm->list_pool, elt_index);
481                   ses_index = elt->value;
482                   while (ses_index != ~0)
483                     {
484                       s =  pool_elt_at_index (sm->sessions, ses_index);
485
486                       if (!addr_only)
487                         {
488                           if ((s->out2in.addr.as_u32 != e_addr.as_u32) &&
489                               (clib_net_to_host_u16 (s->out2in.port) != e_port))
490                             continue;
491                         }
492                       value.key = s->in2out.as_u64;
493                       clib_bihash_add_del_8_8 (&sm->in2out, &value, 0);
494                       value.key = s->out2in.as_u64;
495                       clib_bihash_add_del_8_8 (&sm->out2in, &value, 0);
496                       pool_put (sm->sessions, s);
497
498                       if (!addr_only)
499                         break;
500
501                       elt_index = elt->next;
502                       elt = pool_elt_at_index (sm->list_pool, elt_index);
503                       ses_index = elt->value;
504                     }
505                   if (addr_only)
506                     {
507                       while ((elt_index = clib_dlist_remove_head(sm->list_pool, head_index)) != ~0)
508                         pool_put_index (sm->list_pool, elt_index);
509                       pool_put (sm->users, u);
510                       clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 0);
511                     }
512                   else
513                     {
514                       if (ses_index != ~0)
515                         {
516                           clib_dlist_remove (sm->list_pool, elt_index);
517                           pool_put (sm->list_pool, elt);
518                           u->nstaticsessions--;
519                         }
520                     }
521                 }
522             }
523         }
524
525       /* Delete static mapping from pool */
526       pool_put (sm->static_mappings, m);
527     }
528
529   return 0;
530 }
531
532 static int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
533 {
534   snat_main_t *sm = &snat_main;
535   snat_interface_t *i;
536   u32 ci;
537   ip4_main_t * im = &ip4_main;
538   ip_lookup_main_t * lm = &im->lookup_main;
539   ip_config_main_t * rx_cm = &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
540   u32 feature_index;
541
542   if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
543     feature_index = is_inside ?  sm->rx_feature_in2out_fast
544       : sm->rx_feature_out2in_fast;
545   else
546     feature_index = is_inside ? sm->rx_feature_in2out
547       : sm->rx_feature_out2in;
548
549   ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
550   ci = (is_del
551         ? vnet_config_del_feature
552         : vnet_config_add_feature)
553     (sm->vlib_main, &rx_cm->config_main,
554      ci,
555      feature_index,
556      0 /* config struct */,
557      0 /* sizeof config struct*/);
558   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
559
560   pool_foreach (i, sm->interfaces,
561   ({
562     if (i->sw_if_index == sw_if_index)
563       {
564         if (is_del)
565           pool_put (sm->interfaces, i);
566         else
567           return VNET_API_ERROR_VALUE_EXIST;
568
569         return 0;
570       }
571   }));
572
573   if (is_del)
574     return VNET_API_ERROR_NO_SUCH_ENTRY;
575
576   pool_get (sm->interfaces, i);
577   i->sw_if_index = sw_if_index;
578   i->is_inside = is_inside;
579
580   return 0;
581 }
582
583 static void 
584 vl_api_snat_add_address_range_t_handler
585 (vl_api_snat_add_address_range_t * mp)
586 {
587   snat_main_t * sm = &snat_main;
588   vl_api_snat_add_address_range_reply_t * rmp;
589   ip4_address_t this_addr;
590   u32 start_host_order, end_host_order;
591   int i, count;
592   int rv = 0;
593   u32 * tmp;
594
595   if (mp->is_ip4 != 1)
596     {
597       rv = VNET_API_ERROR_UNIMPLEMENTED;
598       goto send_reply;
599     }
600
601   if (sm->static_mapping_only)
602     {
603       rv = VNET_API_ERROR_FEATURE_DISABLED;
604       goto send_reply;
605     }
606
607   tmp = (u32 *) mp->first_ip_address;
608   start_host_order = clib_host_to_net_u32 (tmp[0]);
609   tmp = (u32 *) mp->last_ip_address;
610   end_host_order = clib_host_to_net_u32 (tmp[0]);
611
612   count = (end_host_order - start_host_order) + 1;
613
614   if (count > 1024)
615     clib_warning ("%U - %U, %d addresses...",
616                   format_ip4_address, mp->first_ip_address,
617                   format_ip4_address, mp->last_ip_address,
618                   count);
619   
620   memcpy (&this_addr.as_u8, mp->first_ip_address, 4);
621
622   for (i = 0; i < count; i++)
623     {
624       if (mp->is_add)
625         snat_add_address (sm, &this_addr);
626       else
627         rv = snat_del_address (sm, this_addr);
628
629       if (rv)
630         goto send_reply;
631
632       increment_v4_address (&this_addr);
633     }
634
635  send_reply:
636   REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY);
637 }
638
639 static void *vl_api_snat_add_address_range_t_print
640 (vl_api_snat_add_address_range_t *mp, void * handle)
641 {
642   u8 * s;
643
644   s = format (0, "SCRIPT: snat_add_address_range ");
645   s = format (s, "%U ", format_ip4_address, mp->first_ip_address);
646   if (memcmp (mp->first_ip_address, mp->last_ip_address, 4))
647     {
648       s = format (s, " - %U ", format_ip4_address, mp->last_ip_address);
649     }
650   FINISH;
651 }
652
653 static void
654 send_snat_address_details
655 (snat_address_t * a, unix_shared_memory_queue_t * q, u32 context)
656 {
657   vl_api_snat_address_details_t *rmp;
658   snat_main_t * sm = &snat_main;
659
660   rmp = vl_msg_api_alloc (sizeof (*rmp));
661   memset (rmp, 0, sizeof (*rmp));
662   rmp->_vl_msg_id = ntohs (VL_API_SNAT_ADDRESS_DETAILS+sm->msg_id_base);
663   rmp->is_ip4 = 1;
664   clib_memcpy (rmp->ip_address, &(a->addr), 4);
665   rmp->context = context;
666
667   vl_msg_api_send_shmem (q, (u8 *) & rmp);
668 }
669
670 static void
671 vl_api_snat_address_dump_t_handler
672 (vl_api_snat_address_dump_t * mp)
673 {
674   unix_shared_memory_queue_t *q;
675   snat_main_t * sm = &snat_main;
676   snat_address_t * a;
677
678   q = vl_api_client_index_to_input_queue (mp->client_index);
679   if (q == 0)
680     return;
681
682   vec_foreach (a, sm->addresses)
683     send_snat_address_details (a, q, mp->context);
684 }
685
686 static void *vl_api_snat_address_dump_t_print
687 (vl_api_snat_address_dump_t *mp, void * handle)
688 {
689   u8 *s;
690
691   s = format (0, "SCRIPT: snat_address_dump ");
692
693   FINISH;
694 }
695
696 static void
697 vl_api_snat_interface_add_del_feature_t_handler
698 (vl_api_snat_interface_add_del_feature_t * mp)
699 {
700   snat_main_t * sm = &snat_main;
701   vl_api_snat_interface_add_del_feature_reply_t * rmp;
702   u8 is_del = mp->is_add == 0;
703   u32 sw_if_index = ntohl(mp->sw_if_index);
704   int rv = 0;
705
706   VALIDATE_SW_IF_INDEX(mp);
707
708   rv = snat_interface_add_del (sw_if_index, mp->is_inside, is_del);
709   
710   BAD_SW_IF_INDEX_LABEL;
711
712   REPLY_MACRO(VL_API_SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY);
713 }
714
715 static void *vl_api_snat_interface_add_del_feature_t_print
716 (vl_api_snat_interface_add_del_feature_t * mp, void *handle)
717 {
718   u8 * s;
719
720   s = format (0, "SCRIPT: snat_interface_add_del_feature ");
721   s = format (s, "sw_if_index %d %s %s",
722               clib_host_to_net_u32(mp->sw_if_index),
723               mp->is_inside ? "in":"out",
724               mp->is_add ? "" : "del");
725
726   FINISH;
727 }
728
729 static void
730 send_snat_interface_details
731 (snat_interface_t * i, unix_shared_memory_queue_t * q, u32 context)
732 {
733   vl_api_snat_interface_details_t *rmp;
734   snat_main_t * sm = &snat_main;
735
736   rmp = vl_msg_api_alloc (sizeof (*rmp));
737   memset (rmp, 0, sizeof (*rmp));
738   rmp->_vl_msg_id = ntohs (VL_API_SNAT_INTERFACE_DETAILS+sm->msg_id_base);
739   rmp->sw_if_index = ntohl (i->sw_if_index);
740   rmp->is_inside = i->is_inside;
741   rmp->context = context;
742
743   vl_msg_api_send_shmem (q, (u8 *) & rmp);
744 }
745
746 static void
747 vl_api_snat_interface_dump_t_handler
748 (vl_api_snat_interface_dump_t * mp)
749 {
750   unix_shared_memory_queue_t *q;
751   snat_main_t * sm = &snat_main;
752   snat_interface_t * i;
753
754   q = vl_api_client_index_to_input_queue (mp->client_index);
755   if (q == 0)
756     return;
757
758   pool_foreach (i, sm->interfaces,
759   ({
760     send_snat_interface_details(i, q, mp->context);
761   }));
762 }
763
764 static void *vl_api_snat_interface_dump_t_print
765 (vl_api_snat_interface_dump_t *mp, void * handle)
766 {
767   u8 *s;
768
769   s = format (0, "SCRIPT: snat_interface_dump ");
770
771   FINISH;
772 }static void
773
774 vl_api_snat_add_static_mapping_t_handler
775 (vl_api_snat_add_static_mapping_t * mp)
776 {
777   snat_main_t * sm = &snat_main;
778   vl_api_snat_add_static_mapping_reply_t * rmp;
779   ip4_address_t local_addr, external_addr;
780   u16 local_port = 0, external_port = 0;
781   u32 vrf_id;
782   int rv = 0;
783
784   if (mp->is_ip4 != 1)
785     {
786       rv = VNET_API_ERROR_UNIMPLEMENTED;
787       goto send_reply;
788     }
789
790   memcpy (&local_addr.as_u8, mp->local_ip_address, 4);
791   memcpy (&external_addr.as_u8, mp->external_ip_address, 4);
792   if (mp->addr_only == 0)
793     {
794       local_port = clib_net_to_host_u16 (mp->local_port);
795       external_port = clib_net_to_host_u16 (mp->external_port);
796     }
797   vrf_id = clib_net_to_host_u32 (mp->vrf_id);
798
799   rv = snat_add_static_mapping(local_addr, external_addr, local_port,
800                                external_port, vrf_id, mp->addr_only,
801                                mp->is_add);
802
803  send_reply:
804   REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY);
805 }
806
807 static void *vl_api_snat_add_static_mapping_t_print
808 (vl_api_snat_add_static_mapping_t *mp, void * handle)
809 {
810   u8 * s;
811
812   s = format (0, "SCRIPT: snat_add_static_mapping ");
813   s = format (s, "local_addr %U external_addr %U ",
814               format_ip4_address, mp->local_ip_address,
815               format_ip4_address, mp->external_ip_address);
816
817   if (mp->addr_only == 0)
818     s = format (s, "local_port %d external_port %d ",
819                 clib_net_to_host_u16 (mp->local_port),
820                 clib_net_to_host_u16 (mp->external_port));
821
822   if (mp->vrf_id != ~0)
823     s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id));
824
825   FINISH;
826 }
827
828 static void
829 send_snat_static_mapping_details
830 (snat_static_mapping_t * m, unix_shared_memory_queue_t * q, u32 context)
831 {
832   vl_api_snat_static_mapping_details_t *rmp;
833   snat_main_t * sm = &snat_main;
834
835   rmp = vl_msg_api_alloc (sizeof (*rmp));
836   memset (rmp, 0, sizeof (*rmp));
837   rmp->_vl_msg_id = ntohs (VL_API_SNAT_STATIC_MAPPING_DETAILS+sm->msg_id_base);
838   rmp->is_ip4 = 1;
839   rmp->addr_only = m->addr_only;
840   clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4);
841   clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4);
842   rmp->local_port = htons (m->local_port);
843   rmp->external_port = htons (m->external_port);
844   rmp->vrf_id = htonl (m->vrf_id);
845   rmp->context = context;
846
847   vl_msg_api_send_shmem (q, (u8 *) & rmp);
848 }
849
850 static void
851 vl_api_snat_static_mapping_dump_t_handler
852 (vl_api_snat_static_mapping_dump_t * mp)
853 {
854   unix_shared_memory_queue_t *q;
855   snat_main_t * sm = &snat_main;
856   snat_static_mapping_t * m;
857
858   q = vl_api_client_index_to_input_queue (mp->client_index);
859   if (q == 0)
860     return;
861
862   pool_foreach (m, sm->static_mappings,
863   ({
864       send_snat_static_mapping_details (m, q, mp->context);
865   }));
866 }
867
868 static void *vl_api_snat_static_mapping_dump_t_print
869 (vl_api_snat_static_mapping_dump_t *mp, void * handle)
870 {
871   u8 *s;
872
873   s = format (0, "SCRIPT: snat_static_mapping_dump ");
874
875   FINISH;
876 }
877
878 static void
879 vl_api_snat_control_ping_t_handler
880 (vl_api_snat_control_ping_t * mp)
881 {
882   vl_api_snat_control_ping_reply_t *rmp;
883   snat_main_t * sm = &snat_main;
884   int rv = 0;
885
886   REPLY_MACRO2(VL_API_SNAT_CONTROL_PING_REPLY,
887   ({
888     rmp->vpe_pid = ntohl (getpid());
889   }));
890 }
891
892 static void *vl_api_snat_control_ping_t_print
893 (vl_api_snat_control_ping_t *mp, void * handle)
894 {
895   u8 *s;
896
897   s = format (0, "SCRIPT: snat_control_ping ");
898
899   FINISH;
900 }
901
902 static void
903 vl_api_snat_show_config_t_handler
904 (vl_api_snat_show_config_t * mp)
905 {
906   vl_api_snat_show_config_reply_t *rmp;
907   snat_main_t * sm = &snat_main;
908   int rv = 0;
909
910   REPLY_MACRO2(VL_API_SNAT_SHOW_CONFIG_REPLY,
911   ({
912     rmp->translation_buckets = htons (sm->translation_buckets);
913     rmp->translation_memory_size = htons (sm->translation_memory_size);
914     rmp->user_buckets = htons (sm->user_buckets);
915     rmp->user_memory_size = htons (sm->user_memory_size);
916     rmp->max_translations_per_user = htons (sm->max_translations_per_user);
917     rmp->outside_vrf_id = htons (sm->outside_vrf_id);
918     rmp->inside_vrf_id = htons (sm->inside_vrf_id);
919     rmp->static_mapping_only = sm->static_mapping_only;
920     rmp->static_mapping_connection_tracking =
921       sm->static_mapping_connection_tracking;
922   }));
923 }
924
925 static void *vl_api_snat_show_config_t_print
926 (vl_api_snat_show_config_t *mp, void * handle)
927 {
928   u8 *s;
929
930   s = format (0, "SCRIPT: snat_show_config ");
931
932   FINISH;
933 }
934
935 /* List of message types that this plugin understands */
936 #define foreach_snat_plugin_api_msg                                     \
937 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
938 _(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature)       \
939 _(SNAT_ADD_STATIC_MAPPING, snat_add_static_mapping)                     \
940 _(SNAT_CONTROL_PING, snat_control_ping)                                 \
941 _(SNAT_STATIC_MAPPING_DUMP, snat_static_mapping_dump)                   \
942 _(SNAT_SHOW_CONFIG, snat_show_config)                                   \
943 _(SNAT_ADDRESS_DUMP, snat_address_dump)                                 \
944 _(SNAT_INTERFACE_DUMP, snat_interface_dump)
945
946 /* Set up the API message handling tables */
947 static clib_error_t *
948 snat_plugin_api_hookup (vlib_main_t *vm)
949 {
950    snat_main_t * sm __attribute__ ((unused)) = &snat_main;
951 #define _(N,n)                                                  \
952     vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base),     \
953                            #n,                                  \
954                            vl_api_##n##_t_handler,              \
955                            vl_noop_handler,                     \
956                            vl_api_##n##_t_endian,               \
957                            vl_api_##n##_t_print,                \
958                            sizeof(vl_api_##n##_t), 1); 
959     foreach_snat_plugin_api_msg;
960 #undef _
961
962     return 0;
963 }
964
965 static void plugin_custom_dump_configure (snat_main_t * sm) 
966 {
967 #define _(n,f) sm->api_main->msg_print_handlers \
968   [VL_API_##n + sm->msg_id_base]                \
969     = (void *) vl_api_##f##_t_print;
970   foreach_snat_plugin_api_msg;
971 #undef _
972 }
973
974 static clib_error_t * snat_init (vlib_main_t * vm)
975 {
976   snat_main_t * sm = &snat_main;
977   clib_error_t * error = 0;
978   ip4_main_t * im = &ip4_main;
979   ip_lookup_main_t * lm = &im->lookup_main;
980   u8 * name;
981
982   name = format (0, "snat_%08x%c", api_version, 0);
983
984   /* Ask for a correctly-sized block of API message decode slots */
985   sm->msg_id_base = vl_msg_api_get_msg_ids 
986       ((char *) name, VL_MSG_FIRST_AVAILABLE);
987
988   sm->vlib_main = vm;
989   sm->vnet_main = vnet_get_main();
990   sm->ip4_main = im;
991   sm->ip4_lookup_main = lm;
992   sm->api_main = &api_main;
993
994   error = snat_plugin_api_hookup (vm);
995   plugin_custom_dump_configure (sm);
996   vec_free(name);
997
998   return error;
999 }
1000
1001 VLIB_INIT_FUNCTION (snat_init);
1002
1003 void snat_free_outside_address_and_port (snat_main_t * sm, 
1004                                          snat_session_key_t * k, 
1005                                          u32 address_index)
1006 {
1007   snat_address_t *a;
1008   u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
1009   
1010   ASSERT (address_index < vec_len (sm->addresses));
1011
1012   a = sm->addresses + address_index;
1013
1014   ASSERT (clib_bitmap_get (a->busy_port_bitmap, port_host_byte_order) == 1);
1015
1016   a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap, 
1017                                          port_host_byte_order, 0);
1018   a->busy_ports--;
1019 }  
1020
1021 /**
1022  * @brief Match SNAT static mapping.
1023  *
1024  * @param sm          SNAT main.
1025  * @param match       Address and port to match.
1026  * @param mapping     External or local address and port of the matched mapping.
1027  * @param by_external If 0 match by local address otherwise match by external
1028  *                    address.
1029  *
1030  * @returns 0 if match found otherwise 1.
1031  */
1032 int snat_static_mapping_match (snat_main_t * sm,
1033                                snat_session_key_t match,
1034                                snat_session_key_t * mapping,
1035                                u8 by_external)
1036 {
1037   clib_bihash_kv_8_8_t kv, value;
1038   snat_static_mapping_t *m;
1039   snat_static_mapping_key_t m_key;
1040   clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local;
1041
1042   if (by_external)
1043     mapping_hash = &sm->static_mapping_by_external;
1044
1045   m_key.addr = match.addr;
1046   m_key.port = clib_net_to_host_u16 (match.port);
1047   m_key.fib_index = match.fib_index;
1048
1049   kv.key = m_key.as_u64;
1050
1051   if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
1052     {
1053       /* Try address only mapping */
1054       m_key.port = 0;
1055       kv.key = m_key.as_u64;
1056       if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
1057         return 1;
1058     }
1059
1060   m = pool_elt_at_index (sm->static_mappings, value.value);
1061
1062   if (by_external)
1063     {
1064       mapping->addr = m->local_addr;
1065       /* Address only mapping doesn't change port */
1066       mapping->port = m->addr_only ? match.port
1067         : clib_host_to_net_u16 (m->local_port);
1068       mapping->fib_index = m->fib_index;
1069     }
1070   else
1071     {
1072       mapping->addr = m->external_addr;
1073       /* Address only mapping doesn't change port */
1074       mapping->port = m->addr_only ? match.port
1075         : clib_host_to_net_u16 (m->external_port);
1076       mapping->fib_index = sm->outside_fib_index;
1077     }
1078
1079   return 0;
1080 }
1081
1082 int snat_alloc_outside_address_and_port (snat_main_t * sm, 
1083                                          snat_session_key_t * k,
1084                                          u32 * address_indexp)
1085 {
1086   int i;
1087   snat_address_t *a;
1088   u32 portnum;
1089
1090   for (i = 0; i < vec_len (sm->addresses); i++)
1091     {
1092       if (sm->addresses[i].busy_ports < (65535-1024))
1093         {
1094           a = sm->addresses + i;
1095
1096           while (1)
1097             {
1098               portnum = random_u32 (&sm->random_seed);
1099               portnum &= 0xFFFF;
1100               if (portnum < 1024)
1101                 continue;
1102               if (clib_bitmap_get (a->busy_port_bitmap, portnum))
1103                 continue;
1104               a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
1105                                                      portnum, 1);
1106               a->busy_ports++;
1107               /* Caller sets protocol and fib index */
1108               k->addr = a->addr;
1109               k->port = clib_host_to_net_u16(portnum);
1110               *address_indexp = i;
1111               return 0;
1112             }
1113         }
1114     }
1115   /* Totally out of translations to use... */
1116   return 1;
1117 }
1118
1119
1120 static clib_error_t *
1121 add_address_command_fn (vlib_main_t * vm,
1122                         unformat_input_t * input,
1123                         vlib_cli_command_t * cmd)
1124 {
1125   unformat_input_t _line_input, *line_input = &_line_input;
1126   snat_main_t * sm = &snat_main;
1127   ip4_address_t start_addr, end_addr, this_addr;
1128   u32 start_host_order, end_host_order;
1129   int i, count;
1130   int is_add = 1;
1131   int rv = 0;
1132
1133   /* Get a line of input. */
1134   if (!unformat_user (input, unformat_line_input, line_input))
1135     return 0;
1136
1137   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1138     {
1139       if (unformat (line_input, "%U - %U",
1140                     unformat_ip4_address, &start_addr,
1141                     unformat_ip4_address, &end_addr))
1142         ;
1143       else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
1144         end_addr = start_addr;
1145       else if (unformat (line_input, "del"))
1146         is_add = 0;
1147       else
1148         return clib_error_return (0, "unknown input '%U'",
1149           format_unformat_error, input);
1150      }
1151   unformat_free (line_input);
1152
1153   if (sm->static_mapping_only)
1154     return clib_error_return (0, "static mapping only mode");
1155
1156   start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
1157   end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
1158   
1159   if (end_host_order < start_host_order)
1160     return clib_error_return (0, "end address less than start address");
1161
1162   count = (end_host_order - start_host_order) + 1;
1163
1164   if (count > 1024)
1165     clib_warning ("%U - %U, %d addresses...",
1166                   format_ip4_address, &start_addr,
1167                   format_ip4_address, &end_addr,
1168                   count);
1169   
1170   this_addr = start_addr;
1171
1172   for (i = 0; i < count; i++)
1173     {
1174       if (is_add)
1175         snat_add_address (sm, &this_addr);
1176       else
1177         rv = snat_del_address (sm, this_addr);
1178
1179       switch (rv)
1180         {
1181         case VNET_API_ERROR_NO_SUCH_ENTRY:
1182           return clib_error_return (0, "S-NAT address not exist.");
1183           break;
1184         case VNET_API_ERROR_UNSPECIFIED:
1185           return clib_error_return (0, "S-NAT address used in static mapping.");
1186           break;
1187         default:
1188           break;
1189         }
1190
1191       increment_v4_address (&this_addr);
1192     }
1193
1194   return 0;
1195 }
1196
1197 VLIB_CLI_COMMAND (add_address_command, static) = {
1198   .path = "snat add address",
1199   .short_help = "snat add addresses <ip4-range-start> [- <ip4-range-end>] [del]",
1200   .function = add_address_command_fn,
1201 };
1202
1203 static clib_error_t *
1204 snat_feature_command_fn (vlib_main_t * vm,
1205                           unformat_input_t * input,
1206                           vlib_cli_command_t * cmd)
1207 {
1208   unformat_input_t _line_input, *line_input = &_line_input;
1209   vnet_main_t * vnm = vnet_get_main();
1210   clib_error_t * error = 0;
1211   u32 sw_if_index;
1212   u32 * inside_sw_if_indices = 0;
1213   u32 * outside_sw_if_indices = 0;
1214   int is_del = 0;
1215   int i;
1216
1217   sw_if_index = ~0;
1218
1219   /* Get a line of input. */
1220   if (!unformat_user (input, unformat_line_input, line_input))
1221     return 0;
1222
1223   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1224     {
1225       if (unformat (line_input, "in %U", unformat_vnet_sw_interface,
1226                     vnm, &sw_if_index))
1227         vec_add1 (inside_sw_if_indices, sw_if_index);
1228       else if (unformat (line_input, "out %U", unformat_vnet_sw_interface,
1229                          vnm, &sw_if_index))
1230         vec_add1 (outside_sw_if_indices, sw_if_index);
1231       else if (unformat (line_input, "del"))
1232         is_del = 1;
1233       else
1234         return clib_error_return (0, "unknown input '%U'",
1235           format_unformat_error, input);
1236     }
1237   unformat_free (line_input);
1238
1239   if (vec_len (inside_sw_if_indices))
1240     {
1241       for (i = 0; i < vec_len(inside_sw_if_indices); i++)
1242         {
1243           sw_if_index = inside_sw_if_indices[i];
1244           snat_interface_add_del (sw_if_index, 1, is_del);
1245         }
1246     }
1247
1248   if (vec_len (outside_sw_if_indices))
1249     {
1250       for (i = 0; i < vec_len(outside_sw_if_indices); i++)
1251         {
1252           sw_if_index = outside_sw_if_indices[i];
1253           snat_interface_add_del (sw_if_index, 0, is_del);
1254         }
1255     }
1256
1257   vec_free (inside_sw_if_indices);
1258   vec_free (outside_sw_if_indices);
1259
1260   return error;
1261 }
1262
1263 VLIB_CLI_COMMAND (set_interface_snat_command, static) = {
1264   .path = "set interface snat",
1265   .function = snat_feature_command_fn,
1266   .short_help = "set interface snat in <intfc> out <intfc> [del]",
1267 };
1268
1269 static clib_error_t *
1270 add_static_mapping_command_fn (vlib_main_t * vm,
1271                                unformat_input_t * input,
1272                                vlib_cli_command_t * cmd)
1273 {
1274   unformat_input_t _line_input, *line_input = &_line_input;
1275   clib_error_t * error = 0;
1276   ip4_address_t l_addr, e_addr;
1277   u32 l_port = 0, e_port = 0, vrf_id = ~0;
1278   int is_add = 1;
1279   int addr_only = 1;
1280   int rv;
1281
1282   /* Get a line of input. */
1283   if (!unformat_user (input, unformat_line_input, line_input))
1284     return 0;
1285
1286   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1287     {
1288       if (unformat (line_input, "local %U %u", unformat_ip4_address, &l_addr,
1289                     &l_port))
1290         addr_only = 0;
1291       else if (unformat (line_input, "local %U", unformat_ip4_address, &l_addr))
1292         ;
1293       else if (unformat (line_input, "external %U %u", unformat_ip4_address,
1294                          &e_addr, &e_port))
1295         addr_only = 0;
1296       else if (unformat (line_input, "external %U", unformat_ip4_address,
1297                          &e_addr))
1298         ;
1299       else if (unformat (line_input, "vrf %u", &vrf_id))
1300         ;
1301       else if (unformat (line_input, "del"))
1302         is_add = 0;
1303       else
1304         return clib_error_return (0, "unknown input: '%U'",
1305           format_unformat_error, line_input);
1306     }
1307   unformat_free (line_input);
1308
1309   rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port,
1310                                vrf_id, addr_only, is_add);
1311
1312   switch (rv)
1313     {
1314     case VNET_API_ERROR_INVALID_VALUE:
1315       return clib_error_return (0, "External port already in use.");
1316       break;
1317     case VNET_API_ERROR_NO_SUCH_ENTRY:
1318       if (is_add)
1319         return clib_error_return (0, "External addres must be allocated.");
1320       else
1321         return clib_error_return (0, "Mapping not exist.");
1322       break;
1323     case VNET_API_ERROR_NO_SUCH_FIB:
1324       return clib_error_return (0, "No such VRF id.");
1325     case VNET_API_ERROR_VALUE_EXIST:
1326       return clib_error_return (0, "Mapping already exist.");
1327     default:
1328       break;
1329     }
1330
1331   return error;
1332 }
1333
1334 /*?
1335  * @cliexpar
1336  * @cliexstart{snat add static mapping}
1337  * Static mapping allows hosts on the external network to initiate connection
1338  * to to the local network host.
1339  * To create static mapping between local host address 10.0.0.3 port 6303 and
1340  * external address 4.4.4.4 port 3606 use:
1341  *  vpp# snat add static mapping local 10.0.0.3 6303 external 4.4.4.4 3606
1342  * If not runnig "static mapping only" S-NAT plugin mode use before:
1343  *  vpp# snat add address 4.4.4.4
1344  * To create static mapping between local and external address use:
1345  *  vpp# snat add static mapping local 10.0.0.3 external 4.4.4.4
1346  * @cliexend
1347 ?*/
1348 VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
1349   .path = "snat add static mapping",
1350   .function = add_static_mapping_command_fn,
1351   .short_help =
1352     "snat add static mapping local <addr> [<port>] external <addr> [<port>] [vrf <table-id>] [del]",
1353 };
1354
1355 static clib_error_t *
1356 snat_config (vlib_main_t * vm, unformat_input_t * input)
1357 {
1358   snat_main_t * sm = &snat_main;
1359   u32 translation_buckets = 1024;
1360   u32 translation_memory_size = 128<<20;
1361   u32 user_buckets = 128;
1362   u32 user_memory_size = 64<<20;
1363   u32 max_translations_per_user = 100;
1364   u32 outside_vrf_id = 0;
1365   u32 inside_vrf_id = 0;
1366   u32 static_mapping_buckets = 1024;
1367   u32 static_mapping_memory_size = 64<<20;
1368   u8 static_mapping_only = 0;
1369   u8 static_mapping_connection_tracking = 0;
1370
1371   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1372     {
1373       if (unformat (input, "translation hash buckets %d", &translation_buckets))
1374         ;
1375       else if (unformat (input, "translation hash memory %d",
1376                          &translation_memory_size));
1377       else if (unformat (input, "user hash buckets %d", &user_buckets))
1378         ;
1379       else if (unformat (input, "user hash memory %d",
1380                          &user_memory_size))
1381         ;
1382       else if (unformat (input, "max translations per user %d",
1383                          &max_translations_per_user))
1384         ;
1385       else if (unformat (input, "outside VRF id %d",
1386                          &outside_vrf_id))
1387         ;
1388       else if (unformat (input, "inside VRF id %d",
1389                          &inside_vrf_id))
1390         ;
1391       else if (unformat (input, "static mapping only"))
1392         {
1393           static_mapping_only = 1;
1394           if (unformat (input, "connection tracking"))
1395             static_mapping_connection_tracking = 1;
1396         }
1397       else 
1398         return clib_error_return (0, "unknown input '%U'",
1399                                   format_unformat_error, input);
1400     }
1401
1402   /* for show commands, etc. */
1403   sm->translation_buckets = translation_buckets;
1404   sm->translation_memory_size = translation_memory_size;
1405   sm->user_buckets = user_buckets;
1406   sm->user_memory_size = user_memory_size;
1407   sm->max_translations_per_user = max_translations_per_user;
1408   sm->outside_vrf_id = outside_vrf_id;
1409   sm->outside_fib_index = ~0;
1410   sm->inside_vrf_id = inside_vrf_id;
1411   sm->inside_fib_index = ~0;
1412   sm->static_mapping_only = static_mapping_only;
1413   sm->static_mapping_connection_tracking = static_mapping_connection_tracking;
1414
1415   if (!static_mapping_only ||
1416       (static_mapping_only && static_mapping_connection_tracking))
1417     {
1418       clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets,
1419                             translation_memory_size);
1420
1421       clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets,
1422                             translation_memory_size);
1423
1424       clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets,
1425                             user_memory_size);
1426     }
1427   clib_bihash_init_8_8 (&sm->static_mapping_by_local,
1428                         "static_mapping_by_local", static_mapping_buckets,
1429                         static_mapping_memory_size);
1430
1431   clib_bihash_init_8_8 (&sm->static_mapping_by_external,
1432                         "static_mapping_by_external", static_mapping_buckets,
1433                         static_mapping_memory_size);
1434   return 0;
1435 }
1436
1437 VLIB_CONFIG_FUNCTION (snat_config, "snat");
1438
1439 u8 * format_snat_key (u8 * s, va_list * args)
1440 {
1441   snat_session_key_t * key = va_arg (*args, snat_session_key_t *);
1442   char * protocol_string = "unknown";
1443   static char *protocol_strings[] = {
1444       "UDP",
1445       "TCP",
1446       "ICMP",
1447   };
1448
1449   if (key->protocol < ARRAY_LEN(protocol_strings))
1450       protocol_string = protocol_strings[key->protocol];
1451
1452   s = format (s, "%U proto %s port %d fib %d",
1453               format_ip4_address, &key->addr, protocol_string,
1454               clib_net_to_host_u16 (key->port), key->fib_index);
1455   return s;
1456 }
1457
1458 u8 * format_snat_session (u8 * s, va_list * args)
1459 {
1460   snat_main_t * sm __attribute__((unused)) = va_arg (*args, snat_main_t *);
1461   snat_session_t * sess = va_arg (*args, snat_session_t *);
1462
1463   s = format (s, "  i2o %U\n", format_snat_key, &sess->in2out);
1464   s = format (s, "    o2i %U\n", format_snat_key, &sess->out2in);
1465   s = format (s, "       last heard %.2f\n", sess->last_heard);
1466   s = format (s, "       total pkts %d, total bytes %lld\n",
1467               sess->total_pkts, sess->total_bytes);
1468   if (snat_is_session_static (sess))
1469     s = format (s, "       static translation\n");
1470   else
1471     s = format (s, "       dynamic translation\n");
1472
1473   return s;
1474 }
1475
1476 u8 * format_snat_user (u8 * s, va_list * args)
1477 {
1478   snat_main_t * sm = va_arg (*args, snat_main_t *);
1479   snat_user_t * u = va_arg (*args, snat_user_t *);
1480   int verbose = va_arg (*args, int);
1481   dlist_elt_t * head, * elt;
1482   u32 elt_index, head_index;
1483   u32 session_index;
1484   snat_session_t * sess;
1485
1486   s = format (s, "%U: %d dynamic translations, %d static translations\n",
1487               format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions);
1488
1489   if (verbose == 0)
1490     return s;
1491
1492   if (u->nsessions || u->nstaticsessions)
1493     {
1494       head_index = u->sessions_per_user_list_head_index;
1495       head = pool_elt_at_index (sm->list_pool, head_index);
1496
1497       elt_index = head->next;
1498       elt = pool_elt_at_index (sm->list_pool, elt_index);
1499       session_index = elt->value;
1500
1501       while (session_index != ~0)
1502         {
1503           sess = pool_elt_at_index (sm->sessions, session_index);
1504
1505           s = format (s, "  %U\n", format_snat_session, sm, sess);
1506
1507           elt_index = elt->next;
1508           elt = pool_elt_at_index (sm->list_pool, elt_index);
1509           session_index = elt->value;
1510         }
1511     }
1512
1513   return s;
1514 }
1515
1516 u8 * format_snat_static_mapping (u8 * s, va_list * args)
1517 {
1518   snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *);
1519
1520   if (m->addr_only)
1521       s = format (s, "local %U external %U vrf %d",
1522                   format_ip4_address, &m->local_addr,
1523                   format_ip4_address, &m->external_addr,
1524                   m->vrf_id);
1525   else
1526       s = format (s, "local %U:%d external %U:%d vrf %d",
1527                   format_ip4_address, &m->local_addr, m->local_port,
1528                   format_ip4_address, &m->external_addr, m->external_port,
1529                   m->vrf_id);
1530
1531   return s;
1532 }
1533
1534 static clib_error_t *
1535 show_snat_command_fn (vlib_main_t * vm,
1536                  unformat_input_t * input,
1537                  vlib_cli_command_t * cmd)
1538 {
1539   int verbose = 0;
1540   snat_main_t * sm = &snat_main;
1541   snat_user_t * u;
1542   snat_static_mapping_t *m;
1543   snat_interface_t *i;
1544   vnet_main_t *vnm = vnet_get_main();
1545
1546   if (unformat (input, "detail"))
1547     verbose = 1;
1548   else if (unformat (input, "verbose"))
1549     verbose = 2;
1550
1551   if (sm->static_mapping_only)
1552     {
1553       if (sm->static_mapping_connection_tracking)
1554         vlib_cli_output (vm, "SNAT mode: static mapping only connection "
1555                          "tracking");
1556       else
1557         vlib_cli_output (vm, "SNAT mode: static mapping only");
1558     }
1559   else
1560     {
1561       vlib_cli_output (vm, "SNAT mode: dynamic translations enabled");
1562     }
1563
1564   if (verbose > 0)
1565     {
1566       pool_foreach (i, sm->interfaces,
1567       ({
1568         vlib_cli_output (vm, "%U %s", format_vnet_sw_interface_name, vnm,
1569                          vnet_get_sw_interface (vnm, i->sw_if_index),
1570                          i->is_inside ? "in" : "out");
1571       }));
1572     }
1573
1574   if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
1575     {
1576       vlib_cli_output (vm, "%d static mappings",
1577                        pool_elts (sm->static_mappings));
1578
1579       if (verbose > 0)
1580         {
1581           pool_foreach (m, sm->static_mappings,
1582           ({
1583             vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
1584           }));
1585         }
1586     }
1587   else
1588     {
1589       vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions,"
1590                        " %d static mappings",
1591                        pool_elts (sm->users),
1592                        vec_len (sm->addresses),
1593                        pool_elts (sm->sessions),
1594                        pool_elts (sm->static_mappings));
1595
1596       if (verbose > 0)
1597         {
1598           vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out,
1599                            verbose - 1);
1600           vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in,
1601                            verbose - 1);
1602           vlib_cli_output (vm, "%d list pool elements",
1603                            pool_elts (sm->list_pool));
1604
1605           pool_foreach (u, sm->users,
1606           ({
1607             vlib_cli_output (vm, "%U", format_snat_user, sm, u, verbose - 1);
1608           }));
1609
1610           if (pool_elts (sm->static_mappings))
1611             {
1612               vlib_cli_output (vm, "static mappings:");
1613               pool_foreach (m, sm->static_mappings,
1614               ({
1615                 vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
1616               }));
1617             }
1618         }
1619     }
1620
1621   return 0;
1622 }
1623
1624 VLIB_CLI_COMMAND (show_snat_command, static) = {
1625     .path = "show snat",
1626     .short_help = "show snat",
1627     .function = show_snat_command_fn,
1628 };