VPP-446: 1:1 SNAT Inside overlapping 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 void 
533 vl_api_snat_add_address_range_t_handler
534 (vl_api_snat_add_address_range_t * mp)
535 {
536   snat_main_t * sm = &snat_main;
537   vl_api_snat_add_address_range_reply_t * rmp;
538   ip4_address_t this_addr;
539   u32 start_host_order, end_host_order;
540   int i, count;
541   int rv = 0;
542   u32 * tmp;
543
544   if (mp->is_ip4 != 1)
545     {
546       rv = VNET_API_ERROR_UNIMPLEMENTED;
547       goto send_reply;
548     }
549
550   if (sm->static_mapping_only)
551     {
552       rv = VNET_API_ERROR_FEATURE_DISABLED;
553       goto send_reply;
554     }
555
556   tmp = (u32 *) mp->first_ip_address;
557   start_host_order = clib_host_to_net_u32 (tmp[0]);
558   tmp = (u32 *) mp->last_ip_address;
559   end_host_order = clib_host_to_net_u32 (tmp[0]);
560
561   count = (end_host_order - start_host_order) + 1;
562
563   if (count > 1024)
564     clib_warning ("%U - %U, %d addresses...",
565                   format_ip4_address, mp->first_ip_address,
566                   format_ip4_address, mp->last_ip_address,
567                   count);
568   
569   memcpy (&this_addr.as_u8, mp->first_ip_address, 4);
570
571   for (i = 0; i < count; i++)
572     {
573       if (mp->is_add)
574         snat_add_address (sm, &this_addr);
575       else
576         rv = snat_del_address (sm, this_addr);
577
578       if (rv)
579         goto send_reply;
580
581       increment_v4_address (&this_addr);
582     }
583
584  send_reply:
585   REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY);
586 }
587
588 static void *vl_api_snat_add_address_range_t_print
589 (vl_api_snat_add_address_range_t *mp, void * handle)
590 {
591   u8 * s;
592
593   s = format (0, "SCRIPT: snat_add_address_range ");
594   s = format (s, "%U ", format_ip4_address, mp->first_ip_address);
595   if (memcmp (mp->first_ip_address, mp->last_ip_address, 4))
596     {
597       s = format (s, " - %U ", format_ip4_address, mp->last_ip_address);
598     }
599   FINISH;
600 }
601
602 static void
603 send_snat_address_details
604 (snat_address_t * a, unix_shared_memory_queue_t * q, u32 context)
605 {
606   vl_api_snat_address_details_t *rmp;
607   snat_main_t * sm = &snat_main;
608
609   rmp = vl_msg_api_alloc (sizeof (*rmp));
610   memset (rmp, 0, sizeof (*rmp));
611   rmp->_vl_msg_id = ntohs (VL_API_SNAT_ADDRESS_DETAILS+sm->msg_id_base);
612   rmp->is_ip4 = 1;
613   clib_memcpy (rmp->ip_address, &(a->addr), 4);
614   rmp->context = context;
615
616   vl_msg_api_send_shmem (q, (u8 *) & rmp);
617 }
618
619 static void
620 vl_api_snat_address_dump_t_handler
621 (vl_api_snat_address_dump_t * mp)
622 {
623   unix_shared_memory_queue_t *q;
624   snat_main_t * sm = &snat_main;
625   snat_address_t * a;
626
627   q = vl_api_client_index_to_input_queue (mp->client_index);
628   if (q == 0)
629     return;
630
631   vec_foreach (a, sm->addresses)
632     send_snat_address_details (a, q, mp->context);
633 }
634
635 static void *vl_api_snat_address_dump_t_print
636 (vl_api_snat_address_dump_t *mp, void * handle)
637 {
638   u8 *s;
639
640   s = format (0, "SCRIPT: snat_address_dump ");
641
642   FINISH;
643 }
644
645 static void
646 vl_api_snat_interface_add_del_feature_t_handler
647 (vl_api_snat_interface_add_del_feature_t * mp)
648 {
649   snat_main_t * sm = &snat_main;
650   vl_api_snat_interface_add_del_feature_reply_t * rmp;
651   u8 is_del = mp->is_add == 0;
652   u32 sw_if_index = ntohl(mp->sw_if_index);
653   u32 ci;
654   ip4_main_t * im = &ip4_main;
655   ip_lookup_main_t * lm = &im->lookup_main;
656   ip_config_main_t * rx_cm = &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
657   u32 feature_index;
658   int rv = 0;
659
660   VALIDATE_SW_IF_INDEX(mp);
661
662   if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
663     feature_index = mp->is_inside ?  sm->rx_feature_in2out_fast
664       : sm->rx_feature_out2in_fast;
665   else
666     feature_index = mp->is_inside ? sm->rx_feature_in2out
667       : sm->rx_feature_out2in;
668
669   ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
670   ci = (is_del
671         ? vnet_config_del_feature
672         : vnet_config_add_feature)
673     (sm->vlib_main, &rx_cm->config_main,
674      ci,
675      feature_index,
676      0 /* config struct */, 
677      0 /* sizeof config struct*/);
678   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
679   
680   BAD_SW_IF_INDEX_LABEL;
681
682   REPLY_MACRO(VL_API_SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY);
683 }
684
685 static void *vl_api_snat_interface_add_del_feature_t_print
686 (vl_api_snat_interface_add_del_feature_t * mp, void *handle)
687 {
688   u8 * s;
689
690   s = format (0, "SCRIPT: snat_interface_add_del_feature ");
691   s = format (s, "sw_if_index %d %s %s",
692               clib_host_to_net_u32(mp->sw_if_index),
693               mp->is_inside ? "in":"out",
694               mp->is_add ? "" : "del");
695
696   FINISH;
697 }
698
699 static void
700 vl_api_snat_add_static_mapping_t_handler
701 (vl_api_snat_add_static_mapping_t * mp)
702 {
703   snat_main_t * sm = &snat_main;
704   vl_api_snat_add_static_mapping_reply_t * rmp;
705   ip4_address_t local_addr, external_addr;
706   u16 local_port = 0, external_port = 0;
707   u32 vrf_id;
708   int rv = 0;
709
710   if (mp->is_ip4 != 1)
711     {
712       rv = VNET_API_ERROR_UNIMPLEMENTED;
713       goto send_reply;
714     }
715
716   memcpy (&local_addr.as_u8, mp->local_ip_address, 4);
717   memcpy (&external_addr.as_u8, mp->external_ip_address, 4);
718   if (mp->addr_only == 0)
719     {
720       local_port = clib_net_to_host_u16 (mp->local_port);
721       external_port = clib_net_to_host_u16 (mp->external_port);
722     }
723   vrf_id = clib_net_to_host_u32 (mp->vrf_id);
724
725   rv = snat_add_static_mapping(local_addr, external_addr, local_port,
726                                external_port, vrf_id, mp->addr_only,
727                                mp->is_add);
728
729  send_reply:
730   REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY);
731 }
732
733 static void *vl_api_snat_add_static_mapping_t_print
734 (vl_api_snat_add_static_mapping_t *mp, void * handle)
735 {
736   u8 * s;
737
738   s = format (0, "SCRIPT: snat_add_static_mapping ");
739   s = format (s, "local_addr %U external_addr %U ",
740               format_ip4_address, mp->local_ip_address,
741               format_ip4_address, mp->external_ip_address);
742
743   if (mp->addr_only == 0)
744     s = format (s, "local_port %d external_port %d ",
745                 clib_net_to_host_u16 (mp->local_port),
746                 clib_net_to_host_u16 (mp->external_port));
747
748   if (mp->vrf_id != ~0)
749     s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id));
750
751   FINISH;
752 }
753
754 static void
755 send_snat_static_mapping_details
756 (snat_static_mapping_t * m, unix_shared_memory_queue_t * q, u32 context)
757 {
758   vl_api_snat_static_mapping_details_t *rmp;
759   snat_main_t * sm = &snat_main;
760
761   rmp = vl_msg_api_alloc (sizeof (*rmp));
762   memset (rmp, 0, sizeof (*rmp));
763   rmp->_vl_msg_id = ntohs (VL_API_SNAT_STATIC_MAPPING_DETAILS+sm->msg_id_base);
764   rmp->is_ip4 = 1;
765   rmp->addr_only = m->addr_only;
766   clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4);
767   clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4);
768   rmp->local_port = htons (m->local_port);
769   rmp->external_port = htons (m->external_port);
770   rmp->vrf_id = htonl (m->vrf_id);
771   rmp->context = context;
772
773   vl_msg_api_send_shmem (q, (u8 *) & rmp);
774 }
775
776 static void
777 vl_api_snat_static_mapping_dump_t_handler
778 (vl_api_snat_static_mapping_dump_t * mp)
779 {
780   unix_shared_memory_queue_t *q;
781   snat_main_t * sm = &snat_main;
782   snat_static_mapping_t * m;
783
784   q = vl_api_client_index_to_input_queue (mp->client_index);
785   if (q == 0)
786     return;
787
788   pool_foreach (m, sm->static_mappings,
789   ({
790       send_snat_static_mapping_details (m, q, mp->context);
791   }));
792 }
793
794 static void *vl_api_snat_static_mapping_dump_t_print
795 (vl_api_snat_static_mapping_dump_t *mp, void * handle)
796 {
797   u8 *s;
798
799   s = format (0, "SCRIPT: snat_static_mapping_dump ");
800
801   FINISH;
802 }
803
804 static void
805 vl_api_snat_control_ping_t_handler
806 (vl_api_snat_control_ping_t * mp)
807 {
808   vl_api_snat_control_ping_reply_t *rmp;
809   snat_main_t * sm = &snat_main;
810   int rv = 0;
811
812   REPLY_MACRO2(VL_API_SNAT_CONTROL_PING_REPLY,
813   ({
814     rmp->vpe_pid = ntohl (getpid());
815   }));
816 }
817
818 static void *vl_api_snat_control_ping_t_print
819 (vl_api_snat_control_ping_t *mp, void * handle)
820 {
821   u8 *s;
822
823   s = format (0, "SCRIPT: snat_control_ping ");
824
825   FINISH;
826 }
827
828 static void
829 vl_api_snat_show_config_t_handler
830 (vl_api_snat_show_config_t * mp)
831 {
832   vl_api_snat_show_config_reply_t *rmp;
833   snat_main_t * sm = &snat_main;
834   int rv = 0;
835
836   REPLY_MACRO2(VL_API_SNAT_SHOW_CONFIG_REPLY,
837   ({
838     rmp->translation_buckets = htons (sm->translation_buckets);
839     rmp->translation_memory_size = htons (sm->translation_memory_size);
840     rmp->user_buckets = htons (sm->user_buckets);
841     rmp->user_memory_size = htons (sm->user_memory_size);
842     rmp->max_translations_per_user = htons (sm->max_translations_per_user);
843     rmp->outside_vrf_id = htons (sm->outside_vrf_id);
844     rmp->inside_vrf_id = htons (sm->inside_vrf_id);
845     rmp->static_mapping_only = sm->static_mapping_only;
846     rmp->static_mapping_connection_tracking =
847       sm->static_mapping_connection_tracking;
848   }));
849 }
850
851 static void *vl_api_snat_show_config_t_print
852 (vl_api_snat_show_config_t *mp, void * handle)
853 {
854   u8 *s;
855
856   s = format (0, "SCRIPT: snat_show_config ");
857
858   FINISH;
859 }
860
861 /* List of message types that this plugin understands */
862 #define foreach_snat_plugin_api_msg                                     \
863 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range)                       \
864 _(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature)       \
865 _(SNAT_ADD_STATIC_MAPPING, snat_add_static_mapping)                     \
866 _(SNAT_CONTROL_PING, snat_control_ping)                                 \
867 _(SNAT_STATIC_MAPPING_DUMP, snat_static_mapping_dump)                   \
868 _(SNAT_SHOW_CONFIG, snat_show_config)                                   \
869 _(SNAT_ADDRESS_DUMP, snat_address_dump)
870
871 /* Set up the API message handling tables */
872 static clib_error_t *
873 snat_plugin_api_hookup (vlib_main_t *vm)
874 {
875    snat_main_t * sm __attribute__ ((unused)) = &snat_main;
876 #define _(N,n)                                                  \
877     vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base),     \
878                            #n,                                  \
879                            vl_api_##n##_t_handler,              \
880                            vl_noop_handler,                     \
881                            vl_api_##n##_t_endian,               \
882                            vl_api_##n##_t_print,                \
883                            sizeof(vl_api_##n##_t), 1); 
884     foreach_snat_plugin_api_msg;
885 #undef _
886
887     return 0;
888 }
889
890 static void plugin_custom_dump_configure (snat_main_t * sm) 
891 {
892 #define _(n,f) sm->api_main->msg_print_handlers \
893   [VL_API_##n + sm->msg_id_base]                \
894     = (void *) vl_api_##f##_t_print;
895   foreach_snat_plugin_api_msg;
896 #undef _
897 }
898
899 static clib_error_t * snat_init (vlib_main_t * vm)
900 {
901   snat_main_t * sm = &snat_main;
902   clib_error_t * error = 0;
903   ip4_main_t * im = &ip4_main;
904   ip_lookup_main_t * lm = &im->lookup_main;
905   u8 * name;
906
907   name = format (0, "snat_%08x%c", api_version, 0);
908
909   /* Ask for a correctly-sized block of API message decode slots */
910   sm->msg_id_base = vl_msg_api_get_msg_ids 
911       ((char *) name, VL_MSG_FIRST_AVAILABLE);
912
913   sm->vlib_main = vm;
914   sm->vnet_main = vnet_get_main();
915   sm->ip4_main = im;
916   sm->ip4_lookup_main = lm;
917   sm->api_main = &api_main;
918
919   error = snat_plugin_api_hookup (vm);
920   plugin_custom_dump_configure (sm);
921   vec_free(name);
922
923   return error;
924 }
925
926 VLIB_INIT_FUNCTION (snat_init);
927
928 void snat_free_outside_address_and_port (snat_main_t * sm, 
929                                          snat_session_key_t * k, 
930                                          u32 address_index)
931 {
932   snat_address_t *a;
933   u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
934   
935   ASSERT (address_index < vec_len (sm->addresses));
936
937   a = sm->addresses + address_index;
938
939   ASSERT (clib_bitmap_get (a->busy_port_bitmap, port_host_byte_order) == 1);
940
941   a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap, 
942                                          port_host_byte_order, 0);
943   a->busy_ports--;
944 }  
945
946 /**
947  * @brief Match SNAT static mapping.
948  *
949  * @param sm          SNAT main.
950  * @param match       Address and port to match.
951  * @param mapping     External or local address and port of the matched mapping.
952  * @param by_external If 0 match by local address otherwise match by external
953  *                    address.
954  *
955  * @returns 0 if match found otherwise 1.
956  */
957 int snat_static_mapping_match (snat_main_t * sm,
958                                snat_session_key_t match,
959                                snat_session_key_t * mapping,
960                                u8 by_external)
961 {
962   clib_bihash_kv_8_8_t kv, value;
963   snat_static_mapping_t *m;
964   snat_static_mapping_key_t m_key;
965   clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local;
966
967   if (by_external)
968     mapping_hash = &sm->static_mapping_by_external;
969
970   m_key.addr = match.addr;
971   m_key.port = clib_net_to_host_u16 (match.port);
972   m_key.fib_index = match.fib_index;
973
974   kv.key = m_key.as_u64;
975
976   if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
977     {
978       /* Try address only mapping */
979       m_key.port = 0;
980       kv.key = m_key.as_u64;
981       if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
982         return 1;
983     }
984
985   m = pool_elt_at_index (sm->static_mappings, value.value);
986
987   if (by_external)
988     {
989       mapping->addr = m->local_addr;
990       /* Address only mapping doesn't change port */
991       mapping->port = m->addr_only ? match.port
992         : clib_host_to_net_u16 (m->local_port);
993       mapping->fib_index = m->fib_index;
994     }
995   else
996     {
997       mapping->addr = m->external_addr;
998       /* Address only mapping doesn't change port */
999       mapping->port = m->addr_only ? match.port
1000         : clib_host_to_net_u16 (m->external_port);
1001       mapping->fib_index = sm->outside_fib_index;
1002     }
1003
1004   return 0;
1005 }
1006
1007 int snat_alloc_outside_address_and_port (snat_main_t * sm, 
1008                                          snat_session_key_t * k,
1009                                          u32 * address_indexp)
1010 {
1011   int i;
1012   snat_address_t *a;
1013   u32 portnum;
1014
1015   for (i = 0; i < vec_len (sm->addresses); i++)
1016     {
1017       if (sm->addresses[i].busy_ports < (65535-1024))
1018         {
1019           a = sm->addresses + i;
1020
1021           while (1)
1022             {
1023               portnum = random_u32 (&sm->random_seed);
1024               portnum &= 0xFFFF;
1025               if (portnum < 1024)
1026                 continue;
1027               if (clib_bitmap_get (a->busy_port_bitmap, portnum))
1028                 continue;
1029               a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
1030                                                      portnum, 1);
1031               a->busy_ports++;
1032               /* Caller sets protocol and fib index */
1033               k->addr = a->addr;
1034               k->port = clib_host_to_net_u16(portnum);
1035               *address_indexp = i;
1036               return 0;
1037             }
1038         }
1039     }
1040   /* Totally out of translations to use... */
1041   return 1;
1042 }
1043
1044
1045 static clib_error_t *
1046 add_address_command_fn (vlib_main_t * vm,
1047                         unformat_input_t * input,
1048                         vlib_cli_command_t * cmd)
1049 {
1050   unformat_input_t _line_input, *line_input = &_line_input;
1051   snat_main_t * sm = &snat_main;
1052   ip4_address_t start_addr, end_addr, this_addr;
1053   u32 start_host_order, end_host_order;
1054   int i, count;
1055   int is_add = 1;
1056   int rv = 0;
1057
1058   /* Get a line of input. */
1059   if (!unformat_user (input, unformat_line_input, line_input))
1060     return 0;
1061
1062   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1063     {
1064       if (unformat (line_input, "%U - %U",
1065                     unformat_ip4_address, &start_addr,
1066                     unformat_ip4_address, &end_addr))
1067         ;
1068       else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
1069         end_addr = start_addr;
1070       else if (unformat (line_input, "del"))
1071         is_add = 0;
1072       else
1073         return clib_error_return (0, "unknown input '%U'",
1074           format_unformat_error, input);
1075      }
1076   unformat_free (line_input);
1077
1078   if (sm->static_mapping_only)
1079     return clib_error_return (0, "static mapping only mode");
1080
1081   start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
1082   end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
1083   
1084   if (end_host_order < start_host_order)
1085     return clib_error_return (0, "end address less than start address");
1086
1087   count = (end_host_order - start_host_order) + 1;
1088
1089   if (count > 1024)
1090     clib_warning ("%U - %U, %d addresses...",
1091                   format_ip4_address, &start_addr,
1092                   format_ip4_address, &end_addr,
1093                   count);
1094   
1095   this_addr = start_addr;
1096
1097   for (i = 0; i < count; i++)
1098     {
1099       if (is_add)
1100         snat_add_address (sm, &this_addr);
1101       else
1102         rv = snat_del_address (sm, this_addr);
1103
1104       switch (rv)
1105         {
1106         case VNET_API_ERROR_NO_SUCH_ENTRY:
1107           return clib_error_return (0, "S-NAT address not exist.");
1108           break;
1109         case VNET_API_ERROR_UNSPECIFIED:
1110           return clib_error_return (0, "S-NAT address used in static mapping.");
1111           break;
1112         default:
1113           break;
1114         }
1115
1116       increment_v4_address (&this_addr);
1117     }
1118
1119   return 0;
1120 }
1121
1122 VLIB_CLI_COMMAND (add_address_command, static) = {
1123   .path = "snat add address",
1124   .short_help = "snat add addresses <ip4-range-start> [- <ip4-range-end>] [del]",
1125   .function = add_address_command_fn,
1126 };
1127
1128 static clib_error_t *
1129 snat_feature_command_fn (vlib_main_t * vm,
1130                           unformat_input_t * input,
1131                           vlib_cli_command_t * cmd)
1132 {
1133   unformat_input_t _line_input, *line_input = &_line_input;
1134   vnet_main_t * vnm = vnet_get_main();
1135   snat_main_t * sm = &snat_main;
1136   ip4_main_t * im = &ip4_main;
1137   ip_lookup_main_t * lm = &im->lookup_main;
1138   ip_config_main_t * rx_cm = &lm->feature_config_mains[VNET_IP_RX_UNICAST_FEAT];
1139   clib_error_t * error = 0;
1140   u32 sw_if_index, ci;
1141   u32 feature_index;
1142   u32 * inside_sw_if_indices = 0;
1143   u32 * outside_sw_if_indices = 0;
1144   int is_del = 0;
1145   int i;
1146
1147   sw_if_index = ~0;
1148
1149   /* Get a line of input. */
1150   if (!unformat_user (input, unformat_line_input, line_input))
1151     return 0;
1152
1153   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1154     {
1155       if (unformat (line_input, "in %U", unformat_vnet_sw_interface,
1156                     vnm, &sw_if_index))
1157         vec_add1 (inside_sw_if_indices, sw_if_index);
1158       else if (unformat (line_input, "out %U", unformat_vnet_sw_interface,
1159                          vnm, &sw_if_index))
1160         vec_add1 (outside_sw_if_indices, sw_if_index);
1161       else if (unformat (line_input, "del"))
1162         is_del = 1;
1163       else
1164         return clib_error_return (0, "unknown input '%U'",
1165           format_unformat_error, input);
1166     }
1167   unformat_free (line_input);
1168
1169   if (vec_len (inside_sw_if_indices))
1170     {
1171       if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
1172         feature_index = sm->rx_feature_in2out_fast;
1173       else
1174         feature_index = sm->rx_feature_in2out;
1175
1176       for (i = 0; i < vec_len(inside_sw_if_indices); i++)
1177         {
1178           sw_if_index = inside_sw_if_indices[i];
1179           ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
1180           ci = (is_del
1181                 ? vnet_config_del_feature
1182                 : vnet_config_add_feature)
1183             (vm, &rx_cm->config_main,
1184              ci,
1185              feature_index,
1186              0 /* config struct */, 
1187              0 /* sizeof config struct*/);
1188           rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
1189         }
1190     }
1191
1192   if (vec_len (outside_sw_if_indices))
1193     {
1194       if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
1195         feature_index = sm->rx_feature_out2in_fast;
1196       else
1197         feature_index = sm->rx_feature_out2in;
1198
1199       for (i = 0; i < vec_len(outside_sw_if_indices); i++)
1200         {
1201           sw_if_index = outside_sw_if_indices[i];
1202           ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
1203           ci = (is_del
1204                 ? vnet_config_del_feature
1205                 : vnet_config_add_feature)
1206             (vm, &rx_cm->config_main,
1207              ci,
1208              feature_index,
1209              0 /* config struct */, 
1210              0 /* sizeof config struct*/);
1211           rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
1212         }
1213     }
1214
1215   vec_free (inside_sw_if_indices);
1216   vec_free (outside_sw_if_indices);
1217
1218   return error;
1219 }
1220
1221 VLIB_CLI_COMMAND (set_interface_snat_command, static) = {
1222   .path = "set interface snat",
1223   .function = snat_feature_command_fn,
1224   .short_help = "set interface snat in <intfc> out <intfc> [del]",
1225 };
1226
1227 static clib_error_t *
1228 add_static_mapping_command_fn (vlib_main_t * vm,
1229                                unformat_input_t * input,
1230                                vlib_cli_command_t * cmd)
1231 {
1232   unformat_input_t _line_input, *line_input = &_line_input;
1233   clib_error_t * error = 0;
1234   ip4_address_t l_addr, e_addr;
1235   u32 l_port = 0, e_port = 0, vrf_id = ~0;
1236   int is_add = 1;
1237   int addr_only = 1;
1238   int rv;
1239
1240   /* Get a line of input. */
1241   if (!unformat_user (input, unformat_line_input, line_input))
1242     return 0;
1243
1244   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1245     {
1246       if (unformat (line_input, "local %U %u", unformat_ip4_address, &l_addr,
1247                     &l_port))
1248         addr_only = 0;
1249       else if (unformat (line_input, "local %U", unformat_ip4_address, &l_addr))
1250         ;
1251       else if (unformat (line_input, "external %U %u", unformat_ip4_address,
1252                          &e_addr, &e_port))
1253         addr_only = 0;
1254       else if (unformat (line_input, "external %U", unformat_ip4_address,
1255                          &e_addr))
1256         ;
1257       else if (unformat (line_input, "vrf %u", &vrf_id))
1258         ;
1259       else if (unformat (line_input, "del"))
1260         is_add = 0;
1261       else
1262         return clib_error_return (0, "unknown input: '%U'",
1263           format_unformat_error, line_input);
1264     }
1265   unformat_free (line_input);
1266
1267   rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port,
1268                                vrf_id, addr_only, is_add);
1269
1270   switch (rv)
1271     {
1272     case VNET_API_ERROR_INVALID_VALUE:
1273       return clib_error_return (0, "External port already in use.");
1274       break;
1275     case VNET_API_ERROR_NO_SUCH_ENTRY:
1276       if (is_add)
1277         return clib_error_return (0, "External addres must be allocated.");
1278       else
1279         return clib_error_return (0, "Mapping not exist.");
1280       break;
1281     case VNET_API_ERROR_NO_SUCH_FIB:
1282       return clib_error_return (0, "No such VRF id.");
1283     case VNET_API_ERROR_VALUE_EXIST:
1284       return clib_error_return (0, "Mapping already exist.");
1285     default:
1286       break;
1287     }
1288
1289   return error;
1290 }
1291
1292 /*?
1293  * @cliexpar
1294  * @cliexstart{snat add static mapping}
1295  * Static mapping allows hosts on the external network to initiate connection
1296  * to to the local network host.
1297  * To create static mapping between local host address 10.0.0.3 port 6303 and
1298  * external address 4.4.4.4 port 3606 use:
1299  *  vpp# snat add static mapping local 10.0.0.3 6303 external 4.4.4.4 3606
1300  * If not runnig "static mapping only" S-NAT plugin mode use before:
1301  *  vpp# snat add address 4.4.4.4
1302  * To create static mapping between local and external address use:
1303  *  vpp# snat add static mapping local 10.0.0.3 external 4.4.4.4
1304  * @cliexend
1305 ?*/
1306 VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
1307   .path = "snat add static mapping",
1308   .function = add_static_mapping_command_fn,
1309   .short_help =
1310     "snat add static mapping local <addr> [<port>] external <addr> [<port>] [vrf <table-id>] [del]",
1311 };
1312
1313 static clib_error_t *
1314 snat_config (vlib_main_t * vm, unformat_input_t * input)
1315 {
1316   snat_main_t * sm = &snat_main;
1317   u32 translation_buckets = 1024;
1318   u32 translation_memory_size = 128<<20;
1319   u32 user_buckets = 128;
1320   u32 user_memory_size = 64<<20;
1321   u32 max_translations_per_user = 100;
1322   u32 outside_vrf_id = 0;
1323   u32 inside_vrf_id = 0;
1324   u32 static_mapping_buckets = 1024;
1325   u32 static_mapping_memory_size = 64<<20;
1326   u8 static_mapping_only = 0;
1327   u8 static_mapping_connection_tracking = 0;
1328
1329   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1330     {
1331       if (unformat (input, "translation hash buckets %d", &translation_buckets))
1332         ;
1333       else if (unformat (input, "translation hash memory %d",
1334                          &translation_memory_size));
1335       else if (unformat (input, "user hash buckets %d", &user_buckets))
1336         ;
1337       else if (unformat (input, "user hash memory %d",
1338                          &user_memory_size))
1339         ;
1340       else if (unformat (input, "max translations per user %d",
1341                          &max_translations_per_user))
1342         ;
1343       else if (unformat (input, "outside VRF id %d",
1344                          &outside_vrf_id))
1345         ;
1346       else if (unformat (input, "inside VRF id %d",
1347                          &inside_vrf_id))
1348         ;
1349       else if (unformat (input, "static mapping only"))
1350         {
1351           static_mapping_only = 1;
1352           if (unformat (input, "connection tracking"))
1353             static_mapping_connection_tracking = 1;
1354         }
1355       else 
1356         return clib_error_return (0, "unknown input '%U'",
1357                                   format_unformat_error, input);
1358     }
1359
1360   /* for show commands, etc. */
1361   sm->translation_buckets = translation_buckets;
1362   sm->translation_memory_size = translation_memory_size;
1363   sm->user_buckets = user_buckets;
1364   sm->user_memory_size = user_memory_size;
1365   sm->max_translations_per_user = max_translations_per_user;
1366   sm->outside_vrf_id = outside_vrf_id;
1367   sm->outside_fib_index = ~0;
1368   sm->inside_vrf_id = inside_vrf_id;
1369   sm->inside_fib_index = ~0;
1370   sm->static_mapping_only = static_mapping_only;
1371   sm->static_mapping_connection_tracking = static_mapping_connection_tracking;
1372
1373   if (!static_mapping_only ||
1374       (static_mapping_only && static_mapping_connection_tracking))
1375     {
1376       clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets,
1377                             translation_memory_size);
1378
1379       clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets,
1380                             translation_memory_size);
1381
1382       clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets,
1383                             user_memory_size);
1384     }
1385   clib_bihash_init_8_8 (&sm->static_mapping_by_local,
1386                         "static_mapping_by_local", static_mapping_buckets,
1387                         static_mapping_memory_size);
1388
1389   clib_bihash_init_8_8 (&sm->static_mapping_by_external,
1390                         "static_mapping_by_external", static_mapping_buckets,
1391                         static_mapping_memory_size);
1392   return 0;
1393 }
1394
1395 VLIB_CONFIG_FUNCTION (snat_config, "snat");
1396
1397 u8 * format_snat_key (u8 * s, va_list * args)
1398 {
1399   snat_session_key_t * key = va_arg (*args, snat_session_key_t *);
1400   char * protocol_string = "unknown";
1401   static char *protocol_strings[] = {
1402       "UDP",
1403       "TCP",
1404       "ICMP",
1405   };
1406
1407   if (key->protocol < ARRAY_LEN(protocol_strings))
1408       protocol_string = protocol_strings[key->protocol];
1409
1410   s = format (s, "%U proto %s port %d fib %d",
1411               format_ip4_address, &key->addr, protocol_string,
1412               clib_net_to_host_u16 (key->port), key->fib_index);
1413   return s;
1414 }
1415
1416 u8 * format_snat_session (u8 * s, va_list * args)
1417 {
1418   snat_main_t * sm __attribute__((unused)) = va_arg (*args, snat_main_t *);
1419   snat_session_t * sess = va_arg (*args, snat_session_t *);
1420
1421   s = format (s, "  i2o %U\n", format_snat_key, &sess->in2out);
1422   s = format (s, "    o2i %U\n", format_snat_key, &sess->out2in);
1423   s = format (s, "       last heard %.2f\n", sess->last_heard);
1424   s = format (s, "       total pkts %d, total bytes %lld\n",
1425               sess->total_pkts, sess->total_bytes);
1426   if (snat_is_session_static (sess))
1427     s = format (s, "       static translation\n");
1428   else
1429     s = format (s, "       dynamic translation\n");
1430
1431   return s;
1432 }
1433
1434 u8 * format_snat_user (u8 * s, va_list * args)
1435 {
1436   snat_main_t * sm = va_arg (*args, snat_main_t *);
1437   snat_user_t * u = va_arg (*args, snat_user_t *);
1438   int verbose = va_arg (*args, int);
1439   dlist_elt_t * head, * elt;
1440   u32 elt_index, head_index;
1441   u32 session_index;
1442   snat_session_t * sess;
1443
1444   s = format (s, "%U: %d dynamic translations, %d static translations\n",
1445               format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions);
1446
1447   if (verbose == 0)
1448     return s;
1449
1450   if (u->nsessions || u->nstaticsessions)
1451     {
1452       head_index = u->sessions_per_user_list_head_index;
1453       head = pool_elt_at_index (sm->list_pool, head_index);
1454
1455       elt_index = head->next;
1456       elt = pool_elt_at_index (sm->list_pool, elt_index);
1457       session_index = elt->value;
1458
1459       while (session_index != ~0)
1460         {
1461           sess = pool_elt_at_index (sm->sessions, session_index);
1462
1463           s = format (s, "  %U\n", format_snat_session, sm, sess);
1464
1465           elt_index = elt->next;
1466           elt = pool_elt_at_index (sm->list_pool, elt_index);
1467           session_index = elt->value;
1468         }
1469     }
1470
1471   return s;
1472 }
1473
1474 u8 * format_snat_static_mapping (u8 * s, va_list * args)
1475 {
1476   snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *);
1477
1478   if (m->addr_only)
1479       s = format (s, "local %U external %U vrf %d",
1480                   format_ip4_address, &m->local_addr,
1481                   format_ip4_address, &m->external_addr,
1482                   m->vrf_id);
1483   else
1484       s = format (s, "local %U:%d external %U:%d vrf %d",
1485                   format_ip4_address, &m->local_addr, m->local_port,
1486                   format_ip4_address, &m->external_addr, m->external_port,
1487                   m->vrf_id);
1488
1489   return s;
1490 }
1491
1492 static clib_error_t *
1493 show_snat_command_fn (vlib_main_t * vm,
1494                  unformat_input_t * input,
1495                  vlib_cli_command_t * cmd)
1496 {
1497   int verbose = 0;
1498   snat_main_t * sm = &snat_main;
1499   snat_user_t * u;
1500   snat_static_mapping_t *m;
1501
1502   if (unformat (input, "detail"))
1503     verbose = 1;
1504   else if (unformat (input, "verbose"))
1505     verbose = 2;
1506
1507   if (sm->static_mapping_only)
1508     {
1509       if (sm->static_mapping_connection_tracking)
1510         vlib_cli_output (vm, "SNAT mode: static mapping only connection "
1511                          "tracking");
1512       else
1513         vlib_cli_output (vm, "SNAT mode: static mapping only");
1514     }
1515   else
1516     {
1517       vlib_cli_output (vm, "SNAT mode: dynamic translations enabled");
1518     }
1519
1520   if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
1521     {
1522       vlib_cli_output (vm, "%d static mappings",
1523                        pool_elts (sm->static_mappings));
1524
1525       if (verbose > 0)
1526         {
1527           pool_foreach (m, sm->static_mappings,
1528           ({
1529             vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
1530           }));
1531         }
1532     }
1533   else
1534     {
1535       vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions,"
1536                        " %d static mappings",
1537                        pool_elts (sm->users),
1538                        vec_len (sm->addresses),
1539                        pool_elts (sm->sessions),
1540                        pool_elts (sm->static_mappings));
1541
1542       if (verbose > 0)
1543         {
1544           vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out,
1545                            verbose - 1);
1546           vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in,
1547                            verbose - 1);
1548           vlib_cli_output (vm, "%d list pool elements",
1549                            pool_elts (sm->list_pool));
1550
1551           pool_foreach (u, sm->users,
1552           ({
1553             vlib_cli_output (vm, "%U", format_snat_user, sm, u, verbose - 1);
1554           }));
1555
1556           if (pool_elts (sm->static_mappings))
1557             {
1558               vlib_cli_output (vm, "static mappings:");
1559               pool_foreach (m, sm->static_mappings,
1560               ({
1561                 vlib_cli_output (vm, "%U", format_snat_static_mapping, m);
1562               }));
1563             }
1564         }
1565     }
1566
1567   return 0;
1568 }
1569
1570 VLIB_CLI_COMMAND (show_snat_command, static) = {
1571     .path = "show snat",
1572     .short_help = "show snat",
1573     .function = show_snat_command_fn,
1574 };