nat: fix update of outside fibs (output-feature)
[vpp.git] / src / plugins / nat / nat.c
index 79c1eaa..85072bc 100755 (executable)
@@ -30,6 +30,7 @@
 #include <nat/nat_inlines.h>
 #include <nat/nat_affinity.h>
 #include <nat/nat_syslog.h>
+#include <nat/nat_ha.h>
 #include <vnet/fib/fib_table.h>
 #include <vnet/fib/ip4_fib.h>
 
@@ -170,12 +171,13 @@ VNET_FEATURE_INIT (ip4_nat44_ed_hairpinning, static) =
 
 VLIB_PLUGIN_REGISTER () = {
     .version = VPP_BUILD_VER,
-    .description = "Network Address Translation",
+    .description = "Network Address Translation (NAT)",
 };
 /* *INDENT-ON* */
 
 void
-nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
+nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index,
+                      u8 is_ha)
 {
   snat_session_key_t key;
   clib_bihash_kv_8_8_t kv;
@@ -186,16 +188,25 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
 
   if (is_fwd_bypass_session (s))
     {
+      if (snat_is_unk_proto_session (s))
+       {
+         ed_key.proto = s->in2out.port;
+         ed_key.r_port = 0;
+         ed_key.l_port = 0;
+       }
+      else
+       {
+         ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol);
+         ed_key.l_port = s->in2out.port;
+         ed_key.r_port = s->ext_host_port;
+       }
       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 (&tsm->in2out_ed, &ed_kv, 0))
-       nat_log_warn ("in2out_ed key del failed");
+       nat_elog_warn ("in2out_ed key del failed");
       return;
     }
 
@@ -223,7 +234,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 (&tsm->out2in_ed, &ed_kv, 0))
-       nat_log_warn ("out2in_ed key del failed");
+       nat_elog_warn ("out2in_ed key del failed");
       ed_key.l_addr = s->in2out.addr;
       ed_key.fib_index = s->in2out.fib_index;
       if (!snat_is_unk_proto_session (s))
@@ -236,40 +247,50 @@ 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 (&tsm->in2out_ed, &ed_kv, 0))
-       nat_log_warn ("in2out_ed key del failed");
+       nat_elog_warn ("in2out_ed key del failed");
 
-      nat_syslog_nat44_sdel (s->user_index, s->in2out.fib_index,
-                            &s->in2out.addr, s->in2out.port,
-                            &s->ext_host_nat_addr, s->ext_host_nat_port,
-                            &s->out2in.addr, s->out2in.port,
-                            &s->ext_host_addr, s->ext_host_port,
-                            s->in2out.protocol, is_twice_nat_session (s));
+      if (!is_ha)
+       nat_syslog_nat44_sdel (s->user_index, s->in2out.fib_index,
+                              &s->in2out.addr, s->in2out.port,
+                              &s->ext_host_nat_addr, s->ext_host_nat_port,
+                              &s->out2in.addr, s->out2in.port,
+                              &s->ext_host_addr, s->ext_host_port,
+                              s->in2out.protocol, is_twice_nat_session (s));
     }
   else
     {
       kv.key = s->in2out.as_u64;
       if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0))
-       nat_log_warn ("in2out key del failed");
+       nat_elog_warn ("in2out key del failed");
       kv.key = s->out2in.as_u64;
       if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
-       nat_log_warn ("out2in key del failed");
+       nat_elog_warn ("out2in key del failed");
 
-      nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
-                              &s->in2out.addr, s->in2out.port,
-                              &s->out2in.addr, s->out2in.port,
-                              s->in2out.protocol);
+      if (!is_ha)
+       nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
+                                &s->in2out.addr, s->in2out.port,
+                                &s->out2in.addr, s->out2in.port,
+                                s->in2out.protocol);
     }
 
   if (snat_is_unk_proto_session (s))
     return;
 
-  /* log NAT event */
-  snat_ipfix_logging_nat44_ses_delete (thread_index,
-                                      s->in2out.addr.as_u32,
-                                      s->out2in.addr.as_u32,
-                                      s->in2out.protocol,
-                                      s->in2out.port,
-                                      s->out2in.port, s->in2out.fib_index);
+  if (!is_ha)
+    {
+      /* log NAT event */
+      snat_ipfix_logging_nat44_ses_delete (thread_index,
+                                          s->in2out.addr.as_u32,
+                                          s->out2in.addr.as_u32,
+                                          s->in2out.protocol,
+                                          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->out2in.protocol, s->out2in.fib_index,
+                  thread_index);
+    }
 
   /* Twice NAT address and port for external host */
   if (is_twice_nat_session (s))
@@ -322,7 +343,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))
-       nat_log_warn ("user_hash keay add failed");
+       nat_elog_warn ("user_hash keay add failed");
 
       vlib_set_simple_counter (&sm->total_users, thread_index, 0,
                               pool_elts (tsm->users));
@@ -337,7 +358,7 @@ nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index,
 
 snat_session_t *
 nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
-                             u32 thread_index)
+                             u32 thread_index, f64 now)
 {
   snat_session_t *s;
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
@@ -368,7 +389,7 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
 
       /* Get the session */
       s = pool_elt_at_index (tsm->sessions, session_index);
-      nat_free_session_data (sm, s, thread_index);
+      nat_free_session_data (sm, s, thread_index, 0);
       if (snat_is_session_static (s))
        u->nstaticsessions--;
       else
@@ -405,6 +426,8 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
                               pool_elts (tsm->sessions));
     }
 
+  s->ha_last_refreshed = now;
+
   return s;
 }
 
@@ -431,7 +454,7 @@ nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
     {
       clib_dlist_addtail (tsm->list_pool,
                          u->sessions_per_user_list_head_index, oldest_index);
-      nat_free_session_data (sm, s, thread_index);
+      nat_free_session_data (sm, s, thread_index, 0);
       if (snat_is_session_static (s))
        u->nstaticsessions--;
       else
@@ -452,8 +475,8 @@ nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
       if ((u->nsessions + u->nstaticsessions) >=
          sm->max_translations_per_user)
        {
-         nat_log_warn ("max translations per user %U", format_ip4_address,
-                       &u->addr);
+         nat_elog_addr (SNAT_LOG_WARNING, "[warn] max translations per user",
+                        clib_net_to_host_u32 (u->addr.as_u32));
          snat_ipfix_logging_max_entries_per_user
            (thread_index, sm->max_translations_per_user, u->addr.as_u32);
          return 0;
@@ -482,6 +505,8 @@ nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
                               pool_elts (tsm->sessions));
     }
 
+  s->ha_last_refreshed = now;
+
   return s;
 }
 
@@ -697,7 +722,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
 
          if (!addr_only)
            {
-             if (rp->l_port != l_port || rp->e_port != e_port
+             if ((rp->l_port != l_port && rp->e_port != e_port)
                  || rp->proto != proto)
                continue;
            }
@@ -805,6 +830,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
        {
          fib_index = sm->inside_fib_index;
          vrf_id = sm->inside_vrf_id;
+         fib_table_lock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW);
        }
 
       if (!(out2in_only || identity_nat))
@@ -845,7 +871,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
                      foreach_snat_protocol
 #undef _
                    default:
-                     nat_log_info ("unknown protocol");
+                     nat_elog_info ("unknown protocol");
                      return VNET_API_ERROR_INVALID_VALUE_2;
                    }
                  break;
@@ -909,7 +935,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
          ip4_header_t ip = {
            .src_address = m->local_addr,
          };
-         vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index));
+         vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index, 0));
          tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]);
        }
       else
@@ -963,7 +989,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
                        continue;
 
                      nat_free_session_data (sm, s,
-                                            tsm - sm->per_thread_data);
+                                            tsm - sm->per_thread_data, 0);
                      nat44_delete_session (sm, s, tsm - sm->per_thread_data);
 
                      if (!addr_only && !sm->endpoint_dependent)
@@ -1027,7 +1053,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
                      foreach_snat_protocol
 #undef _
                    default:
-                     nat_log_info ("unknown protocol");
+                     nat_elog_info ("unknown protocol");
                      return VNET_API_ERROR_INVALID_VALUE_2;
                    }
                  break;
@@ -1087,7 +1113,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
                        continue;
 
                      nat_free_session_data (sm, s,
-                                            tsm - sm->per_thread_data);
+                                            tsm - sm->per_thread_data, 0);
                      nat44_delete_session (sm, s, tsm - sm->per_thread_data);
 
                      if (!addr_only && !sm->endpoint_dependent)
@@ -1208,7 +1234,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                      foreach_snat_protocol
 #undef _
                    default:
-                     nat_log_info ("unknown protocol");
+                     nat_elog_info ("unknown protocol");
                      return VNET_API_ERROR_INVALID_VALUE_2;
                    }
                  break;
@@ -1245,7 +1271,7 @@ 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))
        {
-         nat_log_err ("static_mapping_by_external key add failed");
+         nat_elog_err ("static_mapping_by_external key add failed");
          return VNET_API_ERROR_UNSPECIFIED;
        }
 
@@ -1276,7 +1302,8 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
              };
              bitmap =
                clib_bitmap_set (bitmap,
-                                sm->worker_in2out_cb (&ip, m->fib_index), 1);
+                                sm->worker_in2out_cb (&ip, m->fib_index, 0),
+                                1);
            }
        }
 
@@ -1321,7 +1348,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                      foreach_snat_protocol
 #undef _
                    default:
-                     nat_log_info ("unknown protocol");
+                     nat_elog_info ("unknown protocol");
                      return VNET_API_ERROR_INVALID_VALUE_2;
                    }
                  break;
@@ -1336,7 +1363,7 @@ 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))
        {
-         nat_log_err ("static_mapping_by_external key del failed");
+         nat_elog_err ("static_mapping_by_external key del failed");
          return VNET_API_ERROR_UNSPECIFIED;
        }
 
@@ -1353,7 +1380,7 @@ 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))
                 {
-                  nat_log_err ("static_mapping_by_local key del failed");
+                  nat_elog_err ("static_mapping_by_local key del failed");
                   return VNET_API_ERROR_UNSPECIFIED;
                 }
             }
@@ -1364,7 +1391,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                 .src_address = local->addr,
               };
               tsm = vec_elt_at_index (sm->per_thread_data,
-                                      sm->worker_in2out_cb (&ip, m->fib_index));
+                                      sm->worker_in2out_cb (&ip, m->fib_index, 0));
             }
           else
             tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
@@ -1396,7 +1423,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                           (clib_net_to_host_u16 (s->in2out.port) != local->port))
                         continue;
 
-                      nat_free_session_data (sm, s, tsm - sm->per_thread_data);
+                      nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
                       nat44_delete_session (sm, s, tsm - sm->per_thread_data);
                     }
                 }
@@ -1487,7 +1514,7 @@ nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port,
          kv.key = m_key.as_u64;
          kv.value = m - sm->static_mappings;
          if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1))
-           nat_log_err ("static_mapping_by_local key add failed");
+           nat_elog_err ("static_mapping_by_local key add failed");
        }
     }
   else
@@ -1508,7 +1535,7 @@ nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port,
          m_key.fib_index = match_local->fib_index;
          kv.key = m_key.as_u64;
          if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0))
-           nat_log_err ("static_mapping_by_local key del failed");
+           nat_elog_err ("static_mapping_by_local key del failed");
        }
 
       if (sm->num_workers > 1)
@@ -1517,7 +1544,8 @@ nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port,
            .src_address = local->addr,
          };
          tsm = vec_elt_at_index (sm->per_thread_data,
-                                 sm->worker_in2out_cb (&ip, m->fib_index));
+                                 sm->worker_in2out_cb (&ip, m->fib_index,
+                                                       0));
        }
       else
        tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
@@ -1550,7 +1578,7 @@ nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port,
                       match_local->port))
                    continue;
 
-                 nat_free_session_data (sm, s, tsm - sm->per_thread_data);
+                 nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
                  nat44_delete_session (sm, s, tsm - sm->per_thread_data);
                }
            }
@@ -1570,7 +1598,7 @@ nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port,
         ip4_header_t ip;
         ip.src_address.as_u32 = local->addr.as_u32,
         bitmap = clib_bitmap_set (bitmap,
-                                  sm->worker_in2out_cb (&ip, local->fib_index),
+                                  sm->worker_in2out_cb (&ip, local->fib_index, 0),
                                   1);
       }
   }));
@@ -1643,7 +1671,7 @@ 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))
        {
-         nat_log_notice ("address used in static mapping");
+         nat_elog_notice ("address used in static mapping");
          return VNET_API_ERROR_UNSPECIFIED;
        }
     }
@@ -1660,7 +1688,7 @@ snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm,
           pool_foreach (ses, tsm->sessions, ({
             if (ses->out2in.addr.as_u32 == addr.as_u32)
               {
-                nat_free_session_data (sm, ses, tsm - sm->per_thread_data);
+                nat_free_session_data (sm, ses, tsm - sm->per_thread_data, 0);
                 vec_add1 (ses_to_be_removed, ses - tsm->sessions);
               }
           }));
@@ -2134,6 +2162,7 @@ snat_set_workers (uword * bitmap)
     ({
       vec_add1(sm->workers, i);
       sm->per_thread_data[sm->first_worker_index + i].snat_thread_index = j;
+      sm->per_thread_data[sm->first_worker_index + i].thread_index = i;
       j++;
     }));
   /* *INDENT-ON* */
@@ -2152,6 +2181,7 @@ snat_update_outside_fib (u32 sw_if_index, u32 new_fib_index,
   nat_outside_fib_t *outside_fib;
   snat_interface_t *i;
   u8 is_add = 1;
+  u8 match = 0;
 
   if (new_fib_index == old_fib_index)
     return;
@@ -2159,14 +2189,31 @@ snat_update_outside_fib (u32 sw_if_index, u32 new_fib_index,
   if (!vec_len (sm->outside_fibs))
     return;
 
-  pool_foreach (i, sm->interfaces, (
-                                    {
-                                    if (i->sw_if_index == sw_if_index)
-                                    {
-                                    if (!(nat_interface_is_outside (i)))
-                                    return;}
-                                    }
-               ));
+  /* *INDENT-OFF* */
+  pool_foreach (i, sm->interfaces,
+    ({
+      if (i->sw_if_index == sw_if_index)
+        {
+          if (!(nat_interface_is_outside (i)))
+           return;
+          match = 1;
+        }
+    }));
+
+  pool_foreach (i, sm->output_feature_interfaces,
+    ({
+      if (i->sw_if_index == sw_if_index)
+        {
+          if (!(nat_interface_is_outside (i)))
+           return;
+          match = 1;
+        }
+    }));
+  /* *INDENT-ON* */
+
+  if (!match)
+    return;
+
   vec_foreach (outside_fib, sm->outside_fibs)
   {
     if (outside_fib->fib_index == old_fib_index)
@@ -2253,6 +2300,7 @@ snat_init (vlib_main_t * vm)
   sm->workers = 0;
   sm->port_per_thread = 0xffff - 1024;
   sm->fq_in2out_index = ~0;
+  sm->fq_in2out_output_index = ~0;
   sm->fq_out2in_index = ~0;
   sm->udp_timeout = SNAT_UDP_TIMEOUT;
   sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
@@ -2262,6 +2310,7 @@ snat_init (vlib_main_t * vm)
   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
   sm->forwarding_enabled = 0;
   sm->log_class = vlib_log_register_class ("nat", 0);
+  sm->log_level = SNAT_LOG_ERROR;
   sm->mss_clamping = 0;
 
   node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
@@ -2428,11 +2477,47 @@ snat_free_outside_address_and_port (snat_address_t * addresses,
       foreach_snat_protocol
 #undef _
     default:
-      nat_log_info ("unknown protocol");
+      nat_elog_info ("unknown protocol");
       return;
     }
 }
 
+static int
+nat_set_outside_address_and_port (snat_address_t * addresses,
+                                 u32 thread_index, snat_session_key_t * k)
+{
+  snat_address_t *a = 0;
+  u32 address_index;
+  u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
+
+  for (address_index = 0; address_index < vec_len (addresses);
+       address_index++)
+    {
+      if (addresses[address_index].addr.as_u32 != k->addr.as_u32)
+       continue;
+
+      a = addresses + address_index;
+      switch (k->protocol)
+       {
+#define _(N, j, n, s) \
+        case SNAT_PROTOCOL_##N: \
+          if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, port_host_byte_order)) \
+            return VNET_API_ERROR_INSTANCE_IN_USE; \
+          clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, port_host_byte_order, 1); \
+          a->busy_##n##_ports_per_thread[thread_index]++; \
+          a->busy_##n##_ports++; \
+          return 0;
+         foreach_snat_protocol
+#undef _
+       default:
+         nat_elog_info ("unknown protocol");
+         return 1;
+       }
+    }
+
+  return VNET_API_ERROR_NO_SUCH_ENTRY;
+}
+
 int
 snat_static_mapping_match (snat_main_t * sm,
                           snat_session_key_t match,
@@ -2482,26 +2567,49 @@ snat_static_mapping_match (snat_main_t * sm,
        {
          if (PREDICT_FALSE (lb != 0))
            *lb = m->affinity ? AFFINITY_LB_NAT : LB_NAT;
-         if (m->affinity)
+         if (m->affinity && !nat_affinity_find_and_lock (ext_host_addr[0],
+                                                         match.addr,
+                                                         match.protocol,
+                                                         match.port,
+                                                         &backend_index))
            {
-             if (nat_affinity_find_and_lock (ext_host_addr[0], match.addr,
-                                             match.protocol, match.port,
-                                             &backend_index))
-               goto get_local;
-
              local = pool_elt_at_index (m->locals, backend_index);
              mapping->addr = local->addr;
              mapping->port = clib_host_to_net_u16 (local->port);
              mapping->fib_index = local->fib_index;
              goto end;
            }
-       get_local:
-          /* *INDENT-OFF* */
-          pool_foreach_index (i, m->locals,
-          ({
-            vec_add1 (tmp, i);
-          }));
-          /* *INDENT-ON* */
+         // pick locals matching this worker
+         if (PREDICT_FALSE (sm->num_workers > 1))
+           {
+             u32 thread_index = vlib_get_thread_index ();
+              /* *INDENT-OFF* */
+              pool_foreach_index (i, m->locals,
+              ({
+                local = pool_elt_at_index (m->locals, i);
+
+                ip4_header_t ip = {
+                 .src_address = local->addr,
+               };
+
+               if (sm->worker_in2out_cb (&ip, m->fib_index, 0) ==
+                   thread_index)
+                  {
+                    vec_add1 (tmp, i);
+                  }
+              }));
+              /* *INDENT-ON* */
+             ASSERT (vec_len (tmp) != 0);
+           }
+         else
+           {
+              /* *INDENT-OFF* */
+              pool_foreach_index (i, m->locals,
+              ({
+                vec_add1 (tmp, i);
+              }));
+              /* *INDENT-ON* */
+           }
          hi = vec_len (tmp) - 1;
          local = pool_elt_at_index (m->locals, tmp[hi]);
          rand = 1 + (random_u32 (&sm->random_seed) % local->prefix);
@@ -2514,15 +2622,6 @@ snat_static_mapping_match (snat_main_t * sm,
          local = pool_elt_at_index (m->locals, tmp[lo]);
          if (!(local->prefix >= rand))
            return 1;
-         if (PREDICT_FALSE (sm->num_workers > 1))
-           {
-             ip4_header_t ip = {
-               .src_address = local->addr,
-             };
-             if (sm->worker_in2out_cb (&ip, m->fib_index) !=
-                 vlib_get_thread_index ())
-               goto get_local;
-           }
          mapping->addr = local->addr;
          mapping->port = clib_host_to_net_u16 (local->port);
          mapping->fib_index = local->fib_index;
@@ -2532,7 +2631,7 @@ snat_static_mapping_match (snat_main_t * sm,
                                                match.protocol, match.port,
                                                tmp[lo], m->affinity,
                                                m->affinity_per_service_list_head_index))
-               nat_log_info ("create affinity record failed");
+               nat_elog_info ("create affinity record failed");
            }
          vec_free (tmp);
        }
@@ -2638,7 +2737,7 @@ nat_alloc_addr_and_port_default (snat_address_t * addresses,
          foreach_snat_protocol
 #undef _
        default:
-         nat_log_info ("unknown protocol");
+         nat_elog_info ("unknown protocol");
          return 1;
        }
 
@@ -2669,7 +2768,7 @@ nat_alloc_addr_and_port_default (snat_address_t * addresses,
          foreach_snat_protocol
 #undef _
        default:
-         nat_log_info ("unknown protocol");
+         nat_elog_info ("unknown protocol");
          return 1;
        }
     }
@@ -2719,7 +2818,7 @@ nat_alloc_addr_and_port_mape (snat_address_t * addresses,
       foreach_snat_protocol
 #undef _
     default:
-      nat_log_info ("unknown protocol");
+      nat_elog_info ("unknown protocol");
       return 1;
     }
 
@@ -2767,7 +2866,7 @@ nat_alloc_addr_and_port_range (snat_address_t * addresses,
       foreach_snat_protocol
 #undef _
     default:
-      nat_log_info ("unknown protocol");
+      nat_elog_info ("unknown protocol");
       return 1;
     }
 
@@ -2860,7 +2959,8 @@ format_ed_session_kvp (u8 * s, va_list * args)
 }
 
 static u32
-snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0)
+snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0,
+                          u8 is_output)
 {
   snat_main_t *sm = &snat_main;
   u32 next_worker_index = 0;
@@ -2879,7 +2979,8 @@ snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0)
 }
 
 static u32
-snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0)
+snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0,
+                          u8 is_output)
 {
   snat_main_t *sm = &snat_main;
   udp_header_t *udp;
@@ -3015,35 +3116,197 @@ no_reass:
 }
 
 static u32
-nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index)
+nat44_ed_get_worker_in2out_cb (ip4_header_t * ip, u32 rx_fib_index,
+                              u8 is_output)
+{
+  snat_main_t *sm = &snat_main;
+  u32 next_worker_index = sm->first_worker_index;
+  u32 hash;
+
+  clib_bihash_kv_16_8_t kv16, value16;
+  snat_main_per_thread_data_t *tsm;
+  udp_header_t *udp;
+
+  if (PREDICT_FALSE (is_output))
+    {
+      u32 fib_index = sm->outside_fib_index;
+      nat_outside_fib_t *outside_fib;
+      fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
+      fib_prefix_t pfx = {
+       .fp_proto = FIB_PROTOCOL_IP4,
+       .fp_len = 32,
+       .fp_addr = {
+                   .ip4.as_u32 = ip->dst_address.as_u32,
+                   }
+       ,
+      };
+
+      udp = ip4_next_header (ip);
+
+      switch (vec_len (sm->outside_fibs))
+       {
+       case 0:
+         fib_index = sm->outside_fib_index;
+         break;
+       case 1:
+         fib_index = sm->outside_fibs[0].fib_index;
+         break;
+       default:
+            /* *INDENT-OFF* */
+            vec_foreach (outside_fib, sm->outside_fibs)
+              {
+                fei = fib_table_lookup (outside_fib->fib_index, &pfx);
+                if (FIB_NODE_INDEX_INVALID != fei)
+                  {
+                    if (fib_entry_get_resolving_interface (fei) != ~0)
+                      {
+                        fib_index = outside_fib->fib_index;
+                        break;
+                      }
+                  }
+              }
+            /* *INDENT-ON* */
+         break;
+       }
+
+      make_ed_kv (&kv16, &ip->src_address, &ip->dst_address,
+                 ip->protocol, fib_index, udp->src_port, udp->dst_port);
+
+      /* *INDENT-OFF* */
+      vec_foreach (tsm, sm->per_thread_data)
+        {
+          if (PREDICT_TRUE (!clib_bihash_search_16_8 (&tsm->out2in_ed,
+                                                      &kv16, &value16)))
+            {
+              next_worker_index += tsm->thread_index;
+
+              nat_elog_debug_handoff (
+                "HANDOFF IN2OUT-OUTPUT-FEATURE (session)",
+                next_worker_index, fib_index,
+               clib_net_to_host_u32 (ip->src_address.as_u32),
+               clib_net_to_host_u32 (ip->dst_address.as_u32));
+
+              return next_worker_index;
+            }
+        }
+      /* *INDENT-ON* */
+    }
+
+  hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) +
+    (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24);
+
+  if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
+    next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
+  else
+    next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
+
+  if (PREDICT_TRUE (!is_output))
+    {
+      nat_elog_debug_handoff ("HANDOFF IN2OUT",
+                             next_worker_index, rx_fib_index,
+                             clib_net_to_host_u32 (ip->src_address.as_u32),
+                             clib_net_to_host_u32 (ip->dst_address.as_u32));
+    }
+  else
+    {
+      nat_elog_debug_handoff ("HANDOFF IN2OUT-OUTPUT-FEATURE",
+                             next_worker_index, rx_fib_index,
+                             clib_net_to_host_u32 (ip->src_address.as_u32),
+                             clib_net_to_host_u32 (ip->dst_address.as_u32));
+    }
+
+  return next_worker_index;
+}
+
+static u32
+nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index,
+                              u8 is_output)
 {
   snat_main_t *sm = &snat_main;
   clib_bihash_kv_8_8_t kv, value;
+  clib_bihash_kv_16_8_t kv16, value16;
+  snat_main_per_thread_data_t *tsm;
+
   u32 proto, next_worker_index = 0;
   udp_header_t *udp;
   u16 port;
   snat_static_mapping_t *m;
   u32 hash;
 
+  proto = ip_proto_to_snat_proto (ip->protocol);
+
+  if (PREDICT_TRUE (proto == SNAT_PROTOCOL_UDP || proto == SNAT_PROTOCOL_TCP))
+    {
+      udp = ip4_next_header (ip);
+
+      make_ed_kv (&kv16, &ip->dst_address, &ip->src_address,
+                 ip->protocol, rx_fib_index, udp->dst_port, udp->src_port);
+
+      /* *INDENT-OFF* */
+      vec_foreach (tsm, sm->per_thread_data)
+        {
+          if (PREDICT_TRUE (!clib_bihash_search_16_8 (&tsm->out2in_ed,
+                                                      &kv16, &value16)))
+            {
+              next_worker_index = sm->first_worker_index + tsm->thread_index;
+              nat_elog_debug_handoff ("HANDOFF OUT2IN (session)",
+                          next_worker_index, rx_fib_index,
+                         clib_net_to_host_u32 (ip->src_address.as_u32),
+                         clib_net_to_host_u32 (ip->dst_address.as_u32));
+              return next_worker_index;
+            }
+          }
+        /* *INDENT-ON* */
+    }
+  else if (proto == SNAT_PROTOCOL_ICMP)
+    {
+      nat_ed_ses_key_t key;
+
+      if (!get_icmp_o2i_ed_key (ip, &key))
+       {
+
+         key.fib_index = rx_fib_index;
+         kv16.key[0] = key.as_u64[0];
+         kv16.key[1] = key.as_u64[1];
+
+          /* *INDENT-OFF* */
+          vec_foreach (tsm, sm->per_thread_data)
+            {
+              if (PREDICT_TRUE (!clib_bihash_search_16_8 (&tsm->out2in_ed,
+                                                          &kv16, &value16)))
+                {
+                  next_worker_index = sm->first_worker_index +
+                                      tsm->thread_index;
+                  nat_elog_debug_handoff ("HANDOFF OUT2IN (session)",
+                              next_worker_index, rx_fib_index,
+                             clib_net_to_host_u32 (ip->src_address.as_u32),
+                             clib_net_to_host_u32 (ip->dst_address.as_u32));
+                  return next_worker_index;
+                }
+            }
+          /* *INDENT-ON* */
+       }
+    }
+
   /* first try static mappings without port */
   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
     {
-      make_sm_kv (&kv, &ip->dst_address, 0, rx_fib_index, 0);
+      make_sm_kv (&kv, &ip->dst_address, 0, 0, 0);
       if (!clib_bihash_search_8_8
          (&sm->static_mapping_by_external, &kv, &value))
        {
          m = pool_elt_at_index (sm->static_mappings, value.value);
-         return m->workers[0];
+         next_worker_index = m->workers[0];
+         goto done;
        }
     }
 
-  proto = ip_proto_to_snat_proto (ip->protocol);
-
   /* unknown protocol */
   if (PREDICT_FALSE (proto == ~0))
     {
       /* use current thread */
-      return vlib_get_thread_index ();
+      next_worker_index = vlib_get_thread_index ();
+      goto done;
     }
 
   udp = ip4_next_header (ip);
@@ -3072,7 +3335,8 @@ nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index)
              port = ((tcp_udp_header_t *) l4_header)->src_port;
              break;
            default:
-             return vlib_get_thread_index ();
+             next_worker_index = vlib_get_thread_index ();
+             goto done;
            }
        }
     }
@@ -3080,22 +3344,27 @@ nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index)
   /* try static mappings with port */
   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
     {
-      make_sm_kv (&kv, &ip->dst_address, proto, rx_fib_index,
+      make_sm_kv (&kv, &ip->dst_address, proto, 0,
                  clib_net_to_host_u16 (port));
       if (!clib_bihash_search_8_8
          (&sm->static_mapping_by_external, &kv, &value))
        {
          m = pool_elt_at_index (sm->static_mappings, value.value);
          if (!is_lb_static_mapping (m))
-           return m->workers[0];
+           {
+             next_worker_index = m->workers[0];
+             goto done;
+           }
 
          hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) +
            (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24);
 
          if (PREDICT_TRUE (is_pow2 (_vec_len (m->workers))))
-           return m->workers[hash & (_vec_len (m->workers) - 1)];
+           next_worker_index =
+             m->workers[hash & (_vec_len (m->workers) - 1)];
          else
-           return m->workers[hash % _vec_len (m->workers)];
+           next_worker_index = m->workers[hash % _vec_len (m->workers)];
+         goto done;
        }
     }
 
@@ -3104,9 +3373,341 @@ nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index)
   next_worker_index +=
     sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
 
+done:
+  nat_elog_debug_handoff ("HANDOFF OUT2IN", next_worker_index, rx_fib_index,
+                         clib_net_to_host_u32 (ip->src_address.as_u32),
+                         clib_net_to_host_u32 (ip->dst_address.as_u32));
   return next_worker_index;
 }
 
+void
+nat_ha_sadd_cb (ip4_address_t * in_addr, u16 in_port,
+               ip4_address_t * out_addr, u16 out_port,
+               ip4_address_t * eh_addr, u16 eh_port,
+               ip4_address_t * ehn_addr, u16 ehn_port, u8 proto,
+               u32 fib_index, u16 flags, u32 thread_index)
+{
+  snat_main_t *sm = &snat_main;
+  snat_session_key_t key;
+  snat_user_t *u;
+  snat_session_t *s;
+  clib_bihash_kv_8_8_t kv;
+  f64 now = vlib_time_now (sm->vlib_main);
+  nat_outside_fib_t *outside_fib;
+  fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
+  snat_main_per_thread_data_t *tsm;
+  fib_prefix_t pfx = {
+    .fp_proto = FIB_PROTOCOL_IP4,
+    .fp_len = 32,
+    .fp_addr = {
+               .ip4.as_u32 = eh_addr->as_u32,
+               },
+  };
+
+  tsm = vec_elt_at_index (sm->per_thread_data, thread_index);
+
+  key.addr.as_u32 = out_addr->as_u32;
+  key.port = out_port;
+  key.protocol = proto;
+
+  if (!(flags & SNAT_SESSION_FLAG_STATIC_MAPPING))
+    {
+      if (nat_set_outside_address_and_port
+         (sm->addresses, thread_index, &key))
+       return;
+    }
+
+  u = nat_user_get_or_create (sm, in_addr, fib_index, thread_index);
+  if (!u)
+    return;
+
+  s = nat_session_alloc_or_recycle (sm, u, thread_index, now);
+  if (!s)
+    return;
+
+  s->last_heard = now;
+  s->flags = flags;
+  s->ext_host_addr.as_u32 = eh_addr->as_u32;
+  s->ext_host_port = eh_port;
+  user_session_increment (sm, u, snat_is_session_static (s));
+  switch (vec_len (sm->outside_fibs))
+    {
+    case 0:
+      key.fib_index = sm->outside_fib_index;
+      break;
+    case 1:
+      key.fib_index = sm->outside_fibs[0].fib_index;
+      break;
+    default:
+      /* *INDENT-OFF* */
+      vec_foreach (outside_fib, sm->outside_fibs)
+        {
+          fei = fib_table_lookup (outside_fib->fib_index, &pfx);
+          if (FIB_NODE_INDEX_INVALID != fei)
+            {
+              if (fib_entry_get_resolving_interface (fei) != ~0)
+                {
+                  key.fib_index = outside_fib->fib_index;
+                  break;
+                }
+            }
+        }
+      /* *INDENT-ON* */
+      break;
+    }
+  s->out2in = key;
+  kv.key = key.as_u64;
+  kv.value = s - tsm->sessions;
+  if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 1))
+    nat_elog_warn ("out2in key add failed");
+
+  key.addr.as_u32 = in_addr->as_u32;
+  key.port = in_port;
+  key.fib_index = fib_index;
+  s->in2out = key;
+  kv.key = key.as_u64;
+  if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 1))
+    nat_elog_warn ("in2out key add failed");
+}
+
+void
+nat_ha_sdel_cb (ip4_address_t * out_addr, u16 out_port,
+               ip4_address_t * eh_addr, u16 eh_port, u8 proto, u32 fib_index,
+               u32 ti)
+{
+  snat_main_t *sm = &snat_main;
+  snat_session_key_t key;
+  clib_bihash_kv_8_8_t kv, value;
+  u32 thread_index;
+  snat_session_t *s;
+  snat_main_per_thread_data_t *tsm;
+
+  if (sm->num_workers > 1)
+    thread_index =
+      sm->first_worker_index +
+      (sm->workers[(clib_net_to_host_u16 (out_port) -
+                   1024) / sm->port_per_thread]);
+  else
+    thread_index = sm->num_workers;
+  tsm = vec_elt_at_index (sm->per_thread_data, thread_index);
+
+  key.addr.as_u32 = out_addr->as_u32;
+  key.port = out_port;
+  key.protocol = proto;
+  key.fib_index = fib_index;
+  kv.key = key.as_u64;
+  if (clib_bihash_search_8_8 (&tsm->out2in, &kv, &value))
+    return;
+
+  s = pool_elt_at_index (tsm->sessions, value.value);
+  nat_free_session_data (sm, s, thread_index, 1);
+  nat44_delete_session (sm, s, thread_index);
+}
+
+void
+nat_ha_sref_cb (ip4_address_t * out_addr, u16 out_port,
+               ip4_address_t * eh_addr, u16 eh_port, u8 proto, u32 fib_index,
+               u32 total_pkts, u64 total_bytes, u32 thread_index)
+{
+  snat_main_t *sm = &snat_main;
+  snat_session_key_t key;
+  clib_bihash_kv_8_8_t kv, value;
+  snat_session_t *s;
+  snat_main_per_thread_data_t *tsm;
+
+  tsm = vec_elt_at_index (sm->per_thread_data, thread_index);
+
+  key.addr.as_u32 = out_addr->as_u32;
+  key.port = out_port;
+  key.protocol = proto;
+  key.fib_index = fib_index;
+  kv.key = key.as_u64;
+  if (clib_bihash_search_8_8 (&tsm->out2in, &kv, &value))
+    return;
+
+  s = pool_elt_at_index (tsm->sessions, value.value);
+  s->total_pkts = total_pkts;
+  s->total_bytes = total_bytes;
+}
+
+void
+nat_ha_sadd_ed_cb (ip4_address_t * in_addr, u16 in_port,
+                  ip4_address_t * out_addr, u16 out_port,
+                  ip4_address_t * eh_addr, u16 eh_port,
+                  ip4_address_t * ehn_addr, u16 ehn_port, u8 proto,
+                  u32 fib_index, u16 flags, u32 thread_index)
+{
+  snat_main_t *sm = &snat_main;
+  snat_session_key_t key;
+  snat_user_t *u;
+  snat_session_t *s;
+  clib_bihash_kv_16_8_t kv;
+  f64 now = vlib_time_now (sm->vlib_main);
+  nat_outside_fib_t *outside_fib;
+  fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
+  snat_main_per_thread_data_t *tsm;
+  fib_prefix_t pfx = {
+    .fp_proto = FIB_PROTOCOL_IP4,
+    .fp_len = 32,
+    .fp_addr = {
+               .ip4.as_u32 = eh_addr->as_u32,
+               },
+  };
+
+  tsm = vec_elt_at_index (sm->per_thread_data, thread_index);
+
+  key.addr.as_u32 = out_addr->as_u32;
+  key.port = out_port;
+  key.protocol = proto;
+
+  if (!(flags & SNAT_SESSION_FLAG_STATIC_MAPPING))
+    {
+      if (nat_set_outside_address_and_port
+         (sm->addresses, thread_index, &key))
+       return;
+    }
+
+  key.addr.as_u32 = ehn_addr->as_u32;
+  key.port = ehn_port;
+  if (flags & SNAT_SESSION_FLAG_TWICE_NAT)
+    {
+      if (nat_set_outside_address_and_port
+         (sm->twice_nat_addresses, thread_index, &key))
+       return;
+    }
+
+  u = nat_user_get_or_create (sm, in_addr, fib_index, thread_index);
+  if (!u)
+    return;
+
+  s = nat_ed_session_alloc (sm, u, thread_index, now);
+  if (!s)
+    return;
+
+  s->last_heard = now;
+  s->flags = flags;
+  s->ext_host_nat_addr.as_u32 = s->ext_host_addr.as_u32 = eh_addr->as_u32;
+  s->ext_host_nat_port = s->ext_host_port = eh_port;
+  if (is_twice_nat_session (s))
+    {
+      s->ext_host_nat_addr.as_u32 = ehn_addr->as_u32;
+      s->ext_host_nat_port = ehn_port;
+    }
+  user_session_increment (sm, u, snat_is_session_static (s));
+  switch (vec_len (sm->outside_fibs))
+    {
+    case 0:
+      key.fib_index = sm->outside_fib_index;
+      break;
+    case 1:
+      key.fib_index = sm->outside_fibs[0].fib_index;
+      break;
+    default:
+      /* *INDENT-OFF* */
+      vec_foreach (outside_fib, sm->outside_fibs)
+        {
+          fei = fib_table_lookup (outside_fib->fib_index, &pfx);
+          if (FIB_NODE_INDEX_INVALID != fei)
+            {
+              if (fib_entry_get_resolving_interface (fei) != ~0)
+                {
+                  key.fib_index = outside_fib->fib_index;
+                  break;
+                }
+            }
+        }
+      /* *INDENT-ON* */
+      break;
+    }
+  key.addr.as_u32 = out_addr->as_u32;
+  key.port = out_port;
+  s->out2in = key;
+  kv.value = s - tsm->sessions;
+
+  key.addr.as_u32 = in_addr->as_u32;
+  key.port = in_port;
+  key.fib_index = fib_index;
+  s->in2out = key;
+
+  make_ed_kv (&kv, in_addr, &s->ext_host_nat_addr,
+             snat_proto_to_ip_proto (proto), fib_index, in_port,
+             s->ext_host_nat_port);
+  if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &kv, 1))
+    nat_elog_warn ("in2out key add failed");
+
+  make_ed_kv (&kv, out_addr, eh_addr, snat_proto_to_ip_proto (proto),
+             s->out2in.fib_index, out_port, eh_port);
+  if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 1))
+    nat_elog_warn ("out2in key add failed");
+}
+
+void
+nat_ha_sdel_ed_cb (ip4_address_t * out_addr, u16 out_port,
+                  ip4_address_t * eh_addr, u16 eh_port, u8 proto,
+                  u32 fib_index, u32 ti)
+{
+  snat_main_t *sm = &snat_main;
+  nat_ed_ses_key_t key;
+  clib_bihash_kv_16_8_t kv, value;
+  u32 thread_index;
+  snat_session_t *s;
+  snat_main_per_thread_data_t *tsm;
+
+  if (sm->num_workers > 1)
+    thread_index =
+      sm->first_worker_index +
+      (sm->workers[(clib_net_to_host_u16 (out_port) -
+                   1024) / sm->port_per_thread]);
+  else
+    thread_index = sm->num_workers;
+  tsm = vec_elt_at_index (sm->per_thread_data, thread_index);
+
+  key.l_addr.as_u32 = out_addr->as_u32;
+  key.l_port = out_port;
+  key.r_addr.as_u32 = eh_addr->as_u32;
+  key.r_port = eh_port;
+  key.proto = proto;
+  key.fib_index = fib_index;
+  kv.key[0] = key.as_u64[0];
+  kv.key[1] = key.as_u64[1];
+  if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv, &value))
+    return;
+
+  s = pool_elt_at_index (tsm->sessions, value.value);
+  nat_free_session_data (sm, s, thread_index, 1);
+  nat44_delete_session (sm, s, thread_index);
+}
+
+void
+nat_ha_sref_ed_cb (ip4_address_t * out_addr, u16 out_port,
+                  ip4_address_t * eh_addr, u16 eh_port, u8 proto,
+                  u32 fib_index, u32 total_pkts, u64 total_bytes,
+                  u32 thread_index)
+{
+  snat_main_t *sm = &snat_main;
+  nat_ed_ses_key_t key;
+  clib_bihash_kv_16_8_t kv, value;
+  snat_session_t *s;
+  snat_main_per_thread_data_t *tsm;
+
+  tsm = vec_elt_at_index (sm->per_thread_data, thread_index);
+
+  key.l_addr.as_u32 = out_addr->as_u32;
+  key.l_port = out_port;
+  key.r_addr.as_u32 = eh_addr->as_u32;
+  key.r_port = eh_port;
+  key.proto = proto;
+  key.fib_index = fib_index;
+  kv.key[0] = key.as_u64[0];
+  kv.key[1] = key.as_u64[1];
+  if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv, &value))
+    return;
+
+  s = pool_elt_at_index (tsm->sessions, value.value);
+  s->total_pkts = total_pkts;
+  s->total_bytes = total_bytes;
+}
+
 static clib_error_t *
 snat_config (vlib_main_t * vm, unformat_input_t * input)
 {
@@ -3236,7 +3837,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
     {
       if (sm->endpoint_dependent)
        {
-         sm->worker_in2out_cb = snat_get_worker_in2out_cb;
+         sm->worker_in2out_cb = nat44_ed_get_worker_in2out_cb;
          sm->worker_out2in_cb = nat44_ed_get_worker_out2in_cb;
          sm->in2out_node_index = nat44_ed_in2out_node.index;
          sm->in2out_output_node_index = nat44_ed_in2out_output_node.index;
@@ -3244,6 +3845,8 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
          sm->icmp_match_in2out_cb = icmp_match_in2out_ed;
          sm->icmp_match_out2in_cb = icmp_match_out2in_ed;
          nat_affinity_init (vm);
+         nat_ha_init (vm, nat_ha_sadd_ed_cb, nat_ha_sdel_ed_cb,
+                      nat_ha_sref_ed_cb);
        }
       else
        {
@@ -3254,6 +3857,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input)
          sm->out2in_node_index = snat_out2in_node.index;
          sm->icmp_match_in2out_cb = icmp_match_in2out_slow;
          sm->icmp_match_out2in_cb = icmp_match_out2in_slow;
+         nat_ha_init (vm, nat_ha_sadd_cb, nat_ha_sdel_cb, nat_ha_sref_cb);
        }
       if (!static_mapping_only ||
          (static_mapping_only && static_mapping_connection_tracking))
@@ -3387,7 +3991,7 @@ match:
                                rp->proto, !is_delete, rp->twice_nat,
                                rp->out2in_only, rp->tag, rp->identity_nat);
   if (rv)
-    nat_log_notice ("snat_add_static_mapping returned %d", rv);
+    nat_elog_notice_X1 ("snat_add_static_mapping returned %d", "i4", rv);
 }
 
 static void
@@ -3458,7 +4062,8 @@ match:
                                            rp->out2in_only, rp->tag,
                                            rp->identity_nat);
              if (rv)
-               nat_log_notice ("snat_add_static_mapping returned %d", rv);
+               nat_elog_notice_X1 ("snat_add_static_mapping returned %d",
+                                   "i4", rv);
            }
        }
       return;
@@ -3558,7 +4163,7 @@ nat44_del_session (snat_main_t * sm, ip4_address_t * addr, u16 port,
   if (sm->num_workers > 1)
     tsm =
       vec_elt_at_index (sm->per_thread_data,
-                       sm->worker_in2out_cb (&ip, fib_index));
+                       sm->worker_in2out_cb (&ip, fib_index, 0));
   else
     tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
 
@@ -3574,7 +4179,7 @@ nat44_del_session (snat_main_t * sm, ip4_address_t * addr, u16 port,
        return VNET_API_ERROR_UNSPECIFIED;
 
       s = pool_elt_at_index (tsm->sessions, value.value);
-      nat_free_session_data (sm, s, tsm - sm->per_thread_data);
+      nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
       nat44_delete_session (sm, s, tsm - sm->per_thread_data);
       return 0;
     }
@@ -3602,7 +4207,7 @@ nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port,
   if (sm->num_workers > 1)
     tsm =
       vec_elt_at_index (sm->per_thread_data,
-                       sm->worker_in2out_cb (&ip, fib_index));
+                       sm->worker_in2out_cb (&ip, fib_index, 0));
   else
     tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
 
@@ -3621,7 +4226,7 @@ nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port,
   if (pool_is_free_index (tsm->sessions, value.value))
     return VNET_API_ERROR_UNSPECIFIED;
   s = pool_elt_at_index (tsm->sessions, value.value);
-  nat_free_session_data (sm, s, tsm - sm->per_thread_data);
+  nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
   nat44_delete_session (sm, s, tsm - sm->per_thread_data);
   return 0;
 }