L2 over MPLS
[vpp.git] / src / plugins / snat / snat.c
index 351f8dc..f196b5c 100644 (file)
@@ -72,6 +72,29 @@ VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
   .node_name = "snat-out2in-fast",
   .runs_before = VNET_FEATURES ("ip4-lookup"),
 };
+VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = {
+  .arc_name = "ip4-unicast",
+  .node_name = "snat-hairpin-dst",
+  .runs_before = VNET_FEATURES ("ip4-lookup"),
+};
+
+/* Hook up output features */
+VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
+  .arc_name = "ip4-output",
+  .node_name = "snat-in2out-output",
+  .runs_before = VNET_FEATURES ("interface-output"),
+};
+VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = {
+  .arc_name = "ip4-output",
+  .node_name = "snat-in2out-output-worker-handoff",
+  .runs_before = VNET_FEATURES ("interface-output"),
+};
+VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = {
+  .arc_name = "ip4-output",
+  .node_name = "snat-hairpin-src",
+  .runs_before = VNET_FEATURES ("interface-output"),
+};
+
 
 /* *INDENT-OFF* */
 VLIB_PLUGIN_REGISTER () = {
@@ -112,7 +135,7 @@ snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index,
                                     (FIB_ENTRY_FLAG_CONNECTED |
                                      FIB_ENTRY_FLAG_LOCAL |
                                      FIB_ENTRY_FLAG_EXCLUSIVE),
-                                    FIB_PROTOCOL_IP4,
+                                    DPO_PROTO_IP4,
                                     NULL,
                                     sw_if_index,
                                     ~0,
@@ -142,7 +165,11 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id)
 
   vec_add2 (sm->addresses, ap, 1);
   ap->addr = *addr;
-  ap->fib_index = ip4_fib_index_from_table_id(vrf_id);
+  if (vrf_id != ~0)
+    ap->fib_index =
+      fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id);
+  else
+    ap->fib_index = ~0;
 #define _(N, i, n, s) \
   clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535);
   foreach_snat_protocol
@@ -157,6 +184,14 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id)
     snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
     break;
   }));
+  pool_foreach (i, sm->output_feature_interfaces,
+  ({
+    if (i->is_inside)
+      continue;
+
+    snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
+    break;
+  }));
 }
 
 static int is_snat_address_used_in_static_mapping (snat_main_t *sm,
@@ -468,6 +503,31 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                             continue;
                         }
 
+                      if (snat_is_unk_proto_session (s))
+                        {
+                          clib_bihash_kv_16_8_t up_kv;
+                          snat_unk_proto_ses_key_t up_key;
+                          up_key.l_addr = s->in2out.addr;
+                          up_key.r_addr = s->ext_host_addr;
+                          up_key.fib_index = s->in2out.fib_index;
+                          up_key.proto = s->in2out.port;
+                          up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0;
+                          up_kv.key[0] = up_key.as_u64[0];
+                          up_kv.key[1] = up_key.as_u64[1];
+                          if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto,
+                                                        &up_kv, 0))
+                            clib_warning ("in2out key del failed");
+
+                          up_key.l_addr = s->out2in.addr;
+                          up_key.fib_index = s->out2in.fib_index;
+                          up_kv.key[0] = up_key.as_u64[0];
+                          up_kv.key[1] = up_key.as_u64[1];
+                          if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto,
+                                                        &up_kv, 0))
+                            clib_warning ("out2in key del failed");
+
+                          goto delete;
+                        }
                       /* log NAT event */
                       snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
                                                           s->out2in.addr.as_u32,
@@ -477,9 +537,12 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                                                           s->in2out.fib_index);
 
                       value.key = s->in2out.as_u64;
-                      clib_bihash_add_del_8_8 (&sm->in2out, &value, 0);
+                      if (clib_bihash_add_del_8_8 (&sm->in2out, &value, 0))
+                        clib_warning ("in2out key del failed");
                       value.key = s->out2in.as_u64;
-                      clib_bihash_add_del_8_8 (&sm->out2in, &value, 0);
+                      if (clib_bihash_add_del_8_8 (&sm->out2in, &value, 0))
+                        clib_warning ("out2in key del failed");
+delete:
                       pool_put (tsm->sessions, s);
 
                       clib_dlist_remove (tsm->list_pool, del_elt_index);
@@ -514,6 +577,14 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
     snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
     break;
   }));
+  pool_foreach (interface, sm->output_feature_interfaces,
+  ({
+    if (interface->is_inside)
+      continue;
+
+    snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
+    break;
+  }));
 
   return 0;
 }
@@ -564,6 +635,9 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
         }
     }
 
+  if (a->fib_index != ~0)
+    fib_table_unlock(a->fib_index, FIB_PROTOCOL_IP4);
+
   /* Delete sessions using address */
   if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports)
     {
@@ -572,18 +646,44 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
           pool_foreach (ses, tsm->sessions, ({
             if (ses->out2in.addr.as_u32 == addr.as_u32)
               {
-                /* log NAT event */
-                snat_ipfix_logging_nat44_ses_delete(ses->in2out.addr.as_u32,
-                                                    ses->out2in.addr.as_u32,
-                                                    ses->in2out.protocol,
-                                                    ses->in2out.port,
-                                                    ses->out2in.port,
-                                                    ses->in2out.fib_index);
+                if (snat_is_unk_proto_session (ses))
+                  {
+                    clib_bihash_kv_16_8_t up_kv;
+                    snat_unk_proto_ses_key_t up_key;
+                    up_key.l_addr = ses->in2out.addr;
+                    up_key.r_addr = ses->ext_host_addr;
+                    up_key.fib_index = ses->in2out.fib_index;
+                    up_key.proto = ses->in2out.port;
+                    up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0;
+                    up_kv.key[0] = up_key.as_u64[0];
+                    up_kv.key[1] = up_key.as_u64[1];
+                    if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto,
+                                                  &up_kv, 0))
+                      clib_warning ("in2out key del failed");
+
+                    up_key.l_addr = ses->out2in.addr;
+                    up_key.fib_index = ses->out2in.fib_index;
+                    up_kv.key[0] = up_key.as_u64[0];
+                    up_kv.key[1] = up_key.as_u64[1];
+                    if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto,
+                                                  &up_kv, 0))
+                      clib_warning ("out2in key del failed");
+                  }
+                else
+                  {
+                    /* log NAT event */
+                    snat_ipfix_logging_nat44_ses_delete(ses->in2out.addr.as_u32,
+                                                        ses->out2in.addr.as_u32,
+                                                        ses->in2out.protocol,
+                                                        ses->in2out.port,
+                                                        ses->out2in.port,
+                                                        ses->in2out.fib_index);
+                    kv.key = ses->in2out.as_u64;
+                    clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0);
+                    kv.key = ses->out2in.as_u64;
+                    clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0);
+                  }
                 vec_add1 (ses_to_be_removed, ses - tsm->sessions);
-                kv.key = ses->in2out.as_u64;
-                clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0);
-                kv.key = ses->out2in.as_u64;
-                clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0);
                 clib_dlist_remove (tsm->list_pool, ses->per_user_index);
                 user_key.addr = ses->in2out.addr;
                 user_key.fib_index = ses->in2out.fib_index;
@@ -614,6 +714,14 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
     snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
     break;
   }));
+  pool_foreach (interface, sm->output_feature_interfaces,
+  ({
+    if (interface->is_inside)
+      continue;
+
+    snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
+    break;
+  }));
 
   return 0;
 }
@@ -692,10 +800,95 @@ fib:
   return 0;
 }
 
+int snat_interface_add_del_output_feature (u32 sw_if_index,
+                                           u8 is_inside,
+                                           int is_del)
+{
+  snat_main_t *sm = &snat_main;
+  snat_interface_t *i;
+  snat_address_t * ap;
+  snat_static_mapping_t * m;
+
+  if (sm->deterministic ||
+      (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)))
+    return VNET_API_ERROR_UNSUPPORTED;
+
+  if (is_inside)
+    {
+      vnet_feature_enable_disable ("ip4-unicast", "snat-hairpin-dst",
+                                   sw_if_index, !is_del, 0, 0);
+      vnet_feature_enable_disable ("ip4-output", "snat-hairpin-src",
+                                   sw_if_index, !is_del, 0, 0);
+      goto fq;
+    }
+
+  if (sm->num_workers > 1)
+    {
+      vnet_feature_enable_disable ("ip4-unicast", "snat-out2in-worker-handoff",
+                                   sw_if_index, !is_del, 0, 0);
+      vnet_feature_enable_disable ("ip4-output",
+                                   "snat-in2out-output-worker-handoff",
+                                   sw_if_index, !is_del, 0, 0);
+    }
+  else
+    {
+      vnet_feature_enable_disable ("ip4-unicast", "snat-out2in", sw_if_index,
+                                   !is_del, 0, 0);
+      vnet_feature_enable_disable ("ip4-output", "snat-in2out-output",
+                                   sw_if_index, !is_del, 0, 0);
+    }
+
+fq:
+  if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1)
+    sm->fq_in2out_output_index =
+      vlib_frame_queue_main_init (sm->in2out_output_node_index, 0);
+
+  if (sm->fq_out2in_index == ~0 && sm->num_workers > 1)
+    sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0);
+
+  pool_foreach (i, sm->output_feature_interfaces,
+  ({
+    if (i->sw_if_index == sw_if_index)
+      {
+        if (is_del)
+          pool_put (sm->output_feature_interfaces, i);
+        else
+          return VNET_API_ERROR_VALUE_EXIST;
+
+        goto fib;
+      }
+  }));
+
+  if (is_del)
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+  pool_get (sm->output_feature_interfaces, i);
+  i->sw_if_index = sw_if_index;
+  i->is_inside = is_inside;
+
+  /* Add/delete external addresses to FIB */
+fib:
+  if (is_inside)
+    return 0;
+
+  vec_foreach (ap, sm->addresses)
+    snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del);
+
+  pool_foreach (m, sm->static_mappings,
+  ({
+    if (!(m->addr_only))
+      continue;
+
+    snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
+  }));
+
+  return 0;
+}
+
 int snat_set_workers (uword * bitmap)
 {
   snat_main_t *sm = &snat_main;
-  int i;
+  int i, j = 0;
 
   if (sm->num_workers < 2)
     return VNET_API_ERROR_FEATURE_DISABLED;
@@ -707,8 +900,13 @@ int snat_set_workers (uword * bitmap)
   clib_bitmap_foreach (i, bitmap,
     ({
       vec_add1(sm->workers, i);
+      sm->per_thread_data[i].snat_thread_index = j;
+      j++;
     }));
 
+  sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers);
+  sm->num_snat_thread = _vec_len (sm->workers);
+
   return 0;
 }
 
@@ -725,7 +923,7 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
 static clib_error_t * snat_init (vlib_main_t * vm)
 {
   snat_main_t * sm = &snat_main;
-  clib_error_t * error = 0, * error_nat64 = 0;
+  clib_error_t * error = 0;
   ip4_main_t * im = &ip4_main;
   ip_lookup_main_t * lm = &im->lookup_main;
   uword *p;
@@ -743,7 +941,9 @@ static clib_error_t * snat_init (vlib_main_t * vm)
   sm->first_worker_index = 0;
   sm->next_worker = 0;
   sm->num_workers = 0;
+  sm->num_snat_thread = 1;
   sm->workers = 0;
+  sm->port_per_thread = 0xffff - 1024;
   sm->fq_in2out_index = ~0;
   sm->fq_out2in_index = ~0;
   sm->udp_timeout = SNAT_UDP_TIMEOUT;
@@ -762,6 +962,8 @@ static clib_error_t * snat_init (vlib_main_t * vm)
         }
     }
 
+  vec_validate (sm->per_thread_data, tm->n_vlib_mains - 1);
+
   /* Use all available workers by default */
   if (sm->num_workers > 1)
     {
@@ -770,8 +972,14 @@ static clib_error_t * snat_init (vlib_main_t * vm)
       snat_set_workers(bitmap);
       clib_bitmap_free (bitmap);
     }
+  else
+    {
+      sm->per_thread_data[0].snat_thread_index = 0;
+    }
 
   error = snat_api_init(vm, sm);
+  if (error)
+    return error;
 
   /* Set up the interface address add/del callback */
   cb4.function = snat_ip4_add_del_interface_address_cb;
@@ -782,9 +990,7 @@ static clib_error_t * snat_init (vlib_main_t * vm)
   /* Init IPFIX logging */
   snat_ipfix_logging_init(vm);
 
-  error_nat64 = nat64_init(vm);
-  if (error_nat64)
-    clib_warning("NAT64 init failed: %U", format_clib_error, error_nat64);
+  error = nat64_init(vm);
 
   return error;
 }
@@ -888,8 +1094,16 @@ int snat_static_mapping_match (snat_main_t * sm,
   return 0;
 }
 
-int snat_alloc_outside_address_and_port (snat_main_t * sm, 
+static_always_inline u16
+snat_random_port (snat_main_t * sm, u16 min, u16 max)
+{
+  return min + random_u32 (&sm->random_seed) /
+    (random_u32_max() / (max - min + 1) + 1);
+}
+
+int snat_alloc_outside_address_and_port (snat_main_t * sm,
                                          u32 fib_index,
+                                         u32 thread_index,
                                          snat_session_key_t * k,
                                          u32 * address_indexp)
 {
@@ -906,14 +1120,13 @@ int snat_alloc_outside_address_and_port (snat_main_t * sm,
         {
 #define _(N, j, n, s) \
         case SNAT_PROTOCOL_##N: \
-          if (a->busy_##n##_ports < (65535-1024)) \
+          if (a->busy_##n##_ports < (sm->port_per_thread * sm->num_snat_thread)) \
             { \
               while (1) \
                 { \
-                  portnum = random_u32 (&sm->random_seed); \
-                  portnum &= 0xFFFF; \
-                  if (portnum < 1024) \
-                    continue; \
+                  portnum = (sm->port_per_thread * \
+                    sm->per_thread_data[thread_index].snat_thread_index) + \
+                    snat_random_port(sm, 0, sm->port_per_thread) + 1024; \
                   if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
                     continue; \
                   clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
@@ -1049,6 +1262,7 @@ snat_feature_command_fn (vlib_main_t * vm,
   u32 sw_if_index;
   u32 * inside_sw_if_indices = 0;
   u32 * outside_sw_if_indices = 0;
+  u8 is_output_feature = 0;
   int is_del = 0;
   int i;
 
@@ -1066,6 +1280,8 @@ snat_feature_command_fn (vlib_main_t * vm,
       else if (unformat (line_input, "out %U", unformat_vnet_sw_interface,
                          vnm, &sw_if_index))
         vec_add1 (outside_sw_if_indices, sw_if_index);
+      else if (unformat (line_input, "output-feature"))
+        is_output_feature = 1;
       else if (unformat (line_input, "del"))
         is_del = 1;
       else
@@ -1081,7 +1297,30 @@ snat_feature_command_fn (vlib_main_t * vm,
       for (i = 0; i < vec_len(inside_sw_if_indices); i++)
         {
           sw_if_index = inside_sw_if_indices[i];
-          snat_interface_add_del (sw_if_index, 1, is_del);
+          if (is_output_feature)
+            {
+              if (snat_interface_add_del_output_feature (sw_if_index, 1, is_del))
+                {
+                  error = clib_error_return (0, "%s %U failed",
+                                             is_del ? "del" : "add",
+                                             format_vnet_sw_interface_name, vnm,
+                                             vnet_get_sw_interface (vnm,
+                                                                    sw_if_index));
+                  goto done;
+                }
+            }
+          else
+            {
+              if (snat_interface_add_del (sw_if_index, 1, is_del))
+                {
+                  error = clib_error_return (0, "%s %U failed",
+                                             is_del ? "del" : "add",
+                                             format_vnet_sw_interface_name, vnm,
+                                             vnet_get_sw_interface (vnm,
+                                                                    sw_if_index));
+                  goto done;
+                }
+            }
         }
     }
 
@@ -1090,7 +1329,30 @@ snat_feature_command_fn (vlib_main_t * vm,
       for (i = 0; i < vec_len(outside_sw_if_indices); i++)
         {
           sw_if_index = outside_sw_if_indices[i];
-          snat_interface_add_del (sw_if_index, 0, is_del);
+          if (is_output_feature)
+            {
+              if (snat_interface_add_del_output_feature (sw_if_index, 0, is_del))
+                {
+                  error = clib_error_return (0, "%s %U failed",
+                                             is_del ? "del" : "add",
+                                             format_vnet_sw_interface_name, vnm,
+                                             vnet_get_sw_interface (vnm,
+                                                                    sw_if_index));
+                  goto done;
+                }
+            }
+          else
+            {
+              if (snat_interface_add_del (sw_if_index, 0, is_del))
+                {
+                  error = clib_error_return (0, "%s %U failed",
+                                             is_del ? "del" : "add",
+                                             format_vnet_sw_interface_name, vnm,
+                                             vnet_get_sw_interface (vnm,
+                                                                    sw_if_index));
+                  goto done;
+                }
+            }
         }
     }
 
@@ -1105,7 +1367,8 @@ done:
 VLIB_CLI_COMMAND (set_interface_snat_command, static) = {
   .path = "set interface snat",
   .function = snat_feature_command_fn,
-  .short_help = "set interface snat in <intfc> out <intfc> [del]",
+  .short_help = "set interface snat in <intfc> out <intfc> [output-feature] "
+                "[del]",
 };
 
 uword
@@ -1488,7 +1751,6 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
   u32 static_mapping_memory_size = 64<<20;
   u8 static_mapping_only = 0;
   u8 static_mapping_connection_tracking = 0;
-  vlib_thread_main_t *tm = vlib_get_thread_main ();
 
   sm->deterministic = 0;
 
@@ -1543,6 +1805,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
   if (sm->deterministic)
     {
       sm->in2out_node_index = snat_det_in2out_node.index;
+      sm->in2out_output_node_index = ~0;
       sm->out2in_node_index = snat_det_out2in_node.index;
       sm->icmp_match_in2out_cb = icmp_match_in2out_det;
       sm->icmp_match_out2in_cb = icmp_match_out2in_det;
@@ -1552,6 +1815,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
       sm->worker_in2out_cb = snat_get_worker_in2out_cb;
       sm->worker_out2in_cb = snat_get_worker_out2in_cb;
       sm->in2out_node_index = snat_in2out_node.index;
+      sm->in2out_output_node_index = snat_in2out_output_node.index;
       sm->out2in_node_index = snat_out2in_node.index;
       if (!static_mapping_only ||
           (static_mapping_only && static_mapping_connection_tracking))
@@ -1565,8 +1829,6 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
           clib_bihash_init_8_8 (&sm->worker_by_out, "worker-by-out", user_buckets,
                                 user_memory_size);
 
-          vec_validate (sm->per_thread_data, tm->n_vlib_mains - 1);
-
           clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets,
                                 translation_memory_size);
 
@@ -1575,6 +1837,12 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
 
           clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets,
                                 user_memory_size);
+
+          clib_bihash_init_16_8 (&sm->in2out_unk_proto, "in2out-unk-proto",
+                                 translation_buckets, translation_memory_size);
+
+          clib_bihash_init_16_8 (&sm->out2in_unk_proto, "out2in-unk-proto",
+                                 translation_buckets, translation_memory_size);
         }
       else
         {
@@ -1636,8 +1904,20 @@ u8 * format_snat_session (u8 * s, va_list * args)
   snat_main_t * sm __attribute__((unused)) = va_arg (*args, snat_main_t *);
   snat_session_t * sess = va_arg (*args, snat_session_t *);
 
-  s = format (s, "  i2o %U\n", format_snat_key, &sess->in2out);
-  s = format (s, "    o2i %U\n", format_snat_key, &sess->out2in);
+  if (snat_is_unk_proto_session (sess))
+    {
+      s = format (s, "  i2o %U proto %u fib %u\n",
+                  format_ip4_address, &sess->in2out.addr, sess->in2out.port,
+                  sess->in2out.fib_index);
+      s = format (s, "    o2i %U proto %u fib %u\n",
+                  format_ip4_address, &sess->out2in.addr, sess->out2in.port,
+                  sess->out2in.fib_index);
+    }
+  else
+    {
+      s = format (s, "  i2o %U\n", format_snat_key, &sess->in2out);
+      s = format (s, "    o2i %U\n", format_snat_key, &sess->out2in);
+    }
   s = format (s, "       last heard %.2f\n", sess->last_heard);
   s = format (s, "       total pkts %d, total bytes %lld\n",
               sess->total_pkts, sess->total_bytes);
@@ -1809,6 +2089,14 @@ show_snat_command_fn (vlib_main_t * vm,
                          i->is_inside ? "in" : "out");
       }));
 
+      pool_foreach (i, sm->output_feature_interfaces,
+      ({
+        vlib_cli_output (vm, "%U output-feature %s",
+                         format_vnet_sw_interface_name, vnm,
+                         vnet_get_sw_interface (vnm, i->sw_if_index),
+                         i->is_inside ? "in" : "out");
+      }));
+
       if (vec_len (sm->auto_add_sw_if_indices))
         {
           vlib_cli_output (vm, "SNAT pool addresses interfaces:");