NAT44: Fix interface feature removal.
[vpp.git] / src / plugins / nat / nat.c
index e9b2c2c..bedf4e5 100644 (file)
@@ -20,6 +20,7 @@
 #include <vnet/ip/ip4.h>
 #include <vnet/plugin/plugin.h>
 #include <nat/nat.h>
+#include <nat/nat_dpo.h>
 #include <nat/nat_ipfix_logging.h>
 #include <nat/nat_det.h>
 #include <nat/nat64.h>
@@ -567,7 +568,7 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id,
   /* Add external address to FIB */
   pool_foreach (i, sm->interfaces,
   ({
-    if (nat_interface_is_inside(i))
+    if (nat_interface_is_inside(i) || sm->out2in_dpo)
       continue;
 
     snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
@@ -575,7 +576,7 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id,
   }));
   pool_foreach (i, sm->output_feature_interfaces,
   ({
-    if (nat_interface_is_inside(i))
+    if (nat_interface_is_inside(i) || sm->out2in_dpo)
       continue;
 
     snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1);
@@ -642,13 +643,14 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
  * @param sw_if_index External port instead of specific IP address.
  * @param is_add If 0 delete static mapping, otherwise add.
  * @param twice_nat If 1 translate external host address and port.
+ * @param out2in_only If 1 rule match only out2in direction
  *
  * @returns
  */
 int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                             u16 l_port, u16 e_port, u32 vrf_id, int addr_only,
                             u32 sw_if_index, snat_protocol_t proto, int is_add,
-                            u8 twice_nat)
+                            u8 twice_nat, u8 out2in_only)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -722,7 +724,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
 
       /* Find external address in allocated addresses and reserve port for
          address and port pair mapping when dynamic translations enabled */
-      if (!addr_only && !(sm->static_mapping_only))
+      if (!(addr_only || sm->static_mapping_only || out2in_only))
         {
           for (i = 0; i < vec_len (sm->addresses); i++)
             {
@@ -765,6 +767,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       m->vrf_id = vrf_id;
       m->fib_index = fib_index;
       m->twice_nat = twice_nat;
+      m->out2in_only = out2in_only;
       if (!addr_only)
         {
           m->local_port = l_port;
@@ -789,8 +792,9 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       m_key.fib_index = m->fib_index;
       kv.key = m_key.as_u64;
       kv.value = m - sm->static_mappings;
-      clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
-      if (twice_nat)
+      if (!out2in_only)
+        clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
+      if (twice_nat || out2in_only)
         {
           m_key.port = clib_host_to_net_u16 (l_port);
           kv.key = m_key.as_u64;
@@ -805,7 +809,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       kv.key = m_key.as_u64;
       kv.value = m - sm->static_mappings;
       clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1);
-      if (twice_nat)
+      if (twice_nat || out2in_only)
         {
           m_key.port = clib_host_to_net_u16 (e_port);
           kv.key = m_key.as_u64;
@@ -821,7 +825,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
         return VNET_API_ERROR_NO_SUCH_ENTRY;
 
       /* Free external address port */
-      if (!addr_only && !(sm->static_mapping_only))
+      if (!(addr_only || sm->static_mapping_only || out2in_only))
         {
           for (i = 0; i < vec_len (sm->addresses); i++)
             {
@@ -860,8 +864,9 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       m_key.protocol = m->proto;
       m_key.fib_index = m->fib_index;
       kv.key = m_key.as_u64;
-      clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0);
-      if (twice_nat)
+      if (!out2in_only)
+        clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0);
+      if (twice_nat || out2in_only)
         {
           m_key.port = clib_host_to_net_u16 (m->local_port);
           kv.key = m_key.as_u64;
@@ -875,7 +880,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       m_key.fib_index = sm->outside_fib_index;
       kv.key = m_key.as_u64;
       clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0);
-      if (twice_nat)
+      if (twice_nat || out2in_only)
         {
           m_key.port = clib_host_to_net_u16 (m->external_port);
           kv.key = m_key.as_u64;
@@ -951,7 +956,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
   /* Add/delete external address to FIB */
   pool_foreach (interface, sm->interfaces,
   ({
-    if (nat_interface_is_inside(interface))
+    if (nat_interface_is_inside(interface) || sm->out2in_dpo)
       continue;
 
     snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
@@ -959,7 +964,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
   }));
   pool_foreach (interface, sm->output_feature_interfaces,
   ({
-    if (nat_interface_is_inside(interface))
+    if (nat_interface_is_inside(interface) || sm->out2in_dpo)
       continue;
 
     snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add);
@@ -972,7 +977,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
 int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                                      snat_protocol_t proto, u32 vrf_id,
                                      nat44_lb_addr_port_t *locals, u8 is_add,
-                                     u8 twice_nat)
+                                     u8 twice_nat, u8 out2in_only)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -1013,7 +1018,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
 
       /* Find external address in allocated addresses and reserve port for
          address and port pair mapping when dynamic translations enabled */
-      if (!sm->static_mapping_only)
+      if (!(sm->static_mapping_only || out2in_only))
         {
           for (i = 0; i < vec_len (sm->addresses); i++)
             {
@@ -1057,6 +1062,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       m->external_port = e_port;
       m->proto = proto;
       m->twice_nat = twice_nat;
+      m->out2in_only = out2in_only;
 
       m_key.addr = m->external_addr;
       m_key.port = m->external_port;
@@ -1094,10 +1100,13 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       for (i = 0; i < vec_len (locals); i++)
         {
           m_key.addr = locals[i].addr;
-          m_key.port = locals[i].port;
-          kv.key = m_key.as_u64;
-          kv.value = m - sm->static_mappings;
-          clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
+          if (!out2in_only)
+            {
+              m_key.port = locals[i].port;
+              kv.key = m_key.as_u64;
+              kv.value = m - sm->static_mappings;
+              clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
+            }
           locals[i].prefix = (i == 0) ? locals[i].probability :\
             (locals[i - 1].prefix + locals[i].probability);
           vec_add1 (m->locals, locals[i]);
@@ -1120,7 +1129,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_HI);
 
       /* Free external address port */
-      if (!sm->static_mapping_only)
+      if (!(sm->static_mapping_only || out2in_only))
         {
           for (i = 0; i < vec_len (sm->addresses); i++)
             {
@@ -1172,13 +1181,16 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       vec_foreach (local, m->locals)
         {
           m_key.addr = local->addr;
-          m_key.port = local->port;
-          m_key.fib_index = m->fib_index;
-          kv.key = m_key.as_u64;
-          if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0))
+          if (!out2in_only)
             {
-              clib_warning ("static_mapping_by_local key del failed");
-              return VNET_API_ERROR_UNSPECIFIED;
+              m_key.port = local->port;
+              m_key.fib_index = m->fib_index;
+              kv.key = m_key.as_u64;
+              if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0))
+                {
+                  clib_warning ("static_mapping_by_local key del failed");
+                  return VNET_API_ERROR_UNSPECIFIED;
+                }
             }
 
           m_key.port = clib_host_to_net_u16 (local->port);
@@ -1265,7 +1277,8 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
             (void) snat_add_static_mapping (m->local_addr, m->external_addr,
                                             m->local_port, m->external_port,
                                             m->vrf_id, m->addr_only, ~0,
-                                            m->proto, 0, m->twice_nat);
+                                            m->proto, 0, m->twice_nat,
+                                            m->out2in_only);
       }));
     }
   else
@@ -1324,7 +1337,7 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
   /* Delete external address from FIB */
   pool_foreach (interface, sm->interfaces,
   ({
-    if (nat_interface_is_inside(interface))
+    if (nat_interface_is_inside(interface) || sm->out2in_dpo)
       continue;
 
     snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
@@ -1332,7 +1345,7 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
   }));
   pool_foreach (interface, sm->output_feature_interfaces,
   ({
-    if (nat_interface_is_inside(interface))
+    if (nat_interface_is_inside(interface) || sm->out2in_dpo)
       continue;
 
     snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0);
@@ -1351,6 +1364,9 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
   snat_static_mapping_t * m;
   snat_det_map_t * dm;
 
+  if (sm->out2in_dpo && !is_inside)
+    return VNET_API_ERROR_UNSUPPORTED;
+
   if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
     feature_name = is_inside ?  "nat44-in2out-fast" : "nat44-out2in-fast";
   else
@@ -1383,11 +1399,22 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
                   i->flags &= ~NAT_INTERFACE_FLAG_IS_OUTSIDE;
 
                 if (sm->num_workers > 1 && !sm->deterministic)
-                  del_feature_name = "nat44-handoff-classify";
+                  {
+                    del_feature_name = "nat44-handoff-classify";
+                    feature_name = !is_inside ?  "nat44-in2out-worker-handoff" :
+                                                 "nat44-out2in-worker-handoff";
+                  }
                 else if (sm->deterministic)
-                  del_feature_name = "nat44-det-classify";
+                  {
+                    del_feature_name = "nat44-det-classify";
+                    feature_name = !is_inside ?  "nat44-det-in2out" :
+                                                 "nat44-det-out2in";
+                  }
                 else
-                  del_feature_name = "nat44-classify";
+                  {
+                    del_feature_name = "nat44-classify";
+                    feature_name = !is_inside ?  "nat44-in2out" : "nat44-out2in";
+                  }
 
                 vnet_feature_enable_disable ("ip4-unicast", del_feature_name,
                                              sw_if_index, 0, 0, 0);
@@ -1452,7 +1479,7 @@ set_flags:
 
   /* Add/delete external addresses to FIB */
 fib:
-  if (is_inside)
+  if (is_inside && !sm->out2in_dpo)
     {
       vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
                                    sw_if_index, !is_del, 0, 0);
@@ -1582,7 +1609,7 @@ 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;
+      sm->per_thread_data[sm->first_worker_index + i].snat_thread_index = j;
       j++;
     }));
 
@@ -1680,6 +1707,8 @@ static clib_error_t * snat_init (vlib_main_t * vm)
 
   vec_add1 (im->add_del_interface_address_callbacks, cb4);
 
+  nat_dpo_module_init ();
+
   /* Init IPFIX logging */
   snat_ipfix_logging_init(vm);
 
@@ -1988,6 +2017,29 @@ exhausted:
   return 1;
 }
 
+void
+nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add)
+{
+  dpo_id_t dpo_v4 = DPO_INVALID;
+  fib_prefix_t pfx = {
+    .fp_proto = FIB_PROTOCOL_IP4,
+    .fp_len = 32,
+    .fp_addr.ip4.as_u32 = addr.as_u32,
+  };
+
+  if (is_add)
+    {
+      nat_dpo_create (DPO_PROTO_IP4, 0, &dpo_v4);
+      fib_table_entry_special_dpo_add (0, &pfx, FIB_SOURCE_PLUGIN_HI,
+                                       FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4);
+      dpo_reset (&dpo_v4);
+    }
+  else
+    {
+      fib_table_entry_special_remove (0, &pfx, FIB_SOURCE_PLUGIN_HI);
+    }
+}
+
 static clib_error_t *
 add_address_command_fn (vlib_main_t * vm,
                         unformat_input_t * input,
@@ -2074,6 +2126,9 @@ add_address_command_fn (vlib_main_t * vm,
           break;
         }
 
+      if (sm->out2in_dpo)
+        nat44_add_del_address_dpo (this_addr, is_add);
+
       increment_v4_address (&this_addr);
     }
 
@@ -2260,6 +2315,7 @@ add_static_mapping_command_fn (vlib_main_t * vm,
   snat_protocol_t proto = ~0;
   u8 proto_set = 0;
   u8 twice_nat = 0;
+  u8 out2in_only = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -2292,6 +2348,8 @@ add_static_mapping_command_fn (vlib_main_t * vm,
         proto_set = 1;
       else if (unformat (line_input, "twice-nat"))
         twice_nat = 1;
+      else if (unformat (line_input, "out2in-only"))
+        out2in_only = 1;
       else if (unformat (line_input, "del"))
         is_add = 0;
       else
@@ -2316,7 +2374,7 @@ add_static_mapping_command_fn (vlib_main_t * vm,
 
   rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port,
                                vrf_id, addr_only, sw_if_index, proto, is_add,
-                               twice_nat);
+                               twice_nat, out2in_only);
 
   switch (rv)
     {
@@ -2364,7 +2422,7 @@ VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
   .function = add_static_mapping_command_fn,
   .short_help =
     "nat44 add static mapping tcp|udp|icmp local <addr> [<port>] "
-    "external <addr> [<port>] [vrf <table-id>] [twice-nat] [del]",
+    "external <addr> [<port>] [vrf <table-id>] [twice-nat] [out2in-only] [del]",
 };
 
 static clib_error_t *
@@ -2413,7 +2471,7 @@ add_identity_mapping_command_fn (vlib_main_t * vm,
 
   rv = snat_add_static_mapping(addr, addr, (u16) port, (u16) port,
                                vrf_id, addr_only, sw_if_index, proto, is_add,
-                               0);
+                               0, 0);
 
   switch (rv)
     {
@@ -2477,6 +2535,7 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm,
   u8 proto_set = 0;
   nat44_lb_addr_port_t *locals = 0, local;
   u8 twice_nat = 0;
+  u8 out2in_only = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -2503,6 +2562,8 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm,
         proto_set = 1;
       else if (unformat (line_input, "twice-nat"))
         twice_nat = 1;
+      else if (unformat (line_input, "out2in-only"))
+        out2in_only = 1;
       else if (unformat (line_input, "del"))
         is_add = 0;
       else
@@ -2526,7 +2587,7 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm,
     }
 
   rv = nat44_add_del_lb_static_mapping (e_addr, (u16) e_port, proto, vrf_id,
-                                        locals, is_add, twice_nat);
+                                        locals, is_add, twice_nat, out2in_only);
 
   switch (rv)
     {
@@ -2559,7 +2620,7 @@ VLIB_CLI_COMMAND (add_lb_static_mapping_command, static) = {
   .short_help =
     "nat44 add load-balancing static mapping protocol tcp|udp "
     "external <addr>:<port> local <addr>:<port> probability <n> [twice-nat] "
-    "[vrf <table-id>] [del]",
+    "[vrf <table-id>] [out2in-only] [del]",
 };
 
 static clib_error_t *
@@ -2726,6 +2787,7 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0)
   snat_session_t *s;
   int i;
   u32 proto;
+  u32 next_worker_index = 0;
 
   /* first try static mappings without port */
   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
@@ -2841,7 +2903,10 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0)
     }
 
   /* worker by outside port */
-  return (u32) ((clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread);
+  next_worker_index = sm->first_worker_index;
+  next_worker_index +=
+    sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
+  return next_worker_index;
 }
 
 static clib_error_t *
@@ -2864,8 +2929,10 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
   u8 static_mapping_only = 0;
   u8 static_mapping_connection_tracking = 0;
   snat_main_per_thread_data_t *tsm;
+  dslite_main_t * dm = &dslite_main;
 
   sm->deterministic = 0;
+  sm->out2in_dpo = 0;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
@@ -2906,6 +2973,10 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
       else if (unformat (input, "nat64 st hash memory %d",
                          &nat64_st_memory_size))
         ;
+      else if (unformat (input, "out2in dpo"))
+        sm->out2in_dpo = 1;
+      else if (unformat (input, "dslite ce"))
+        dslite_set_ce(dm, 1);
       else
        return clib_error_return (0, "unknown input '%U'",
                                  format_unformat_error, input);
@@ -3123,22 +3194,24 @@ u8 * format_snat_static_mapping (u8 * s, va_list * args)
    {
       if (vec_len (m->locals))
         {
-          s = format (s, "%U vrf %d external %U:%d %s",
+          s = format (s, "%U vrf %d external %U:%d %s %s",
                       format_snat_protocol, m->proto,
                       m->vrf_id,
                       format_ip4_address, &m->external_addr, m->external_port,
-                      m->twice_nat ? "twice-nat" : "");
+                      m->twice_nat ? "twice-nat" : "",
+                      m->out2in_only ? "out2in-only" : "");
           vec_foreach (local, m->locals)
             s = format (s, "\n  local %U:%d probability %d\%",
                         format_ip4_address, &local->addr, local->port,
                         local->probability);
         }
       else
-        s = format (s, "%U local %U:%d external %U:%d vrf %d %s",
+        s = format (s, "%U local %U:%d external %U:%d vrf %d %s %s",
                     format_snat_protocol, m->proto,
                     format_ip4_address, &m->local_addr, m->local_port,
                     format_ip4_address, &m->external_addr, m->external_port,
-                    m->vrf_id, m->twice_nat ? "twice-nat" : "");
+                    m->vrf_id, m->twice_nat ? "twice-nat" : "",
+                    m->out2in_only ? "out2in-only" : "");
    }
   return s;
 }
@@ -3510,7 +3583,7 @@ match:
                                             ~0 /* sw_if_index */,
                                             rp->proto,
                                             rp->is_add,
-                                            0);
+                                            0, 0);
               if (rv)
                 clib_warning ("snat_add_static_mapping returned %d",
                               rv);