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