L2 over MPLS
[vpp.git] / src / plugins / snat / snat.c
index bce7d21..f196b5c 100644 (file)
@@ -72,6 +72,11 @@ 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) = {
@@ -79,12 +84,16 @@ VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
   .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* */
@@ -126,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,
@@ -156,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
@@ -622,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)
     {
@@ -798,7 +814,13 @@ int snat_interface_add_del_output_feature (u32 sw_if_index,
     return VNET_API_ERROR_UNSUPPORTED;
 
   if (is_inside)
-    goto find;
+    {
+      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)
     {
@@ -816,6 +838,7 @@ int snat_interface_add_del_output_feature (u32 sw_if_index,
                                    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);
@@ -823,7 +846,6 @@ int snat_interface_add_del_output_feature (u32 sw_if_index,
   if (sm->fq_out2in_index == ~0 && sm->num_workers > 1)
     sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0);
 
-find:
   pool_foreach (i, sm->output_feature_interfaces,
   ({
     if (i->sw_if_index == sw_if_index)
@@ -866,7 +888,7 @@ fib:
 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;
@@ -878,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;
 }
 
@@ -914,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;
@@ -933,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)
     {
@@ -941,6 +972,10 @@ 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)
@@ -1059,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)
 {
@@ -1077,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); \
@@ -1709,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;
 
@@ -1788,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);