vcl: ldp support SO_ORIGINAL_DST
[vpp.git] / src / plugins / nat / nat44-ed / nat44_ed.c
index c29ee54..74359cc 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * snat.c - simple nat plugin
- *
  * Copyright (c) 2016 Cisco and/or its affiliates.
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -38,7 +36,7 @@
 #include <nat/nat44-ed/nat44_ed_affinity.h>
 #include <nat/nat44-ed/nat44_ed_inlines.h>
 
-#include <vpp/stats/stat_segment.h>
+#include <vlib/stats/stats.h>
 
 snat_main_t snat_main;
 
@@ -61,7 +59,7 @@ static_always_inline void nat_validate_interface_counters (snat_main_t *sm,
       if (PREDICT_FALSE (sm->enabled))                                        \
        {                                                                     \
          nat_log_err ("plugin enabled");                                     \
-         return 1;                                                           \
+         return VNET_API_ERROR_FEATURE_ALREADY_ENABLED;                      \
        }                                                                     \
     }                                                                         \
   while (0)
@@ -73,12 +71,11 @@ static_always_inline void nat_validate_interface_counters (snat_main_t *sm,
       if (PREDICT_FALSE (!sm->enabled))                                       \
        {                                                                     \
          nat_log_err ("plugin disabled");                                    \
-         return 1;                                                           \
+         return VNET_API_ERROR_FEATURE_ALREADY_DISABLED;                     \
        }                                                                     \
     }                                                                         \
   while (0)
 
-/* Hook up input features */
 VNET_FEATURE_INIT (nat_pre_in2out, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat-pre-in2out",
@@ -92,6 +89,18 @@ VNET_FEATURE_INIT (nat_pre_out2in, static) = {
                                "ip4-dhcp-client-detect",
                               "ip4-sv-reassembly-feature"),
 };
+VNET_FEATURE_INIT (ip4_nat44_ed_classify, static) = {
+  .arc_name = "ip4-unicast",
+  .node_name = "nat44-ed-classify",
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
+                              "ip4-sv-reassembly-feature"),
+};
+VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = {
+  .arc_name = "ip4-unicast",
+  .node_name = "nat44-handoff-classify",
+  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
+                              "ip4-sv-reassembly-feature"),
+};
 VNET_FEATURE_INIT (snat_in2out_worker_handoff, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-in2out-worker-handoff",
@@ -103,17 +112,6 @@ VNET_FEATURE_INIT (snat_out2in_worker_handoff, static) = {
   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa",
                                "ip4-dhcp-client-detect"),
 };
-VNET_FEATURE_INIT (ip4_snat_in2out, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-in2out",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"),
-};
-VNET_FEATURE_INIT (ip4_snat_out2in, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-out2in",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature",
-                               "ip4-dhcp-client-detect"),
-};
 VNET_FEATURE_INIT (ip4_nat44_ed_in2out, static) = {
   .arc_name = "ip4-unicast",
   .node_name = "nat44-ed-in2out",
@@ -125,32 +123,9 @@ VNET_FEATURE_INIT (ip4_nat44_ed_out2in, static) = {
   .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature",
                                "ip4-dhcp-client-detect"),
 };
-VNET_FEATURE_INIT (ip4_nat44_ed_classify, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-ed-classify",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"),
-};
-VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-handoff-classify",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"),
-};
-VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-in2out-fast",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"),
-};
-VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = {
-  .arc_name = "ip4-unicast",
-  .node_name = "nat44-out2in-fast",
-  .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature",
-                               "ip4-dhcp-client-detect"),
-};
-
-/* Hook up output features */
-VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = {
+VNET_FEATURE_INIT (nat_pre_in2out_output, static) = {
   .arc_name = "ip4-output",
-  .node_name = "nat44-in2out-output",
+  .node_name = "nat-pre-in2out-output",
   .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"),
   .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
 };
@@ -160,12 +135,6 @@ VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = {
   .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"),
   .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
 };
-VNET_FEATURE_INIT (nat_pre_in2out_output, static) = {
-  .arc_name = "ip4-output",
-  .node_name = "nat-pre-in2out-output",
-  .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"),
-  .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"),
-};
 VNET_FEATURE_INIT (ip4_nat44_ed_in2out_output, static) = {
   .arc_name = "ip4-output",
   .node_name = "nat44-ed-in2out-output",
@@ -178,7 +147,8 @@ VLIB_PLUGIN_REGISTER () = {
     .description = "Network Address Translation (NAT)",
 };
 
-static void nat44_ed_db_init (u32 translations, u32 translation_buckets);
+static void nat44_ed_db_init ();
+static void nat44_ed_db_free ();
 static void nat44_ed_worker_db_free (snat_main_per_thread_data_t *tsm);
 
 static int nat44_ed_add_static_mapping_internal (
@@ -193,28 +163,6 @@ static int nat44_ed_del_static_mapping_internal (ip4_address_t l_addr,
 
 u32 nat_calc_bihash_buckets (u32 n_elts);
 
-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 *);
-
-  u8 proto;
-  u16 r_port, l_port;
-  ip4_address_t l_addr, r_addr;
-  u32 fib_index;
-
-  split_ed_kv (v, &l_addr, &r_addr, &proto, &fib_index, &l_port, &r_port);
-  s = format (s,
-             "local %U:%d remote %U:%d proto %U fib %d thread-index %u "
-             "session-index %u",
-             format_ip4_address, &l_addr, clib_net_to_host_u16 (l_port),
-             format_ip4_address, &r_addr, clib_net_to_host_u16 (r_port),
-             format_ip_protocol, proto, fib_index,
-             ed_value_get_thread_index (v), ed_value_get_session_index (v));
-
-  return s;
-}
-
 static_always_inline int
 nat44_ed_sm_i2o_add (snat_main_t *sm, snat_static_mapping_t *m,
                     ip4_address_t addr, u16 port, u32 fib_index, u8 proto)
@@ -339,8 +287,7 @@ nat44_ed_resolve_nat_addr_len (snat_address_t *ap,
        {
          ap->addr_len = ia->address_length;
          ap->sw_if_index = i->sw_if_index;
-         ap->net.as_u32 = (ap->addr.as_u32 >> (32 - ap->addr_len))
-                          << (32 - ap->addr_len);
+         ap->net.as_u32 = ap->addr.as_u32 & ip4_main.fib_masks[ap->addr_len];
 
          nat_log_debug ("pool addr %U binds to -> sw_if_idx: %u net: %U/%u",
                         format_ip4_address, &ap->addr, ap->sw_if_index,
@@ -387,8 +334,7 @@ nat44_ed_bind_if_addr_to_nat_addr (u32 sw_if_index)
        {
          ap->addr_len = ia->address_length;
          ap->sw_if_index = sw_if_index;
-         ap->net.as_u32 = (ap->addr.as_u32 >> (32 - ap->addr_len))
-                          << (32 - ap->addr_len);
+         ap->net.as_u32 = ap->addr.as_u32 & ip4_main.fib_masks[ap->addr_len];
 
          nat_log_debug ("pool addr %U binds to -> sw_if_idx: %u net: %U/%u",
                         format_ip4_address, &ap->addr, ap->sw_if_index,
@@ -679,6 +625,134 @@ nat44_ed_del_address (ip4_address_t addr, u8 twice_nat)
   return 0;
 }
 
+vrf_table_t *
+nat44_ed_get_vrf_table (u32 table_vrf_id)
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+
+  pool_foreach (t, sm->vrf_tables)
+    {
+      if (table_vrf_id == t->table_vrf_id)
+       {
+         return t;
+       }
+    }
+  return NULL;
+}
+
+vrf_route_t *
+nat44_ed_get_vrf_route (vrf_table_t *t, u32 vrf_id)
+{
+  vrf_route_t *r;
+
+  pool_foreach (r, t->routes)
+    {
+      if (vrf_id == r->vrf_id)
+       {
+         return r;
+       }
+    }
+  return NULL;
+}
+
+int
+nat44_ed_add_del_vrf_table (u32 table_vrf_id, bool is_add)
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+  vrf_route_t *r;
+
+  t = nat44_ed_get_vrf_table (table_vrf_id);
+  if (t)
+    {
+      if (is_add)
+       {
+         return VNET_API_ERROR_VALUE_EXIST;
+       }
+      pool_foreach (r, t->routes)
+       {
+         fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+       }
+      fib_table_unlock (t->table_fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+
+      pool_free (t->routes);
+      pool_put (sm->vrf_tables, t);
+    }
+  else
+    {
+      if (!is_add)
+       {
+         return VNET_API_ERROR_NO_SUCH_ENTRY;
+       }
+      pool_get (sm->vrf_tables, t);
+      clib_memset (t, 0, sizeof (*t));
+      t->table_vrf_id = table_vrf_id;
+      t->table_fib_index = fib_table_find_or_create_and_lock (
+       FIB_PROTOCOL_IP4, table_vrf_id, sm->fib_src_low);
+    }
+
+  return 0;
+}
+
+void
+nat44_ed_del_vrf_tables ()
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+  vrf_route_t *r;
+
+  pool_foreach (t, sm->vrf_tables)
+    {
+      pool_foreach (r, t->routes)
+       {
+         fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+       }
+      fib_table_unlock (t->table_fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+      pool_free (t->routes);
+    }
+  pool_free (sm->vrf_tables);
+}
+
+int
+nat44_ed_add_del_vrf_route (u32 table_vrf_id, u32 vrf_id, bool is_add)
+{
+  snat_main_t *sm = &snat_main;
+  vrf_table_t *t;
+  vrf_route_t *r;
+
+  t = nat44_ed_get_vrf_table (table_vrf_id);
+  if (!t)
+    {
+      return VNET_API_ERROR_NO_SUCH_ENTRY;
+    }
+
+  r = nat44_ed_get_vrf_route (t, vrf_id);
+  if (r)
+    {
+      if (is_add)
+       {
+         return VNET_API_ERROR_VALUE_EXIST;
+       }
+      fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
+      pool_put (t->routes, r);
+    }
+  else
+    {
+      if (!is_add)
+       {
+         return VNET_API_ERROR_NO_SUCH_ENTRY;
+       }
+      pool_get (t->routes, r);
+      clib_memset (r, 0, sizeof (*r));
+      r->vrf_id = vrf_id;
+      r->fib_index = fib_table_find_or_create_and_lock (
+       FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
+    }
+
+  return 0;
+}
+
 u32
 get_thread_idx_by_port (u16 e_port)
 {
@@ -686,9 +760,9 @@ get_thread_idx_by_port (u16 e_port)
   u32 thread_idx = sm->num_workers;
   if (sm->num_workers > 1)
     {
-      thread_idx =
-       sm->first_worker_index +
-       sm->workers[(e_port - 1024) / sm->port_per_thread];
+      thread_idx = sm->first_worker_index +
+                  sm->workers[(e_port - ED_USER_PORT_OFFSET) /
+                              sm->port_per_thread % _vec_len (sm->workers)];
     }
   return thread_idx;
 }
@@ -1560,19 +1634,19 @@ expire_per_vrf_sessions (u32 fib_index)
 
   vec_foreach (tsm, sm->per_thread_data)
     {
-      vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec)
-        {
-          if ((per_vrf_sessions->rx_fib_index == fib_index) ||
-              (per_vrf_sessions->tx_fib_index == fib_index))
-            {
-              per_vrf_sessions->expired = 1;
-            }
-        }
+      pool_foreach (per_vrf_sessions, tsm->per_vrf_sessions_pool)
+       {
+         if ((per_vrf_sessions->rx_fib_index == fib_index) ||
+             (per_vrf_sessions->tx_fib_index == fib_index))
+           {
+             per_vrf_sessions->expired = 1;
+           }
+       }
     }
 }
 
 void
-update_per_vrf_sessions_vec (u32 fib_index, int is_del)
+update_per_vrf_sessions_pool (u32 fib_index, int is_del)
 {
   snat_main_t *sm = &snat_main;
   nat_fib_t *fib;
@@ -1608,10 +1682,10 @@ update_per_vrf_sessions_vec (u32 fib_index, int is_del)
     }
 }
 
-static_always_inline nat_outside_fib_t *
-nat44_ed_get_outside_fib (nat_outside_fib_t *outside_fibs, u32 fib_index)
+static_always_inline nat_fib_t *
+nat44_ed_get_outside_fib (nat_fib_t *outside_fibs, u32 fib_index)
 {
-  nat_outside_fib_t *f;
+  nat_fib_t *f;
   vec_foreach (f, outside_fibs)
     {
       if (f->fib_index == fib_index)
@@ -1642,7 +1716,7 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside)
   const char *del_feature_name, *feature_name;
   snat_main_t *sm = &snat_main;
 
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u32 fib_index;
   int rv;
@@ -1715,7 +1789,7 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside)
   fib_index =
     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
 
-  update_per_vrf_sessions_vec (fib_index, 0 /*is_del*/);
+  update_per_vrf_sessions_pool (fib_index, 0 /*is_del*/);
 
   if (!is_inside)
     {
@@ -1724,13 +1798,13 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside)
       outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
       if (outside_fib)
        {
-         outside_fib->refcount++;
+         outside_fib->ref_count++;
        }
       else
        {
          vec_add2 (sm->outside_fibs, outside_fib, 1);
          outside_fib->fib_index = fib_index;
-         outside_fib->refcount = 1;
+         outside_fib->ref_count = 1;
        }
 
       nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 1);
@@ -1752,7 +1826,7 @@ nat44_ed_del_interface (u32 sw_if_index, u8 is_inside)
   const char *del_feature_name, *feature_name;
   snat_main_t *sm = &snat_main;
 
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u32 fib_index;
   int rv;
@@ -1830,15 +1904,15 @@ nat44_ed_del_interface (u32 sw_if_index, u8 is_inside)
   fib_index =
     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
 
-  update_per_vrf_sessions_vec (fib_index, 1 /*is_del*/);
+  update_per_vrf_sessions_pool (fib_index, 1 /*is_del*/);
 
   if (!is_inside)
     {
       outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
       if (outside_fib)
        {
-         outside_fib->refcount--;
-         if (!outside_fib->refcount)
+         outside_fib->ref_count--;
+         if (!outside_fib->ref_count)
            {
              vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
            }
@@ -1856,7 +1930,7 @@ nat44_ed_add_output_interface (u32 sw_if_index)
 {
   snat_main_t *sm = &snat_main;
 
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u32 fib_index;
   int rv;
@@ -1929,18 +2003,18 @@ nat44_ed_add_output_interface (u32 sw_if_index)
 
   fib_index =
     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
-  update_per_vrf_sessions_vec (fib_index, 0 /*is_del*/);
+  update_per_vrf_sessions_pool (fib_index, 0 /*is_del*/);
 
   outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
   if (outside_fib)
     {
-      outside_fib->refcount++;
+      outside_fib->ref_count++;
     }
   else
     {
       vec_add2 (sm->outside_fibs, outside_fib, 1);
       outside_fib->fib_index = fib_index;
-      outside_fib->refcount = 1;
+      outside_fib->ref_count = 1;
     }
 
   nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 1);
@@ -1956,7 +2030,7 @@ nat44_ed_del_output_interface (u32 sw_if_index)
 {
   snat_main_t *sm = &snat_main;
 
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u32 fib_index;
   int rv;
@@ -2019,13 +2093,13 @@ nat44_ed_del_output_interface (u32 sw_if_index)
 
   fib_index =
     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
-  update_per_vrf_sessions_vec (fib_index, 1 /*is_del*/);
+  update_per_vrf_sessions_pool (fib_index, 1 /*is_del*/);
 
   outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index);
   if (outside_fib)
     {
-      outside_fib->refcount--;
-      if (!outside_fib->refcount)
+      outside_fib->ref_count--;
+      if (!outside_fib->ref_count)
        {
          vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
        }
@@ -2058,7 +2132,7 @@ snat_set_workers (uword * bitmap)
       j++;
     }
 
-  sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers);
+  sm->port_per_thread = (65536 - ED_USER_PORT_OFFSET) / _vec_len (sm->workers);
 
   return 0;
 }
@@ -2068,6 +2142,18 @@ nat44_ed_set_frame_queue_nelts (u32 frame_queue_nelts)
 {
   fail_if_enabled ();
   snat_main_t *sm = &snat_main;
+
+  if ((sm->fq_in2out_index != ~0) || (sm->fq_out2in_index != ~0) ||
+      (sm->fq_in2out_output_index != ~0))
+    {
+      // frame queu nelts can be set only before first
+      // call to nat44_plugin_enable after that it
+      // doesn't make sense
+      nat_log_err ("Frame queue was already initialized. "
+                  "Change is not possible");
+      return 1;
+    }
+
   sm->frame_queue_nelts = frame_queue_nelts;
   return 0;
 }
@@ -2077,7 +2163,7 @@ nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque, u32 sw_if_index,
                                u32 new_fib_index, u32 old_fib_index)
 {
   snat_main_t *sm = &snat_main;
-  nat_outside_fib_t *outside_fib;
+  nat_fib_t *outside_fib;
   snat_interface_t *i;
   u8 is_add = 1;
   u8 match = 0;
@@ -2115,8 +2201,8 @@ nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque, u32 sw_if_index,
     {
       if (outside_fib->fib_index == old_fib_index)
        {
-         outside_fib->refcount--;
-         if (!outside_fib->refcount)
+         outside_fib->ref_count--;
+         if (!outside_fib->ref_count)
            vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs);
          break;
        }
@@ -2126,7 +2212,7 @@ nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque, u32 sw_if_index,
     {
       if (outside_fib->fib_index == new_fib_index)
        {
-         outside_fib->refcount++;
+         outside_fib->ref_count++;
          is_add = 0;
          break;
        }
@@ -2135,7 +2221,7 @@ nat44_ed_update_outside_fib_cb (ip4_main_t *im, uword opaque, u32 sw_if_index,
   if (is_add)
     {
       vec_add2 (sm->outside_fibs, outside_fib, 1);
-      outside_fib->refcount = 1;
+      outside_fib->ref_count = 1;
       outside_fib->fib_index = new_fib_index;
     }
 }
@@ -2211,21 +2297,6 @@ nat_ip_table_add_del (vnet_main_t * vnm, u32 table_id, u32 is_add)
 
 VNET_IP_TABLE_ADD_DEL_FUNCTION (nat_ip_table_add_del);
 
-void
-nat44_set_node_indexes (snat_main_t * sm, vlib_main_t * vm)
-{
-  vlib_node_t *node;
-
-  node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in");
-  sm->out2in_node_index = node->index;
-
-  node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out");
-  sm->in2out_node_index = node->index;
-
-  node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-output");
-  sm->in2out_output_node_index = node->index;
-}
-
 #define nat_validate_simple_counter(c, i)                                     \
   do                                                                          \
     {                                                                         \
@@ -2269,12 +2340,8 @@ nat_init (vlib_main_t * vm)
 
   clib_memset (sm, 0, sizeof (*sm));
 
-  // required
-  sm->vnet_main = vnet_get_main ();
   // convenience
   sm->ip4_main = &ip4_main;
-  sm->api_main = vlibapi_get_main ();
-  sm->ip4_lookup_main = &ip4_main.lookup_main;
 
   // frame queue indices used for handoff
   sm->fq_out2in_index = ~0;
@@ -2283,15 +2350,13 @@ nat_init (vlib_main_t * vm)
 
   sm->log_level = NAT_LOG_ERROR;
 
-  nat44_set_node_indexes (sm, vm);
-
   sm->log_class = vlib_log_register_class ("nat", 0);
   nat_ipfix_logging_init (vm);
 
   nat_init_simple_counter (sm->total_sessions, "total-sessions",
                           "/nat44-ed/total-sessions");
-  sm->max_cfg_sessions_gauge = stat_segment_new_entry (
-    (u8 *) "/nat44-ed/max-cfg-sessions", STAT_DIR_TYPE_SCALAR_INDEX);
+  sm->max_cfg_sessions_gauge =
+    vlib_stats_add_gauge ("/nat44-ed/max-cfg-sessions");
 
 #define _(x)                                                                  \
   nat_init_simple_counter (sm->counters.fastpath.in2out.x, #x,                \
@@ -2318,7 +2383,7 @@ nat_init (vlib_main_t * vm)
        }
     }
   num_threads = tm->n_vlib_mains - 1;
-  sm->port_per_thread = 0xffff - 1024;
+  sm->port_per_thread = 65536 - ED_USER_PORT_OFFSET;
   vec_validate (sm->per_thread_data, num_threads);
 
   /* Use all available workers by default */
@@ -2373,8 +2438,8 @@ nat44_plugin_enable (nat44_config_t c)
     c.sessions = 63 * 1024;
 
   sm->max_translations_per_thread = c.sessions;
-  stat_segment_set_state_counter (sm->max_cfg_sessions_gauge,
-                                 sm->max_translations_per_thread);
+  vlib_stats_set_gauge (sm->max_cfg_sessions_gauge,
+                       sm->max_translations_per_thread);
   sm->translation_buckets = nat_calc_bihash_buckets (c.sessions);
 
   vec_add1 (sm->max_translations_per_fib, sm->max_translations_per_thread);
@@ -2388,9 +2453,7 @@ nat44_plugin_enable (nat44_config_t c)
   sm->outside_fib_index = fib_table_find_or_create_and_lock (
     FIB_PROTOCOL_IP4, c.outside_vrf, sm->fib_src_hi);
 
-  nat44_ed_db_init (sm->max_translations_per_thread, sm->translation_buckets);
-
-  nat44_ed_init_tcp_state_stable (sm);
+  nat44_ed_db_init ();
 
   nat_affinity_enable ();
 
@@ -2405,20 +2468,26 @@ nat44_plugin_enable (nat44_config_t c)
 
   if (sm->num_workers > 1)
     {
+      vlib_main_t *vm = vlib_get_main ();
+      vlib_node_t *node;
+
       if (sm->fq_in2out_index == ~0)
        {
-         sm->fq_in2out_index = vlib_frame_queue_main_init (
-           sm->in2out_node_index, sm->frame_queue_nelts);
+         node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out");
+         sm->fq_in2out_index =
+           vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts);
        }
       if (sm->fq_out2in_index == ~0)
        {
-         sm->fq_out2in_index = vlib_frame_queue_main_init (
-           sm->out2in_node_index, sm->frame_queue_nelts);
+         node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in");
+         sm->fq_out2in_index =
+           vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts);
        }
       if (sm->fq_in2out_output_index == ~0)
        {
-         sm->fq_in2out_output_index = vlib_frame_queue_main_init (
-           sm->in2out_output_node_index, sm->frame_queue_nelts);
+         node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-output");
+         sm->fq_in2out_output_index =
+           vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts);
        }
     }
 
@@ -2519,6 +2588,55 @@ nat44_ed_del_output_interfaces ()
   return error;
 }
 
+static clib_error_t *
+nat44_ed_sw_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_add)
+{
+  snat_main_t *sm = &snat_main;
+  snat_interface_t *i;
+  int error = 0;
+
+  if (is_add)
+    return 0;
+
+  if (!sm->enabled)
+    return 0;
+
+  i = nat44_ed_get_interface (sm->interfaces, sw_if_index);
+  if (i)
+    {
+      bool is_inside = nat44_ed_is_interface_inside (i);
+      bool is_outside = nat44_ed_is_interface_outside (i);
+
+      if (is_inside)
+       {
+         error |= nat44_ed_del_interface (sw_if_index, 1);
+       }
+      if (is_outside)
+       {
+         error |= nat44_ed_del_interface (sw_if_index, 0);
+       }
+
+      if (error)
+       {
+         nat_log_err ("error occurred while removing interface");
+       }
+    }
+
+  i = nat44_ed_get_interface (sm->output_feature_interfaces, sw_if_index);
+  if (i)
+    {
+      error = nat44_ed_del_output_interface (sw_if_index);
+      if (error)
+       {
+         nat_log_err ("error occurred while removing output interface");
+       }
+    }
+
+  return 0;
+}
+
+VNET_SW_INTERFACE_ADD_DEL_FUNCTION (nat44_ed_sw_interface_add_del);
+
 int
 nat44_ed_del_static_mappings ()
 {
@@ -2550,7 +2668,6 @@ nat44_ed_del_static_mappings ()
 int
 nat44_plugin_disable ()
 {
-  snat_main_per_thread_data_t *tsm;
   snat_main_t *sm = &snat_main;
   int rc, error = 0;
 
@@ -2558,29 +2675,26 @@ nat44_plugin_disable ()
 
   rc = nat44_ed_del_static_mappings ();
   if (rc)
-    error = 1;
+    error = VNET_API_ERROR_BUG;
 
   rc = nat44_ed_del_addresses ();
   if (rc)
-    error = 1;
+    error = VNET_API_ERROR_BUG;
 
   rc = nat44_ed_del_interfaces ();
   if (rc)
-    error = 1;
+    error = VNET_API_ERROR_BUG;
 
   rc = nat44_ed_del_output_interfaces ();
   if (rc)
-    error = 1;
+    error = VNET_API_ERROR_BUG;
+
+  nat44_ed_del_vrf_tables ();
 
   vec_free (sm->max_translations_per_fib);
   sm->max_translations_per_fib = 0;
 
-  clib_bihash_free_16_8 (&sm->flow_hash);
-
-  vec_foreach (tsm, sm->per_thread_data)
-    {
-      nat44_ed_worker_db_free (tsm);
-    }
+  nat44_ed_db_free ();
 
   clib_memset (&sm->rconfig, 0, sizeof (sm->rconfig));
 
@@ -2641,11 +2755,12 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
       if (m)
        return m;
 
-      /* Try address only mapping */
+      // try address only mapping
       m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, match_fib_index, 0);
       if (m)
        return m;
 
+      // default static mapping fib index (based on configuration)
       if (sm->inside_fib_index != match_fib_index)
        {
          m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port,
@@ -2653,12 +2768,13 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
          if (m)
            return m;
 
-         /* Try address only mapping */
+         // try address only mapping
          m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, sm->inside_fib_index,
                                      0);
          if (m)
            return m;
        }
+      // TODO: this specific use case may be deprecated (needs testing)
       if (sm->outside_fib_index != match_fib_index)
        {
          m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port,
@@ -2666,7 +2782,7 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
          if (m)
            return m;
 
-         /* Try address only mapping */
+         // try address only mapping
          m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, sm->outside_fib_index,
                                      0);
          if (m)
@@ -2680,7 +2796,7 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
       if (m)
        return m;
 
-      /* Try address only mapping */
+      // try address only mapping
       m = nat44_ed_sm_o2i_lookup (sm, match_addr, 0, 0, 0);
       if (m)
        return m;
@@ -2689,15 +2805,16 @@ nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port,
 }
 
 int
-snat_static_mapping_match (vlib_main_t *vm, snat_main_t *sm,
-                          ip4_address_t match_addr, u16 match_port,
-                          u32 match_fib_index, ip_protocol_t match_protocol,
+snat_static_mapping_match (vlib_main_t *vm, ip4_address_t match_addr,
+                          u16 match_port, u32 match_fib_index,
+                          ip_protocol_t match_protocol,
                           ip4_address_t *mapping_addr, u16 *mapping_port,
                           u32 *mapping_fib_index, int by_external,
                           u8 *is_addr_only, twice_nat_type_t *twice_nat,
                           lb_nat_type_t *lb, ip4_address_t *ext_host_addr,
                           u8 *is_identity_nat, snat_static_mapping_t **out)
 {
+  snat_main_t *sm = &snat_main;
   snat_static_mapping_t *m;
   u32 rand, lo = 0, hi, mid, *tmp = 0, i;
   nat44_lb_addr_port_t *local;
@@ -2841,7 +2958,7 @@ nat44_ed_get_in2out_worker_index (vlib_buffer_t *b, ip4_header_t *ip,
       if (PREDICT_FALSE (is_output))
        {
          fib_index = sm->outside_fib_index;
-         nat_outside_fib_t *outside_fib;
+         nat_fib_t *outside_fib;
          fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
          fib_prefix_t pfx = {
                  .fp_proto = FIB_PROTOCOL_IP4,
@@ -2927,7 +3044,9 @@ nat44_ed_get_in2out_worker_index (vlib_buffer_t *b, ip4_header_t *ip,
     }
 
   hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) +
-    (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24);
+        (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24) +
+        rx_fib_index + (rx_fib_index >> 8) + (rx_fib_index >> 16) +
+        (rx_fib_index >> 24);
 
   if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
     next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
@@ -3037,7 +3156,7 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip,
     {
       udp_header_t *udp = ip4_next_header (ip);
       icmp46_header_t *icmp = (icmp46_header_t *) udp;
-      icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
+      nat_icmp_echo_header_t *echo = (nat_icmp_echo_header_t *) (icmp + 1);
       if (!icmp_type_is_error_message
          (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
        port = vnet_buffer (b)->ip.reass.l4_src_port;
@@ -3051,13 +3170,13 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip,
            {
            case IP_PROTOCOL_ICMP:
              icmp = (icmp46_header_t *) l4_header;
-             echo = (icmp_echo_header_t *) (icmp + 1);
+             echo = (nat_icmp_echo_header_t *) (icmp + 1);
              port = echo->identifier;
              break;
            case IP_PROTOCOL_UDP:
              /* breakthrough */
            case IP_PROTOCOL_TCP:
-             port = ((tcp_udp_header_t *) l4_header)->src_port;
+             port = ((nat_tcp_udp_header_t *) l4_header)->src_port;
              break;
            default:
              next_worker_index = vlib_get_thread_index ();
@@ -3091,9 +3210,7 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip,
     }
 
   /* worker by outside port */
-  next_worker_index = sm->first_worker_index;
-  next_worker_index +=
-    sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
+  next_worker_index = get_thread_idx_by_port (clib_net_to_host_u16 (port));
 
 done:
   nat_elog_debug_handoff (sm, "HANDOFF OUT2IN", next_worker_index,
@@ -3145,8 +3262,8 @@ nat44_update_session_limit (u32 session_limit, u32 vrf_id)
     return 1;
   sm->max_translations_per_thread = nat44_get_max_session_limit ();
 
-  stat_segment_set_state_counter (sm->max_cfg_sessions_gauge,
-                                 sm->max_translations_per_thread);
+  vlib_stats_set_gauge (sm->max_cfg_sessions_gauge,
+                       sm->max_translations_per_thread);
 
   sm->translation_buckets =
     nat_calc_bihash_buckets (sm->max_translations_per_thread);
@@ -3156,11 +3273,11 @@ nat44_update_session_limit (u32 session_limit, u32 vrf_id)
 }
 
 static void
-nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations,
-                        u32 translation_buckets)
+nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations)
 {
   dlist_elt_t *head;
 
+  pool_alloc (tsm->per_vrf_sessions_pool, translations);
   pool_alloc (tsm->sessions, translations);
   pool_alloc (tsm->lru_pool, translations);
 
@@ -3186,7 +3303,7 @@ nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations,
 }
 
 static void
-reinit_ed_flow_hash ()
+nat44_ed_flow_hash_init ()
 {
   snat_main_t *sm = &snat_main;
   // we expect 2 flows per session, so multiply translation_buckets by 2
@@ -3197,17 +3314,16 @@ reinit_ed_flow_hash ()
 }
 
 static void
-nat44_ed_db_init (u32 translations, u32 translation_buckets)
+nat44_ed_db_init ()
 {
   snat_main_t *sm = &snat_main;
   snat_main_per_thread_data_t *tsm;
 
-  reinit_ed_flow_hash ();
+  nat44_ed_flow_hash_init ();
 
   vec_foreach (tsm, sm->per_thread_data)
     {
-      nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread,
-                              sm->translation_buckets);
+      nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread);
     }
 }
 
@@ -3216,23 +3332,38 @@ nat44_ed_worker_db_free (snat_main_per_thread_data_t *tsm)
 {
   pool_free (tsm->lru_pool);
   pool_free (tsm->sessions);
-  vec_free (tsm->per_vrf_sessions_vec);
+  pool_free (tsm->per_vrf_sessions_pool);
 }
 
-void
-nat44_ed_sessions_clear ()
+static void
+nat44_ed_flow_hash_free ()
 {
   snat_main_t *sm = &snat_main;
-  snat_main_per_thread_data_t *tsm;
 
-  reinit_ed_flow_hash ();
+  clib_bihash_free_16_8 (&sm->flow_hash);
+}
+
+static void
+nat44_ed_db_free ()
+{
+  snat_main_t *sm = &snat_main;
+  snat_main_per_thread_data_t *tsm;
 
   vec_foreach (tsm, sm->per_thread_data)
     {
       nat44_ed_worker_db_free (tsm);
-      nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread,
-                              sm->translation_buckets);
     }
+
+  nat44_ed_flow_hash_free ();
+}
+
+void
+nat44_ed_sessions_clear ()
+{
+  snat_main_t *sm = &snat_main;
+
+  nat44_ed_db_free ();
+  nat44_ed_db_init ();
   vlib_zero_simple_counter (&sm->total_sessions, 0);
 }
 
@@ -3361,8 +3492,8 @@ nat44_ed_add_del_interface_address_cb (ip4_main_t *im, uword opaque,
                    {
                      ap->addr_len = address_length;
                      ap->sw_if_index = sw_if_index;
-                     ap->net.as_u32 = (ap->addr.as_u32 >> (32 - ap->addr_len))
-                                      << (32 - ap->addr_len);
+                     ap->net.as_u32 =
+                       ap->addr.as_u32 & ip4_main.fib_masks[ap->addr_len];
 
                      nat_log_debug (
                        "pool addr %U binds to -> sw_if_idx: %u net: %U/%u",
@@ -3421,6 +3552,7 @@ nat44_ed_add_interface_address (u32 sw_if_index, u8 twice_nat)
 
   if (!sm->enabled)
     {
+      nat_log_err ("nat44 is disabled");
       return VNET_API_ERROR_UNSUPPORTED;
     }
 
@@ -3458,6 +3590,7 @@ nat44_ed_del_interface_address (u32 sw_if_index, u8 twice_nat)
 
   if (!sm->enabled)
     {
+      nat_log_err ("nat44 is disabled");
       return VNET_API_ERROR_UNSUPPORTED;
     }
 
@@ -3705,7 +3838,7 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
     return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED;
 
   icmp46_header_t *icmp = ip4_next_header (ip);
-  icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
+  nat_icmp_echo_header_t *echo = (nat_icmp_echo_header_t *) (icmp + 1);
 
   if ((!vnet_buffer (b)->ip.reass.is_non_first_fragment))
     {
@@ -3721,7 +3854,7 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
            {
              ip_csum_t sum = icmp->checksum;
              sum = ip_csum_update (sum, echo->identifier, f->rewrite.icmp_id,
-                                   icmp_echo_header_t,
+                                   nat_icmp_echo_header_t,
                                    identifier /* changed member */);
              echo->identifier = f->rewrite.icmp_id;
              icmp->checksum = ip_csum_fold (sum);
@@ -3801,6 +3934,9 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
              icmp->checksum = new_icmp_sum;
              break;
            case IP_PROTOCOL_ICMP:
+             nat_6t_flow_ip4_translate (sm, b, inner_ip, f, inner_proto,
+                                        1 /* is_icmp_inner_ip4 */,
+                                        0 /* skip_saddr_rewrite */);
              if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE)
                {
                  icmp46_header_t *inner_icmp = ip4_next_header (inner_ip);
@@ -3808,19 +3944,21 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b,
                    {
                      return NAT_ED_TRNSL_ERR_PACKET_TRUNCATED;
                    }
-                 icmp_echo_header_t *inner_echo =
-                   (icmp_echo_header_t *) (inner_icmp + 1);
+                 nat_icmp_echo_header_t *inner_echo =
+                   (nat_icmp_echo_header_t *) (inner_icmp + 1);
                  if (f->rewrite.icmp_id != inner_echo->identifier)
                    {
                      ip_csum_t sum = icmp->checksum;
-                     sum = ip_csum_update (
-                       sum, inner_echo->identifier, f->rewrite.icmp_id,
-                       icmp_echo_header_t, identifier /* changed member */);
+                     sum = ip_csum_update (sum, inner_echo->identifier,
+                                           f->rewrite.icmp_id,
+                                           nat_icmp_echo_header_t,
+                                           identifier /* changed member */);
                      icmp->checksum = ip_csum_fold (sum);
                      ip_csum_t inner_sum = inner_icmp->checksum;
                      inner_sum = ip_csum_update (
                        sum, inner_echo->identifier, f->rewrite.icmp_id,
-                       icmp_echo_header_t, identifier /* changed member */);
+                       nat_icmp_echo_header_t,
+                       identifier /* changed member */);
                      inner_icmp->checksum = ip_csum_fold (inner_sum);
                      inner_echo->identifier = f->rewrite.icmp_id;
                    }
@@ -3892,110 +4030,7 @@ nat_6t_flow_buf_translate_o2i (vlib_main_t *vm, snat_main_t *sm,
                                    0 /* is_i2o */);
 }
 
-u8 *
-format_nat_6t (u8 *s, va_list *args)
-{
-  nat_6t_t *t = va_arg (*args, nat_6t_t *);
-
-  s = format (s, "saddr %U sport %u daddr %U dport %u proto %U fib_idx %u",
-             format_ip4_address, t->saddr.as_u8,
-             clib_net_to_host_u16 (t->sport), format_ip4_address,
-             t->daddr.as_u8, clib_net_to_host_u16 (t->dport),
-             format_ip_protocol, t->proto, t->fib_index);
-  return s;
-}
-
-u8 *
-format_nat_ed_translation_error (u8 *s, va_list *args)
-{
-  nat_translation_error_e e = va_arg (*args, nat_translation_error_e);
-
-  switch (e)
-    {
-    case NAT_ED_TRNSL_ERR_SUCCESS:
-      s = format (s, "success");
-      break;
-    case NAT_ED_TRNSL_ERR_TRANSLATION_FAILED:
-      s = format (s, "translation-failed");
-      break;
-    case NAT_ED_TRNSL_ERR_FLOW_MISMATCH:
-      s = format (s, "flow-mismatch");
-      break;
-    case NAT_ED_TRNSL_ERR_PACKET_TRUNCATED:
-      s = format (s, "packet-truncated");
-      break;
-    case NAT_ED_TRNSL_ERR_INNER_IP_CORRUPT:
-      s = format (s, "inner-ip-corrupted");
-      break;
-    case NAT_ED_TRNSL_ERR_INVALID_CSUM:
-      s = format (s, "invalid-checksum");
-      break;
-    }
-  return s;
-}
-
-u8 *
-format_nat_6t_flow (u8 *s, va_list *args)
-{
-  nat_6t_flow_t *f = va_arg (*args, nat_6t_flow_t *);
-
-  s = format (s, "match: %U ", format_nat_6t, &f->match);
-  int r = 0;
-  if (f->ops & NAT_FLOW_OP_SADDR_REWRITE)
-    {
-      s = format (s, "rewrite: saddr %U ", format_ip4_address,
-                 f->rewrite.saddr.as_u8);
-      r = 1;
-    }
-  if (f->ops & NAT_FLOW_OP_SPORT_REWRITE)
-    {
-      if (!r)
-       {
-         s = format (s, "rewrite: ");
-         r = 1;
-       }
-      s = format (s, "sport %u ", clib_net_to_host_u16 (f->rewrite.sport));
-    }
-  if (f->ops & NAT_FLOW_OP_DADDR_REWRITE)
-    {
-      if (!r)
-       {
-         s = format (s, "rewrite: ");
-         r = 1;
-       }
-      s = format (s, "daddr %U ", format_ip4_address, f->rewrite.daddr.as_u8);
-    }
-  if (f->ops & NAT_FLOW_OP_DPORT_REWRITE)
-    {
-      if (!r)
-       {
-         s = format (s, "rewrite: ");
-         r = 1;
-       }
-      s = format (s, "dport %u ", clib_net_to_host_u16 (f->rewrite.dport));
-    }
-  if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE)
-    {
-      if (!r)
-       {
-         s = format (s, "rewrite: ");
-         r = 1;
-       }
-      s = format (s, "icmp-id %u ", clib_net_to_host_u16 (f->rewrite.icmp_id));
-    }
-  if (f->ops & NAT_FLOW_OP_TXFIB_REWRITE)
-    {
-      if (!r)
-       {
-         s = format (s, "rewrite: ");
-         r = 1;
-       }
-      s = format (s, "txfib %u ", f->rewrite.fib_index);
-    }
-  return s;
-}
-
-static inline void
+static_always_inline void
 nat_syslog_nat44_sess (u32 ssubix, u32 sfibix, ip4_address_t *isaddr,
                       u16 isport, ip4_address_t *xsaddr, u16 xsport,
                       ip4_address_t *idaddr, u16 idport,
@@ -4069,52 +4104,49 @@ nat_syslog_nat44_sdel (u32 ssubix, u32 sfibix, ip4_address_t *isaddr,
                         idaddr, idport, xdaddr, xdport, proto, 0,
                         is_twicenat);
 }
-
-u8 *
-format_nat44_ed_tcp_state (u8 *s, va_list *args)
+__clib_export void
+nat44_original_dst_lookup (ip4_address_t *i2o_src, u16 i2o_src_port,
+                          ip4_address_t *i2o_dst, u16 i2o_dst_port,
+                          ip_protocol_t proto, u32 *original_dst,
+                          u16 *original_dst_port)
 {
-  nat44_ed_tcp_state_e e = va_arg (*args, nat44_ed_tcp_state_e);
-  switch (e)
+  snat_main_per_thread_data_t *tsm;
+  snat_main_t *sm = &snat_main;
+  u32 fib_index = 0;
+  snat_session_t *s;
+  ip4_header_t ip;
+
+  ip.src_address.as_u32 = i2o_src->as_u32;
+  fib_index = fib_table_find (FIB_PROTOCOL_IP4, 0);
+
+  if (sm->num_workers > 1)
     {
-    case NAT44_ED_TCP_STATE_CLOSED:
-      s = format (s, "closed");
-      break;
-    case NAT44_ED_TCP_STATE_SYN_I2O:
-      s = format (s, "SYN seen in in2out direction");
-      break;
-    case NAT44_ED_TCP_STATE_SYN_O2I:
-      s = format (s, "SYN seen in out2in direction");
-      break;
-    case NAT44_ED_TCP_STATE_ESTABLISHED:
-      s = format (s, "SYN seen in both directions/established");
-      break;
-    case NAT44_ED_TCP_STATE_FIN_I2O:
-      s = format (s, "FIN seen in in2out direction");
-      break;
-    case NAT44_ED_TCP_STATE_FIN_O2I:
-      s = format (s, "FIN seen in out2in direction");
-      break;
-    case NAT44_ED_TCP_STATE_RST_TRANS:
-      s = format (s, "RST seen/transitory timeout");
-      break;
-    case NAT44_ED_TCP_STATE_FIN_TRANS:
-      s = format (s, "FIN seen in both directions/transitory timeout");
-      break;
-    case NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_O2I:
-      s = format (s, "FIN seen in both directions/transitory timeout/session "
-                    "reopening in out2in direction");
-      break;
-    case NAT44_ED_TCP_STATE_FIN_REOPEN_SYN_I2O:
-      s = format (s, "FIN seen in both directions/transitory timeout/session "
-                    "reopening in in2out direction");
-      break;
-    case NAT44_ED_TCP_N_STATE:
-      s = format (s, "BUG! unexpected N_STATE! BUG!");
-      break;
+      tsm = vec_elt_at_index (
+       sm->per_thread_data,
+       nat44_ed_get_in2out_worker_index (0, &ip, fib_index, 0));
+    }
+  else
+    {
+      tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
     }
-  return s;
-}
 
+  /* query */
+  clib_bihash_kv_16_8_t kv = { 0 }, value;
+  init_ed_k (&kv, i2o_src->as_u32, i2o_src_port, i2o_dst->as_u32, i2o_dst_port,
+            fib_index, proto);
+  if (tsm->sessions == NULL ||
+      clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value))
+    {
+      return;
+    }
+  s = pool_elt_at_index (tsm->sessions, ed_value_get_session_index (&value));
+  if (s)
+    {
+      *original_dst = s->i2o.rewrite.saddr.as_u32;
+      *original_dst_port = s->i2o.rewrite.sport;
+    }
+  return;
+}
 /*
  * fd.io coding-style-patch-verification: ON
  *