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