nat: fixed return values of enable/disable call
[vpp.git] / src / plugins / nat / nat44-ei / nat44_ei.c
index 2dbef8e..171ca7d 100644 (file)
@@ -40,7 +40,6 @@
 nat44_ei_main_t nat44_ei_main;
 
 extern vlib_node_registration_t nat44_ei_hairpinning_node;
-extern vlib_node_registration_t nat44_ei_hairpin_dst_node;
 extern vlib_node_registration_t
   nat44_ei_in2out_hairpinning_finish_ip4_lookup_node;
 extern vlib_node_registration_t
@@ -62,7 +61,7 @@ extern vlib_node_registration_t
       if (PREDICT_FALSE (nm->enabled))                                        \
        {                                                                     \
          nat44_ei_log_err ("plugin enabled");                                \
-         return 1;                                                           \
+         return VNET_API_ERROR_FEATURE_ALREADY_ENABLED;                      \
        }                                                                     \
     }                                                                         \
   while (0)
@@ -74,7 +73,7 @@ extern vlib_node_registration_t
       if (PREDICT_FALSE (!nm->enabled))                                       \
        {                                                                     \
          nat44_ei_log_err ("plugin disabled");                               \
-         return 1;                                                           \
+         return VNET_API_ERROR_FEATURE_ALREADY_DISABLED;                     \
        }                                                                     \
     }                                                                         \
   while (0)
@@ -111,31 +110,6 @@ VNET_FEATURE_INIT (ip4_nat44_ei_in2out_output, static) = {
   .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa",
                               "ip4-sv-reassembly-output-feature"),
 };
-VNET_FEATURE_INIT (ip4_nat44_ei_in2out_fast, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-ei-in2out-fast",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
-                              "ip4-sv-reassembly-feature"),
-};
-VNET_FEATURE_INIT (ip4_nat44_ei_out2in_fast, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-ei-out2in-fast",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
-                              "ip4-sv-reassembly-feature",
-                              "ip4-dhcp-client-detect"),
-};
-VNET_FEATURE_INIT (ip4_nat44_ei_hairpin_dst, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-ei-hairpin-dst",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
-                              "ip4-sv-reassembly-feature"),
-};
-VNET_FEATURE_INIT (ip4_nat44_ei_hairpin_src, static) = {
-  .arc_name = "ip4-output",
-  .node_name = "nat44-ei-hairpin-src",
-  .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa",
-                              "ip4-sv-reassembly-output-feature"),
-};
 VNET_FEATURE_INIT (ip4_nat44_ei_hairpinning, static) = {
   .arc_name = "ip4-local",
   .node_name = "nat44-ei-hairpinning",
@@ -200,6 +174,39 @@ typedef struct
 void nat44_ei_add_del_addr_to_fib (ip4_address_t *addr, u8 p_len,
                                   u32 sw_if_index, int is_add);
 
+static void nat44_ei_worker_db_free (nat44_ei_main_per_thread_data_t *tnm);
+
+static int nat44_ei_add_static_mapping_internal (
+  ip4_address_t l_addr, ip4_address_t e_addr, u16 l_port, u16 e_port,
+  nat_protocol_t proto, u32 vrf_id, u32 sw_if_index, u32 flags,
+  ip4_address_t pool_addr, u8 *tag);
+
+static int nat44_ei_del_static_mapping_internal (
+  ip4_address_t l_addr, ip4_address_t e_addr, u16 l_port, u16 e_port,
+  nat_protocol_t proto, u32 vrf_id, u32 sw_if_index, u32 flags);
+
+always_inline bool
+nat44_ei_port_is_used (nat44_ei_address_t *a, u8 proto, u16 port)
+{
+  return clib_bitmap_get (a->busy_port_bitmap[proto], port);
+}
+
+always_inline void
+nat44_ei_port_get (nat44_ei_address_t *a, u8 proto, u16 port)
+{
+  ASSERT (!nat44_ei_port_is_used (a, proto, port));
+  a->busy_port_bitmap[proto] =
+    clib_bitmap_set (a->busy_port_bitmap[proto], port, 1);
+}
+
+always_inline void
+nat44_ei_port_put (nat44_ei_address_t *a, u8 proto, u16 port)
+{
+  ASSERT (nat44_ei_port_is_used (a, proto, port));
+  a->busy_port_bitmap[proto] =
+    clib_bitmap_set (a->busy_port_bitmap[proto], port, 0);
+}
+
 static u8 *
 format_nat44_ei_classify_trace (u8 *s, va_list *args)
 {
@@ -219,8 +226,6 @@ format_nat44_ei_classify_trace (u8 *s, va_list *args)
   return s;
 }
 
-static void nat44_ei_db_free ();
-
 static void nat44_ei_db_init (u32 translations, u32 translation_buckets,
                              u32 user_buckets);
 
@@ -304,6 +309,76 @@ nat_validate_interface_counters (nat44_ei_main_t *nm, u32 sw_if_index)
   nat_validate_simple_counter (nm->counters.hairpinning, sw_if_index);
 }
 
+static void
+nat44_ei_add_del_addr_to_fib_foreach_out_if (ip4_address_t *addr, u8 is_add)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  nat44_ei_interface_t *i;
+
+  pool_foreach (i, nm->interfaces)
+    {
+      if (nat44_ei_interface_is_outside (i) && !nm->out2in_dpo)
+       {
+         nat44_ei_add_del_addr_to_fib (addr, 32, i->sw_if_index, is_add);
+       }
+    }
+  pool_foreach (i, nm->output_feature_interfaces)
+    {
+      if (nat44_ei_interface_is_outside (i) && !nm->out2in_dpo)
+       {
+         nat44_ei_add_del_addr_to_fib (addr, 32, i->sw_if_index, is_add);
+       }
+    }
+}
+
+static_always_inline void
+nat44_ei_add_del_addr_to_fib_foreach_addr (u32 sw_if_index, u8 is_add)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  nat44_ei_address_t *ap;
+
+  vec_foreach (ap, nm->addresses)
+    {
+      nat44_ei_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, is_add);
+    }
+}
+
+static_always_inline void
+nat44_ei_add_del_addr_to_fib_foreach_addr_only_sm (u32 sw_if_index, u8 is_add)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  nat44_ei_static_mapping_t *m;
+
+  pool_foreach (m, nm->static_mappings)
+    {
+      if (is_sm_addr_only (m->flags) &&
+         !(m->local_addr.as_u32 == m->external_addr.as_u32))
+       {
+         nat44_ei_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index,
+                                       is_add);
+       }
+    }
+}
+
+static int
+nat44_ei_is_address_used_in_static_mapping (ip4_address_t addr)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  nat44_ei_static_mapping_t *m;
+  pool_foreach (m, nm->static_mappings)
+    {
+      if (is_sm_addr_only (m->flags) || is_sm_identity_nat (m->flags))
+       {
+         continue;
+       }
+      if (m->external_addr.as_u32 == addr.as_u32)
+       {
+         return 1;
+       }
+    }
+  return 0;
+}
+
 clib_error_t *
 nat44_ei_init (vlib_main_t *vm)
 {
@@ -372,14 +447,15 @@ nat44_ei_init (vlib_main_t *vm)
   /* Use all available workers by default */
   if (nm->num_workers > 1)
     {
-
       for (i = 0; i < nm->num_workers; i++)
        bitmap = clib_bitmap_set (bitmap, i, 1);
       nat44_ei_set_workers (bitmap);
       clib_bitmap_free (bitmap);
     }
   else
-    nm->per_thread_data[0].snat_thread_index = 0;
+    {
+      nm->per_thread_data[0].snat_thread_index = 0;
+    }
 
   /* callbacks to call when interface address changes. */
   cbi.function = nat44_ei_ip4_add_del_interface_address_cb;
@@ -402,8 +478,6 @@ nat44_ei_init (vlib_main_t *vm)
 
   nm->hairpinning_fq_index =
     vlib_frame_queue_main_init (nat44_ei_hairpinning_node.index, 0);
-  nm->hairpin_dst_fq_index =
-    vlib_frame_queue_main_init (nat44_ei_hairpin_dst_node.index, 0);
   nm->in2out_hairpinning_finish_ip4_lookup_node_fq_index =
     vlib_frame_queue_main_init (
       nat44_ei_in2out_hairpinning_finish_ip4_lookup_node.index, 0);
@@ -466,43 +540,104 @@ nat44_ei_plugin_enable (nat44_ei_config_t c)
                    nm->user_buckets);
   nat44_ei_set_alloc_default ();
 
-  // TODO: zero simple counter for all counters missing
-
   vlib_zero_simple_counter (&nm->total_users, 0);
   vlib_zero_simple_counter (&nm->total_sessions, 0);
   vlib_zero_simple_counter (&nm->user_limit_reached, 0);
 
+  if (nm->num_workers > 1)
+    {
+      if (nm->fq_in2out_index == ~0)
+       {
+         nm->fq_in2out_index = vlib_frame_queue_main_init (
+           nm->in2out_node_index, nm->frame_queue_nelts);
+       }
+      if (nm->fq_out2in_index == ~0)
+       {
+         nm->fq_out2in_index = vlib_frame_queue_main_init (
+           nm->out2in_node_index, nm->frame_queue_nelts);
+       }
+      if (nm->fq_in2out_output_index == ~0)
+       {
+         nm->fq_in2out_output_index = vlib_frame_queue_main_init (
+           nm->in2out_output_node_index, nm->frame_queue_nelts);
+       }
+    }
+
   nat_ha_enable ();
   nm->enabled = 1;
 
   return 0;
 }
 
-void
-nat44_ei_addresses_free (nat44_ei_address_t **addresses)
+static_always_inline nat44_ei_outside_fib_t *
+nat44_ei_get_outside_fib (nat44_ei_outside_fib_t *outside_fibs, u32 fib_index)
 {
-  nat44_ei_address_t *ap;
-  vec_foreach (ap, *addresses)
+  nat44_ei_outside_fib_t *f;
+  vec_foreach (f, outside_fibs)
     {
-#define _(N, i, n, s) vec_free (ap->busy_##n##_ports_per_thread);
-      foreach_nat_protocol
-#undef _
+      if (f->fib_index == fib_index)
+       {
+         return f;
+       }
+    }
+  return 0;
+}
+
+static_always_inline nat44_ei_interface_t *
+nat44_ei_get_interface (nat44_ei_interface_t *interfaces, u32 sw_if_index)
+{
+  nat44_ei_interface_t *i;
+  pool_foreach (i, interfaces)
+    {
+      if (i->sw_if_index == sw_if_index)
+       {
+         return i;
+       }
+    }
+  return 0;
+}
+
+static_always_inline int
+nat44_ei_hairpinning_enable (u8 is_enable)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  u32 sw_if_index = 0; // local0
+
+  if (is_enable)
+    {
+      nm->hairpin_reg += 1;
+      if (1 == nm->hairpin_reg)
+       {
+         return vnet_feature_enable_disable (
+           "ip4-local", "nat44-ei-hairpinning", sw_if_index, is_enable, 0, 0);
+       }
+    }
+  else
+    {
+      if (0 == nm->hairpin_reg)
+       return 1;
+
+      nm->hairpin_reg -= 1;
+      if (0 == nm->hairpin_reg)
+       {
+         return vnet_feature_enable_disable (
+           "ip4-local", "nat44-ei-hairpinning", sw_if_index, is_enable, 0, 0);
+       }
     }
-  vec_free (*addresses);
-  *addresses = 0;
+
+  return 0;
 }
 
 int
-nat44_ei_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
+nat44_ei_add_interface (u32 sw_if_index, u8 is_inside)
 {
   const char *feature_name, *del_feature_name;
   nat44_ei_main_t *nm = &nat44_ei_main;
-  nat44_ei_interface_t *i;
-  nat44_ei_address_t *ap;
-  nat44_ei_static_mapping_t *m;
+
   nat44_ei_outside_fib_t *outside_fib;
-  u32 fib_index =
-    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
+  nat44_ei_interface_t *i;
+  u32 fib_index;
+  int rv;
 
   fail_if_disabled ();
 
@@ -512,512 +647,649 @@ nat44_ei_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
       return VNET_API_ERROR_UNSUPPORTED;
     }
 
-  pool_foreach (i, nm->output_feature_interfaces)
+  if (nat44_ei_get_interface (nm->output_feature_interfaces, sw_if_index))
     {
-      if (i->sw_if_index == sw_if_index)
+      nat44_ei_log_err ("error interface already configured");
+      return VNET_API_ERROR_VALUE_EXIST;
+    }
+
+  i = nat44_ei_get_interface (nm->interfaces, sw_if_index);
+  if (i)
+    {
+      if ((nat44_ei_interface_is_inside (i) && is_inside) ||
+         (nat44_ei_interface_is_outside (i) && !is_inside))
        {
-         nat44_ei_log_err ("error interface already configured");
-         return VNET_API_ERROR_VALUE_EXIST;
+         return 0;
+       }
+      if (nm->num_workers > 1)
+       {
+         del_feature_name = !is_inside ? "nat44-ei-in2out-worker-handoff" :
+                                         "nat44-ei-out2in-worker-handoff";
+         feature_name = "nat44-ei-handoff-classify";
+       }
+      else
+       {
+         del_feature_name =
+           !is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in";
+
+         feature_name = "nat44-ei-classify";
        }
-    }
 
-  if (nm->static_mapping_only && !(nm->static_mapping_connection_tracking))
-    feature_name = is_inside ? "nat44-ei-in2out-fast" : "nat44-ei-out2in-fast";
+      rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
+      if (rv)
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable ("ip4-unicast", del_feature_name,
+                                       sw_if_index, 0, 0, 0);
+      if (rv)
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable ("ip4-unicast", feature_name,
+                                       sw_if_index, 1, 0, 0);
+      if (rv)
+       {
+         return rv;
+       }
+      if (!is_inside)
+       {
+         rv = nat44_ei_hairpinning_enable (0);
+         if (rv)
+           {
+             return rv;
+           }
+       }
+    }
   else
     {
       if (nm->num_workers > 1)
-       feature_name = is_inside ? "nat44-ei-in2out-worker-handoff" :
-                                  "nat44-ei-out2in-worker-handoff";
+       {
+         feature_name = is_inside ? "nat44-ei-in2out-worker-handoff" :
+                                    "nat44-ei-out2in-worker-handoff";
+       }
       else
-       feature_name = is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in";
-    }
+       {
+         feature_name = is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in";
+       }
+      nat_validate_interface_counters (nm, sw_if_index);
+
+      rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
+      if (rv)
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable ("ip4-unicast", feature_name,
+                                       sw_if_index, 1, 0, 0);
+      if (rv)
+       {
+         return rv;
+       }
+      if (is_inside && !nm->out2in_dpo)
+       {
+         rv = nat44_ei_hairpinning_enable (1);
+         if (rv)
+           {
+             return rv;
+           }
+       }
 
-  if (nm->fq_in2out_index == ~0 && nm->num_workers > 1)
-    nm->fq_in2out_index = vlib_frame_queue_main_init (nm->in2out_node_index,
-                                                     nm->frame_queue_nelts);
+      pool_get (nm->interfaces, i);
+      i->sw_if_index = sw_if_index;
+      i->flags = 0;
+    }
 
-  if (nm->fq_out2in_index == ~0 && nm->num_workers > 1)
-    nm->fq_out2in_index = vlib_frame_queue_main_init (nm->out2in_node_index,
-                                                     nm->frame_queue_nelts);
+  fib_index =
+    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
 
   if (!is_inside)
     {
-      vec_foreach (outside_fib, nm->outside_fibs)
+      i->flags |= NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE;
+
+      outside_fib = nat44_ei_get_outside_fib (nm->outside_fibs, fib_index);
+      if (outside_fib)
        {
-         if (outside_fib->fib_index == fib_index)
-           {
-             if (is_del)
-               {
-                 outside_fib->refcount--;
-                 if (!outside_fib->refcount)
-                   vec_del1 (nm->outside_fibs,
-                             outside_fib - nm->outside_fibs);
-               }
-             else
-               outside_fib->refcount++;
-             goto feature_set;
-           }
+         outside_fib->refcount++;
        }
-      if (!is_del)
+      else
        {
          vec_add2 (nm->outside_fibs, outside_fib, 1);
-         outside_fib->refcount = 1;
          outside_fib->fib_index = fib_index;
+         outside_fib->refcount = 1;
        }
-    }
 
-feature_set:
-  pool_foreach (i, nm->interfaces)
+      nat44_ei_add_del_addr_to_fib_foreach_addr (sw_if_index, 1);
+      nat44_ei_add_del_addr_to_fib_foreach_addr_only_sm (sw_if_index, 1);
+    }
+  else
     {
-      if (i->sw_if_index == sw_if_index)
-       {
-         if (is_del)
-           {
-             if (nat44_ei_interface_is_inside (i) &&
-                 nat44_ei_interface_is_outside (i))
-               {
-                 if (is_inside)
-                   i->flags &= ~NAT44_EI_INTERFACE_FLAG_IS_INSIDE;
-                 else
-                   i->flags &= ~NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE;
+      i->flags |= NAT44_EI_INTERFACE_FLAG_IS_INSIDE;
+    }
 
-                 if (nm->num_workers > 1)
-                   {
-                     del_feature_name = "nat44-ei-handoff-classify";
-                     clib_warning (
-                       "del_feature_name = nat44-ei-handoff-classify");
-                     feature_name = !is_inside ?
-                                      "nat44-ei-in2out-worker-handoff" :
-                                      "nat44-ei-out2in-worker-handoff";
-                   }
-                 else
-                   {
-                     del_feature_name = "nat44-ei-classify";
-                     clib_warning ("del_feature_name = nat44-ei-classify");
-                     feature_name =
-                       !is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in";
-                   }
+  return 0;
+}
 
-                 int rv =
-                   ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
-                 if (rv)
-                   return rv;
-                 rv = vnet_feature_enable_disable (
-                   "ip4-unicast", del_feature_name, sw_if_index, 0, 0, 0);
-                 if (rv)
-                   return rv;
-                 rv = vnet_feature_enable_disable (
-                   "ip4-unicast", feature_name, sw_if_index, 1, 0, 0);
-                 if (rv)
-                   return rv;
-                 if (!is_inside)
-                   {
-                     rv = vnet_feature_enable_disable ("ip4-local",
-                                                       "nat44-ei-hairpinning",
-                                                       sw_if_index, 1, 0, 0);
-                     if (rv)
-                       return rv;
-                   }
-               }
-             else
-               {
-                 int rv =
-                   ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
-                 if (rv)
-                   return rv;
-                 rv = vnet_feature_enable_disable (
-                   "ip4-unicast", feature_name, sw_if_index, 0, 0, 0);
-                 if (rv)
-                   return rv;
-                 pool_put (nm->interfaces, i);
-                 if (is_inside)
-                   {
-                     rv = vnet_feature_enable_disable ("ip4-local",
-                                                       "nat44-ei-hairpinning",
-                                                       sw_if_index, 0, 0, 0);
-                     if (rv)
-                       return rv;
-                   }
-               }
-           }
-         else
-           {
-             if ((nat44_ei_interface_is_inside (i) && is_inside) ||
-                 (nat44_ei_interface_is_outside (i) && !is_inside))
-               return 0;
+int
+nat44_ei_del_interface (u32 sw_if_index, u8 is_inside)
+{
+  const char *feature_name, *del_feature_name;
+  nat44_ei_main_t *nm = &nat44_ei_main;
 
-             if (nm->num_workers > 1)
-               {
-                 del_feature_name = !is_inside ?
-                                      "nat44-ei-in2out-worker-handoff" :
-                                      "nat44-ei-out2in-worker-handoff";
-                 feature_name = "nat44-ei-handoff-classify";
-                 clib_warning ("feature_name = nat44-ei-handoff-classify");
-               }
-             else
-               {
-                 del_feature_name =
-                   !is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in";
-                 feature_name = "nat44-ei-classify";
-                 clib_warning ("feature_name = nat44-ei-classify");
-               }
+  nat44_ei_outside_fib_t *outside_fib;
+  nat44_ei_interface_t *i;
+  u32 fib_index;
+  int rv;
 
-             int rv =
-               ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
-             if (rv)
-               return rv;
-             rv = vnet_feature_enable_disable (
-               "ip4-unicast", del_feature_name, sw_if_index, 0, 0, 0);
-             if (rv)
-               return rv;
-             rv = vnet_feature_enable_disable ("ip4-unicast", feature_name,
-                                               sw_if_index, 1, 0, 0);
-             if (rv)
-               return rv;
-             if (!is_inside)
-               {
-                 rv = vnet_feature_enable_disable (
-                   "ip4-local", "nat44-ei-hairpinning", sw_if_index, 0, 0, 0);
-                 if (rv)
-                   return rv;
-               }
-             goto set_flags;
-           }
+  fail_if_disabled ();
 
-         goto fib;
-       }
+  if (nm->out2in_dpo && !is_inside)
+    {
+      nat44_ei_log_err ("error unsupported");
+      return VNET_API_ERROR_UNSUPPORTED;
     }
 
-  if (is_del)
+  i = nat44_ei_get_interface (nm->interfaces, sw_if_index);
+  if (i == 0)
     {
       nat44_ei_log_err ("error interface couldn't be found");
       return VNET_API_ERROR_NO_SUCH_ENTRY;
     }
 
-  pool_get (nm->interfaces, i);
-  i->sw_if_index = sw_if_index;
-  i->flags = 0;
-  nat_validate_interface_counters (nm, sw_if_index);
-
-  int rv = vnet_feature_enable_disable ("ip4-unicast", feature_name,
-                                       sw_if_index, 1, 0, 0);
-  if (rv)
-    return rv;
-
-  rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
-  if (rv)
-    return rv;
-
-  if (is_inside && !nm->out2in_dpo)
+  if (nat44_ei_interface_is_inside (i) && nat44_ei_interface_is_outside (i))
     {
-      rv = vnet_feature_enable_disable ("ip4-local", "nat44-ei-hairpinning",
+      if (nm->num_workers > 1)
+       {
+         del_feature_name = "nat44-ei-handoff-classify";
+         feature_name = !is_inside ? "nat44-ei-in2out-worker-handoff" :
+                                     "nat44-ei-out2in-worker-handoff";
+       }
+      else
+       {
+         del_feature_name = "nat44-ei-classify";
+         feature_name = !is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in";
+       }
+
+      rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
+      if (rv)
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable ("ip4-unicast", del_feature_name,
+                                       sw_if_index, 0, 0, 0);
+      if (rv)
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable ("ip4-unicast", feature_name,
                                        sw_if_index, 1, 0, 0);
       if (rv)
-       return rv;
-    }
-
-set_flags:
-  if (is_inside)
-    {
-      i->flags |= NAT44_EI_INTERFACE_FLAG_IS_INSIDE;
-      return 0;
+       {
+         return rv;
+       }
+      if (is_inside)
+       {
+         i->flags &= ~NAT44_EI_INTERFACE_FLAG_IS_INSIDE;
+       }
+      else
+       {
+         rv = nat44_ei_hairpinning_enable (1);
+         if (rv)
+           {
+             return rv;
+           }
+         i->flags &= ~NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE;
+       }
     }
   else
-    i->flags |= NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE;
+    {
+      if (nm->num_workers > 1)
+       {
+         feature_name = is_inside ? "nat44-ei-in2out-worker-handoff" :
+                                    "nat44-ei-out2in-worker-handoff";
+       }
+      else
+       {
+         feature_name = is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in";
+       }
 
-  /* Add/delete external addresses to FIB */
-fib:
-  vec_foreach (ap, nm->addresses)
-    nat44_ei_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, !is_del);
+      rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
+      if (rv)
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable ("ip4-unicast", feature_name,
+                                       sw_if_index, 0, 0, 0);
+      if (rv)
+       {
+         return rv;
+       }
+      if (is_inside)
+       {
+         rv = nat44_ei_hairpinning_enable (0);
+         if (rv)
+           {
+             return rv;
+           }
+       }
 
-  pool_foreach (m, nm->static_mappings)
+      // remove interface
+      pool_put (nm->interfaces, i);
+    }
+
+  if (!is_inside)
     {
-      if (!(nat44_ei_is_addr_only_static_mapping (m)) ||
-         (m->local_addr.as_u32 == m->external_addr.as_u32))
-       continue;
+      fib_index =
+       fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
+      outside_fib = nat44_ei_get_outside_fib (nm->outside_fibs, fib_index);
+      if (outside_fib)
+       {
+         outside_fib->refcount--;
+         if (!outside_fib->refcount)
+           {
+             vec_del1 (nm->outside_fibs, outside_fib - nm->outside_fibs);
+           }
+       }
 
-      nat44_ei_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index,
-                                   !is_del);
+      nat44_ei_add_del_addr_to_fib_foreach_addr (sw_if_index, 0);
+      nat44_ei_add_del_addr_to_fib_foreach_addr_only_sm (sw_if_index, 0);
     }
 
   return 0;
 }
 
 int
-nat44_ei_interface_add_del_output_feature (u32 sw_if_index, u8 is_inside,
-                                          int is_del)
+nat44_ei_add_output_interface (u32 sw_if_index)
 {
   nat44_ei_main_t *nm = &nat44_ei_main;
-  nat44_ei_interface_t *i;
-  nat44_ei_address_t *ap;
-  nat44_ei_static_mapping_t *m;
+
   nat44_ei_outside_fib_t *outside_fib;
-  u32 fib_index =
-    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
+  nat44_ei_interface_t *i;
+  u32 fib_index;
+  int rv;
 
   fail_if_disabled ();
 
-  if (nm->static_mapping_only && !(nm->static_mapping_connection_tracking))
+  if (nat44_ei_get_interface (nm->interfaces, sw_if_index))
     {
-      nat44_ei_log_err ("error unsupported");
-      return VNET_API_ERROR_UNSUPPORTED;
+      nat44_ei_log_err ("error interface already configured");
+      return VNET_API_ERROR_VALUE_EXIST;
     }
 
-  pool_foreach (i, nm->interfaces)
+  if (nat44_ei_get_interface (nm->output_feature_interfaces, sw_if_index))
     {
-      if (i->sw_if_index == sw_if_index)
-       {
-         nat44_ei_log_err ("error interface already configured");
-         return VNET_API_ERROR_VALUE_EXIST;
-       }
+      nat44_ei_log_err ("error interface already configured");
+      return VNET_API_ERROR_VALUE_EXIST;
     }
 
-  if (!is_inside)
+  if (nm->num_workers > 1)
     {
-      vec_foreach (outside_fib, nm->outside_fibs)
+      rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
+      if (rv)
        {
-         if (outside_fib->fib_index == fib_index)
-           {
-             if (is_del)
-               {
-                 outside_fib->refcount--;
-                 if (!outside_fib->refcount)
-                   vec_del1 (nm->outside_fibs,
-                             outside_fib - nm->outside_fibs);
-               }
-             else
-               outside_fib->refcount++;
-             goto feature_set;
-           }
+         return rv;
        }
-      if (!is_del)
+      rv = ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, 1);
+      if (rv)
        {
-         vec_add2 (nm->outside_fibs, outside_fib, 1);
-         outside_fib->refcount = 1;
-         outside_fib->fib_index = fib_index;
+         return rv;
+       }
+      rv = vnet_feature_enable_disable (
+       "ip4-unicast", "nat44-ei-out2in-worker-handoff", sw_if_index, 1, 0, 0);
+      if (rv)
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable (
+       "ip4-output", "nat44-ei-in2out-output-worker-handoff", sw_if_index, 1,
+       0, 0);
+      if (rv)
+       {
+         return rv;
        }
     }
-
-feature_set:
-  if (is_inside)
+  else
     {
-      int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del);
+      rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1);
       if (rv)
-       return rv;
-      rv =
-       ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del);
+       {
+         return rv;
+       }
+      rv = ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, 1);
       if (rv)
-       return rv;
-      rv = vnet_feature_enable_disable ("ip4-unicast", "nat44-ei-hairpin-dst",
-                                       sw_if_index, !is_del, 0, 0);
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable ("ip4-unicast", "nat44-ei-out2in",
+                                       sw_if_index, 1, 0, 0);
       if (rv)
-       return rv;
-      rv = vnet_feature_enable_disable ("ip4-output", "nat44-ei-hairpin-src",
-                                       sw_if_index, !is_del, 0, 0);
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable ("ip4-output", "nat44-ei-in2out-output",
+                                       sw_if_index, 1, 0, 0);
       if (rv)
-       return rv;
-      goto fq;
+       {
+         return rv;
+       }
+    }
+
+  nat_validate_interface_counters (nm, sw_if_index);
+
+  pool_get (nm->output_feature_interfaces, i);
+  i->sw_if_index = sw_if_index;
+  i->flags = 0;
+  i->flags |= NAT44_EI_INTERFACE_FLAG_IS_INSIDE;
+  i->flags |= NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE;
+
+  fib_index =
+    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
+  outside_fib = nat44_ei_get_outside_fib (nm->outside_fibs, fib_index);
+  if (outside_fib)
+    {
+      outside_fib->refcount++;
+    }
+  else
+    {
+      vec_add2 (nm->outside_fibs, outside_fib, 1);
+      outside_fib->fib_index = fib_index;
+      outside_fib->refcount = 1;
+    }
+
+  nat44_ei_add_del_addr_to_fib_foreach_addr (sw_if_index, 1);
+  nat44_ei_add_del_addr_to_fib_foreach_addr_only_sm (sw_if_index, 1);
+
+  return 0;
+}
+
+int
+nat44_ei_del_output_interface (u32 sw_if_index)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+
+  nat44_ei_outside_fib_t *outside_fib;
+  nat44_ei_interface_t *i;
+  u32 fib_index;
+  int rv;
+
+  fail_if_disabled ();
+
+  i = nat44_ei_get_interface (nm->output_feature_interfaces, sw_if_index);
+  if (!i)
+    {
+      nat44_ei_log_err ("error interface couldn't be found");
+      return VNET_API_ERROR_NO_SUCH_ENTRY;
     }
 
   if (nm->num_workers > 1)
     {
-      int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del);
+      rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
       if (rv)
-       return rv;
-      rv =
-       ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del);
+       {
+         return rv;
+       }
+      rv = ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, 0);
       if (rv)
-       return rv;
-      rv = vnet_feature_enable_disable ("ip4-unicast",
-                                       "nat44-ei-out2in-worker-handoff",
-                                       sw_if_index, !is_del, 0, 0);
+       {
+         return rv;
+       }
+      rv = vnet_feature_enable_disable (
+       "ip4-unicast", "nat44-ei-out2in-worker-handoff", sw_if_index, 0, 0, 0);
       if (rv)
-       return rv;
+       {
+         return rv;
+       }
       rv = vnet_feature_enable_disable (
-       "ip4-output", "nat44-ei-in2out-output-worker-handoff", sw_if_index,
-       !is_del, 0, 0);
+       "ip4-output", "nat44-ei-in2out-output-worker-handoff", sw_if_index, 0,
+       0, 0);
       if (rv)
-       return rv;
+       {
+         return rv;
+       }
     }
   else
     {
-      int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del);
+      rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0);
       if (rv)
-       return rv;
-      rv =
-       ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del);
+       {
+         return rv;
+       }
+      rv = ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, 0);
       if (rv)
-       return rv;
+       {
+         return rv;
+       }
       rv = vnet_feature_enable_disable ("ip4-unicast", "nat44-ei-out2in",
-                                       sw_if_index, !is_del, 0, 0);
+                                       sw_if_index, 0, 0, 0);
       if (rv)
-       return rv;
+       {
+         return rv;
+       }
       rv = vnet_feature_enable_disable ("ip4-output", "nat44-ei-in2out-output",
-                                       sw_if_index, !is_del, 0, 0);
+                                       sw_if_index, 0, 0, 0);
       if (rv)
-       return rv;
+       {
+         return rv;
+       }
     }
 
-fq:
-  if (nm->fq_in2out_output_index == ~0 && nm->num_workers > 1)
-    nm->fq_in2out_output_index =
-      vlib_frame_queue_main_init (nm->in2out_output_node_index, 0);
+  pool_put (nm->output_feature_interfaces, i);
 
-  if (nm->fq_out2in_index == ~0 && nm->num_workers > 1)
-    nm->fq_out2in_index =
-      vlib_frame_queue_main_init (nm->out2in_node_index, 0);
-
-  pool_foreach (i, nm->output_feature_interfaces)
+  fib_index =
+    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
+  outside_fib = nat44_ei_get_outside_fib (nm->outside_fibs, fib_index);
+  if (outside_fib)
     {
-      if (i->sw_if_index == sw_if_index)
+      outside_fib->refcount--;
+      if (!outside_fib->refcount)
        {
-         if (is_del)
-           pool_put (nm->output_feature_interfaces, i);
-         else
-           return VNET_API_ERROR_VALUE_EXIST;
-
-         goto fib;
+         vec_del1 (nm->outside_fibs, outside_fib - nm->outside_fibs);
        }
     }
 
+  nat44_ei_add_del_addr_to_fib_foreach_addr (sw_if_index, 0);
+  nat44_ei_add_del_addr_to_fib_foreach_addr_only_sm (sw_if_index, 0);
+
+  return 0;
+}
+
+int
+nat44_ei_add_del_output_interface (u32 sw_if_index, int is_del)
+{
   if (is_del)
     {
-      nat44_ei_log_err ("error interface couldn't be found");
-      return VNET_API_ERROR_NO_SUCH_ENTRY;
+      return nat44_ei_del_output_interface (sw_if_index);
     }
-
-  pool_get (nm->output_feature_interfaces, i);
-  i->sw_if_index = sw_if_index;
-  i->flags = 0;
-  nat_validate_interface_counters (nm, sw_if_index);
-  if (is_inside)
-    i->flags |= NAT44_EI_INTERFACE_FLAG_IS_INSIDE;
   else
-    i->flags |= NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE;
-
-  /* Add/delete external addresses to FIB */
-fib:
-  if (is_inside)
-    return 0;
+    {
+      return nat44_ei_add_output_interface (sw_if_index);
+    }
+}
 
-  vec_foreach (ap, nm->addresses)
-    nat44_ei_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, !is_del);
+int
+nat44_ei_del_addresses ()
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  nat44_ei_address_t *a, *vec;
+  int error = 0;
 
-  pool_foreach (m, nm->static_mappings)
+  vec = vec_dup (nm->addresses);
+  vec_foreach (a, vec)
     {
-      if (!((nat44_ei_is_addr_only_static_mapping (m))) ||
-         (m->local_addr.as_u32 == m->external_addr.as_u32))
-       continue;
+      error = nat44_ei_del_address (a->addr, 0);
 
-      nat44_ei_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index,
-                                   !is_del);
+      if (error)
+       {
+         nat44_ei_log_err ("error occurred while removing adderess");
+       }
     }
+  vec_free (vec);
+  vec_free (nm->addresses);
+  nm->addresses = 0;
 
-  return 0;
+  vec_free (nm->auto_add_sw_if_indices);
+  nm->auto_add_sw_if_indices = 0;
+  return error;
 }
 
 int
-nat44_ei_plugin_disable ()
+nat44_ei_del_interfaces ()
 {
   nat44_ei_main_t *nm = &nat44_ei_main;
   nat44_ei_interface_t *i, *pool;
   int error = 0;
 
-  // first unregister all nodes from interfaces
   pool = pool_dup (nm->interfaces);
   pool_foreach (i, pool)
     {
       if (nat44_ei_interface_is_inside (i))
-       error = nat44_ei_interface_add_del (i->sw_if_index, 1, 1);
+       {
+         error = nat44_ei_del_interface (i->sw_if_index, 1);
+       }
       if (nat44_ei_interface_is_outside (i))
-       error = nat44_ei_interface_add_del (i->sw_if_index, 0, 1);
+       {
+         error = nat44_ei_del_interface (i->sw_if_index, 0);
+       }
 
       if (error)
        {
-         nat44_ei_log_err ("error occurred while removing interface %u",
-                           i->sw_if_index);
+         nat44_ei_log_err ("error occurred while removing interface");
        }
     }
   pool_free (pool);
   pool_free (nm->interfaces);
+  nm->interfaces = 0;
+  return error;
+}
+
+int
+nat44_ei_del_output_interfaces ()
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  nat44_ei_interface_t *i, *pool;
+  int error = 0;
 
   pool = pool_dup (nm->output_feature_interfaces);
   pool_foreach (i, pool)
     {
-      if (nat44_ei_interface_is_inside (i))
-       error =
-         nat44_ei_interface_add_del_output_feature (i->sw_if_index, 1, 1);
-      if (nat44_ei_interface_is_outside (i))
-       error =
-         nat44_ei_interface_add_del_output_feature (i->sw_if_index, 0, 1);
-
+      error = nat44_ei_del_output_interface (i->sw_if_index);
       if (error)
        {
-         nat44_ei_log_err ("error occurred while removing interface %u",
-                           i->sw_if_index);
+         nat44_ei_log_err ("error occurred while removing output interface");
        }
     }
   pool_free (pool);
   pool_free (nm->output_feature_interfaces);
+  nm->output_feature_interfaces = 0;
+  return error;
+}
 
-  nat_ha_disable ();
-  nat44_ei_db_free ();
+int
+nat44_ei_del_static_mappings ()
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  nat44_ei_static_mapping_t *m, *pool;
+  int error = 0;
 
-  nat44_ei_addresses_free (&nm->addresses);
+  pool = pool_dup (nm->static_mappings);
+  pool_foreach (m, pool)
+    {
+      error = nat44_ei_del_static_mapping_internal (
+       m->local_addr, m->external_addr, m->local_port, m->external_port,
+       m->proto, m->vrf_id, ~0, m->flags);
+      if (error)
+       {
+         nat44_ei_log_err ("error occurred while removing mapping");
+       }
+    }
+  pool_free (pool);
+  pool_free (nm->static_mappings);
+  nm->static_mappings = 0;
 
   vec_free (nm->to_resolve);
-  vec_free (nm->auto_add_sw_if_indices);
-
   nm->to_resolve = 0;
-  nm->auto_add_sw_if_indices = 0;
-
-  nm->forwarding_enabled = 0;
 
-  nm->enabled = 0;
-  clib_memset (&nm->rconfig, 0, sizeof (nm->rconfig));
+  clib_bihash_free_8_8 (&nm->static_mapping_by_local);
+  clib_bihash_free_8_8 (&nm->static_mapping_by_external);
 
-  return 0;
+  return error;
 }
 
 int
-nat44_ei_set_outside_address_and_port (nat44_ei_address_t *addresses,
-                                      u32 thread_index, ip4_address_t addr,
-                                      u16 port, nat_protocol_t protocol)
+nat44_ei_plugin_disable ()
 {
   nat44_ei_main_t *nm = &nat44_ei_main;
-  nat44_ei_address_t *a = 0;
-  u32 address_index;
-  u16 port_host_byte_order = clib_net_to_host_u16 (port);
+  nat44_ei_main_per_thread_data_t *tnm;
+  int rc, error = 0;
 
-  for (address_index = 0; address_index < vec_len (addresses); address_index++)
-    {
-      if (addresses[address_index].addr.as_u32 != addr.as_u32)
-       continue;
+  fail_if_disabled ();
 
-      a = addresses + address_index;
-      switch (protocol)
-       {
-#define _(N, j, n, s)                                                         \
-  case NAT_PROTOCOL_##N:                                                      \
-    if (a->busy_##n##_port_refcounts[port_host_byte_order])                   \
-      return VNET_API_ERROR_INSTANCE_IN_USE;                                  \
-    ++a->busy_##n##_port_refcounts[port_host_byte_order];                     \
-    a->busy_##n##_ports_per_thread[thread_index]++;                           \
-    a->busy_##n##_ports++;                                                    \
-    return 0;
-         foreach_nat_protocol
-#undef _
-           default : nat_elog_info (nm, "unknown protocol");
-         return 1;
-       }
-    }
+  nat_ha_disable ();
 
-  return VNET_API_ERROR_NO_SUCH_ENTRY;
-}
+  rc = nat44_ei_del_static_mappings ();
+  if (rc)
+    error = VNET_API_ERROR_BUG;
 
-void
-nat44_ei_add_del_address_dpo (ip4_address_t addr, u8 is_add)
-{
+  rc = nat44_ei_del_addresses ();
+  if (rc)
+    error = VNET_API_ERROR_BUG;
+
+  rc = nat44_ei_del_interfaces ();
+  if (rc)
+    error = VNET_API_ERROR_BUG;
+
+  rc = nat44_ei_del_output_interfaces ();
+  if (rc)
+    error = VNET_API_ERROR_BUG;
+
+  if (nm->pat)
+    {
+      clib_bihash_free_8_8 (&nm->in2out);
+      clib_bihash_free_8_8 (&nm->out2in);
+
+      vec_foreach (tnm, nm->per_thread_data)
+       {
+         nat44_ei_worker_db_free (tnm);
+       }
+    }
+
+  clib_memset (&nm->rconfig, 0, sizeof (nm->rconfig));
+
+  nm->forwarding_enabled = 0;
+  nm->enabled = 0;
+
+  return error;
+}
+
+int
+nat44_ei_set_outside_address_and_port (nat44_ei_address_t *addresses,
+                                      u32 thread_index, ip4_address_t addr,
+                                      u16 port, nat_protocol_t protocol)
+{
+  nat44_ei_address_t *a = 0;
+  u32 address_index;
+  u16 port_host_byte_order = clib_net_to_host_u16 (port);
+
+  for (address_index = 0; address_index < vec_len (addresses); address_index++)
+    {
+      if (addresses[address_index].addr.as_u32 != addr.as_u32)
+       continue;
+
+      a = addresses + address_index;
+      if (nat44_ei_port_is_used (a, protocol, port_host_byte_order))
+       return VNET_API_ERROR_INSTANCE_IN_USE;
+
+      nat44_ei_port_get (a, protocol, port_host_byte_order);
+      a->busy_ports_per_thread[protocol][thread_index]++;
+      a->busy_ports[protocol]++;
+      return 0;
+    }
+
+  return VNET_API_ERROR_NO_SUCH_ENTRY;
+}
+
+void
+nat44_ei_add_del_address_dpo (ip4_address_t addr, u8 is_add)
+{
   nat44_ei_main_t *nm = &nat44_ei_main;
   dpo_id_t dpo_v4 = DPO_INVALID;
   fib_prefix_t pfx = {
@@ -1044,7 +1316,6 @@ nat44_ei_free_outside_address_and_port (nat44_ei_address_t *addresses,
                                        u32 thread_index, ip4_address_t *addr,
                                        u16 port, nat_protocol_t protocol)
 {
-  nat44_ei_main_t *nm = &nat44_ei_main;
   nat44_ei_address_t *a;
   u32 address_index;
   u16 port_host_byte_order = clib_net_to_host_u16 (port);
@@ -1058,21 +1329,9 @@ nat44_ei_free_outside_address_and_port (nat44_ei_address_t *addresses,
   ASSERT (address_index < vec_len (addresses));
 
   a = addresses + address_index;
-
-  switch (protocol)
-    {
-#define _(N, i, n, s)                                                         \
-  case NAT_PROTOCOL_##N:                                                      \
-    ASSERT (a->busy_##n##_port_refcounts[port_host_byte_order] >= 1);         \
-    --a->busy_##n##_port_refcounts[port_host_byte_order];                     \
-    a->busy_##n##_ports--;                                                    \
-    a->busy_##n##_ports_per_thread[thread_index]--;                           \
-    break;
-      foreach_nat_protocol
-#undef _
-       default : nat_elog_info (nm, "unknown protocol");
-      return;
-    }
+  nat44_ei_port_put (a, protocol, port_host_byte_order);
+  a->busy_ports[protocol]--;
+  a->busy_ports_per_thread[protocol][thread_index]--;
 }
 
 void
@@ -1102,7 +1361,8 @@ nat44_ei_free_session_data_v2 (nat44_ei_main_t *nm, nat44_ei_session_t *s,
       /* log NAT event */
       nat_ipfix_logging_nat44_ses_delete (
        thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
-       s->nat_proto, s->in2out.port, s->out2in.port, s->in2out.fib_index);
+       nat_proto_to_ip_proto (s->nat_proto), s->in2out.port, s->out2in.port,
+       s->in2out.fib_index);
 
       nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
                   s->ext_host_port, s->nat_proto, s->out2in.fib_index,
@@ -1270,7 +1530,8 @@ nat44_ei_free_session_data (nat44_ei_main_t *nm, nat44_ei_session_t *s,
 
       nat_ipfix_logging_nat44_ses_delete (
        thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
-       s->nat_proto, s->in2out.port, s->out2in.port, s->in2out.fib_index);
+       nat_proto_to_ip_proto (s->nat_proto), s->in2out.port, s->out2in.port,
+       s->in2out.fib_index);
 
       nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
                   s->ext_host_port, s->nat_proto, s->out2in.fib_index,
@@ -1424,6 +1685,20 @@ nat44_ei_get_in2out_worker_index (ip4_header_t *ip0, u32 rx_fib_index0,
   return next_worker_index;
 }
 
+u32
+nat44_ei_get_thread_idx_by_port (u16 e_port)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  u32 thread_idx = nm->num_workers;
+  if (nm->num_workers > 1)
+    {
+      thread_idx = nm->first_worker_index +
+                  nm->workers[(e_port - 1024) / nm->port_per_thread %
+                              _vec_len (nm->workers)];
+    }
+  return thread_idx;
+}
+
 u32
 nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0,
                                  u32 rx_fib_index0, u8 is_output)
@@ -1502,9 +1777,8 @@ nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0,
     }
 
   /* worker by outside port */
-  next_worker_index = nm->first_worker_index;
-  next_worker_index +=
-    nm->workers[(clib_net_to_host_u16 (port) - 1024) / nm->port_per_thread];
+  next_worker_index =
+    nat44_ei_get_thread_idx_by_port (clib_net_to_host_u16 (port));
   return next_worker_index;
 }
 
@@ -1522,75 +1796,95 @@ nat44_ei_alloc_default_cb (nat44_ei_address_t *addresses, u32 fib_index,
 
   if (vec_len (addresses) > 0)
     {
-
       int s_addr_offset = s_addr.as_u32 % vec_len (addresses);
 
       for (i = s_addr_offset; i < vec_len (addresses); ++i)
        {
          a = addresses + i;
-         switch (proto)
+
+         if (a->busy_ports_per_thread[proto][thread_index] < port_per_thread)
            {
-#define _(N, j, n, s)                                                         \
-  case NAT_PROTOCOL_##N:                                                      \
-    if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread)       \
-      {                                                                       \
-       if (a->fib_index == fib_index)                                        \
-         {                                                                   \
-           while (1)                                                         \
-             {                                                               \
-               portnum = (port_per_thread * snat_thread_index) +             \
-                         nat_random_port (&nm->random_seed, 0,               \
-                                          port_per_thread - 1) +             \
-                         1024;                                               \
-               if (a->busy_##n##_port_refcounts[portnum])                    \
-                 continue;                                                   \
-               --a->busy_##n##_port_refcounts[portnum];                      \
-               a->busy_##n##_ports_per_thread[thread_index]++;               \
-               a->busy_##n##_ports++;                                        \
-               *addr = a->addr;                                              \
-               *port = clib_host_to_net_u16 (portnum);                       \
-               return 0;                                                     \
-             }                                                               \
-         }                                                                   \
-       else if (a->fib_index == ~0)                                          \
-         {                                                                   \
-           ga = a;                                                           \
-         }                                                                   \
-      }                                                                       \
-    break;
-             foreach_nat_protocol;
-           default:
-             nat_elog_info (nm, "unknown protocol");
-             return 1;
+             if (a->fib_index == fib_index)
+               {
+                 while (1)
+                   {
+                     portnum = (port_per_thread * snat_thread_index) +
+                               nat_random_port (&nm->random_seed, 0,
+                                                port_per_thread - 1) +
+                               1024;
+                     if (nat44_ei_port_is_used (a, proto, portnum))
+                       continue;
+                     nat44_ei_port_get (a, proto, portnum);
+                     a->busy_ports_per_thread[proto][thread_index]++;
+                     a->busy_ports[proto]++;
+                     *addr = a->addr;
+                     *port = clib_host_to_net_u16 (portnum);
+                     return 0;
+                   }
+               }
+             else if (a->fib_index == ~0)
+               {
+                 ga = a;
+               }
            }
        }
 
       for (i = 0; i < s_addr_offset; ++i)
        {
          a = addresses + i;
-         switch (proto)
+         if (a->busy_ports_per_thread[proto][thread_index] < port_per_thread)
            {
-             foreach_nat_protocol;
-           default:
-             nat_elog_info (nm, "unknown protocol");
-             return 1;
+             if (a->fib_index == fib_index)
+               {
+                 while (1)
+                   {
+                     portnum = (port_per_thread * snat_thread_index) +
+                               nat_random_port (&nm->random_seed, 0,
+                                                port_per_thread - 1) +
+                               1024;
+                     if (nat44_ei_port_is_used (a, proto, portnum))
+                       continue;
+                     nat44_ei_port_get (a, proto, portnum);
+                     a->busy_ports_per_thread[proto][thread_index]++;
+                     a->busy_ports[proto]++;
+                     *addr = a->addr;
+                     *port = clib_host_to_net_u16 (portnum);
+                     return 0;
+                   }
+               }
+             else if (a->fib_index == ~0)
+               {
+                 ga = a;
+               }
            }
        }
-  if (ga)
-    {
-      a = ga;
-      // fake fib index to reuse macro
-      fib_index = ~0;
-      switch (proto)
+
+      if (ga)
        {
-         foreach_nat_protocol;
-           default : nat_elog_info (nm, "unknown protocol");
-         return 1;
+         a = ga;
+         if (a->busy_ports_per_thread[proto][thread_index] < port_per_thread)
+           {
+             if (a->fib_index == ~0)
+               {
+                 while (1)
+                   {
+                     portnum = (port_per_thread * snat_thread_index) +
+                               nat_random_port (&nm->random_seed, 0,
+                                                port_per_thread - 1) +
+                               1024;
+                     if (nat44_ei_port_is_used (a, proto, portnum))
+                       continue;
+                     nat44_ei_port_get (a, proto, portnum);
+                     a->busy_ports_per_thread[proto][thread_index]++;
+                     a->busy_ports[proto]++;
+                     *addr = a->addr;
+                     *port = clib_host_to_net_u16 (portnum);
+                     return 0;
+                   }
+               }
+           }
        }
     }
-    }
-
-#undef _
 
   /* Totally out of translations to use... */
   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
@@ -1612,30 +1906,20 @@ nat44_ei_alloc_range_cb (nat44_ei_address_t *addresses, u32 fib_index,
   if (!vec_len (addresses))
     goto exhausted;
 
-  switch (proto)
-    {
-#define _(N, i, n, s)                                                         \
-  case NAT_PROTOCOL_##N:                                                      \
-    if (a->busy_##n##_ports < ports)                                          \
-      {                                                                       \
-       while (1)                                                             \
-         {                                                                   \
-           portnum = nat_random_port (&nm->random_seed, nm->start_port,      \
-                                      nm->end_port);                         \
-           if (a->busy_##n##_port_refcounts[portnum])                        \
-             continue;                                                       \
-           ++a->busy_##n##_port_refcounts[portnum];                          \
-           a->busy_##n##_ports++;                                            \
-           *addr = a->addr;                                                  \
-           *port = clib_host_to_net_u16 (portnum);                           \
-           return 0;                                                         \
-         }                                                                   \
-      }                                                                       \
-    break;
-      foreach_nat_protocol
-#undef _
-       default : nat_elog_info (nm, "unknown protocol");
-      return 1;
+  if (a->busy_ports[proto] < ports)
+    {
+      while (1)
+       {
+         portnum =
+           nat_random_port (&nm->random_seed, nm->start_port, nm->end_port);
+         if (nat44_ei_port_is_used (a, proto, portnum))
+           continue;
+         nat44_ei_port_get (a, proto, portnum);
+         a->busy_ports[proto]++;
+         *addr = a->addr;
+         *port = clib_host_to_net_u16 (portnum);
+         return 0;
+       }
     }
 
 exhausted:
@@ -1659,32 +1943,22 @@ nat44_ei_alloc_mape_cb (nat44_ei_address_t *addresses, u32 fib_index,
   if (!vec_len (addresses))
     goto exhausted;
 
-  switch (proto)
-    {
-#define _(N, i, n, s)                                                         \
-  case NAT_PROTOCOL_##N:                                                      \
-    if (a->busy_##n##_ports < ports)                                          \
-      {                                                                       \
-       while (1)                                                             \
-         {                                                                   \
-           A = nat_random_port (&nm->random_seed, 1,                         \
-                                pow2_mask (nm->psid_offset));                \
-           j = nat_random_port (&nm->random_seed, 0, pow2_mask (m));         \
-           portnum = A | (nm->psid << nm->psid_offset) | (j << (16 - m));    \
-           if (a->busy_##n##_port_refcounts[portnum])                        \
-             continue;                                                       \
-           ++a->busy_##n##_port_refcounts[portnum];                          \
-           a->busy_##n##_ports++;                                            \
-           *addr = a->addr;                                                  \
-           *port = clib_host_to_net_u16 (portnum);                           \
-           return 0;                                                         \
-         }                                                                   \
-      }                                                                       \
-    break;
-      foreach_nat_protocol
-#undef _
-       default : nat_elog_info (nm, "unknown protocol");
-      return 1;
+  if (a->busy_ports[proto] < ports)
+    {
+      while (1)
+       {
+         A =
+           nat_random_port (&nm->random_seed, 1, pow2_mask (nm->psid_offset));
+         j = nat_random_port (&nm->random_seed, 0, pow2_mask (m));
+         portnum = A | (nm->psid << nm->psid_offset) | (j << (16 - m));
+         if (nat44_ei_port_is_used (a, proto, portnum))
+           continue;
+         nat44_ei_port_get (a, proto, portnum);
+         a->busy_ports[proto]++;
+         *addr = a->addr;
+         *port = clib_host_to_net_u16 (portnum);
+         return 0;
+       }
     }
 
 exhausted:
@@ -1725,30 +1999,6 @@ nat44_ei_set_alloc_mape (u16 psid, u16 psid_offset, u16 psid_length)
   nm->psid_length = psid_length;
 }
 
-static void
-nat44_ei_add_static_mapping_when_resolved (ip4_address_t l_addr, u16 l_port,
-                                          u16 e_port, nat_protocol_t proto,
-                                          u32 sw_if_index, u32 vrf_id,
-                                          int addr_only, int identity_nat,
-                                          u8 *tag)
-{
-  nat44_ei_main_t *nm = &nat44_ei_main;
-  nat44_ei_static_map_resolve_t *rp;
-
-  vec_add2 (nm->to_resolve, rp, 1);
-  clib_memset (rp, 0, sizeof (*rp));
-
-  rp->l_addr.as_u32 = l_addr.as_u32;
-  rp->l_port = l_port;
-  rp->e_port = e_port;
-  rp->sw_if_index = sw_if_index;
-  rp->vrf_id = vrf_id;
-  rp->proto = proto;
-  rp->addr_only = addr_only;
-  rp->identity_nat = identity_nat;
-  rp->tag = vec_dup (tag);
-}
-
 void
 nat44_ei_delete_session (nat44_ei_main_t *nm, nat44_ei_session_t *ses,
                         u32 thread_index)
@@ -1787,10 +2037,13 @@ nat44_ei_del_session (nat44_ei_main_t *nm, ip4_address_t *addr, u16 port,
 {
   nat44_ei_main_per_thread_data_t *tnm;
   clib_bihash_kv_8_8_t kv, value;
-  u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
+  u32 fib_index;
   nat44_ei_session_t *s;
   clib_bihash_8_8_t *t;
 
+  fail_if_disabled ();
+
+  fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
   init_nat_k (&kv, *addr, port, fib_index, proto);
   t = is_in ? &nm->in2out : &nm->out2in;
   if (!clib_bihash_search_8_8 (t, &kv, &value))
@@ -1812,19 +2065,6 @@ nat44_ei_del_session (nat44_ei_main_t *nm, ip4_address_t *addr, u16 port,
   return VNET_API_ERROR_NO_SUCH_ENTRY;
 }
 
-u32
-nat44_ei_get_thread_idx_by_port (u16 e_port)
-{
-  nat44_ei_main_t *nm = &nat44_ei_main;
-  u32 thread_idx = nm->num_workers;
-  if (nm->num_workers > 1)
-    {
-      thread_idx = nm->first_worker_index +
-                  nm->workers[(e_port - 1024) / nm->port_per_thread];
-    }
-  return thread_idx;
-}
-
 void
 nat44_ei_add_del_addr_to_fib (ip4_address_t *addr, u8 p_len, u32 sw_if_index,
                              int is_add)
@@ -1840,412 +2080,560 @@ nat44_ei_add_del_addr_to_fib (ip4_address_t *addr, u8 p_len, u32 sw_if_index,
   u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
 
   if (is_add)
-    fib_table_entry_update_one_path (
-      fib_index, &prefix, nm->fib_src_low,
-      (FIB_ENTRY_FLAG_CONNECTED | FIB_ENTRY_FLAG_LOCAL |
-       FIB_ENTRY_FLAG_EXCLUSIVE),
-      DPO_PROTO_IP4, NULL, sw_if_index, ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
+    {
+      fib_table_entry_update_one_path (fib_index, &prefix, nm->fib_src_low,
+                                      (FIB_ENTRY_FLAG_CONNECTED |
+                                       FIB_ENTRY_FLAG_LOCAL |
+                                       FIB_ENTRY_FLAG_EXCLUSIVE),
+                                      DPO_PROTO_IP4, NULL, sw_if_index, ~0, 1,
+                                      NULL, FIB_ROUTE_PATH_FLAG_NONE);
+    }
   else
-    fib_table_entry_delete (fib_index, &prefix, nm->fib_src_low);
+    {
+      fib_table_entry_delete (fib_index, &prefix, nm->fib_src_low);
+    }
 }
 
 int
-nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
-                                u16 l_port, u16 e_port, nat_protocol_t proto,
-                                u32 sw_if_index, u32 vrf_id, u8 addr_only,
-                                u8 identity_nat, u8 *tag, u8 is_add)
+nat44_ei_reserve_port (ip4_address_t addr, u16 port, nat_protocol_t proto)
 {
+  u32 ti = nat44_ei_get_thread_idx_by_port (port);
   nat44_ei_main_t *nm = &nat44_ei_main;
-  nat44_ei_static_mapping_t *m = 0;
-  clib_bihash_kv_8_8_t kv, value;
   nat44_ei_address_t *a = 0;
-  u32 fib_index = ~0;
-  nat44_ei_interface_t *interface;
-  nat44_ei_main_per_thread_data_t *tnm;
-  nat44_ei_user_key_t u_key;
-  nat44_ei_user_t *u;
-  dlist_elt_t *head, *elt;
-  u32 elt_index, head_index;
-  u32 ses_index;
-  u64 user_index;
-  nat44_ei_session_t *s;
-  nat44_ei_static_map_resolve_t *rp, *rp_match = 0;
-  nat44_ei_lb_addr_port_t *local;
-  u32 find = ~0;
   int i;
 
-  if (sw_if_index != ~0)
+  for (i = 0; i < vec_len (nm->addresses); i++)
     {
-      ip4_address_t *first_int_addr;
+      a = nm->addresses + i;
 
-      for (i = 0; i < vec_len (nm->to_resolve); i++)
-       {
-         rp = nm->to_resolve + i;
-         if (rp->sw_if_index != sw_if_index ||
-             rp->l_addr.as_u32 != l_addr.as_u32 || rp->vrf_id != vrf_id ||
-             rp->addr_only != addr_only)
-           continue;
+      if (a->addr.as_u32 != addr.as_u32)
+       continue;
 
-         if (!addr_only)
-           {
-             if ((rp->l_port != l_port && rp->e_port != e_port) ||
-                 rp->proto != proto)
-               continue;
-           }
+      if (nat44_ei_port_is_used (a, proto, port))
+       continue;
 
-         rp_match = rp;
-         break;
+      nat44_ei_port_get (a, proto, port);
+      if (port > 1024)
+       {
+         a->busy_ports[proto]++;
+         a->busy_ports_per_thread[proto][ti]++;
        }
+      return 0;
+    }
 
-      /* Might be already set... */
-      first_int_addr = ip4_interface_first_address (
-       nm->ip4_main, sw_if_index, 0 /* just want the address */);
+  return 1;
+}
 
-      if (is_add)
-       {
-         if (rp_match)
-           return VNET_API_ERROR_VALUE_EXIST;
+int
+nat44_ei_free_port (ip4_address_t addr, u16 port, nat_protocol_t proto)
+{
+  u32 ti = nat44_ei_get_thread_idx_by_port (port);
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  nat44_ei_address_t *a = 0;
+  int i;
 
-         nat44_ei_add_static_mapping_when_resolved (
-           l_addr, l_port, e_port, proto, sw_if_index, vrf_id, addr_only,
-           identity_nat, tag);
+  for (i = 0; i < vec_len (nm->addresses); i++)
+    {
+      a = nm->addresses + i;
 
-         /* DHCP resolution required? */
-         if (!first_int_addr)
-           return 0;
+      if (a->addr.as_u32 != addr.as_u32)
+       continue;
 
-         e_addr.as_u32 = first_int_addr->as_u32;
-         /* Identity mapping? */
-         if (l_addr.as_u32 == 0)
-           l_addr.as_u32 = e_addr.as_u32;
-       }
-      else
+      nat44_ei_port_put (a, proto, port);
+      if (port > 1024)
        {
-         if (!rp_match)
-           return VNET_API_ERROR_NO_SUCH_ENTRY;
-
-         vec_del1 (nm->to_resolve, i);
-
-         if (!first_int_addr)
-           return 0;
-
-         e_addr.as_u32 = first_int_addr->as_u32;
-         /* Identity mapping? */
-         if (l_addr.as_u32 == 0)
-           l_addr.as_u32 = e_addr.as_u32;
+         a->busy_ports[proto]--;
+         a->busy_ports_per_thread[proto][ti]--;
        }
+      return 0;
     }
 
-  init_nat_k (&kv, e_addr, addr_only ? 0 : e_port, 0, addr_only ? 0 : proto);
-  if (!clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value))
-    m = pool_elt_at_index (nm->static_mappings, value.value);
+  return 1;
+}
 
-  if (is_add)
-    {
-      if (m)
-       {
-         // identity mapping for second vrf
-         if (nat44_ei_is_identity_static_mapping (m))
-           {
-             pool_foreach (local, m->locals)
-               {
-                 if (local->vrf_id == vrf_id)
-                   return VNET_API_ERROR_VALUE_EXIST;
-               }
-             pool_get (m->locals, local);
-             local->vrf_id = vrf_id;
-             local->fib_index = fib_table_find_or_create_and_lock (
-               FIB_PROTOCOL_IP4, vrf_id, nm->fib_src_low);
-             init_nat_kv (&kv, m->local_addr, m->local_port, local->fib_index,
-                          m->proto, 0, m - nm->static_mappings);
-             clib_bihash_add_del_8_8 (&nm->static_mapping_by_local, &kv, 1);
-             return 0;
-           }
-         return VNET_API_ERROR_VALUE_EXIST;
-       }
+void
+nat44_ei_add_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port,
+                            nat_protocol_t proto, u32 vrf_id, u32 sw_if_index,
+                            u32 flags, ip4_address_t pool_addr, u8 *tag)
+{
+  nat44_ei_static_map_resolve_t *rp;
+  nat44_ei_main_t *nm = &nat44_ei_main;
 
-      /* Convert VRF id to FIB index */
-      if (vrf_id != ~0)
-       {
-         fib_index = fib_table_find_or_create_and_lock (
-           FIB_PROTOCOL_IP4, vrf_id, nm->fib_src_low);
-       }
-      /* If not specified use inside VRF id from NAT44 plugin config */
-      else
-       {
-         fib_index = nm->inside_fib_index;
-         vrf_id = nm->inside_vrf_id;
-         fib_table_lock (fib_index, FIB_PROTOCOL_IP4, nm->fib_src_low);
-       }
+  vec_add2 (nm->to_resolve, rp, 1);
+  rp->l_addr.as_u32 = l_addr.as_u32;
+  rp->l_port = l_port;
+  rp->e_port = e_port;
+  rp->sw_if_index = sw_if_index;
+  rp->vrf_id = vrf_id;
+  rp->proto = proto;
+  rp->flags = flags;
+  rp->pool_addr = pool_addr;
+  rp->tag = vec_dup (tag);
+}
 
-      if (!identity_nat)
-       {
-         init_nat_k (&kv, l_addr, addr_only ? 0 : l_port, fib_index,
-                     addr_only ? 0 : proto);
-         if (!clib_bihash_search_8_8 (&nm->static_mapping_by_local, &kv,
-                                      &value))
-           return VNET_API_ERROR_VALUE_EXIST;
-       }
+int
+nat44_ei_get_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port,
+                            nat_protocol_t proto, u32 vrf_id, u32 sw_if_index,
+                            u32 flags, int *out)
+{
+  nat44_ei_static_map_resolve_t *rp;
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  int i;
 
-      /* Find external address in allocated addresses and reserve port for
-        address and port pair mapping when dynamic translations enabled */
-      if (!(addr_only || nm->static_mapping_only))
+  for (i = 0; i < vec_len (nm->to_resolve); i++)
+    {
+      rp = nm->to_resolve + i;
+
+      if (rp->sw_if_index == sw_if_index && rp->vrf_id == vrf_id)
        {
-         for (i = 0; i < vec_len (nm->addresses); i++)
+         if (is_sm_identity_nat (rp->flags) && is_sm_identity_nat (flags))
            {
-             if (nm->addresses[i].addr.as_u32 == e_addr.as_u32)
+             if (!(is_sm_addr_only (rp->flags) && is_sm_addr_only (flags)))
                {
-                 a = nm->addresses + i;
-                 /* External port must be unused */
-                 switch (proto)
+                 if (rp->e_port != e_port || rp->proto != proto)
                    {
-#define _(N, j, n, s)                                                         \
-  case NAT_PROTOCOL_##N:                                                      \
-    if (a->busy_##n##_port_refcounts[e_port])                                 \
-      return VNET_API_ERROR_INVALID_VALUE;                                    \
-    ++a->busy_##n##_port_refcounts[e_port];                                   \
-    if (e_port > 1024)                                                        \
-      {                                                                       \
-       a->busy_##n##_ports++;                                                \
-       a->busy_##n##_ports_per_thread[nat44_ei_get_thread_idx_by_port (      \
-         e_port)]++;                                                         \
-      }                                                                       \
-    break;
-                     foreach_nat_protocol
-#undef _
-                       default : nat_elog_info (nm, "unknown protocol");
-                     return VNET_API_ERROR_INVALID_VALUE_2;
+                     continue;
                    }
-                 break;
                }
            }
-         /* External address must be allocated */
-         if (!a && (l_addr.as_u32 != e_addr.as_u32))
+         else if (rp->l_addr.as_u32 == l_addr.as_u32)
            {
-             if (sw_if_index != ~0)
+             if (!(is_sm_addr_only (rp->flags) && is_sm_addr_only (flags)))
                {
-                 for (i = 0; i < vec_len (nm->to_resolve); i++)
+                 if (rp->l_port != l_port || rp->e_port != e_port ||
+                     rp->proto != proto)
                    {
-                     rp = nm->to_resolve + i;
-                     if (rp->addr_only)
-                       continue;
-                     if (rp->sw_if_index != sw_if_index &&
-                         rp->l_addr.as_u32 != l_addr.as_u32 &&
-                         rp->vrf_id != vrf_id && rp->l_port != l_port &&
-                         rp->e_port != e_port && rp->proto != proto)
-                       continue;
-
-                     vec_del1 (nm->to_resolve, i);
-                     break;
+                     continue;
                    }
                }
-             return VNET_API_ERROR_NO_SUCH_ENTRY;
            }
+         else
+           {
+             continue;
+           }
+         if (out)
+           {
+             *out = i;
+           }
+         return 0;
        }
+    }
+  return 1;
+}
 
-      pool_get (nm->static_mappings, m);
-      clib_memset (m, 0, sizeof (*m));
-      m->tag = vec_dup (tag);
-      m->local_addr = l_addr;
-      m->external_addr = e_addr;
+int
+nat44_ei_del_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port,
+                            nat_protocol_t proto, u32 vrf_id, u32 sw_if_index,
+                            u32 flags)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  int i;
+  if (!nat44_ei_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
+                                   sw_if_index, flags, &i))
+    {
+      vec_del1 (nm->to_resolve, i);
+      return 0;
+    }
+  return 1;
+}
 
-      if (addr_only)
-       m->flags |= NAT44_EI_STATIC_MAPPING_FLAG_ADDR_ONLY;
-      else
+void
+delete_matching_dynamic_sessions (const nat44_ei_static_mapping_t *m,
+                                 u32 worker_index)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  clib_bihash_kv_8_8_t kv, value;
+  nat44_ei_session_t *s;
+  nat44_ei_user_key_t u_key;
+  nat44_ei_user_t *u;
+  nat44_ei_main_per_thread_data_t *tnm;
+  dlist_elt_t *head, *elt;
+  u32 elt_index, head_index;
+  u32 ses_index;
+  u64 user_index;
+
+  if (nm->static_mapping_only)
+    return;
+
+  tnm = vec_elt_at_index (nm->per_thread_data, worker_index);
+
+  u_key.addr = m->local_addr;
+  u_key.fib_index = m->fib_index;
+  kv.key = u_key.as_u64;
+  if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value))
+    {
+      user_index = value.value;
+      u = pool_elt_at_index (tnm->users, user_index);
+      if (u->nsessions)
+       {
+         head_index = u->sessions_per_user_list_head_index;
+         head = pool_elt_at_index (tnm->list_pool, head_index);
+         elt_index = head->next;
+         elt = pool_elt_at_index (tnm->list_pool, elt_index);
+         ses_index = elt->value;
+         while (ses_index != ~0)
+           {
+             s = pool_elt_at_index (tnm->sessions, ses_index);
+             elt = pool_elt_at_index (tnm->list_pool, elt->next);
+             ses_index = elt->value;
+
+             if (nat44_ei_is_session_static (s))
+               continue;
+
+             if (!is_sm_addr_only (m->flags) &&
+                 s->in2out.port != m->local_port)
+               continue;
+
+             nat44_ei_free_session_data_v2 (nm, s, tnm - nm->per_thread_data,
+                                            0);
+             nat44_ei_delete_session (nm, s, tnm - nm->per_thread_data);
+
+             if (!is_sm_addr_only (m->flags))
+               break;
+           }
+       }
+    }
+}
+
+int
+nat44_ei_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
+                            u16 l_port, u16 e_port, nat_protocol_t proto,
+                            u32 vrf_id, u32 sw_if_index, u32 flags,
+                            ip4_address_t pool_addr, u8 *tag)
+
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+
+  if (is_sm_switch_address (flags))
+    {
+      if (!nat44_ei_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
+                                       sw_if_index, flags, 0))
        {
-         m->local_port = l_port;
-         m->external_port = e_port;
-         m->proto = proto;
+         return VNET_API_ERROR_VALUE_EXIST;
        }
 
-      if (identity_nat)
+      nat44_ei_add_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
+                                  sw_if_index, flags, pool_addr, tag);
+
+      ip4_address_t *first_int_addr =
+       ip4_interface_first_address (nm->ip4_main, sw_if_index, 0);
+      if (!first_int_addr)
        {
-         m->flags |= NAT44_EI_STATIC_MAPPING_FLAG_IDENTITY_NAT;
-         pool_get (m->locals, local);
-         local->vrf_id = vrf_id;
-         local->fib_index = fib_index;
+         // dhcp resolution required
+         return 0;
        }
-      else
+
+      e_addr.as_u32 = first_int_addr->as_u32;
+    }
+
+  return nat44_ei_add_static_mapping_internal (l_addr, e_addr, l_port, e_port,
+                                              proto, vrf_id, sw_if_index,
+                                              flags, pool_addr, tag);
+}
+
+int
+nat44_ei_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
+                            u16 l_port, u16 e_port, nat_protocol_t proto,
+                            u32 vrf_id, u32 sw_if_index, u32 flags)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+
+  if (is_sm_switch_address (flags))
+    {
+
+      if (nat44_ei_del_resolve_record (l_addr, l_port, e_port, proto, vrf_id,
+                                      sw_if_index, flags))
        {
-         m->vrf_id = vrf_id;
-         m->fib_index = fib_index;
+         return VNET_API_ERROR_NO_SUCH_ENTRY;
        }
 
-      if (nm->num_workers > 1)
+      ip4_address_t *first_int_addr =
+       ip4_interface_first_address (nm->ip4_main, sw_if_index, 0);
+      if (!first_int_addr)
        {
-         ip4_header_t ip = {
-           .src_address = m->local_addr,
-         };
-         vec_add1 (m->workers,
-                   nat44_ei_get_in2out_worker_index (&ip, m->fib_index, 0));
-         tnm = vec_elt_at_index (nm->per_thread_data, m->workers[0]);
+         // dhcp resolution required
+         return 0;
        }
-      else
-       tnm = vec_elt_at_index (nm->per_thread_data, nm->num_workers);
 
-      init_nat_kv (&kv, m->local_addr, m->local_port, fib_index, m->proto, 0,
-                  m - nm->static_mappings);
-      clib_bihash_add_del_8_8 (&nm->static_mapping_by_local, &kv, 1);
+      e_addr.as_u32 = first_int_addr->as_u32;
+    }
 
-      init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0,
-                  m - nm->static_mappings);
-      clib_bihash_add_del_8_8 (&nm->static_mapping_by_external, &kv, 1);
+  return nat44_ei_del_static_mapping_internal (
+    l_addr, e_addr, l_port, e_port, proto, vrf_id, sw_if_index, flags);
+}
+
+static int
+nat44_ei_add_static_mapping_internal (ip4_address_t l_addr,
+                                     ip4_address_t e_addr, u16 l_port,
+                                     u16 e_port, nat_protocol_t proto,
+                                     u32 vrf_id, u32 sw_if_index, u32 flags,
+                                     ip4_address_t pool_addr, u8 *tag)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  clib_bihash_kv_8_8_t kv, value;
+  nat44_ei_lb_addr_port_t *local;
+  nat44_ei_static_mapping_t *m;
+  u32 fib_index = ~0;
+  u32 worker_index;
+
+  fail_if_disabled ();
+
+  if (is_sm_addr_only (flags))
+    {
+      e_port = l_port = proto = 0;
+    }
 
-      /* Delete dynamic sessions matching local address (+ local port) */
-      // TODO: based on type of NAT EI/ED
-      if (!(nm->static_mapping_only))
+  if (is_sm_identity_nat (flags))
+    {
+      l_port = e_port;
+      l_addr.as_u32 = e_addr.as_u32;
+    }
+
+  // fib index 0
+  init_nat_k (&kv, e_addr, e_port, 0, proto);
+
+  if (!clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value))
+    {
+      m = pool_elt_at_index (nm->static_mappings, value.value);
+      if (!is_sm_identity_nat (m->flags))
        {
-         u_key.addr = m->local_addr;
-         u_key.fib_index = m->fib_index;
-         kv.key = u_key.as_u64;
-         if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value))
+         return VNET_API_ERROR_VALUE_EXIST;
+       }
+
+      // case:
+      // adding local identity nat record for different vrf table
+      pool_foreach (local, m->locals)
+       {
+         if (local->vrf_id == vrf_id)
            {
-             user_index = value.value;
-             u = pool_elt_at_index (tnm->users, user_index);
-             if (u->nsessions)
-               {
-                 head_index = u->sessions_per_user_list_head_index;
-                 head = pool_elt_at_index (tnm->list_pool, head_index);
-                 elt_index = head->next;
-                 elt = pool_elt_at_index (tnm->list_pool, elt_index);
-                 ses_index = elt->value;
-                 while (ses_index != ~0)
-                   {
-                     s = pool_elt_at_index (tnm->sessions, ses_index);
-                     elt = pool_elt_at_index (tnm->list_pool, elt->next);
-                     ses_index = elt->value;
+             return VNET_API_ERROR_VALUE_EXIST;
+           }
+       }
 
-                     if (nat44_ei_is_session_static (s))
-                       continue;
+      pool_get (m->locals, local);
 
-                     if (!addr_only && s->in2out.port != m->local_port)
-                       continue;
+      local->vrf_id = vrf_id;
+      local->fib_index = fib_table_find_or_create_and_lock (
+       FIB_PROTOCOL_IP4, vrf_id, nm->fib_src_low);
 
-                     nat44_ei_free_session_data_v2 (
-                       nm, s, tnm - nm->per_thread_data, 0);
-                     nat44_ei_delete_session (nm, s,
-                                              tnm - nm->per_thread_data);
+      init_nat_kv (&kv, m->local_addr, m->local_port, local->fib_index,
+                  m->proto, 0, m - nm->static_mappings);
+      clib_bihash_add_del_8_8 (&nm->static_mapping_by_local, &kv, 1);
 
-                     if (!addr_only)
-                       break;
-                   }
-               }
+      return 0;
+    }
+
+  if (vrf_id != ~0)
+    {
+      fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
+                                                    nm->fib_src_low);
+    }
+  else
+    {
+      // fallback to default vrf
+      vrf_id = nm->inside_vrf_id;
+      fib_index = nm->inside_fib_index;
+      fib_table_lock (fib_index, FIB_PROTOCOL_IP4, nm->fib_src_low);
+    }
+
+  if (!is_sm_identity_nat (flags))
+    {
+      init_nat_k (&kv, l_addr, l_port, fib_index, proto);
+      if (!clib_bihash_search_8_8 (&nm->static_mapping_by_local, &kv, &value))
+       {
+         return VNET_API_ERROR_VALUE_EXIST;
+       }
+    }
+
+  if (!(is_sm_addr_only (flags) || nm->static_mapping_only))
+    {
+      if (nat44_ei_reserve_port (e_addr, e_port, proto))
+       {
+         // remove resolve record
+         if ((is_sm_switch_address (flags)) && !is_sm_identity_nat (flags))
+           {
+             nat44_ei_del_resolve_record (l_addr, l_port, e_port, proto,
+                                          vrf_id, sw_if_index, flags);
            }
+         return VNET_API_ERROR_NO_SUCH_ENTRY;
        }
     }
+
+  pool_get (nm->static_mappings, m);
+  clib_memset (m, 0, sizeof (*m));
+
+  m->flags = flags;
+  m->local_addr = l_addr;
+  m->external_addr = e_addr;
+
+  m->tag = vec_dup (tag);
+
+  if (!is_sm_addr_only (flags))
+    {
+      m->local_port = l_port;
+      m->external_port = e_port;
+      m->proto = proto;
+    }
+
+  if (is_sm_identity_nat (flags))
+    {
+      pool_get (m->locals, local);
+
+      local->vrf_id = vrf_id;
+      local->fib_index = fib_index;
+    }
   else
     {
-      if (!m)
+      m->vrf_id = vrf_id;
+      m->fib_index = fib_index;
+    }
+
+  init_nat_kv (&kv, m->local_addr, m->local_port, fib_index, m->proto, 0,
+              m - nm->static_mappings);
+  clib_bihash_add_del_8_8 (&nm->static_mapping_by_local, &kv, 1);
+
+  init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0,
+              m - nm->static_mappings);
+  clib_bihash_add_del_8_8 (&nm->static_mapping_by_external, &kv, 1);
+
+  if (nm->num_workers > 1)
+    {
+      // store worker index for this record
+      ip4_header_t ip = {
+       .src_address = m->local_addr,
+      };
+      worker_index = nat44_ei_get_in2out_worker_index (&ip, m->fib_index, 0);
+      vec_add1 (m->workers, worker_index);
+    }
+  else
+    {
+      worker_index = nm->num_workers;
+    }
+  delete_matching_dynamic_sessions (m, worker_index);
+
+  if (is_sm_addr_only (flags))
+    {
+      nat44_ei_add_del_addr_to_fib_foreach_out_if (&e_addr, 1);
+    }
+
+  return 0;
+}
+
+static int
+nat44_ei_del_static_mapping_internal (ip4_address_t l_addr,
+                                     ip4_address_t e_addr, u16 l_port,
+                                     u16 e_port, nat_protocol_t proto,
+                                     u32 vrf_id, u32 sw_if_index, u32 flags)
+{
+  nat44_ei_main_per_thread_data_t *tnm;
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  clib_bihash_kv_8_8_t kv, value;
+  nat44_ei_lb_addr_port_t *local;
+  nat44_ei_static_mapping_t *m;
+  u32 fib_index = ~0;
+  nat44_ei_user_key_t u_key;
+
+  fail_if_disabled ();
+
+  if (is_sm_addr_only (flags))
+    {
+      e_port = l_port = proto = 0;
+    }
+
+  if (is_sm_identity_nat (flags))
+    {
+      l_port = e_port;
+      l_addr.as_u32 = e_addr.as_u32;
+    }
+
+  // fib index 0
+  init_nat_k (&kv, e_addr, e_port, 0, proto);
+
+  if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value))
+    {
+      if (is_sm_switch_address (flags))
+       {
+         return 0;
+       }
+      return VNET_API_ERROR_NO_SUCH_ENTRY;
+    }
+
+  m = pool_elt_at_index (nm->static_mappings, value.value);
+
+  if (is_sm_identity_nat (flags))
+    {
+      u8 found = 0;
+
+      if (vrf_id == ~0)
        {
-         if (sw_if_index != ~0)
-           return 0;
-         else
-           return VNET_API_ERROR_NO_SUCH_ENTRY;
+         vrf_id = nm->inside_vrf_id;
        }
 
-      if (identity_nat)
+      pool_foreach (local, m->locals)
        {
-         if (vrf_id == ~0)
-           vrf_id = nm->inside_vrf_id;
-
-         pool_foreach (local, m->locals)
+         if (local->vrf_id == vrf_id)
            {
-             if (local->vrf_id == vrf_id)
-               find = local - m->locals;
+             local = pool_elt_at_index (m->locals, local - m->locals);
+             fib_index = local->fib_index;
+             pool_put (m->locals, local);
+             found = 1;
            }
-         if (find == ~0)
-           return VNET_API_ERROR_NO_SUCH_ENTRY;
-
-         local = pool_elt_at_index (m->locals, find);
-         fib_index = local->fib_index;
-         pool_put (m->locals, local);
        }
-      else
-       fib_index = m->fib_index;
+      if (!found)
+       {
+         return VNET_API_ERROR_NO_SUCH_ENTRY;
+       }
+    }
+  else
+    {
+      fib_index = m->fib_index;
+    }
 
-      /* Free external address port */
-      if (!(addr_only || nm->static_mapping_only))
+  if (!(is_sm_addr_only (flags) || nm->static_mapping_only))
+    {
+      if (nat44_ei_free_port (e_addr, e_port, proto))
        {
-         for (i = 0; i < vec_len (nm->addresses); i++)
-           {
-             if (nm->addresses[i].addr.as_u32 == e_addr.as_u32)
-               {
-                 a = nm->addresses + i;
-                 switch (proto)
-                   {
-#define _(N, j, n, s)                                                         \
-  case NAT_PROTOCOL_##N:                                                      \
-    --a->busy_##n##_port_refcounts[e_port];                                   \
-    if (e_port > 1024)                                                        \
-      {                                                                       \
-       a->busy_##n##_ports--;                                                \
-       a->busy_##n##_ports_per_thread[nat44_ei_get_thread_idx_by_port (      \
-         e_port)]--;                                                         \
-      }                                                                       \
-    break;
-                     foreach_nat_protocol
-#undef _
-                       default : return VNET_API_ERROR_INVALID_VALUE_2;
-                   }
-                 break;
-               }
-           }
+         return VNET_API_ERROR_INVALID_VALUE;
        }
+    }
+
+  init_nat_k (&kv, l_addr, l_port, fib_index, proto);
+  clib_bihash_add_del_8_8 (&nm->static_mapping_by_local, &kv, 0);
 
+  if (!nm->static_mapping_only || nm->static_mapping_connection_tracking)
+    {
+      // delete sessions for static mapping
       if (nm->num_workers > 1)
        tnm = vec_elt_at_index (nm->per_thread_data, m->workers[0]);
       else
        tnm = vec_elt_at_index (nm->per_thread_data, nm->num_workers);
 
-      init_nat_k (&kv, m->local_addr, m->local_port, fib_index, m->proto);
-      clib_bihash_add_del_8_8 (&nm->static_mapping_by_local, &kv, 0);
-
-      /* Delete session(s) for static mapping if exist */
-      if (!(nm->static_mapping_only) ||
-         (nm->static_mapping_only && nm->static_mapping_connection_tracking))
-       {
-         u_key.addr = m->local_addr;
-         u_key.fib_index = fib_index;
-         kv.key = u_key.as_u64;
-         nat44_ei_static_mapping_del_sessions (nm, tnm, u_key, addr_only,
-                                               e_addr, e_port);
-       }
+      u_key.addr = m->local_addr;
+      u_key.fib_index = fib_index;
+      kv.key = u_key.as_u64;
+      nat44_ei_static_mapping_del_sessions (
+       nm, tnm, u_key, is_sm_addr_only (flags), e_addr, e_port);
+    }
 
-      fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, nm->fib_src_low);
-      if (pool_elts (m->locals))
-       return 0;
+  fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, nm->fib_src_low);
 
-      init_nat_k (&kv, m->external_addr, m->external_port, 0, m->proto);
+  if (!pool_elts (m->locals))
+    {
+      // this is last record remove all required stuff
+      // fib_index 0
+      init_nat_k (&kv, e_addr, e_port, 0, proto);
       clib_bihash_add_del_8_8 (&nm->static_mapping_by_external, &kv, 0);
 
       vec_free (m->tag);
       vec_free (m->workers);
-      /* Delete static mapping from pool */
       pool_put (nm->static_mappings, m);
-    }
-
-  if (!addr_only || (l_addr.as_u32 == e_addr.as_u32))
-    return 0;
 
-  /* Add/delete external address to FIB */
-  pool_foreach (interface, nm->interfaces)
-    {
-      if (nat44_ei_interface_is_inside (interface) || nm->out2in_dpo)
-       continue;
-
-      nat44_ei_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index,
-                                   is_add);
-      break;
+      if (is_sm_addr_only (flags) && !is_sm_identity_nat (flags))
+       {
+         nat44_ei_add_del_addr_to_fib_foreach_out_if (&e_addr, 0);
+       }
     }
-  pool_foreach (interface, nm->output_feature_interfaces)
-    {
-      if (nat44_ei_interface_is_inside (interface) || nm->out2in_dpo)
-       continue;
 
-      nat44_ei_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index,
-                                   is_add);
-      break;
-    }
   return 0;
 }
 
@@ -2300,16 +2688,16 @@ nat44_ei_static_mapping_match (ip4_address_t match_addr, u16 match_port,
     }
 
   /* Address only mapping doesn't change port */
-  if (nat44_ei_is_addr_only_static_mapping (m))
+  if (is_sm_addr_only (m->flags))
     *mapping_port = match_port;
   else
     *mapping_port = port;
 
   if (PREDICT_FALSE (is_addr_only != 0))
-    *is_addr_only = nat44_ei_is_addr_only_static_mapping (m);
+    *is_addr_only = is_sm_addr_only (m->flags);
 
   if (PREDICT_FALSE (is_identity_nat != 0))
-    *is_identity_nat = nat44_ei_is_identity_static_mapping (m);
+    *is_identity_nat = is_sm_identity_nat (m->flags);
 
   return 0;
 }
@@ -2417,27 +2805,6 @@ nat44_ei_worker_db_init (nat44_ei_main_per_thread_data_t *tnm,
   clib_dlist_init (tnm->lru_pool, tnm->unk_proto_lru_head_index);
 }
 
-static void
-nat44_ei_db_free ()
-{
-  nat44_ei_main_t *nm = &nat44_ei_main;
-  nat44_ei_main_per_thread_data_t *tnm;
-
-  pool_free (nm->static_mappings);
-  clib_bihash_free_8_8 (&nm->static_mapping_by_local);
-  clib_bihash_free_8_8 (&nm->static_mapping_by_external);
-
-  if (nm->pat)
-    {
-      clib_bihash_free_8_8 (&nm->in2out);
-      clib_bihash_free_8_8 (&nm->out2in);
-      vec_foreach (tnm, nm->per_thread_data)
-       {
-         nat44_ei_worker_db_free (tnm);
-       }
-    }
-}
-
 static void
 nat44_ei_db_init (u32 translations, u32 translation_buckets, u32 user_buckets)
 {
@@ -2572,11 +2939,13 @@ nat44_ei_update_outside_fib (ip4_main_t *im, uword opaque, u32 sw_if_index,
 }
 
 int
-nat44_ei_add_address (nat44_ei_main_t *nm, ip4_address_t *addr, u32 vrf_id)
+nat44_ei_add_address (ip4_address_t *addr, u32 vrf_id)
 {
-  nat44_ei_address_t *ap;
-  nat44_ei_interface_t *i;
+  nat44_ei_main_t *nm = &nat44_ei_main;
   vlib_thread_main_t *tm = vlib_get_thread_main ();
+  nat44_ei_address_t *ap;
+
+  fail_if_disabled ();
 
   /* Check if address already exists */
   vec_foreach (ap, nm->addresses)
@@ -2590,137 +2959,49 @@ nat44_ei_add_address (nat44_ei_main_t *nm, ip4_address_t *addr, u32 vrf_id)
 
   vec_add2 (nm->addresses, ap, 1);
 
+  ap->fib_index = ~0;
   ap->addr = *addr;
-  if (vrf_id != ~0)
-    ap->fib_index = fib_table_find_or_create_and_lock (
-      FIB_PROTOCOL_IP4, vrf_id, nm->fib_src_low);
-  else
-    ap->fib_index = ~0;
-
-#define _(N, i, n, s)                                                         \
-  clib_memset (ap->busy_##n##_port_refcounts, 0,                              \
-              sizeof (ap->busy_##n##_port_refcounts));                       \
-  ap->busy_##n##_ports = 0;                                                   \
-  ap->busy_##n##_ports_per_thread = 0;                                        \
-  vec_validate_init_empty (ap->busy_##n##_ports_per_thread,                   \
-                          tm->n_vlib_mains - 1, 0);
-  foreach_nat_protocol
-#undef _
-
-    /* Add external address to FIB */
-    pool_foreach (i, nm->interfaces)
-  {
-    if (nat44_ei_interface_is_inside (i) || nm->out2in_dpo)
-      continue;
 
-    nat44_ei_add_del_addr_to_fib (addr, 32, i->sw_if_index, 1);
-    break;
-  }
-  pool_foreach (i, nm->output_feature_interfaces)
+  if (vrf_id != ~0)
     {
-      if (nat44_ei_interface_is_inside (i) || nm->out2in_dpo)
-       continue;
-
-      nat44_ei_add_del_addr_to_fib (addr, 32, i->sw_if_index, 1);
-      break;
+      ap->fib_index = fib_table_find_or_create_and_lock (
+       FIB_PROTOCOL_IP4, vrf_id, nm->fib_src_low);
     }
 
-  return 0;
-}
-
-int
-nat44_ei_add_interface_address (nat44_ei_main_t *nm, u32 sw_if_index,
-                               int is_del)
-{
-  ip4_main_t *ip4_main = nm->ip4_main;
-  ip4_address_t *first_int_addr;
-  nat44_ei_static_map_resolve_t *rp;
-  u32 *indices_to_delete = 0;
-  int i, j;
-  u32 *auto_add_sw_if_indices = nm->auto_add_sw_if_indices;
-
-  first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index,
-                                               0 /* just want the address */);
-
-  for (i = 0; i < vec_len (auto_add_sw_if_indices); i++)
+  nat_protocol_t proto;
+  for (proto = 0; proto < NAT_N_PROTOCOLS; ++proto)
     {
-      if (auto_add_sw_if_indices[i] == sw_if_index)
-       {
-         if (is_del)
-           {
-             /* if have address remove it */
-             if (first_int_addr)
-               (void) nat44_ei_del_address (nm, first_int_addr[0], 1);
-             else
-               {
-                 for (j = 0; j < vec_len (nm->to_resolve); j++)
-                   {
-                     rp = nm->to_resolve + j;
-                     if (rp->sw_if_index == sw_if_index)
-                       vec_add1 (indices_to_delete, j);
-                   }
-                 if (vec_len (indices_to_delete))
-                   {
-                     for (j = vec_len (indices_to_delete) - 1; j >= 0; j--)
-                       vec_del1 (nm->to_resolve, j);
-                     vec_free (indices_to_delete);
-                   }
-               }
-             vec_del1 (nm->auto_add_sw_if_indices, i);
-           }
-         else
-           return VNET_API_ERROR_VALUE_EXIST;
-
-         return 0;
-       }
+      ap->busy_port_bitmap[proto] = 0;
+      ap->busy_ports[proto] = 0;
+      ap->busy_ports_per_thread[proto] = 0;
+      vec_validate_init_empty (ap->busy_ports_per_thread[proto],
+                              tm->n_vlib_mains - 1, 0);
     }
 
-  if (is_del)
-    return VNET_API_ERROR_NO_SUCH_ENTRY;
-
-  /* add to the auto-address list */
-  vec_add1 (nm->auto_add_sw_if_indices, sw_if_index);
-
-  /* If the address is already bound - or static - add it now */
-  if (first_int_addr)
-    (void) nat44_ei_add_address (nm, first_int_addr, ~0);
+    nat44_ei_add_del_addr_to_fib_foreach_out_if (addr, 1);
 
   return 0;
 }
 
-static int
-nat44_ei_is_address_used_in_static_mapping (ip4_address_t addr)
-{
-  nat44_ei_main_t *nm = &nat44_ei_main;
-  nat44_ei_static_mapping_t *m;
-  pool_foreach (m, nm->static_mappings)
-    {
-      if (nat44_ei_is_addr_only_static_mapping (m) ||
-         nat44_ei_is_identity_static_mapping (m))
-       continue;
-      if (m->external_addr.as_u32 == addr.as_u32)
-       return 1;
-    }
-  return 0;
-}
-
 int
-nat44_ei_del_address (nat44_ei_main_t *nm, ip4_address_t addr, u8 delete_sm)
+nat44_ei_del_address (ip4_address_t addr, u8 delete_sm)
 {
+  nat44_ei_main_t *nm = &nat44_ei_main;
   nat44_ei_address_t *a = 0;
   nat44_ei_session_t *ses;
   u32 *ses_to_be_removed = 0, *ses_index;
   nat44_ei_main_per_thread_data_t *tnm;
-  nat44_ei_interface_t *interface;
   nat44_ei_static_mapping_t *m;
-  int i;
+  int j;
+
+  fail_if_disabled ();
 
   /* Find SNAT address */
-  for (i = 0; i < vec_len (nm->addresses); i++)
+  for (j = 0; j < vec_len (nm->addresses); j++)
     {
-      if (nm->addresses[i].addr.as_u32 == addr.as_u32)
+      if (nm->addresses[j].addr.as_u32 == addr.as_u32)
        {
-         a = nm->addresses + i;
+         a = nm->addresses + j;
          break;
        }
     }
@@ -2735,11 +3016,9 @@ nat44_ei_del_address (nat44_ei_main_t *nm, ip4_address_t addr, u8 delete_sm)
       pool_foreach (m, nm->static_mappings)
        {
          if (m->external_addr.as_u32 == addr.as_u32)
-           (void) nat44_ei_add_del_static_mapping (
+           nat44_ei_del_static_mapping_internal (
              m->local_addr, m->external_addr, m->local_port, m->external_port,
-             m->proto, ~0 /* sw_if_index */, m->vrf_id,
-             nat44_ei_is_addr_only_static_mapping (m),
-             nat44_ei_is_identity_static_mapping (m), m->tag, 0);
+             m->proto, m->vrf_id, ~0, m->flags);
        }
     }
   else
@@ -2752,11 +3031,9 @@ nat44_ei_del_address (nat44_ei_main_t *nm, ip4_address_t addr, u8 delete_sm)
        }
     }
 
-  if (a->fib_index != ~0)
-    fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP4, nm->fib_src_low);
-
   /* Delete sessions using address */
-  if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports)
+  if (a->busy_ports[NAT_PROTOCOL_TCP] || a->busy_ports[NAT_PROTOCOL_UDP] ||
+      a->busy_ports[NAT_PROTOCOL_ICMP])
     {
       vec_foreach (tnm, nm->per_thread_data)
        {
@@ -2778,28 +3055,116 @@ nat44_ei_del_address (nat44_ei_main_t *nm, ip4_address_t addr, u8 delete_sm)
        }
     }
 
-#define _(N, i, n, s) vec_free (a->busy_##n##_ports_per_thread);
-  foreach_nat_protocol
-#undef _
-    vec_del1 (nm->addresses, i);
+  nat44_ei_add_del_addr_to_fib_foreach_out_if (&addr, 0);
 
-  /* Delete external address from FIB */
-  pool_foreach (interface, nm->interfaces)
+  if (a->fib_index != ~0)
     {
-      if (nat44_ei_interface_is_inside (interface) || nm->out2in_dpo)
-       continue;
-      nat44_ei_add_del_addr_to_fib (&addr, 32, interface->sw_if_index, 0);
-      break;
+      fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP4, nm->fib_src_low);
     }
 
-  pool_foreach (interface, nm->output_feature_interfaces)
+  nat_protocol_t proto;
+  for (proto = 0; proto < NAT_N_PROTOCOLS; ++proto)
     {
-      if (nat44_ei_interface_is_inside (interface) || nm->out2in_dpo)
-       continue;
-      nat44_ei_add_del_addr_to_fib (&addr, 32, interface->sw_if_index, 0);
-      break;
+      vec_free (a->busy_ports_per_thread[proto]);
+    }
+
+  vec_del1 (nm->addresses, j);
+  return 0;
+}
+
+int
+nat44_ei_add_interface_address (u32 sw_if_index)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  ip4_main_t *ip4_main = nm->ip4_main;
+  ip4_address_t *first_int_addr;
+  u32 *auto_add_sw_if_indices = nm->auto_add_sw_if_indices;
+  int i;
+
+  for (i = 0; i < vec_len (auto_add_sw_if_indices); i++)
+    {
+      if (auto_add_sw_if_indices[i] == sw_if_index)
+       {
+         return VNET_API_ERROR_VALUE_EXIST;
+       }
+    }
+
+  /* add to the auto-address list */
+  vec_add1 (nm->auto_add_sw_if_indices, sw_if_index);
+
+  // if the address is already bound - or static - add it now
+  first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0);
+  if (first_int_addr)
+    {
+      (void) nat44_ei_add_address (first_int_addr, ~0);
+    }
+
+  return 0;
+}
+
+int
+nat44_ei_del_interface_address (u32 sw_if_index)
+{
+  nat44_ei_main_t *nm = &nat44_ei_main;
+  ip4_main_t *ip4_main = nm->ip4_main;
+  ip4_address_t *first_int_addr;
+  nat44_ei_static_map_resolve_t *rp;
+  u32 *indices_to_delete = 0;
+  int i, j;
+  u32 *auto_add_sw_if_indices = nm->auto_add_sw_if_indices;
+
+  fail_if_disabled ();
+
+  first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0);
+
+  for (i = 0; i < vec_len (auto_add_sw_if_indices); i++)
+    {
+      if (auto_add_sw_if_indices[i] == sw_if_index)
+       {
+         first_int_addr =
+           ip4_interface_first_address (ip4_main, sw_if_index, 0);
+         if (first_int_addr)
+           {
+             (void) nat44_ei_del_address (first_int_addr[0], 1);
+           }
+         else
+           {
+             for (j = 0; j < vec_len (nm->to_resolve); j++)
+               {
+                 rp = nm->to_resolve + j;
+                 if (rp->sw_if_index == sw_if_index)
+                   {
+                     vec_add1 (indices_to_delete, j);
+                   }
+               }
+             if (vec_len (indices_to_delete))
+               {
+                 for (j = vec_len (indices_to_delete) - 1; j >= 0; j--)
+                   {
+                     vec_del1 (nm->to_resolve, j);
+                   }
+                 vec_free (indices_to_delete);
+               }
+           }
+
+         vec_del1 (nm->auto_add_sw_if_indices, i);
+         return 0;
+       }
     }
+  return VNET_API_ERROR_NO_SUCH_ENTRY;
+}
 
+static_always_inline int
+is_sw_if_index_reg_for_auto_resolve (u32 *sw_if_indices, u32 sw_if_index)
+{
+  u32 *i;
+  vec_foreach (i, sw_if_indices)
+    {
+      if (*i == sw_if_index)
+       {
+         return 1;
+       }
+    }
   return 0;
 }
 
@@ -2812,61 +3177,59 @@ nat44_ei_ip4_add_del_interface_address_cb (ip4_main_t *im, uword opaque,
 {
   nat44_ei_main_t *nm = &nat44_ei_main;
   nat44_ei_static_map_resolve_t *rp;
-  ip4_address_t l_addr;
-  int i, j;
-  int rv;
   nat44_ei_address_t *addresses = nm->addresses;
+  int rv, i;
 
   if (!nm->enabled)
-    return;
-
-  for (i = 0; i < vec_len (nm->auto_add_sw_if_indices); i++)
     {
-      if (sw_if_index == nm->auto_add_sw_if_indices[i])
-       goto match;
+      return;
     }
 
-  return;
+  if (!is_sw_if_index_reg_for_auto_resolve (nm->auto_add_sw_if_indices,
+                                           sw_if_index))
+    {
+      return;
+    }
 
-match:
   if (!is_delete)
     {
       /* Don't trip over lease renewal, static config */
-      for (j = 0; j < vec_len (addresses); j++)
-       if (addresses[j].addr.as_u32 == address->as_u32)
-         return;
+      for (i = 0; i < vec_len (addresses); i++)
+       {
+         if (addresses[i].addr.as_u32 == address->as_u32)
+           {
+             return;
+           }
+       }
+
+      (void) nat44_ei_add_address (address, ~0);
 
-      (void) nat44_ei_add_address (nm, address, ~0);
       /* Scan static map resolution vector */
-      for (j = 0; j < vec_len (nm->to_resolve); j++)
+      for (i = 0; i < vec_len (nm->to_resolve); i++)
        {
-         rp = nm->to_resolve + j;
-         if (rp->addr_only)
-           continue;
+         rp = nm->to_resolve + i;
+         if (is_sm_addr_only (rp->flags))
+           {
+             continue;
+           }
          /* On this interface? */
          if (rp->sw_if_index == sw_if_index)
            {
-             /* Indetity mapping? */
-             if (rp->l_addr.as_u32 == 0)
-               l_addr.as_u32 = address[0].as_u32;
-             else
-               l_addr.as_u32 = rp->l_addr.as_u32;
-             /* Add the static mapping */
-             rv = nat44_ei_add_del_static_mapping (
-               l_addr, address[0], rp->l_port, rp->e_port, rp->proto,
-               ~0 /* sw_if_index */, rp->vrf_id, rp->addr_only,
-               rp->identity_nat, rp->tag, 1);
+             rv = nat44_ei_add_static_mapping_internal (
+               rp->l_addr, address[0], rp->l_port, rp->e_port, rp->proto,
+               rp->vrf_id, ~0, rp->flags, rp->pool_addr, rp->tag);
              if (rv)
-               nat_elog_notice_X1 (
-                 nm, "nat44_ei_add_del_static_mapping returned %d", "i4", rv);
+               {
+                 nat_elog_notice_X1 (
+                   nm, "add_static_mapping_internal returned %d", "i4", rv);
+               }
            }
        }
-      return;
     }
   else
     {
-      (void) nat44_ei_del_address (nm, address[0], 1);
-      return;
+      // remove all static mapping records
+      (void) nat44_ei_del_address (address[0], 1);
     }
 }
 
@@ -2889,57 +3252,64 @@ nat44_ei_ip4_add_del_addr_only_sm_cb (ip4_main_t *im, uword opaque,
   nat44_ei_static_map_resolve_t *rp;
   nat44_ei_static_mapping_t *m;
   clib_bihash_kv_8_8_t kv, value;
-  int i, rv;
-  ip4_address_t l_addr;
+  int i, rv = 0, match = 0;
 
   if (!nm->enabled)
-    return;
+    {
+      return;
+    }
 
   for (i = 0; i < vec_len (nm->to_resolve); i++)
     {
       rp = nm->to_resolve + i;
-      if (rp->addr_only == 0)
-       continue;
-      if (rp->sw_if_index == sw_if_index)
-       goto match;
+
+      if (is_sm_addr_only (rp->flags) && rp->sw_if_index == sw_if_index)
+       {
+         match = 1;
+         break;
+       }
     }
 
-  return;
+  if (!match)
+    {
+      return;
+    }
 
-match:
-  init_nat_k (&kv, *address, rp->addr_only ? 0 : rp->e_port,
-             nm->outside_fib_index, rp->addr_only ? 0 : rp->proto);
+  init_nat_k (&kv, *address, is_sm_addr_only (rp->flags) ? 0 : rp->e_port,
+             nm->outside_fib_index,
+             is_sm_addr_only (rp->flags) ? 0 : rp->proto);
   if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value))
     m = 0;
   else
     m = pool_elt_at_index (nm->static_mappings, value.value);
 
-  if (!is_delete)
+  if (is_delete)
     {
-      /* Don't trip over lease renewal, static config */
-      if (m)
+      if (!m)
        return;
+      rv = nat44_ei_del_static_mapping_internal (
+       rp->l_addr, address[0], rp->l_port, rp->e_port, rp->proto, rp->vrf_id,
+       ~0, rp->flags);
+      if (rv)
+       {
+         nat_elog_notice_X1 (nm, "nat44_ei_del_static_mapping returned %d",
+                             "i4", rv);
+       }
     }
   else
     {
-      if (!m)
+      if (m)
        return;
-    }
+      rv = nat44_ei_add_static_mapping_internal (
+       rp->l_addr, address[0], rp->l_port, rp->e_port, rp->proto, rp->vrf_id,
+       ~0, rp->flags, rp->pool_addr, rp->tag);
 
-  /* Indetity mapping? */
-  if (rp->l_addr.as_u32 == 0)
-    l_addr.as_u32 = address[0].as_u32;
-  else
-    l_addr.as_u32 = rp->l_addr.as_u32;
-  /* Add the static mapping */
-
-  rv = nat44_ei_add_del_static_mapping (
-    l_addr, address[0], rp->l_port, rp->e_port, rp->proto,
-    ~0 /* sw_if_index */, rp->vrf_id, rp->addr_only, rp->identity_nat, rp->tag,
-    !is_delete);
-  if (rv)
-    nat_elog_notice_X1 (nm, "nat44_ei_add_del_static_mapping returned %d",
-                       "i4", rv);
+      if (rv)
+       {
+         nat_elog_notice_X1 (nm, "nat44_ei_add_static_mapping returned %d",
+                             "i4", rv);
+       }
+    }
 }
 
 static_always_inline uword