Fix multiple NAT translation with interface address as external
[vpp.git] / src / plugins / nat / nat.c
old mode 100644 (file)
new mode 100755 (executable)
index 9a62047..5953c42
@@ -27,6 +27,7 @@
 #include <nat/nat66.h>
 #include <nat/dslite.h>
 #include <nat/nat_reass.h>
+#include <nat/nat_inlines.h>
 #include <vnet/fib/fib_table.h>
 #include <vnet/fib/ip4_fib.h>
 
@@ -44,7 +45,8 @@ VNET_FEATURE_INIT (ip4_snat_in2out, static) = {
 VNET_FEATURE_INIT (ip4_snat_out2in, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-out2in",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", 
+                               "ip4-dhcp-client-detect"),
 };
 VNET_FEATURE_INIT (ip4_nat_classify, static) = {
   .arc_name = "ip4-unicast",
@@ -152,6 +154,21 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
   snat_main_per_thread_data_t *tsm =
     vec_elt_at_index (sm->per_thread_data, thread_index);
 
+  if (is_fwd_bypass_session (s))
+    {
+      ed_key.l_addr = s->in2out.addr;
+      ed_key.r_addr = s->ext_host_addr;
+      ed_key.l_port = s->in2out.port;
+      ed_key.r_port = s->ext_host_port;
+      ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol);
+      ed_key.fib_index = 0;
+      ed_kv.key[0] = ed_key.as_u64[0];
+      ed_kv.key[1] = ed_key.as_u64[1];
+      if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0))
+        nat_log_warn ("in2out_ed key del failed");
+      return;
+    }
+
   /* Endpoint dependent session lookup tables */
   if (is_ed_session (s))
     {
@@ -173,7 +190,7 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
       ed_kv.key[0] = ed_key.as_u64[0];
       ed_kv.key[1] = ed_key.as_u64[1];
       if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &ed_kv, 0))
-        clib_warning ("out2in_ed key del failed");
+        nat_log_warn ("out2in_ed key del failed");
 
       ed_key.l_addr = s->in2out.addr;
       ed_key.fib_index = s->in2out.fib_index;
@@ -187,7 +204,7 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
       ed_kv.key[0] = ed_key.as_u64[0];
       ed_kv.key[1] = ed_key.as_u64[1];
       if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0))
-        clib_warning ("in2out_ed key del failed");
+        nat_log_warn ("in2out_ed key del failed");
     }
 
   if (snat_is_unk_proto_session (s))
@@ -224,10 +241,10 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
   /* Session lookup tables */
   kv.key = s->in2out.as_u64;
   if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0))
-    clib_warning ("in2out key del failed");
+    nat_log_warn ("in2out key del failed");
   kv.key = s->out2in.as_u64;
   if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
-    clib_warning ("out2in key del failed");
+    nat_log_warn ("out2in key del failed");
 
   if (snat_is_session_static (s))
     return;
@@ -271,7 +288,7 @@ nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr, u32 fib_index,
 
       /* add user */
       if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
-        clib_warning ("user_hash keay add failed");
+        nat_log_warn ("user_hash keay add failed");
     }
   else
     {
@@ -314,10 +331,19 @@ nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u, u32 thread_index)
       /* Get the session */
       s = pool_elt_at_index (tsm->sessions, session_index);
       nat_free_session_data (sm, s, thread_index);
+      if (snat_is_session_static(s))
+        u->nstaticsessions--;
+      else
+        u->nsessions--;
       s->outside_address_index = ~0;
       s->flags = 0;
       s->total_bytes = 0;
       s->total_pkts = 0;
+      s->state = 0;
+      s->ext_host_addr.as_u32 = 0;
+      s->ext_host_port = 0;
+      s->ext_host_nat_addr.as_u32 = 0;
+      s->ext_host_nat_port = 0;
     }
   else
     {
@@ -342,6 +368,24 @@ nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u, u32 thread_index)
   return s;
 }
 
+typedef struct {
+  u8 next_in2out;
+} nat44_classify_trace_t;
+
+static u8 * format_nat44_classify_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  nat44_classify_trace_t *t = va_arg (*args, nat44_classify_trace_t *);
+  char *next;
+
+  next = t->next_in2out ? "nat44-in2out" : "nat44-out2in";
+
+  s = format (s, "nat44-classify: next %s", next);
+
+  return s;
+}
+
 static inline uword
 nat44_classify_node_fn_inline (vlib_main_t * vm,
                                vlib_node_runtime_t * node,
@@ -350,6 +394,7 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
   u32 n_left_from, * from, * to_next;
   nat44_classify_next_t next_index;
   snat_main_t *sm = &snat_main;
+  snat_static_mapping_t *m;
 
   from = vlib_frame_vector_args (frame);
   n_left_from = frame->n_vectors;
@@ -401,7 +446,9 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
               kv0.key = m_key0.as_u64;
               if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv0, &value0))
                 {
-                  next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                  m = pool_elt_at_index (sm->static_mappings, value0.value);
+                  if (m->local_addr.as_u32 != m->external_addr.as_u32)
+                    next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
                   goto enqueue0;
                 }
               udp_header_t * udp0 = ip4_next_header (ip0);
@@ -409,10 +456,22 @@ nat44_classify_node_fn_inline (vlib_main_t * vm,
               m_key0.protocol = ip_proto_to_snat_proto (ip0->protocol);
               kv0.key = m_key0.as_u64;
               if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv0, &value0))
-                next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                {
+                  m = pool_elt_at_index (sm->static_mappings, value0.value);
+                  if (m->local_addr.as_u32 != m->external_addr.as_u32)
+                    next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
+                }
             }
 
         enqueue0:
+          if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+            {
+              nat44_classify_trace_t *t =
+                  vlib_add_trace (vm, node, b0, sizeof (*t));
+              t->next_in2out = next0 == NAT44_CLASSIFY_NEXT_IN2OUT ? 1 : 0;
+            }
+
           /* verify speculative enqueue, maybe switch current next frame */
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                           to_next, n_left_to_next,
@@ -437,6 +496,7 @@ VLIB_REGISTER_NODE (nat44_classify_node) = {
   .function = nat44_classify_node_fn,
   .name = "nat44-classify",
   .vector_size = sizeof (u32),
+  .format_trace = format_nat44_classify_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
   .next_nodes = {
@@ -460,6 +520,7 @@ VLIB_REGISTER_NODE (nat44_det_classify_node) = {
   .function = nat44_det_classify_node_fn,
   .name = "nat44-det-classify",
   .vector_size = sizeof (u32),
+  .format_trace = format_nat44_classify_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
   .next_nodes = {
@@ -483,6 +544,7 @@ VLIB_REGISTER_NODE (nat44_handoff_classify_node) = {
   .function = nat44_handoff_classify_node_fn,
   .name = "nat44-handoff-classify",
   .vector_size = sizeof (u32),
+  .format_trace = format_nat44_classify_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
   .next_nodes = {
@@ -522,7 +584,7 @@ snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index,
   if (is_add)
     fib_table_entry_update_one_path(fib_index,
                                     &prefix,
-                                    FIB_SOURCE_PLUGIN_HI,
+                                    FIB_SOURCE_PLUGIN_LOW,
                                     (FIB_ENTRY_FLAG_CONNECTED |
                                      FIB_ENTRY_FLAG_LOCAL |
                                      FIB_ENTRY_FLAG_EXCLUSIVE),
@@ -536,7 +598,7 @@ snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index,
   else
     fib_table_entry_delete(fib_index,
                            &prefix,
-                           FIB_SOURCE_PLUGIN_HI);
+                           FIB_SOURCE_PLUGIN_LOW);
 }
 
 void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id,
@@ -562,7 +624,7 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id,
   if (vrf_id != ~0)
     ap->fib_index =
       fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
-                                         FIB_SOURCE_PLUGIN_HI);
+                                         FIB_SOURCE_PLUGIN_LOW);
   else
     ap->fib_index = ~0;
 #define _(N, i, n, s) \
@@ -654,7 +716,11 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
  * @param addr_only If 0 address port and pair mapping, otherwise address only.
  * @param sw_if_index External port instead of specific IP address.
  * @param is_add If 0 delete static mapping, otherwise add.
- * @param twice_nat If 1 translate external host address and port.
+ * @param twice_nat If value is TWICE_NAT then translate external host address
+ *                  and port.
+ *                  If value is TWICE_NAT_SELF then translate external host
+ *                  address and port whenever external host address equals
+ *                  local address of internal host.
  * @param out2in_only If 1 rule match only out2in direction
  * @param tag - opaque string tag
  *
@@ -663,7 +729,8 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
 int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                             u16 l_port, u16 e_port, u32 vrf_id, int addr_only,
                             u32 sw_if_index, snat_protocol_t proto, int is_add,
-                            u8 twice_nat, u8 out2in_only, u8 * tag)
+                            twice_nat_type_t twice_nat, u8 out2in_only,
+                            u8 * tag)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -682,24 +749,24 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
   u32 ses_index;
   u64 user_index;
   snat_session_t * s;
+  snat_static_map_resolve_t *rp, *rp_match = 0;
 
   /* If the external address is a specific interface address */
   if (sw_if_index != ~0)
     {
       ip4_address_t * first_int_addr;
-      snat_static_map_resolve_t *rp, *rp_match = 0;
 
       for (i = 0; i < vec_len (sm->to_resolve); i++)
         {
           rp = sm->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)
+          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 (!addr_only)
             {
-              if (rp->l_port != l_port && rp->e_port != e_port && rp->proto != proto)
+              if (rp->l_port != l_port || rp->e_port != e_port || rp->proto != proto)
                 continue;
             }
 
@@ -822,7 +889,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                       foreach_snat_protocol
 #undef _
                     default:
-                      clib_warning("unknown_protocol");
+                      nat_log_info ("unknown protocol");
                       return VNET_API_ERROR_INVALID_VALUE_2;
                     }
                   break;
@@ -830,7 +897,26 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
             }
           /* External address must be allocated */
           if (!a && (l_addr.as_u32 != e_addr.as_u32))
-            return VNET_API_ERROR_NO_SUCH_ENTRY;
+            {
+              if (sw_if_index != ~0)
+                {
+                  for (i = 0; i < vec_len (sm->to_resolve); i++)
+                    {
+                      rp = sm->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 (sm->to_resolve, i);
+                      break;
+                    }
+                }
+              return VNET_API_ERROR_NO_SUCH_ENTRY;
+            }
         }
 
       pool_get (sm->static_mappings, m);
@@ -871,11 +957,11 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
         clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1);
       if (twice_nat || out2in_only)
         {
-          m_key.port = clib_host_to_net_u16 (l_port);
+          m_key.port = clib_host_to_net_u16 (m->local_port);
           kv.key = m_key.as_u64;
           kv.value = ~0ULL;
           if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 1))
-            clib_warning ("in2out key add failed");
+            nat_log_warn ("in2out key add failed");
         }
 
       m_key.addr = m->external_addr;
@@ -890,7 +976,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
           kv.key = m_key.as_u64;
           kv.value = ~0ULL;
           if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 1))
-            clib_warning ("out2in key add failed");
+            nat_log_warn ("out2in key add failed");
         }
 
       /* Delete dynamic sessions matching local address (+ local port) */
@@ -919,18 +1005,11 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                       if (snat_is_session_static (s))
                         continue;
 
-                      if (!addr_only)
-                        {
-                          if ((s->out2in.addr.as_u32 != e_addr.as_u32) &&
-                              (clib_net_to_host_u16 (s->out2in.port) != e_port))
-                            continue;
-                        }
+                      if (!addr_only && (clib_net_to_host_u16 (s->in2out.port) != m->local_port))
+                        continue;
 
                       nat_free_session_data (sm, s, tsm - sm->per_thread_data);
-                      clib_dlist_remove (tsm->list_pool, s->per_user_index);
-                      pool_put_index (tsm->list_pool, s->per_user_index);
-                      pool_put (tsm->sessions, s);
-                      u->nsessions--;
+                      nat44_delete_session (sm, s, tsm - sm->per_thread_data);
 
                       if (!addr_only)
                         break;
@@ -942,7 +1021,12 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
   else
     {
       if (!m)
-        return VNET_API_ERROR_NO_SUCH_ENTRY;
+        {
+          if (sw_if_index != ~0)
+            return 0;
+          else
+            return VNET_API_ERROR_NO_SUCH_ENTRY;
+        }
 
       /* Free external address port */
       if (!(addr_only || sm->static_mapping_only || out2in_only))
@@ -966,7 +1050,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                       foreach_snat_protocol
 #undef _
                     default:
-                      clib_warning("unknown_protocol");
+                      nat_log_info ("unknown protocol");
                       return VNET_API_ERROR_INVALID_VALUE_2;
                     }
                   break;
@@ -992,7 +1076,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
           kv.key = m_key.as_u64;
           kv.value = ~0ULL;
           if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0))
-            clib_warning ("in2out key del failed");
+            nat_log_warn ("in2out key del failed");
         }
 
       m_key.addr = m->external_addr;
@@ -1006,7 +1090,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
           kv.key = m_key.as_u64;
           kv.value = ~0ULL;
           if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 0))
-            clib_warning ("in2out key del failed");
+            nat_log_warn ("out2in key del failed");
         }
 
       /* Delete session(s) for static mapping if exist */
@@ -1040,16 +1124,19 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                             continue;
                         }
 
+                      if (is_lb_session (s))
+                        continue;
+
+                      if (!snat_is_session_static (s))
+                        continue;
+
                       nat_free_session_data (sm, s, tsm - sm->per_thread_data);
-                      clib_dlist_remove (tsm->list_pool, s->per_user_index);
-                      pool_put_index (tsm->list_pool, s->per_user_index);
-                      pool_put (tsm->sessions, s);
-                      u->nstaticsessions--;
+                      nat44_delete_session (sm, s, tsm - sm->per_thread_data);
 
                       if (!addr_only)
                         break;
                     }
-                  if (addr_only)
+                  if (addr_only && (u->nstaticsessions == 0) && (u->nsessions == 0))
                     {
                       pool_put (tsm->users, u);
                       clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 0);
@@ -1117,7 +1204,8 @@ static int lb_local_exists (nat44_lb_addr_port_t * local,
 int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                                      snat_protocol_t proto, u32 vrf_id,
                                      nat44_lb_addr_port_t *locals, u8 is_add,
-                                     u8 twice_nat, u8 out2in_only, u8 *tag)
+                                     twice_nat_type_t twice_nat, u8 out2in_only,
+                                     u8 *tag)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -1154,7 +1242,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
 
       fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
                                                      vrf_id,
-                                                     FIB_SOURCE_PLUGIN_HI);
+                                                     FIB_SOURCE_PLUGIN_LOW);
 
       /* Find external address in allocated addresses and reserve port for
          address and port pair mapping when dynamic translations enabled */
@@ -1182,7 +1270,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                       foreach_snat_protocol
 #undef _
                     default:
-                      clib_warning("unknown_protocol");
+                      nat_log_info ("unknown protocol");
                       return VNET_API_ERROR_INVALID_VALUE_2;
                     }
                   break;
@@ -1213,7 +1301,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       kv.value = m - sm->static_mappings;
       if (clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1))
         {
-          clib_warning ("static_mapping_by_external key add failed");
+          nat_log_err ("static_mapping_by_external key add failed");
           return VNET_API_ERROR_UNSPECIFIED;
         }
 
@@ -1233,7 +1321,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       kv.value = ~0ULL;
       if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 1))
         {
-          clib_warning ("out2in key add failed");
+          nat_log_err ("out2in key add failed");
           return VNET_API_ERROR_UNSPECIFIED;
         }
 
@@ -1257,7 +1345,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
           kv.value = ~0ULL;
           if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 1))
             {
-              clib_warning ("in2out key add failed");
+              nat_log_err ("in2out key add failed");
               return VNET_API_ERROR_UNSPECIFIED;
             }
         }
@@ -1267,7 +1355,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       if (!m)
         return VNET_API_ERROR_NO_SUCH_ENTRY;
 
-      fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_HI);
+      fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW);
 
       /* Free external address port */
       if (!(sm->static_mapping_only || out2in_only))
@@ -1291,7 +1379,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                       foreach_snat_protocol
 #undef _
                     default:
-                      clib_warning("unknown_protocol");
+                      nat_log_info ("unknown protocol");
                       return VNET_API_ERROR_INVALID_VALUE_2;
                     }
                   break;
@@ -1307,7 +1395,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       kv.key = m_key.as_u64;
       if (clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0))
         {
-          clib_warning ("static_mapping_by_external key del failed");
+          nat_log_err ("static_mapping_by_external key del failed");
           return VNET_API_ERROR_UNSPECIFIED;
         }
 
@@ -1315,7 +1403,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       kv.key = m_key.as_u64;
       if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 0))
         {
-          clib_warning ("outi2in key del failed");
+          nat_log_err ("outi2in key del failed");
           return VNET_API_ERROR_UNSPECIFIED;
         }
 
@@ -1329,7 +1417,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
               kv.key = m_key.as_u64;
               if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0))
                 {
-                  clib_warning ("static_mapping_by_local key del failed");
+                  nat_log_err ("static_mapping_by_local key del failed");
                   return VNET_API_ERROR_UNSPECIFIED;
                 }
             }
@@ -1340,7 +1428,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
               kv.key = m_key.as_u64;
               if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0))
                 {
-                  clib_warning ("in2out key del failed");
+                  nat_log_err ("in2out key del failed");
                   return VNET_API_ERROR_UNSPECIFIED;
                 }
             }
@@ -1364,15 +1452,15 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                       elt = pool_elt_at_index (tsm->list_pool, elt->next);
                       ses_index = elt->value;
 
+                      if (!(is_lb_session (s)))
+                        continue;
+
                       if ((s->in2out.addr.as_u32 != local->addr.as_u32) &&
                           (clib_net_to_host_u16 (s->in2out.port) != local->port))
                         continue;
 
                       nat_free_session_data (sm, s, tsm - sm->per_thread_data);
-                      clib_dlist_remove (tsm->list_pool, s->per_user_index);
-                      pool_put_index (tsm->list_pool, s->per_user_index);
-                      pool_put (tsm->sessions, s);
-                      u->nstaticsessions--;
+                      nat44_delete_session (sm, s, tsm - sm->per_thread_data);
                     }
                 }
             }
@@ -1393,9 +1481,6 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
   snat_address_t *a = 0;
   snat_session_t *ses;
   u32 *ses_to_be_removed = 0, *ses_index;
-  clib_bihash_kv_8_8_t kv, value;
-  snat_user_key_t user_key;
-  snat_user_t *u;
   snat_main_per_thread_data_t *tsm;
   snat_static_mapping_t *m;
   snat_interface_t *interface;
@@ -1431,14 +1516,14 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
       /* Check if address is used in some static mapping */
       if (is_snat_address_used_in_static_mapping(sm, addr))
         {
-          clib_warning ("address used in static mapping");
+          nat_log_notice ("address used in static mapping");
           return VNET_API_ERROR_UNSPECIFIED;
         }
     }
 
   if (a->fib_index != ~0)
     fib_table_unlock(a->fib_index, FIB_PROTOCOL_IP4,
-                     FIB_SOURCE_PLUGIN_HI);
+                     FIB_SOURCE_PLUGIN_LOW);
 
   /* Delete sessions using address */
   if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports)
@@ -1450,22 +1535,15 @@ snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
               {
                 ses->outside_address_index = ~0;
                 nat_free_session_data (sm, ses, tsm - sm->per_thread_data);
-                clib_dlist_remove (tsm->list_pool, ses->per_user_index);
-                pool_put_index (tsm->list_pool, ses->per_user_index);
                 vec_add1 (ses_to_be_removed, ses - tsm->sessions);
-                user_key.addr = ses->in2out.addr;
-                user_key.fib_index = ses->in2out.fib_index;
-                kv.key = user_key.as_u64;
-                if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
-                  {
-                    u = pool_elt_at_index (tsm->users, value.value);
-                    u->nsessions--;
-                  }
               }
           }));
 
           vec_foreach (ses_index, ses_to_be_removed)
-            pool_put_index (tsm->sessions, ses_index[0]);
+            {
+              ses = pool_elt_at_index (tsm->sessions, ses_index[0]);
+              nat44_delete_session (sm, ses, tsm - sm->per_thread_data);
+            }
 
           vec_free (ses_to_be_removed);
        }
@@ -1531,10 +1609,12 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
     }
 
   if (sm->fq_in2out_index == ~0 && !sm->deterministic && sm->num_workers > 1)
-    sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index, 0);
+    sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index,
+                                                      NAT_FQ_NELTS);
 
   if (sm->fq_out2in_index == ~0 && !sm->deterministic && sm->num_workers > 1)
-    sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0);
+    sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index,
+                                                      NAT_FQ_NELTS);
 
   pool_foreach (i, sm->interfaces,
   ({
@@ -1571,12 +1651,18 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
                                              sw_if_index, 0, 0, 0);
                 vnet_feature_enable_disable ("ip4-unicast", feature_name,
                                              sw_if_index, 1, 0, 0);
+                if (!is_inside)
+                  vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
+                                               sw_if_index, 1, 0, 0);
               }
             else
               {
                 vnet_feature_enable_disable ("ip4-unicast", feature_name,
                                              sw_if_index, 0, 0, 0);
                 pool_put (sm->interfaces, i);
+                if (is_inside)
+                  vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
+                                               sw_if_index, 0, 0, 0);
               }
           }
         else
@@ -1607,6 +1693,9 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
                                          sw_if_index, 0, 0, 0);
             vnet_feature_enable_disable ("ip4-unicast", feature_name,
                                          sw_if_index, 1, 0, 0);
+            if (!is_inside)
+              vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
+                                           sw_if_index, 0, 0, 0);
             goto set_flags;
           }
 
@@ -1622,21 +1711,21 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
   i->flags = 0;
   vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, 0);
 
+  if (is_inside && !sm->out2in_dpo)
+    vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
+                                 sw_if_index, 1, 0, 0);
+
 set_flags:
   if (is_inside)
-    i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE;
+    {
+      i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE;
+      return 0;
+    }
   else
     i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE;
 
   /* Add/delete external addresses to FIB */
 fib:
-  if (is_inside && !sm->out2in_dpo)
-    {
-      vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning",
-                                   sw_if_index, !is_del, 0, 0);
-      return 0;
-    }
-
   vec_foreach (ap, sm->addresses)
     snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del);
 
@@ -1742,7 +1831,7 @@ fib:
 
   pool_foreach (m, sm->static_mappings,
   ({
-    if (!(m->addr_only))
+    if (!(m->addr_only)  || (m->local_addr.as_u32 == m->external_addr.as_u32))
       continue;
 
     snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
@@ -1816,6 +1905,7 @@ static clib_error_t * snat_init (vlib_main_t * vm)
   uword *bitmap = 0;
   u32 i;
   ip4_add_del_interface_address_callback_t cb4;
+  vlib_node_t * error_drop_node;
 
   sm->vlib_main = vm;
   sm->vnet_main = vnet_get_main();
@@ -1836,6 +1926,9 @@ static clib_error_t * snat_init (vlib_main_t * vm)
   sm->icmp_timeout = SNAT_ICMP_TIMEOUT;
   sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
   sm->forwarding_enabled = 0;
+  sm->log_class = vlib_log_register_class ("nat", 0);
+  error_drop_node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
+  sm->error_node_index = error_drop_node->index;
 
   p = hash_get_mem (tm->thread_registrations_by_name, "workers");
   if (p)
@@ -1924,7 +2017,7 @@ void snat_free_outside_address_and_port (snat_address_t * addresses,
       foreach_snat_protocol
 #undef _
     default:
-      clib_warning("unknown_protocol");
+      nat_log_info ("unknown protocol");
       return;
     }
 }
@@ -1939,6 +2032,7 @@ void snat_free_outside_address_and_port (snat_address_t * addresses,
  *                    address.
  * @param is_addr_only If matched mapping is address only
  * @param twice_nat If matched mapping is twice NAT.
+ * @param lb If matched mapping is load-balanced.
  *
  * @returns 0 if match found otherwise 1.
  */
@@ -1947,7 +2041,8 @@ int snat_static_mapping_match (snat_main_t * sm,
                                snat_session_key_t * mapping,
                                u8 by_external,
                                u8 *is_addr_only,
-                               u8 *twice_nat)
+                               twice_nat_type_t *twice_nat,
+                               u8 *lb)
 {
   clib_bihash_kv_8_8_t kv, value;
   snat_static_mapping_t *m;
@@ -2018,6 +2113,9 @@ int snat_static_mapping_match (snat_main_t * sm,
   if (PREDICT_FALSE(twice_nat != 0))
     *twice_nat = m->twice_nat;
 
+  if (PREDICT_FALSE(lb != 0))
+    *lb = vec_len (m->locals) > 0;
+
   return 0;
 }
 
@@ -2095,7 +2193,7 @@ nat_alloc_addr_and_port_default (snat_address_t * addresses,
           foreach_snat_protocol
 #undef _
         default:
-          clib_warning("unknown protocol");
+          nat_log_info ("unknown protocol");
           return 1;
         }
 
@@ -2127,7 +2225,7 @@ nat_alloc_addr_and_port_default (snat_address_t * addresses,
          foreach_snat_protocol
 #undef _
        default:
-         clib_warning ("unknown protocol");
+         nat_log_info ("unknown protocol");
          return 1;
        }
     }
@@ -2180,7 +2278,7 @@ nat_alloc_addr_and_port_mape (snat_address_t * addresses,
       foreach_snat_protocol
 #undef _
     default:
-      clib_warning("unknown protocol");
+      nat_log_info ("unknown protocol");
       return 1;
     }
 
@@ -2246,6 +2344,65 @@ format_snat_protocol (u8 * s, va_list * args)
   return s;
 }
 
+u8 * format_snat_key (u8 * s, va_list * args);
+
+u8 *
+format_session_kvp (u8 * s, va_list * args)
+{
+  clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *);
+  snat_session_key_t k;
+
+  k.as_u64 = v->key;
+
+  s = format (s, "%U session-index %llu", format_snat_key, &k, v->value);
+
+  return s;
+}
+
+u8 *
+format_static_mapping_kvp (u8 * s, va_list * args)
+{
+  clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *);
+  snat_session_key_t k;
+
+  k.as_u64 = v->key;
+
+  s = format (s, "%U static-mapping-index %llu", format_snat_key, &k, v->value);
+
+  return s;
+}
+
+u8 *
+format_user_kvp (u8 * s, va_list * args)
+{
+  clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *);
+  snat_user_key_t k;
+
+  k.as_u64 = v->key;
+
+  s = format (s, "%U fib %d user-index %llu", format_ip4_address, &k.addr,
+              k.fib_index, v->value);
+
+  return s;
+}
+
+u8 *
+format_ed_session_kvp (u8 * s, va_list * args)
+{
+  clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *);
+  nat_ed_ses_key_t k;
+
+  k.as_u64[0] = v->key[0];
+  k.as_u64[1] = v->key[1];
+
+  s = format (s, "local %U:%d remote %U:%d proto %U fib %d session-index %llu",
+              format_ip4_address, &k.l_addr, clib_net_to_host_u16 (k.l_port),
+              format_ip4_address, &k.r_addr, clib_net_to_host_u16 (k.r_port),
+              format_ip_protocol, k.proto, k.fib_index, v->value);
+
+  return s;
+}
+
 static u32
 snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0)
 {
@@ -2406,12 +2563,14 @@ static clib_error_t *
 snat_config (vlib_main_t * vm, unformat_input_t * input)
 {
   snat_main_t * sm = &snat_main;
+  nat66_main_t * nm = &nat66_main;
   u32 translation_buckets = 1024;
   u32 translation_memory_size = 128<<20;
   u32 user_buckets = 128;
   u32 user_memory_size = 64<<20;
   u32 max_translations_per_user = 100;
   u32 outside_vrf_id = 0;
+  u32 outside_ip6_vrf_id = 0;
   u32 inside_vrf_id = 0;
   u32 static_mapping_buckets = 1024;
   u32 static_mapping_memory_size = 64<<20;
@@ -2444,6 +2603,9 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
       else if (unformat (input, "outside VRF id %d",
                          &outside_vrf_id))
         ;
+      else if (unformat (input, "outside ip6 VRF id %d",
+                         &outside_ip6_vrf_id))
+        ;
       else if (unformat (input, "inside VRF id %d",
                          &inside_vrf_id))
         ;
@@ -2487,6 +2649,10 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
   sm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
                                                              outside_vrf_id,
                                                              FIB_SOURCE_PLUGIN_HI);
+  nm->outside_vrf_id = outside_ip6_vrf_id;
+  nm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
+                                                             outside_ip6_vrf_id,
+                                                             FIB_SOURCE_PLUGIN_HI);
   sm->inside_vrf_id = inside_vrf_id;
   sm->inside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
                                                             inside_vrf_id,
@@ -2522,19 +2688,29 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
             {
               clib_bihash_init_8_8 (&tsm->in2out, "in2out", translation_buckets,
                                     translation_memory_size);
+              clib_bihash_set_kvp_format_fn_8_8 (&tsm->in2out,
+                                                 format_session_kvp);
 
               clib_bihash_init_8_8 (&tsm->out2in, "out2in", translation_buckets,
                                     translation_memory_size);
+              clib_bihash_set_kvp_format_fn_8_8 (&tsm->out2in,
+                                                 format_session_kvp);
 
               clib_bihash_init_8_8 (&tsm->user_hash, "users", user_buckets,
                                     user_memory_size);
+              clib_bihash_set_kvp_format_fn_8_8 (&tsm->user_hash,
+                                                 format_user_kvp);
             }
 
           clib_bihash_init_16_8 (&sm->in2out_ed, "in2out-ed",
                                  translation_buckets, translation_memory_size);
+          clib_bihash_set_kvp_format_fn_16_8 (&sm->in2out_ed,
+                                              format_ed_session_kvp);
 
           clib_bihash_init_16_8 (&sm->out2in_ed, "out2in-ed",
                                  translation_buckets, translation_memory_size);
+          clib_bihash_set_kvp_format_fn_16_8 (&sm->out2in_ed,
+                                              format_ed_session_kvp);
         }
       else
         {
@@ -2544,10 +2720,14 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
       clib_bihash_init_8_8 (&sm->static_mapping_by_local,
                             "static_mapping_by_local", static_mapping_buckets,
                             static_mapping_memory_size);
+      clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_local,
+                                         format_static_mapping_kvp);
 
       clib_bihash_init_8_8 (&sm->static_mapping_by_external,
                             "static_mapping_by_external", static_mapping_buckets,
                             static_mapping_memory_size);
+      clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_external,
+                                         format_static_mapping_kvp);
     }
 
   return 0;
@@ -2585,7 +2765,7 @@ u8 * format_snat_key (u8 * s, va_list * args)
 
 u8 * format_snat_session (u8 * s, va_list * args)
 {
-  snat_main_t * sm __attribute__((unused)) = va_arg (*args, snat_main_t *);
+  snat_main_per_thread_data_t * sm = va_arg (*args, snat_main_per_thread_data_t *);
   snat_session_t * sess = va_arg (*args, snat_session_t *);
 
   if (snat_is_unk_proto_session (sess))
@@ -2604,20 +2784,25 @@ u8 * format_snat_session (u8 * s, va_list * args)
       s = format (s, "  i2o %U\n", format_snat_key, &sess->in2out);
       s = format (s, "    o2i %U\n", format_snat_key, &sess->out2in);
     }
-  if (is_twice_nat_session (sess))
-    {
-      s = format (s, "       external host o2i %U:%d i2o %U:%d\n",
-                  format_ip4_address, &sess->ext_host_addr,
-                  clib_net_to_host_u16 (sess->ext_host_port),
-                  format_ip4_address, &sess->ext_host_nat_addr,
-                  clib_net_to_host_u16 (sess->ext_host_nat_port));
-    }
-  else
+  if (is_ed_session (sess) || is_fwd_bypass_session (sess))
     {
-      if (sess->ext_host_addr.as_u32)
-          s = format (s, "       external host %U\n",
-                      format_ip4_address, &sess->ext_host_addr);
+      if (is_twice_nat_session (sess))
+        {
+          s = format (s, "       external host o2i %U:%d i2o %U:%d\n",
+                      format_ip4_address, &sess->ext_host_addr,
+                      clib_net_to_host_u16 (sess->ext_host_port),
+                      format_ip4_address, &sess->ext_host_nat_addr,
+                      clib_net_to_host_u16 (sess->ext_host_nat_port));
+        }
+      else
+        {
+          if (sess->ext_host_addr.as_u32)
+              s = format (s, "       external host %U:%u\n",
+                          format_ip4_address, &sess->ext_host_addr,
+                          clib_net_to_host_u16 (sess->ext_host_port));
+        }
     }
+  s = format (s, "       index %llu\n", sess - sm->sessions);
   s = format (s, "       last heard %.2f\n", sess->last_heard);
   s = format (s, "       total pkts %d, total bytes %lld\n",
               sess->total_pkts, sess->total_bytes);
@@ -2625,7 +2810,9 @@ u8 * format_snat_session (u8 * s, va_list * args)
     s = format (s, "       static translation\n");
   else
     s = format (s, "       dynamic translation\n");
-  if (sess->flags & SNAT_SESSION_FLAG_LOAD_BALANCING)
+  if (is_fwd_bypass_session (sess))
+    s = format (s, "       forwarding-bypass\n");
+  if (is_lb_session (sess))
     s = format (s, "       load-balancing\n");
   if (is_twice_nat_session (sess))
     s = format (s, "       twice-nat\n");
@@ -2679,10 +2866,13 @@ u8 * format_snat_static_mapping (u8 * s, va_list * args)
   nat44_lb_addr_port_t *local;
 
   if (m->addr_only)
-      s = format (s, "local %U external %U vrf %d %s",
+      s = format (s, "local %U external %U vrf %d %s %s",
                   format_ip4_address, &m->local_addr,
                   format_ip4_address, &m->external_addr,
-                  m->vrf_id, m->twice_nat ? "twice-nat" : "");
+                  m->vrf_id,
+                  m->twice_nat == TWICE_NAT ? "twice-nat" :
+                  m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "",
+                  m->out2in_only ? "out2in-only" : "");
   else
    {
       if (vec_len (m->locals))
@@ -2691,7 +2881,8 @@ u8 * format_snat_static_mapping (u8 * s, va_list * args)
                       format_snat_protocol, m->proto,
                       m->vrf_id,
                       format_ip4_address, &m->external_addr, m->external_port,
-                      m->twice_nat ? "twice-nat" : "",
+                      m->twice_nat == TWICE_NAT ? "twice-nat" :
+                      m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "",
                       m->out2in_only ? "out2in-only" : "");
           vec_foreach (local, m->locals)
             s = format (s, "\n  local %U:%d probability %d\%",
@@ -2703,7 +2894,9 @@ u8 * format_snat_static_mapping (u8 * s, va_list * args)
                     format_snat_protocol, m->proto,
                     format_ip4_address, &m->local_addr, m->local_port,
                     format_ip4_address, &m->external_addr, m->external_port,
-                    m->vrf_id, m->twice_nat ? "twice-nat" : "",
+                    m->vrf_id,
+                    m->twice_nat == TWICE_NAT ? "twice-nat" :
+                    m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "",
                     m->out2in_only ? "out2in-only" : "");
    }
   return s;
@@ -2826,7 +3019,7 @@ match:
                                 !is_delete,
                                 0, 0, rp->tag);
   if (rv)
-    clib_warning ("snat_add_static_mapping returned %d", rv);
+    nat_log_notice ("snat_add_static_mapping returned %d", rv);
 }
 
 static void
@@ -2897,8 +3090,7 @@ match:
                                             rp->is_add,
                                             0, 0, rp->tag);
               if (rv)
-                clib_warning ("snat_add_static_mapping returned %d",
-                              rv);
+                nat_log_notice ("snat_add_static_mapping returned %d", rv);
             }
         }
       return;
@@ -2988,8 +3180,6 @@ nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
   snat_session_key_t key;
   snat_session_t *s;
   clib_bihash_8_8_t *t;
-  snat_user_key_t u_key;
-  snat_user_t *u;
 
   ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32;
   if (sm->num_workers > 1)
@@ -3007,27 +3197,57 @@ nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
   t = is_in ? &tsm->in2out : &tsm->out2in;
   if (!clib_bihash_search_8_8 (t, &kv, &value))
     {
+      if (pool_is_free_index (tsm->sessions, value.value))
+        return VNET_API_ERROR_UNSPECIFIED;
+
       s = pool_elt_at_index (tsm->sessions, value.value);
-      kv.key = s->in2out.as_u64;
-      clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0);
-      kv.key = s->out2in.as_u64;
-      clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0);
-      u_key.addr = s->in2out.addr;
-      u_key.fib_index = s->in2out.fib_index;
-      kv.key = u_key.as_u64;
-      if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
-        {
-          u = pool_elt_at_index (tsm->users, value.value);
-          u->nsessions--;
-        }
-      clib_dlist_remove (tsm->list_pool, s->per_user_index);
-      pool_put (tsm->sessions, s);
+      nat_free_session_data (sm, s, tsm - sm->per_thread_data);
+      nat44_delete_session (sm, s, tsm - sm->per_thread_data);
       return 0;
     }
 
   return VNET_API_ERROR_NO_SUCH_ENTRY;
 }
 
+int
+nat44_del_ed_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
+                      ip4_address_t *eh_addr, u16 eh_port, u8 proto,
+                      u32 vrf_id, int is_in)
+{
+  ip4_header_t ip;
+  clib_bihash_16_8_t *t;
+  nat_ed_ses_key_t key;
+  clib_bihash_kv_16_8_t kv, value;
+  u32 thread_index;
+  u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
+  snat_session_t *s;
+
+  ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32;
+  if (sm->num_workers > 1)
+    thread_index = sm->worker_in2out_cb (&ip, fib_index);
+  else
+    thread_index = sm->num_workers;
+
+  t = is_in ? &sm->in2out_ed : &sm->out2in_ed;
+  key.l_addr.as_u32 = addr->as_u32;
+  key.r_addr.as_u32 = eh_addr->as_u32;
+  key.l_port = clib_host_to_net_u16 (port);
+  key.r_port = clib_host_to_net_u16 (eh_port);
+  key.proto = proto;
+  key.fib_index = clib_host_to_net_u32 (fib_index);
+  kv.key[0] = key.as_u64[0];
+  kv.key[1] = key.as_u64[1];
+  if (clib_bihash_search_16_8 (t, &kv, &value))
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+  if (pool_is_free_index (sm->per_thread_data[thread_index].sessions, value.value))
+    return VNET_API_ERROR_UNSPECIFIED;
+  s = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, value.value);
+  nat_free_session_data (sm, s, thread_index);
+  nat44_delete_session (sm, s, thread_index);
+  return 0;
+}
+
 void
 nat_set_alloc_addr_and_port_mape (u16 psid, u16 psid_offset, u16 psid_length)
 {