NAT44: add support for session timeout (VPP-1272)

NAT44 (vanilla/simple and endpoint-dependent mode) now lazily delete expired
sessions. When inserting to session lookup hash and bucket is full, expired
session is overwritten.

Change-Id: Ib1b34959f60f0ca4f5b13525b1d41dd2f992288d
Signed-off-by: Matus Fabian <matfabia@cisco.com>
diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c
index ac0490f..9c8b2c6 100755
--- a/src/plugins/nat/in2out.c
+++ b/src/plugins/nat/in2out.c
@@ -311,6 +311,43 @@
   return 0;
 }
 
+int
+nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void * arg)
+{
+  snat_main_t *sm = &snat_main;
+  nat44_is_idle_session_ctx_t *ctx = arg;
+  snat_session_t *s;
+  u64 sess_timeout_time;
+  snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
+                                                       ctx->thread_index);
+  clib_bihash_kv_8_8_t s_kv;
+
+  s = pool_elt_at_index (tsm->sessions, kv->value);
+  sess_timeout_time = s->last_heard + (f64)nat44_session_get_timeout(sm, s);
+  if (ctx->now >= sess_timeout_time)
+    {
+      s_kv.key = s->out2in.as_u64;
+      if (clib_bihash_add_del_8_8 (&tsm->out2in, &s_kv, 0))
+        nat_log_warn ("out2in key del failed");
+
+      snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
+                                          s->out2in.addr.as_u32,
+                                          s->in2out.protocol,
+                                          s->in2out.port,
+                                          s->out2in.port,
+                                          s->in2out.fib_index);
+
+      if (!snat_is_session_static (s))
+        snat_free_outside_address_and_port (sm->addresses, ctx->thread_index,
+                                            &s->out2in);
+
+      nat44_delete_session (sm, s, ctx->thread_index);
+      return 1;
+    }
+
+  return 0;
+}
+
 static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
                       ip4_header_t * ip0,
                       u32 rx_fib_index0,
@@ -318,7 +355,8 @@
                       snat_session_t ** sessionp,
                       vlib_node_runtime_t * node,
                       u32 next0,
-                      u32 thread_index)
+                      u32 thread_index,
+                      f64 now)
 {
   snat_user_t *u;
   snat_session_t *s;
@@ -336,6 +374,7 @@
         .ip4.as_u32 = ip0->dst_address.as_u32,
     },
   };
+  nat44_is_idle_session_ctx_t ctx0;
 
   if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index)))
     {
@@ -416,17 +455,21 @@
   *sessionp = s;
 
   /* Add to translation hashes */
+  ctx0.now = now;
+  ctx0.thread_index = thread_index;
   kv0.key = s->in2out.as_u64;
   kv0.value = s - sm->per_thread_data[thread_index].sessions;
-  if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].in2out, &kv0,
-                               1 /* is_add */))
+  if (clib_bihash_add_or_overwrite_stale_8_8 (
+        &sm->per_thread_data[thread_index].in2out, &kv0,
+        nat44_i2o_is_idle_session_cb, &ctx0))
       nat_log_notice ("in2out key add failed");
 
   kv0.key = s->out2in.as_u64;
   kv0.value = s - sm->per_thread_data[thread_index].sessions;
 
-  if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0,
-                               1 /* is_add */))
+  if (clib_bihash_add_or_overwrite_stale_8_8 (
+        &sm->per_thread_data[thread_index].out2in, &kv0,
+        nat44_o2i_is_idle_session_cb, &ctx0))
       nat_log_notice ("out2in key add failed");
 
   /* log NAT event */
@@ -558,8 +601,8 @@
           goto out;
         }
 
-      next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
-                         &s0, node, next0, thread_index);
+      next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, &s0, node, next0,
+                         thread_index, vlib_time_now (sm->vlib_main));
 
       if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
         goto out;
@@ -1252,7 +1295,7 @@
                     }
 
                   next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
-                                     &s0, node, next0, thread_index);
+                                     &s0, node, next0, thread_index, now);
                   if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
                     goto trace00;
                 }
@@ -1412,7 +1455,7 @@
                     }
 
                   next1 = slow_path (sm, b1, ip1, rx_fib_index1, &key1,
-                                     &s1, node, next1, thread_index);
+                                     &s1, node, next1, thread_index, now);
                   if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP))
                     goto trace01;
                 }
@@ -1608,7 +1651,7 @@
                     }
 
                   next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
-                                     &s0, node, next0, thread_index);
+                                     &s0, node, next0, thread_index, now);
 
                   if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
                     goto trace0;
@@ -2156,7 +2199,7 @@
                     goto trace0;
 
                   next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
-                                     &s0, node, next0, thread_index);
+                                     &s0, node, next0, thread_index, now);
 
                   if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
                     goto trace0;
@@ -2374,6 +2417,110 @@
   return 0;
 }
 
+int
+nat44_i2o_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void * arg)
+{
+  snat_main_t *sm = &snat_main;
+  nat44_is_idle_session_ctx_t *ctx = arg;
+  snat_session_t *s;
+  u64 sess_timeout_time;
+  nat_ed_ses_key_t ed_key;
+  clib_bihash_kv_16_8_t ed_kv;
+  int i;
+  snat_address_t *a;
+  snat_session_key_t key;
+  snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
+                                                       ctx->thread_index);
+
+  s = pool_elt_at_index (tsm->sessions, kv->value);
+  sess_timeout_time = s->last_heard + (f64)nat44_session_get_timeout(sm, s);
+  if (ctx->now >= sess_timeout_time)
+    {
+      if (is_fwd_bypass_session (s))
+        goto delete;
+
+      ed_key.l_addr = s->out2in.addr;
+      ed_key.r_addr = s->ext_host_addr;
+      ed_key.fib_index = s->out2in.fib_index;
+      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->out2in.port;
+          ed_key.r_port = s->ext_host_port;
+        }
+      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");
+
+      if (snat_is_unk_proto_session (s))
+        goto delete;
+
+      snat_ipfix_logging_nat44_ses_delete(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_twice_nat_session (s))
+        {
+          for (i = 0; i < vec_len (sm->twice_nat_addresses); i++)
+            {
+              key.protocol = s->in2out.protocol;
+              key.port = s->ext_host_nat_port;
+              a = sm->twice_nat_addresses + i;
+              if (a->addr.as_u32 == s->ext_host_nat_addr.as_u32)
+                {
+                  snat_free_outside_address_and_port (sm->twice_nat_addresses,
+                                                      ctx->thread_index, &key);
+                  break;
+                }
+            }
+        }
+
+      if (snat_is_session_static (s))
+        goto delete;
+
+      if (s->outside_address_index != ~0)
+        snat_free_outside_address_and_port (sm->addresses, ctx->thread_index,
+                                            &s->out2in);
+    delete:
+      nat44_delete_session (sm, s, ctx->thread_index);
+      return 1;
+    }
+
+  return 0;
+}
+
+static inline u32
+icmp_in2out_ed_slow_path (snat_main_t * sm, vlib_buffer_t * b0,
+                          ip4_header_t * ip0, icmp46_header_t * icmp0,
+                          u32 sw_if_index0, u32 rx_fib_index0,
+                          vlib_node_runtime_t * node, u32 next0, f64 now,
+                          u32 thread_index, snat_session_t ** p_s0)
+{
+  next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
+                      next0, thread_index, p_s0, 0);
+  snat_session_t * s0 = *p_s0;
+  if (PREDICT_TRUE(next0 != SNAT_IN2OUT_NEXT_DROP && s0))
+    {
+      /* Hairpinning */
+      if (vnet_buffer(b0)->sw_if_index[VLIB_TX] == ~0)
+        snat_icmp_hairpinning(sm, b0, ip0, icmp0, sm->endpoint_dependent);
+      /* Accounting */
+      nat44_session_update_counters (s0, now,
+                                     vlib_buffer_length_in_chain (sm->vlib_main, b0));
+    }
+  return next0;
+}
+
 static u32
 slow_path_ed (snat_main_t *sm,
               vlib_buffer_t *b,
@@ -2382,7 +2529,8 @@
               snat_session_t ** sessionp,
               vlib_node_runtime_t * node,
               u32 next,
-              u32 thread_index)
+              u32 thread_index,
+              f64 now)
 {
   snat_session_t *s;
   snat_user_t *u;
@@ -2401,6 +2549,7 @@
         .ip4.as_u32 = key->r_addr.as_u32,
     },
   };
+  nat44_is_idle_session_ctx_t ctx;
 
   if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index)))
     {
@@ -2437,14 +2586,20 @@
   if (!u)
     {
       nat_log_warn ("create NAT user failed");
+      if (!is_sm)
+        snat_free_outside_address_and_port (sm->addresses,
+                                            thread_index, &key1);
       return SNAT_IN2OUT_NEXT_DROP;
     }
 
-  s = nat_session_alloc_or_recycle (sm, u, thread_index);
+  s = nat_ed_session_alloc (sm, u, thread_index);
   if (!s)
     {
       nat44_delete_user_with_no_session (sm, u, thread_index);
       nat_log_warn ("create NAT session failed");
+      if (!is_sm)
+        snat_free_outside_address_and_port (sm->addresses,
+                                            thread_index, &key1);
       return SNAT_IN2OUT_NEXT_DROP;
     }
 
@@ -2487,13 +2642,19 @@
 
   /* Add to lookup tables */
   kv->value = s - tsm->sessions;
-  if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, kv, 1))
+  ctx.now = now;
+  ctx.thread_index = thread_index;
+  if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->in2out_ed, kv,
+                                               nat44_i2o_ed_is_idle_session_cb,
+                                               &ctx))
     nat_log_notice ("in2out-ed key add failed");
 
   make_ed_kv (kv, &key1.addr, &key->r_addr, key->proto, s->out2in.fib_index,
               key1.port, key->r_port);
   kv->value = s - tsm->sessions;
-  if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, kv, 1))
+  if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->out2in_ed, kv,
+                                               nat44_o2i_ed_is_idle_session_cb,
+                                               &ctx))
     nat_log_notice ("out2in-ed key add failed");
 
   *sessionp = s;
@@ -2588,8 +2749,6 @@
               if (nat44_set_tcp_session_state_i2o (sm, s, tcp, thread_index))
                 return 1;
             }
-          /* Per-user LRU list maintenance */
-          nat44_session_update_lru (sm, s, thread_index);
           /* Accounting */
           nat44_session_update_counters (s, now,
                                          vlib_buffer_length_in_chain (vm, b));
@@ -2703,7 +2862,7 @@
         }
 
       next = slow_path_ed (sm, b, rx_fib_index, &kv, &s, node, next,
-                           thread_index);
+                           thread_index, vlib_time_now (sm->vlib_main));
 
       if (PREDICT_FALSE (next == SNAT_IN2OUT_NEXT_DROP))
         goto out;
@@ -2923,7 +3082,7 @@
         }
 
 create_ses:
-      s = nat_session_alloc_or_recycle (sm, u, thread_index);
+      s = nat_ed_session_alloc (sm, u, thread_index);
       if (!s)
         {
           nat44_delete_user_with_no_session (sm, u, thread_index);
@@ -2965,8 +3124,6 @@
 
   /* Accounting */
   nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b));
-  /* Per-user LRU list maintenance */
-  nat44_session_update_lru (sm, s, thread_index);
 
   /* Hairpinning */
   if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0)
@@ -3088,7 +3245,7 @@
 
               if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
                 {
-                  next0 = icmp_in2out_slow_path
+                  next0 = icmp_in2out_ed_slow_path
                     (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
                      next0, now, thread_index, &s0);
                   goto trace00;
@@ -3140,7 +3297,7 @@
                     }
 
                   next0 = slow_path_ed (sm, b0, rx_fib_index0, &kv0, &s0, node,
-                                        next0, thread_index);
+                                        next0, thread_index, now);
 
                   if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
                     goto trace00;
@@ -3211,8 +3368,6 @@
           /* Accounting */
           nat44_session_update_counters (s0, now,
                                          vlib_buffer_length_in_chain (vm, b0));
-          /* Per-user LRU list maintenance */
-          nat44_session_update_lru (sm, s0, thread_index);
 
         trace00:
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -3273,7 +3428,7 @@
 
               if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP))
                 {
-                  next1 = icmp_in2out_slow_path
+                  next1 = icmp_in2out_ed_slow_path
                     (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node,
                      next1, now, thread_index, &s1);
                   goto trace01;
@@ -3325,7 +3480,7 @@
                     }
 
                   next1 = slow_path_ed (sm, b1, rx_fib_index1, &kv1, &s1, node,
-                                        next1, thread_index);
+                                        next1, thread_index, now);
 
                   if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP))
                     goto trace01;
@@ -3396,8 +3551,6 @@
           /* Accounting */
           nat44_session_update_counters (s1, now,
                                          vlib_buffer_length_in_chain (vm, b1));
-          /* Per-user LRU list maintenance */
-          nat44_session_update_lru (sm, s1, thread_index);
 
         trace01:
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -3487,7 +3640,7 @@
 
               if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
                 {
-                  next0 = icmp_in2out_slow_path
+                  next0 = icmp_in2out_ed_slow_path
                     (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
                      next0, now, thread_index, &s0);
                   goto trace0;
@@ -3539,7 +3692,7 @@
                     }
 
                   next0 = slow_path_ed (sm, b0, rx_fib_index0, &kv0, &s0, node,
-                                        next0, thread_index);
+                                        next0, thread_index, now);
 
                   if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
                     goto trace0;
@@ -3610,8 +3763,6 @@
           /* Accounting */
           nat44_session_update_counters (s0, now,
                                          vlib_buffer_length_in_chain (vm, b0));
-          /* Per-user LRU list maintenance */
-          nat44_session_update_lru (sm, s0, thread_index);
 
         trace0:
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api
index 45e504f..8e37567 100644
--- a/src/plugins/nat/nat.api
+++ b/src/plugins/nat/nat.api
@@ -13,7 +13,7 @@
  * limitations under the License.
  */
 
-option version = "3.1.0";
+option version = "4.0.0";
 
 /**
  * @file nat.api
@@ -234,6 +234,49 @@
   u8 frag_n;
 };
 
+/** \brief Set values of timeouts for NAT sessions (seconds)
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param udp - UDP timeout (default 300sec)
+    @param tcp_established - TCP established timeout (default 7440sec)
+    @param tcp_transitory - TCP transitory timeout (default 240sec)
+    @param icmp - ICMP timeout (default 60sec)
+*/
+autoreply define nat_set_timeouts {
+  u32 client_index;
+  u32 context;
+  u32 udp;
+  u32 tcp_established;
+  u32 tcp_transitory;
+  u32 icmp;
+};
+
+/** \brief Get values of timeouts for NAT sessions (seconds)
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define nat_get_timeouts {
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief Get values of timeouts for NAT sessions reply
+    @param context - sender context, to match reply w/ request
+    @param retval - return code
+    @param udp - UDP timeout
+    @param tcp_established - TCP established timeout
+    @param tcp_transitory - TCP transitory timeout
+    @param icmp - ICMP timeout
+*/
+define nat_get_timeouts_reply {
+  u32 context;
+  i32 retval;
+  u32 udp;
+  u32 tcp_established;
+  u32 tcp_transitory;
+  u32 icmp;
+};
+
 /*
  * NAT44 APIs
  */
@@ -849,49 +892,6 @@
   u32 ses_num;
 };
 
-/** \brief Set values of timeouts for deterministic NAT (seconds, 0 = default)
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-    @param udp - UDP timeout (default 300sec)
-    @param tcp_established - TCP established timeout (default 7440sec)
-    @param tcp_transitory - TCP transitory timeout (default 240sec)
-    @param icmp - ICMP timeout (default 60sec)
-*/
-autoreply define nat_det_set_timeouts {
-  u32 client_index;
-  u32 context;
-  u32 udp;
-  u32 tcp_established;
-  u32 tcp_transitory;
-  u32 icmp;
-};
-
-/** \brief Get values of timeouts for deterministic NAT (seconds)
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-*/
-define nat_det_get_timeouts {
-  u32 client_index;
-  u32 context;
-};
-
-/** \brief Get values of timeouts for deterministic NAT reply
-    @param context - sender context, to match reply w/ request
-    @param retval - return code
-    @param udp - UDP timeout (default 300sec)
-    @param tcp_established - TCP established timeout (default 7440sec)
-    @param tcp_transitory - TCP transitory timeout (default 240sec)
-    @param icmp - ICMP timeout (default 60sec)
-*/
-define nat_det_get_timeouts_reply {
-  u32 context;
-  i32 retval;
-  u32 udp;
-  u32 tcp_established;
-  u32 tcp_transitory;
-  u32 icmp;
-};
-
 /** \brief Close deterministic NAT session by outside address and port
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
@@ -1097,53 +1097,6 @@
   u32 ses_num;
 };
 
-/** \brief Set values of timeouts for NAT64 (seconds, 0 = default)
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-    @param udp - UDP timeout (default 300sec)
-    @param icmp - ICMP timeout (default 60sec)
-    @param tcp_trans - TCP transitory timeout (default 240sec)
-    @param tcp_est - TCP established timeout (default 7440sec)
-    @param tcp_incoming_syn - TCP incoming SYN timeout (default 6sec)
-*/
-autoreply define nat64_set_timeouts {
-  u32 client_index;
-  u32 context;
-  u32 udp;
-  u32 icmp;
-  u32 tcp_trans;
-  u32 tcp_est;
-  u32 tcp_incoming_syn;
-};
-
-/** \brief Get values of timeouts for NAT64 (seconds)
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-*/
-define nat64_get_timeouts {
-  u32 client_index;
-  u32 context;
-};
-
-/** \brief Get values of timeouts for NAT64 reply
-    @param context - sender context, to match reply w/ request
-    @param retval - return code
-    @param udp - UDP timeout
-    @param icmp - ICMP timeout
-    @param tcp_trans - TCP transitory timeout
-    @param tcp_est - TCP established timeout
-    @param tcp_incoming_syn - TCP incoming SYN timeout
-*/
-define nat64_get_timeouts_reply {
-  u32 context;
-  i32 retval;
-  u32 udp;
-  u32 icmp;
-  u32 tcp_trans;
-  u32 tcp_est;
-  u32 tcp_incoming_syn;
-};
-
 /** \brief Dump NAT64 session table
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index bb82e92..364d5f5 100755
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -319,8 +319,6 @@
       /* add user */
       if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
         nat_log_warn ("user_hash keay add failed");
-
-      clib_warning("%U %d", format_ip4_address, addr, fib_index);
     }
   else
     {
@@ -400,6 +398,41 @@
   return s;
 }
 
+snat_session_t *
+nat_ed_session_alloc (snat_main_t *sm, snat_user_t *u, u32 thread_index)
+{
+  snat_session_t *s;
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+  dlist_elt_t * per_user_translation_list_elt;
+
+  if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user)
+    {
+      nat_log_warn ("max translations per user %U", format_ip4_address, &u->addr);
+      snat_ipfix_logging_max_entries_per_user (sm->max_translations_per_user,
+                                               u->addr.as_u32);
+      return 0;
+    }
+
+  pool_get (tsm->sessions, s);
+  memset (s, 0, sizeof (*s));
+  s->outside_address_index = ~0;
+
+  /* Create list elts */
+  pool_get (tsm->list_pool, per_user_translation_list_elt);
+  clib_dlist_init (tsm->list_pool,
+                   per_user_translation_list_elt - tsm->list_pool);
+
+  per_user_translation_list_elt->value = s - tsm->sessions;
+  s->per_user_index = per_user_translation_list_elt - tsm->list_pool;
+  s->per_user_list_head_index = u->sessions_per_user_list_head_index;
+
+  clib_dlist_addtail (tsm->list_pool,
+                      s->per_user_list_head_index,
+                      per_user_translation_list_elt - tsm->list_pool);
+
+  return s;
+}
+
 typedef struct {
   u8 next_in2out;
 } nat44_classify_trace_t;
@@ -2456,6 +2489,7 @@
 }
 
 u8 * format_snat_key (u8 * s, va_list * args);
+u8 * format_static_mapping_key (u8 * s, va_list * args);
 
 u8 *
 format_session_kvp (u8 * s, va_list * args)
@@ -2478,7 +2512,8 @@
 
   k.as_u64 = v->key;
 
-  s = format (s, "%U static-mapping-index %llu", format_snat_key, &k, v->value);
+  s = format (s, "%U static-mapping-index %llu",
+              format_static_mapping_key, &k, v->value);
 
   return s;
 }
@@ -2972,6 +3007,17 @@
   return s;
 }
 
+u8 * format_static_mapping_key (u8 * s, va_list * args)
+{
+  snat_session_key_t * key = va_arg (*args, snat_session_key_t *);
+
+  s = format (s, "%U proto %U port %d fib %d",
+              format_ip4_address, &key->addr,
+              format_snat_protocol, key->protocol,
+              key->port, key->fib_index);
+  return s;
+}
+
 u8 * format_snat_session (u8 * s, va_list * args)
 {
   snat_main_per_thread_data_t * sm = va_arg (*args, snat_main_per_thread_data_t *);
diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h
index 6ff6cd8..76f5754 100644
--- a/src/plugins/nat/nat.h
+++ b/src/plugins/nat/nat.h
@@ -32,10 +32,8 @@
 
 
 #define SNAT_UDP_TIMEOUT 300
-#define SNAT_UDP_TIMEOUT_MIN 120
 #define SNAT_TCP_TRANSITORY_TIMEOUT 240
 #define SNAT_TCP_ESTABLISHED_TIMEOUT 7440
-#define SNAT_TCP_INCOMING_SYN 6
 #define SNAT_ICMP_TIMEOUT 60
 
 #define NAT_FQ_NELTS 64
@@ -133,6 +131,8 @@
 #define NAT44_SES_O2I_FIN 2
 #define NAT44_SES_I2O_FIN_ACK 4
 #define NAT44_SES_O2I_FIN_ACK 8
+#define NAT44_SES_I2O_SYN 16
+#define NAT44_SES_O2I_SYN 32
 
 #define nat44_is_ses_closed(s) s->state == 0xf
 
@@ -172,7 +172,7 @@
   ip4_address_t ext_host_addr;  /* 68-71 */
   u16 ext_host_port;            /* 72-73 */
 
-  /* External hos address and port after translation */
+  /* External host address and port after translation */
   ip4_address_t ext_host_nat_addr; /* 74-77 */
   u16 ext_host_nat_port;           /* 78-79 */
 
@@ -427,6 +427,11 @@
   api_main_t * api_main;
 } snat_main_t;
 
+typedef struct {
+  u32 thread_index;
+  f64 now;
+} nat44_is_idle_session_ctx_t;
+
 extern snat_main_t snat_main;
 extern vlib_node_registration_t snat_in2out_node;
 extern vlib_node_registration_t snat_in2out_output_node;
@@ -626,8 +631,14 @@
                                       u32 fib_index, u32 thread_index);
 snat_session_t * nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u,
                                                u32 thread_index);
+snat_session_t * nat_ed_session_alloc (snat_main_t *sm, snat_user_t *u,
+                                       u32 thread_index);
 void nat_set_alloc_addr_and_port_mape (u16 psid, u16 psid_offset,
                                        u16 psid_length);
 void nat_set_alloc_addr_and_port_default (void);
+int nat44_i2o_ed_is_idle_session_cb (clib_bihash_kv_16_8_t *kv, void *arg);
+int nat44_o2i_ed_is_idle_session_cb (clib_bihash_kv_16_8_t *kv, void *arg);
+int nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t *kv, void *arg);
+int nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t *kv, void *arg);
 
 #endif /* __included_snat_h__ */
diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c
index e4fd0ca..e51f6d6 100644
--- a/src/plugins/nat/nat44_cli.c
+++ b/src/plugins/nat/nat44_cli.c
@@ -20,6 +20,7 @@
 #include <nat/nat.h>
 #include <nat/nat_ipfix_logging.h>
 #include <nat/nat_det.h>
+#include <nat/nat64.h>
 #include <nat/nat_inlines.h>
 #include <vnet/fib/fib_table.h>
 
@@ -978,8 +979,9 @@
     {
       tsm = vec_elt_at_index (sm->per_thread_data, i);
 
-      vlib_cli_output (vm, "-------- thread %d %s --------\n",
-                       i, vlib_worker_threads[i].name);
+      vlib_cli_output (vm, "-------- thread %d %s: %d sessions --------\n",
+                       i, vlib_worker_threads[i].name,
+                       pool_elts (tsm->sessions));
       pool_foreach (u, tsm->users,
       ({
         vlib_cli_output (vm, "  %U", format_snat_user, tsm, u, verbose);
@@ -1307,9 +1309,6 @@
   unformat_input_t _line_input, *line_input = &_line_input;
   clib_error_t *error = 0;
 
-  if (!sm->deterministic)
-    return clib_error_return (0, SUPPORTED_ONLY_IN_DET_MODE_STR);
-
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
     return 0;
@@ -1317,21 +1316,54 @@
   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     {
       if (unformat (line_input, "udp %u", &sm->udp_timeout))
-	;
+	{
+	  if (nat64_set_udp_timeout (sm->udp_timeout))
+	    {
+	      error = clib_error_return (0, "Invalid UDP timeout value");
+	      goto done;
+	    }
+	}
       else if (unformat (line_input, "tcp-established %u",
 			 &sm->tcp_established_timeout))
-	;
+	{
+	  if (nat64_set_tcp_timeouts
+	      (sm->tcp_transitory_timeout, sm->tcp_established_timeout))
+	    {
+	      error =
+		clib_error_return (0,
+				   "Invalid TCP established timeouts value");
+	      goto done;
+	    }
+	}
       else if (unformat (line_input, "tcp-transitory %u",
 			 &sm->tcp_transitory_timeout))
-	;
+	{
+	  if (nat64_set_tcp_timeouts
+	      (sm->tcp_transitory_timeout, sm->tcp_established_timeout))
+	    {
+	      error =
+		clib_error_return (0,
+				   "Invalid TCP transitory timeouts value");
+	      goto done;
+	    }
+	}
       else if (unformat (line_input, "icmp %u", &sm->icmp_timeout))
-	;
+	{
+	  if (nat64_set_icmp_timeout (sm->icmp_timeout))
+	    {
+	      error = clib_error_return (0, "Invalid ICMP timeout value");
+	      goto done;
+	    }
+	}
       else if (unformat (line_input, "reset"))
 	{
 	  sm->udp_timeout = SNAT_UDP_TIMEOUT;
 	  sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
 	  sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
 	  sm->icmp_timeout = SNAT_ICMP_TIMEOUT;
+	  nat64_set_udp_timeout (0);
+	  nat64_set_icmp_timeout (0);
+	  nat64_set_tcp_timeouts (0, 0);
 	}
       else
 	{
@@ -1348,15 +1380,12 @@
 }
 
 static clib_error_t *
-nat44_det_show_timeouts_command_fn (vlib_main_t * vm,
-				    unformat_input_t * input,
-				    vlib_cli_command_t * cmd)
+nat_show_timeouts_command_fn (vlib_main_t * vm,
+			      unformat_input_t * input,
+			      vlib_cli_command_t * cmd)
 {
   snat_main_t *sm = &snat_main;
 
-  if (!sm->deterministic)
-    return clib_error_return (0, SUPPORTED_ONLY_IN_DET_MODE_STR);
-
   vlib_cli_output (vm, "udp timeout: %dsec", sm->udp_timeout);
   vlib_cli_output (vm, "tcp-established timeout: %dsec",
 		   sm->tcp_established_timeout);
@@ -1544,6 +1573,40 @@
 
 /*?
  * @cliexpar
+ * @cliexstart{set nat timeout}
+ * Set values of timeouts for NAT sessions (in seconds), use:
+ *  vpp# set nat timeout udp 120 tcp-established 7500 tcp-transitory 250 icmp 90
+ * To reset default values use:
+ *  vpp# set nat44 deterministic timeout reset
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (set_timeout_command, static) = {
+  .path = "set nat timeout",
+  .function = set_timeout_command_fn,
+  .short_help =
+    "set nat timeout [udp <sec> | tcp-established <sec> "
+    "tcp-transitory <sec> | icmp <sec> | reset]",
+};
+
+/*?
+ * @cliexpar
+ * @cliexstart{show nat timeouts}
+ * Show values of timeouts for NAT sessions.
+ * vpp# show nat timeouts
+ * udp timeout: 300sec
+ * tcp-established timeout: 7440sec
+ * tcp-transitory timeout: 240sec
+ * icmp timeout: 60sec
+ * @cliexend
+?*/
+VLIB_CLI_COMMAND (nat_show_timeouts_command, static) = {
+  .path = "show nat timeouts",
+  .short_help = "show nat timeouts",
+  .function = nat_show_timeouts_command_fn,
+};
+
+/*?
+ * @cliexpar
  * @cliexstart{snat ipfix logging}
  * To enable NAT IPFIX logging use:
  *  vpp# nat ipfix logging
@@ -1893,41 +1956,6 @@
 
 /*?
  * @cliexpar
- * @cliexstart{set nat44 deterministic timeout}
- * Set values of timeouts for deterministic NAT (in seconds), use:
- *  vpp# set nat44 deterministic timeout udp 120 tcp-established 7500
- *  tcp-transitory 250 icmp 90
- * To reset default values use:
- *  vpp# set nat44 deterministic timeout reset
- * @cliexend
-?*/
-VLIB_CLI_COMMAND (set_timeout_command, static) = {
-  .path = "set nat44 deterministic timeout",
-  .function = set_timeout_command_fn,
-  .short_help =
-    "set nat44 deterministic timeout [udp <sec> | tcp-established <sec> "
-    "tcp-transitory <sec> | icmp <sec> | reset]",
-};
-
-/*?
- * @cliexpar
- * @cliexstart{show nat44 deterministic timeouts}
- * Show values of timeouts for deterministic NAT.
- * vpp# show nat44 deterministic timeouts
- * udp timeout: 300sec
- * tcp-established timeout: 7440sec
- * tcp-transitory timeout: 240sec
- * icmp timeout: 60sec
- * @cliexend
-?*/
-VLIB_CLI_COMMAND (nat44_det_show_timeouts_command, static) = {
-  .path = "show nat44 deterministic timeouts",
-  .short_help = "show nat44 deterministic timeouts",
-  .function = nat44_det_show_timeouts_command_fn,
-};
-
-/*?
- * @cliexpar
  * @cliexstart{show nat44 deterministic sessions}
  * Show NAT44 deterministic sessions.
  * vpp# show nat44 deterministic sessions
diff --git a/src/plugins/nat/nat64.c b/src/plugins/nat/nat64.c
index 1ca5e36..04055af 100644
--- a/src/plugins/nat/nat64.c
+++ b/src/plugins/nat/nat64.c
@@ -231,7 +231,6 @@
   nm->icmp_timeout = SNAT_ICMP_TIMEOUT;
   nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
   nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
-  nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN;
 
   nm->total_enabled_count = 0;
 
@@ -757,8 +756,6 @@
 
   if (timeout == 0)
     nm->udp_timeout = SNAT_UDP_TIMEOUT;
-  else if (timeout < SNAT_UDP_TIMEOUT_MIN)
-    return VNET_API_ERROR_INVALID_VALUE;
   else
     nm->udp_timeout = timeout;
 
@@ -795,7 +792,7 @@
 }
 
 int
-nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn)
+nat64_set_tcp_timeouts (u32 trans, u32 est)
 {
   nat64_main_t *nm = &nat64_main;
 
@@ -809,11 +806,6 @@
   else
     nm->tcp_est_timeout = est;
 
-  if (incoming_syn == 0)
-    nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN;
-  else
-    nm->tcp_incoming_syn_timeout = incoming_syn;
-
   return 0;
 }
 
@@ -833,14 +825,6 @@
   return nm->tcp_est_timeout;
 }
 
-u32
-nat64_get_tcp_incoming_syn_timeout (void)
-{
-  nat64_main_t *nm = &nat64_main;
-
-  return nm->tcp_incoming_syn_timeout;
-}
-
 void
 nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm)
 {
diff --git a/src/plugins/nat/nat64.h b/src/plugins/nat/nat64.h
index addba7a..d365e12 100644
--- a/src/plugins/nat/nat64.h
+++ b/src/plugins/nat/nat64.h
@@ -102,7 +102,6 @@
   u32 icmp_timeout;
   u32 tcp_trans_timeout;
   u32 tcp_est_timeout;
-  u32 tcp_incoming_syn_timeout;
 
   /* Total count of interfaces enabled */
   u32 total_enabled_count;
@@ -256,11 +255,10 @@
  *
  * @param trans Transitory timeout in seconds (if 0 reset to default value 240sec).
  * @param est Established timeout in seconds (if 0 reset to default value 7440sec).
- * @param incoming_syn Incoming SYN timeout in seconds (if 0 reset to default value 6sec).
  *
  * @returns 0 on success, non-zero value otherwise.
  */
-int nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn);
+int nat64_set_tcp_timeouts (u32 trans, u32 est);
 
 /**
  * @brief Get TCP transitory timeout.
@@ -277,13 +275,6 @@
 u32 nat64_get_tcp_est_timeout (void);
 
 /**
- * @brief Get TCP incoming SYN timeout.
- *
- * @returns TCP incoming SYN timeout in seconds.
- */
-u32 nat64_get_tcp_incoming_syn_timeout (void);
-
-/**
  * @brief Reset NAT64 session timeout.
  *
  * @param ste Session table entry.
diff --git a/src/plugins/nat/nat64_cli.c b/src/plugins/nat/nat64_cli.c
index c2639f4..efeaa0b 100644
--- a/src/plugins/nat/nat64_cli.c
+++ b/src/plugins/nat/nat64_cli.c
@@ -451,107 +451,6 @@
   return error;
 }
 
-static clib_error_t *
-nat64_set_timeouts_command_fn (vlib_main_t * vm, unformat_input_t * input,
-			       vlib_cli_command_t * cmd)
-{
-  unformat_input_t _line_input, *line_input = &_line_input;
-  clib_error_t *error = 0;
-  u32 timeout, tcp_trans, tcp_est, tcp_incoming_syn;
-
-  tcp_trans = nat64_get_tcp_trans_timeout ();
-  tcp_est = nat64_get_tcp_est_timeout ();
-  tcp_incoming_syn = nat64_get_tcp_incoming_syn_timeout ();
-
-  if (!unformat_user (input, unformat_line_input, line_input))
-    return 0;
-
-  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
-    {
-      if (unformat (line_input, "udp %u", &timeout))
-	{
-	  if (nat64_set_udp_timeout (timeout))
-	    {
-	      error = clib_error_return (0, "Invalid UDP timeout value");
-	      goto done;
-	    }
-	}
-      else if (unformat (line_input, "icmp %u", &timeout))
-	{
-	  if (nat64_set_icmp_timeout (timeout))
-	    {
-	      error = clib_error_return (0, "Invalid ICMP timeout value");
-	      goto done;
-	    }
-	}
-      else if (unformat (line_input, "tcp-trans %u", &tcp_trans))
-	{
-	  if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn))
-	    {
-	      error =
-		clib_error_return (0,
-				   "Invalid TCP transitory timeouts value");
-	      goto done;
-	    }
-	}
-      else if (unformat (line_input, "tcp-est %u", &tcp_est))
-	{
-	  if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn))
-	    {
-	      error =
-		clib_error_return (0,
-				   "Invalid TCP established timeouts value");
-	      goto done;
-	    }
-	}
-      else
-	if (unformat (line_input, "tcp-incoming-syn %u", &tcp_incoming_syn))
-	{
-	  if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn))
-	    {
-	      error =
-		clib_error_return (0,
-				   "Invalid TCP incoming SYN timeouts value");
-	      goto done;
-	    }
-	}
-      else if (unformat (line_input, "reset"))
-	{
-	  nat64_set_udp_timeout (0);
-	  nat64_set_icmp_timeout (0);
-	  nat64_set_tcp_timeouts (0, 0, 0);
-	}
-      else
-	{
-	  error = clib_error_return (0, "unknown input '%U'",
-				     format_unformat_error, line_input);
-	  goto done;
-	}
-    }
-
-done:
-  unformat_free (line_input);
-
-  return error;
-}
-
-static clib_error_t *
-nat64_show_timeouts_command_fn (vlib_main_t * vm, unformat_input_t * input,
-				vlib_cli_command_t * cmd)
-{
-  vlib_cli_output (vm, "NAT64 session timeouts:");
-  vlib_cli_output (vm, " UDP %usec", nat64_get_udp_timeout ());
-  vlib_cli_output (vm, " ICMP %usec", nat64_get_icmp_timeout ());
-  vlib_cli_output (vm, " TCP transitory %usec",
-		   nat64_get_tcp_trans_timeout ());
-  vlib_cli_output (vm, " TCP established %usec",
-		   nat64_get_tcp_est_timeout ());
-  vlib_cli_output (vm, " TCP incoming SYN %usec",
-		   nat64_get_tcp_incoming_syn_timeout ());
-
-  return 0;
-}
-
 typedef struct nat64_cli_st_walk_ctx_t_
 {
   vlib_main_t *vm;
@@ -946,42 +845,6 @@
 
 /*?
  * @cliexpar
- * @cliexstart{set nat64 timeouts}
- * Set NAT64 session timeouts (in seconds).
- * To set NAT64 session timeoutes use use:
- *  vpp# set nat64 timeouts udp 200 icmp 30 tcp-trans 250 tcp-est 7450
- * To reset NAT64 session timeoutes to default values use:
- *  vpp# set nat64 timeouts reset
- * @cliexend
-?*/
-VLIB_CLI_COMMAND (set_nat64_timeouts_command, static) = {
-  .path = "set nat64 timeouts",
-  .short_help = "set nat64 timeouts udp <sec> icmp <sec> tcp-trans <sec> "
-                "tcp-est <sec> tcp-incoming-syn <sec> | reset",
-  .function = nat64_set_timeouts_command_fn,
-};
-
-/*?
- * @cliexpar
- * @cliexstart{show nat64 timeoutss}
- * Show NAT64 session timeouts:
- *  vpp# show nat64 timeouts
- *  NAT64 session timeouts:
- *   UDP 300sec
- *   ICMP 60sec
- *   TCP transitory 240sec
- *   TCP established 7440sec
- *   TCP incoming SYN 6sec
- * @cliexend
-?*/
-VLIB_CLI_COMMAND (show_nat64_timeouts_command, static) = {
-  .path = "show nat64 timeouts",
-  .short_help = "show nat64 timeouts",
-  .function = nat64_show_timeouts_command_fn,
-};
-
-/*?
- * @cliexpar
  * @cliexstart{show nat64 session table}
  * Show NAT64 session table.
  * To show NAT64 TCP session table use:
diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c
index 629045f..8055259 100644
--- a/src/plugins/nat/nat_api.c
+++ b/src/plugins/nat/nat_api.c
@@ -416,6 +416,74 @@
   FINISH;
 }
 
+static void
+vl_api_nat_set_timeouts_t_handler (vl_api_nat_set_timeouts_t * mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat_set_timeouts_reply_t *rmp;
+  int rv = 0;
+
+  sm->udp_timeout = ntohl (mp->udp);
+  sm->tcp_established_timeout = ntohl (mp->tcp_established);
+  sm->tcp_transitory_timeout = ntohl (mp->tcp_transitory);
+  sm->icmp_timeout = ntohl (mp->icmp);
+
+  rv = nat64_set_icmp_timeout (ntohl (mp->icmp));
+  if (rv)
+    goto send_reply;
+  rv = nat64_set_udp_timeout (ntohl (mp->udp));
+  if (rv)
+    goto send_reply;
+  rv =
+    nat64_set_tcp_timeouts (ntohl (mp->tcp_transitory),
+			    ntohl (mp->tcp_established));
+
+send_reply:
+  REPLY_MACRO (VL_API_NAT_SET_TIMEOUTS_REPLY);
+}
+
+static void *
+vl_api_nat_set_timeouts_t_print (vl_api_nat_set_timeouts_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat_set_timeouts ");
+  s = format (s, "udp %d tcp_established %d tcp_transitory %d icmp %d\n",
+	      ntohl (mp->udp),
+	      ntohl (mp->tcp_established),
+	      ntohl (mp->tcp_transitory), ntohl (mp->icmp));
+
+  FINISH;
+}
+
+static void
+vl_api_nat_get_timeouts_t_handler (vl_api_nat_get_timeouts_t * mp)
+{
+  snat_main_t *sm = &snat_main;
+  vl_api_nat_get_timeouts_reply_t *rmp;
+  int rv = 0;
+
+  /* *INDENT-OFF* */
+  REPLY_MACRO2 (VL_API_NAT_GET_TIMEOUTS_REPLY,
+  ({
+    rmp->udp = htonl (sm->udp_timeout);
+    rmp->tcp_established = htonl (sm->tcp_established_timeout);
+    rmp->tcp_transitory = htonl (sm->tcp_transitory_timeout);
+    rmp->icmp = htonl (sm->icmp_timeout);
+  }))
+  /* *INDENT-ON* */
+}
+
+static void *
+vl_api_nat_get_timeouts_t_print (vl_api_nat_get_timeouts_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: nat_get_timeouts");
+
+  FINISH;
+}
+
 /*************/
 /*** NAT44 ***/
 /*************/
@@ -1839,79 +1907,6 @@
 }
 
 static void
-vl_api_nat_det_set_timeouts_t_handler (vl_api_nat_det_set_timeouts_t * mp)
-{
-  snat_main_t *sm = &snat_main;
-  vl_api_nat_det_set_timeouts_reply_t *rmp;
-  int rv = 0;
-
-  if (!sm->deterministic)
-    {
-      rv = VNET_API_ERROR_UNSUPPORTED;
-      goto send_reply;
-    }
-
-  sm->udp_timeout = ntohl (mp->udp);
-  sm->tcp_established_timeout = ntohl (mp->tcp_established);
-  sm->tcp_transitory_timeout = ntohl (mp->tcp_transitory);
-  sm->icmp_timeout = ntohl (mp->icmp);
-
-send_reply:
-  REPLY_MACRO (VL_API_NAT_DET_SET_TIMEOUTS_REPLY);
-}
-
-static void *
-vl_api_nat_det_set_timeouts_t_print (vl_api_nat_det_set_timeouts_t * mp,
-				     void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: nat_det_set_timeouts ");
-  s = format (s, "udp %d tcp_established %d tcp_transitory %d icmp %d\n",
-	      ntohl (mp->udp),
-	      ntohl (mp->tcp_established),
-	      ntohl (mp->tcp_transitory), ntohl (mp->icmp));
-
-  FINISH;
-}
-
-static void
-vl_api_nat_det_get_timeouts_t_handler (vl_api_nat_det_get_timeouts_t * mp)
-{
-  snat_main_t *sm = &snat_main;
-  vl_api_nat_det_get_timeouts_reply_t *rmp;
-  int rv = 0;
-
-  if (!sm->deterministic)
-    {
-      rv = VNET_API_ERROR_UNSUPPORTED;
-      REPLY_MACRO (VL_API_NAT_DET_GET_TIMEOUTS_REPLY);
-      return;
-    }
-
-  /* *INDENT-OFF* */
-  REPLY_MACRO2 (VL_API_NAT_DET_GET_TIMEOUTS_REPLY,
-  ({
-    rmp->udp = htonl (sm->udp_timeout);
-    rmp->tcp_established = htonl (sm->tcp_established_timeout);
-    rmp->tcp_transitory = htonl (sm->tcp_transitory_timeout);
-    rmp->icmp = htonl (sm->icmp_timeout);
-  }))
-  /* *INDENT-ON* */
-}
-
-static void *
-vl_api_nat_det_get_timeouts_t_print (vl_api_nat_det_get_timeouts_t * mp,
-				     void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: nat_det_get_timeouts");
-
-  FINISH;
-}
-
-static void
 vl_api_nat_det_close_session_out_t_handler (vl_api_nat_det_close_session_out_t
 					    * mp)
 {
@@ -2396,71 +2391,6 @@
   FINISH;
 }
 
-static void
-vl_api_nat64_set_timeouts_t_handler (vl_api_nat64_set_timeouts_t * mp)
-{
-  snat_main_t *sm = &snat_main;
-  vl_api_nat64_set_timeouts_reply_t *rmp;
-  int rv = 0;
-
-  rv = nat64_set_icmp_timeout (ntohl (mp->icmp));
-  if (rv)
-    goto send_reply;
-  rv = nat64_set_udp_timeout (ntohl (mp->udp));
-  if (rv)
-    goto send_reply;
-  rv =
-    nat64_set_tcp_timeouts (ntohl (mp->tcp_trans), ntohl (mp->tcp_est),
-			    ntohl (mp->tcp_incoming_syn));
-
-send_reply:
-  REPLY_MACRO (VL_API_NAT64_SET_TIMEOUTS_REPLY);
-}
-
-static void *vl_api_nat64_set_timeouts_t_print
-  (vl_api_nat64_set_timeouts_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: nat64_set_timeouts ");
-  s =
-    format (s,
-	    "udp %d icmp %d, tcp_trans %d, tcp_est %d, tcp_incoming_syn %d\n",
-	    ntohl (mp->udp), ntohl (mp->icmp), ntohl (mp->tcp_trans),
-	    ntohl (mp->tcp_est), ntohl (mp->tcp_incoming_syn));
-
-  FINISH;
-}
-
-static void
-vl_api_nat64_get_timeouts_t_handler (vl_api_nat64_get_timeouts_t * mp)
-{
-  snat_main_t *sm = &snat_main;
-  vl_api_nat64_get_timeouts_reply_t *rmp;
-  int rv = 0;
-
-  /* *INDENT-OFF* */
-  REPLY_MACRO2 (VL_API_NAT64_GET_TIMEOUTS_REPLY,
-  ({
-    rmp->udp = htonl (nat64_get_udp_timeout());
-    rmp->icmp = htonl (nat64_get_icmp_timeout());
-    rmp->tcp_trans = htonl (nat64_get_tcp_trans_timeout());
-    rmp->tcp_est = htonl (nat64_get_tcp_est_timeout());
-    rmp->tcp_incoming_syn = htonl (nat64_get_tcp_incoming_syn_timeout());
-  }))
-  /* *INDENT-ON* */
-}
-
-static void *vl_api_nat64_get_timeouts_t_print
-  (vl_api_nat64_get_timeouts_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: nat64_get_timeouts");
-
-  FINISH;
-}
-
 static int
 nat64_api_st_walk (nat64_db_st_entry_t * ste, void *arg)
 {
@@ -3059,6 +2989,8 @@
 _(NAT_SET_REASS, nat_set_reass)                                         \
 _(NAT_GET_REASS, nat_get_reass)                                         \
 _(NAT_REASS_DUMP, nat_reass_dump)                                       \
+_(NAT_SET_TIMEOUTS, nat_set_timeouts)                           \
+_(NAT_GET_TIMEOUTS, nat_get_timeouts)                           \
 _(NAT44_ADD_DEL_ADDRESS_RANGE, nat44_add_del_address_range)             \
 _(NAT44_INTERFACE_ADD_DEL_FEATURE, nat44_interface_add_del_feature)     \
 _(NAT44_ADD_DEL_STATIC_MAPPING, nat44_add_del_static_mapping)           \
@@ -3084,8 +3016,6 @@
 _(NAT_DET_FORWARD, nat_det_forward)                                     \
 _(NAT_DET_REVERSE, nat_det_reverse)                                     \
 _(NAT_DET_MAP_DUMP, nat_det_map_dump)                                   \
-_(NAT_DET_SET_TIMEOUTS, nat_det_set_timeouts)                           \
-_(NAT_DET_GET_TIMEOUTS, nat_det_get_timeouts)                           \
 _(NAT_DET_CLOSE_SESSION_OUT, nat_det_close_session_out)                 \
 _(NAT_DET_CLOSE_SESSION_IN, nat_det_close_session_in)                   \
 _(NAT_DET_SESSION_DUMP, nat_det_session_dump)                           \
@@ -3095,8 +3025,6 @@
 _(NAT64_INTERFACE_DUMP, nat64_interface_dump)                           \
 _(NAT64_ADD_DEL_STATIC_BIB, nat64_add_del_static_bib)                   \
 _(NAT64_BIB_DUMP, nat64_bib_dump)                                       \
-_(NAT64_SET_TIMEOUTS, nat64_set_timeouts)                               \
-_(NAT64_GET_TIMEOUTS, nat64_get_timeouts)                               \
 _(NAT64_ST_DUMP, nat64_st_dump)                                         \
 _(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix)                           \
 _(NAT64_PREFIX_DUMP, nat64_prefix_dump)                                 \
diff --git a/src/plugins/nat/nat_inlines.h b/src/plugins/nat/nat_inlines.h
index a069d66..adfb1d5 100644
--- a/src/plugins/nat/nat_inlines.h
+++ b/src/plugins/nat/nat_inlines.h
@@ -198,6 +198,11 @@
 nat44_set_tcp_session_state_i2o (snat_main_t * sm, snat_session_t * ses,
 				 tcp_header_t * tcp, u32 thread_index)
 {
+  if ((tcp->flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) &&
+      (ses->state & NAT44_SES_O2I_SYN))
+    ses->state = 0;
+  if (tcp->flags & TCP_FLAG_SYN)
+    ses->state |= NAT44_SES_I2O_SYN;
   if (tcp->flags & TCP_FLAG_FIN)
     {
       ses->i2o_fin_seq = clib_net_to_host_u32 (tcp->seq_number);
@@ -223,6 +228,11 @@
 nat44_set_tcp_session_state_o2i (snat_main_t * sm, snat_session_t * ses,
 				 tcp_header_t * tcp, u32 thread_index)
 {
+  if ((tcp->flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) &&
+      (ses->state & NAT44_SES_O2I_SYN))
+    ses->state = 0;
+  if (tcp->flags & TCP_FLAG_SYN)
+    ses->state |= NAT44_SES_O2I_SYN;
   if (tcp->flags & TCP_FLAG_FIN)
     {
       ses->o2i_fin_seq = clib_net_to_host_u32 (tcp->seq_number);
@@ -244,6 +254,29 @@
   return 0;
 }
 
+always_inline u32
+nat44_session_get_timeout (snat_main_t * sm, snat_session_t * s)
+{
+  switch (s->in2out.protocol)
+    {
+    case SNAT_PROTOCOL_ICMP:
+      return sm->icmp_timeout;
+    case SNAT_PROTOCOL_UDP:
+      return sm->udp_timeout;
+    case SNAT_PROTOCOL_TCP:
+      {
+	if (s->state)
+	  return sm->tcp_transitory_timeout;
+	else
+	  return sm->tcp_established_timeout;
+      }
+    default:
+      return sm->udp_timeout;
+    }
+
+  return 0;
+}
+
 always_inline void
 nat44_session_update_counters (snat_session_t * s, f64 now, uword bytes)
 {
diff --git a/src/plugins/nat/nat_ipfix_logging.c b/src/plugins/nat/nat_ipfix_logging.c
index 4a61fb0..d863d0c 100644
--- a/src/plugins/nat/nat_ipfix_logging.c
+++ b/src/plugins/nat/nat_ipfix_logging.c
@@ -2027,6 +2027,19 @@
 	  clib_warning ("vnet_flow_report_add_del returned %d", rv);
 	  return -1;
 	}
+
+      if (sm->endpoint_dependent)
+        {
+          a.rewrite_callback = snat_template_rewrite_max_entries_per_usr;
+          a.flow_data_callback = snat_data_callback_max_entries_per_usr;
+
+          rv = vnet_flow_report_add_del (frm, &a, NULL);
+          if (rv)
+            {
+              clib_warning ("vnet_flow_report_add_del returned %d", rv);
+              return -1;
+            }
+        }
     }
 
   return 0;
diff --git a/src/plugins/nat/nat_test.c b/src/plugins/nat/nat_test.c
index c5a96d3..0a2896b 100644
--- a/src/plugins/nat/nat_test.c
+++ b/src/plugins/nat/nat_test.c
@@ -69,7 +69,7 @@
 _(nat44_add_del_interface_addr_reply)            \
 _(nat_ipfix_enable_disable_reply)                \
 _(nat_det_add_del_map_reply)                     \
-_(nat_det_set_timeouts_reply)                    \
+_(nat_set_timeouts_reply)                        \
 _(nat_det_close_session_out_reply)               \
 _(nat_det_close_session_in_reply)
 
@@ -118,8 +118,8 @@
 _(NAT_DET_FORWARD_REPLY, nat_det_forward_reply)                 \
 _(NAT_DET_REVERSE_REPLY, nat_det_reverse_reply)                 \
 _(NAT_DET_MAP_DETAILS, nat_det_map_details)                     \
-_(NAT_DET_SET_TIMEOUTS_REPLY, nat_det_set_timeouts_reply)       \
-_(NAT_DET_GET_TIMEOUTS_REPLY, nat_det_get_timeouts_reply)       \
+_(NAT_SET_TIMEOUTS_REPLY, nat_set_timeouts_reply)               \
+_(NAT_GET_TIMEOUTS_REPLY, nat_get_timeouts_reply)               \
 _(NAT_DET_CLOSE_SESSION_OUT_REPLY,                              \
   nat_det_close_session_out_reply)                              \
 _(NAT_DET_CLOSE_SESSION_IN_REPLY,                               \
@@ -904,10 +904,10 @@
   return ret;
 }
 
-static int api_nat_det_set_timeouts (vat_main_t * vam)
+static int api_nat_set_timeouts (vat_main_t * vam)
 {
   unformat_input_t * i = vam->input;
-  vl_api_nat_det_set_timeouts_t * mp;
+  vl_api_nat_set_timeouts_t * mp;
   u32 udp = SNAT_UDP_TIMEOUT;
   u32 tcp_established = SNAT_TCP_ESTABLISHED_TIMEOUT;
   u32 tcp_transitory = SNAT_TCP_TRANSITORY_TIMEOUT;
@@ -928,7 +928,7 @@
       return -99;
     }
 
-  M(NAT_DET_SET_TIMEOUTS, mp);
+  M(NAT_SET_TIMEOUTS, mp);
   mp->udp = htonl(udp);
   mp->tcp_established = htonl(tcp_established);
   mp->tcp_transitory = htonl(tcp_transitory);
@@ -939,8 +939,8 @@
   return ret;
 }
 
-static void vl_api_nat_det_get_timeouts_reply_t_handler
-  (vl_api_nat_det_get_timeouts_reply_t *mp)
+static void vl_api_nat_get_timeouts_reply_t_handler
+  (vl_api_nat_get_timeouts_reply_t *mp)
 {
   snat_test_main_t * sm = &snat_test_main;
   vat_main_t *vam = sm->vat_main;
@@ -959,18 +959,18 @@
   vam->result_ready = 1;
 }
 
-static int api_nat_det_get_timeouts(vat_main_t * vam)
+static int api_nat_get_timeouts(vat_main_t * vam)
 {
-  vl_api_nat_det_get_timeouts_t * mp;
+  vl_api_nat_get_timeouts_t * mp;
   int ret;
 
   if (vam->json_output)
     {
-      clib_warning ("JSON output not supported for nat_show_config");
+      clib_warning ("JSON output not supported for nat_get_timeouts");
       return -99;
     }
 
-  M(NAT_DET_GET_TIMEOUTS, mp);
+  M(NAT_GET_TIMEOUTS, mp);
   S(mp);
   W (ret);
   return ret;
@@ -1110,9 +1110,9 @@
 _(nat_det_forward, "<in_addr>")                                   \
 _(nat_det_reverse, "<out_addr> <out_port>")                       \
 _(nat_det_map_dump, "")                                           \
-_(nat_det_set_timeouts, "[udp <sec> | tcp_established <sec> | "   \
+_(nat_set_timeouts, "[udp <sec> | tcp_established <sec> | "       \
   "tcp_transitory <sec> | icmp <sec>]")                           \
-_(nat_det_get_timeouts, "")                                       \
+_(nat_get_timeouts, "")                                           \
 _(nat_det_close_session_out, "<out_addr>:<out_port> "             \
   "<ext_addr>:<ext_port>")                                        \
 _(nat_det_close_session_in, "<in_addr>:<in_port> "                \
diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c
index d5fad80..46a8a1e 100755
--- a/src/plugins/nat/out2in.c
+++ b/src/plugins/nat/out2in.c
@@ -140,6 +140,43 @@
   SNAT_OUT2IN_N_NEXT,
 } snat_out2in_next_t;
 
+int
+nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void * arg)
+{
+  snat_main_t *sm = &snat_main;
+  nat44_is_idle_session_ctx_t *ctx = arg;
+  snat_session_t *s;
+  u64 sess_timeout_time;
+  snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
+                                                       ctx->thread_index);
+  clib_bihash_kv_8_8_t s_kv;
+
+  s = pool_elt_at_index (tsm->sessions, kv->value);
+  sess_timeout_time = s->last_heard + (f64)nat44_session_get_timeout(sm, s);
+  if (ctx->now >= sess_timeout_time)
+    {
+      s_kv.key = s->in2out.as_u64;
+      if (clib_bihash_add_del_8_8 (&tsm->in2out, &s_kv, 0))
+        nat_log_warn ("out2in key del failed");
+
+      snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
+                                          s->out2in.addr.as_u32,
+                                          s->in2out.protocol,
+                                          s->in2out.port,
+                                          s->out2in.port,
+                                          s->in2out.fib_index);
+
+      if (!snat_is_session_static (s))
+        snat_free_outside_address_and_port (sm->addresses, ctx->thread_index,
+                                            &s->out2in);
+
+      nat44_delete_session (sm, s, ctx->thread_index);
+      return 1;
+    }
+
+  return 0;
+}
+
 /**
  * @brief Create session for static mapping.
  *
@@ -160,13 +197,15 @@
                                    snat_session_key_t in2out,
                                    snat_session_key_t out2in,
                                    vlib_node_runtime_t * node,
-                                   u32 thread_index)
+                                   u32 thread_index,
+                                   f64 now)
 {
   snat_user_t *u;
   snat_session_t *s;
   clib_bihash_kv_8_8_t kv0;
   ip4_header_t *ip0;
   udp_header_t *udp0;
+  nat44_is_idle_session_ctx_t ctx0;
 
   if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index)))
     {
@@ -203,16 +242,20 @@
   s->in2out.protocol = out2in.protocol;
 
   /* Add to translation hashes */
+  ctx0.now = now;
+  ctx0.thread_index = thread_index;
   kv0.key = s->in2out.as_u64;
   kv0.value = s - sm->per_thread_data[thread_index].sessions;
-  if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].in2out, &kv0,
-                               1 /* is_add */))
+  if (clib_bihash_add_or_overwrite_stale_8_8 (
+       &sm->per_thread_data[thread_index].in2out, &kv0,
+       nat44_i2o_is_idle_session_cb, &ctx0))
       nat_log_notice ("in2out key add failed");
 
   kv0.key = s->out2in.as_u64;
 
-  if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0,
-                               1 /* is_add */))
+  if (clib_bihash_add_or_overwrite_stale_8_8 (
+        &sm->per_thread_data[thread_index].out2in, &kv0,
+        nat44_o2i_is_idle_session_cb, &ctx0))
       nat_log_notice ("out2in key add failed");
 
   /* log NAT event */
@@ -222,7 +265,7 @@
                                       s->in2out.port,
                                       s->out2in.port,
                                       s->in2out.fib_index);
-   return s;
+  return s;
 }
 
 static_always_inline
@@ -356,7 +399,8 @@
 
       /* Create session initiated by host from external network */
       s0 = create_session_for_static_mapping(sm, b0, sm0, key0,
-                                             node, thread_index);
+                                             node, thread_index,
+                                             vlib_time_now (sm->vlib_main));
 
       if (!s0)
         {
@@ -801,7 +845,7 @@
 
               /* Create session initiated by host from external network */
               s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node,
-                                                     thread_index);
+                                                     thread_index, now);
               if (!s0)
                 {
                   next0 = SNAT_OUT2IN_NEXT_DROP;
@@ -952,7 +996,7 @@
 
               /* Create session initiated by host from external network */
               s1 = create_session_for_static_mapping(sm, b1, sm1, key1, node,
-                                                     thread_index);
+                                                     thread_index, now);
               if (!s1)
                 {
                   next1 = SNAT_OUT2IN_NEXT_DROP;
@@ -1139,7 +1183,7 @@
 
               /* Create session initiated by host from external network */
               s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node,
-                                                     thread_index);
+                                                     thread_index, now);
               if (!s0)
                 {
                   next0 = SNAT_OUT2IN_NEXT_DROP;
@@ -1364,7 +1408,7 @@
 
                   /* Create session initiated by host from external network */
                   s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node,
-                                                         thread_index);
+                                                         thread_index, now);
                   if (!s0)
                     {
                       b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
@@ -1564,6 +1608,109 @@
   return s;
 }
 
+static inline u32
+icmp_out2in_ed_slow_path (snat_main_t * sm, vlib_buffer_t * b0,
+                          ip4_header_t * ip0, icmp46_header_t * icmp0,
+                          u32 sw_if_index0, u32 rx_fib_index0,
+                          vlib_node_runtime_t * node, u32 next0, f64 now,
+                          u32 thread_index, snat_session_t ** p_s0)
+{
+  next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
+                      next0, thread_index, p_s0, 0);
+  snat_session_t * s0 = *p_s0;
+  if (PREDICT_TRUE(next0 != SNAT_OUT2IN_NEXT_DROP && s0))
+    {
+      /* Accounting */
+      nat44_session_update_counters (s0, now,
+                                     vlib_buffer_length_in_chain (sm->vlib_main, b0));
+    }
+  return next0;
+}
+
+int
+nat44_o2i_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void * arg)
+{
+  snat_main_t *sm = &snat_main;
+  nat44_is_idle_session_ctx_t *ctx = arg;
+  snat_session_t *s;
+  u64 sess_timeout_time;
+  nat_ed_ses_key_t ed_key;
+  clib_bihash_kv_16_8_t ed_kv;
+  int i;
+  snat_address_t *a;
+  snat_session_key_t key;
+  snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
+                                                       ctx->thread_index);
+
+  s = pool_elt_at_index (tsm->sessions, kv->value);
+  sess_timeout_time = s->last_heard + (f64)nat44_session_get_timeout(sm, s);
+  if (ctx->now >= sess_timeout_time)
+    {
+      ed_key.l_addr = s->in2out.addr;
+      ed_key.r_addr = s->ext_host_addr;
+      ed_key.fib_index = s->out2in.fib_index;
+      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;
+        }
+      if (is_twice_nat_session (s))
+        {
+          ed_key.r_addr = s->ext_host_nat_addr;
+          ed_key.r_port = s->ext_host_nat_port;
+        }
+      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");
+
+      if (snat_is_unk_proto_session (s))
+        goto delete;
+
+      snat_ipfix_logging_nat44_ses_delete(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_twice_nat_session (s))
+        {
+          for (i = 0; i < vec_len (sm->twice_nat_addresses); i++)
+            {
+              key.protocol = s->in2out.protocol;
+              key.port = s->ext_host_nat_port;
+              a = sm->twice_nat_addresses + i;
+              if (a->addr.as_u32 == s->ext_host_nat_addr.as_u32)
+                {
+                  snat_free_outside_address_and_port (sm->twice_nat_addresses,
+                                                      ctx->thread_index, &key);
+                  break;
+                }
+            }
+        }
+
+      if (snat_is_session_static (s))
+        goto delete;
+
+      if (s->outside_address_index != ~0)
+        snat_free_outside_address_and_port (sm->addresses, ctx->thread_index,
+                                            &s->out2in);
+    delete:
+      nat44_delete_session (sm, s, ctx->thread_index);
+      return 1;
+    }
+
+  return 0;
+}
+
 static snat_session_t *
 create_session_for_static_mapping_ed (snat_main_t * sm,
                                       vlib_buffer_t *b,
@@ -1572,7 +1719,8 @@
                                       vlib_node_runtime_t * node,
                                       u32 thread_index,
                                       twice_nat_type_t twice_nat,
-                                      u8 is_lb)
+                                      u8 is_lb,
+                                      f64 now)
 {
   snat_session_t *s;
   snat_user_t *u;
@@ -1582,6 +1730,7 @@
   clib_bihash_kv_16_8_t kv;
   snat_session_key_t eh_key;
   u32 address_index;
+  nat44_is_idle_session_ctx_t ctx;
 
   if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index)))
     {
@@ -1597,7 +1746,7 @@
       return 0;
     }
 
-  s = nat_session_alloc_or_recycle (sm, u, thread_index);
+  s = nat_ed_session_alloc (sm, u, thread_index);
   if (!s)
     {
       nat44_delete_user_with_no_session (sm, u, thread_index);
@@ -1624,7 +1773,11 @@
   make_ed_kv (&kv, &e_key.addr, &s->ext_host_addr, ip->protocol,
               e_key.fib_index, e_key.port, s->ext_host_port);
   kv.value = s - tsm->sessions;
-  if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 1))
+  ctx.now = now;
+  ctx.thread_index = thread_index;
+  if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->out2in_ed, &kv,
+                                               nat44_o2i_ed_is_idle_session_cb,
+                                               &ctx))
     nat_log_notice ("out2in-ed key add failed");
 
   if (twice_nat == TWICE_NAT || (twice_nat == TWICE_NAT_SELF &&
@@ -1655,7 +1808,9 @@
                   l_key.fib_index, l_key.port, s->ext_host_port);
     }
   kv.value = s - tsm->sessions;
-  if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &kv, 1))
+  if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->in2out_ed, &kv,
+                                               nat44_i2o_ed_is_idle_session_cb,
+                                               &ctx))
     nat_log_notice ("in2out-ed key add failed");
 
   return s;
@@ -1779,7 +1934,7 @@
           return;
         }
 
-      s = nat_session_alloc_or_recycle (sm, u, thread_index);
+      s = nat_ed_session_alloc (sm, u, thread_index);
       if (!s)
         {
           nat44_delete_user_with_no_session (sm, u, thread_index);
@@ -1810,8 +1965,6 @@
         return;
     }
 
-  /* Per-user LRU list maintenance */
-  nat44_session_update_lru (sm, s, thread_index);
   /* Accounting */
   nat44_session_update_counters (s, now, 0);
 }
@@ -1891,7 +2044,8 @@
 
       /* Create session initiated by host from external network */
       s = create_session_for_static_mapping_ed(sm, b, l_key, e_key, node,
-                                               thread_index, 0, 0);
+                                               thread_index, 0, 0,
+                                               vlib_time_now (sm->vlib_main));
 
       if (!s)
         {
@@ -1981,7 +2135,7 @@
         }
 
       /* Create a new session */
-      s = nat_session_alloc_or_recycle (sm, u, thread_index);
+      s = nat_ed_session_alloc (sm, u, thread_index);
       if (!s)
         {
           nat44_delete_user_with_no_session (sm, u, thread_index);
@@ -2023,8 +2177,6 @@
   /* Accounting */
   nat44_session_update_counters (s, now,
                                  vlib_buffer_length_in_chain (vm, b));
-  /* Per-user LRU list maintenance */
-  nat44_session_update_lru (sm, s, thread_index);
 
   return s;
 }
@@ -2136,7 +2288,7 @@
 
               if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
                 {
-                  next0 = icmp_out2in_slow_path
+                  next0 = icmp_out2in_ed_slow_path
                     (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
                      next0, now, thread_index, &s0);
                   goto trace00;
@@ -2210,7 +2362,8 @@
                   s0 = create_session_for_static_mapping_ed(sm, b0, l_key0,
                                                             e_key0, node,
                                                             thread_index,
-                                                            twice_nat0, is_lb0);
+                                                            twice_nat0, is_lb0,
+                                                            now);
 
                   if (!s0)
                     {
@@ -2281,8 +2434,6 @@
           /* Accounting */
           nat44_session_update_counters (s0, now,
                                          vlib_buffer_length_in_chain (vm, b0));
-          /* Per-user LRU list maintenance */
-          nat44_session_update_lru (sm, s0, thread_index);
 
         trace00:
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -2339,7 +2490,7 @@
 
               if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP))
                 {
-                  next1 = icmp_out2in_slow_path
+                  next1 = icmp_out2in_ed_slow_path
                     (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node,
                      next1, now, thread_index, &s1);
                   goto trace01;
@@ -2413,7 +2564,8 @@
                   s1 = create_session_for_static_mapping_ed(sm, b1, l_key1,
                                                             e_key1, node,
                                                             thread_index,
-                                                            twice_nat1, is_lb1);
+                                                            twice_nat1, is_lb1,
+                                                            now);
 
                   if (!s1)
                     {
@@ -2484,8 +2636,6 @@
           /* Accounting */
           nat44_session_update_counters (s1, now,
                                          vlib_buffer_length_in_chain (vm, b1));
-          /* Per-user LRU list maintenance */
-          nat44_session_update_lru (sm, s1, thread_index);
 
         trace01:
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -2574,7 +2724,7 @@
 
               if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
                 {
-                  next0 = icmp_out2in_slow_path
+                  next0 = icmp_out2in_ed_slow_path
                     (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
                      next0, now, thread_index, &s0);
                   goto trace0;
@@ -2648,7 +2798,8 @@
                   s0 = create_session_for_static_mapping_ed(sm, b0, l_key0,
                                                             e_key0, node,
                                                             thread_index,
-                                                            twice_nat0, is_lb0);
+                                                            twice_nat0, is_lb0,
+                                                            now);
 
                   if (!s0)
                     {
@@ -2719,8 +2870,6 @@
           /* Accounting */
           nat44_session_update_counters (s0, now,
                                          vlib_buffer_length_in_chain (vm, b0));
-          /* Per-user LRU list maintenance */
-          nat44_session_update_lru (sm, s0, thread_index);
 
         trace0:
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
diff --git a/test/test_nat.py b/test/test_nat.py
index 286c74a..79d2622 100644
--- a/test/test_nat.py
+++ b/test/test_nat.py
@@ -137,6 +137,7 @@
         self.vapi.nat_set_reass()
         self.vapi.nat_set_reass(is_ip6=1)
         self.verify_no_nat44_user()
+        self.vapi.nat_set_timeouts()
 
     def nat44_add_static_mapping(self, local_ip, external_ip='0.0.0.0',
                                  local_port=0, external_port=0, vrf_id=0,
@@ -993,6 +994,25 @@
         users = self.vapi.nat44_user_dump()
         self.assertEqual(len(users), 0)
 
+    def verify_ipfix_max_entries_per_user(self, data, limit, src_addr):
+        """
+        Verify IPFIX maximum entries per user exceeded event
+
+        :param data: Decoded IPFIX data records
+        :param limit: Number of maximum entries per user
+        :param src_addr: IPv4 source address
+        """
+        self.assertEqual(1, len(data))
+        record = data[0]
+        # natEvent
+        self.assertEqual(ord(record[230]), 13)
+        # natQuotaExceededEvent
+        self.assertEqual(struct.pack("I", 3), record[466])
+        # maxEntriesPerUser
+        self.assertEqual(struct.pack("I", limit), record[473])
+        # sourceIPv4Address
+        self.assertEqual(src_addr, record[8])
+
 
 class TestNAT44(MethodHolder):
     """ NAT44 Test Cases """
@@ -3217,6 +3237,48 @@
             self.pg1.resolve_arp()
             self.pg2.resolve_arp()
 
+    @unittest.skipUnless(running_extended_tests(), "part of extended tests")
+    def test_session_timeout(self):
+        """ NAT44 session timeouts """
+        self.nat44_add_address(self.nat_addr)
+        self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index)
+        self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index,
+                                                  is_inside=0)
+        self.vapi.nat_set_timeouts(udp=5)
+
+        max_sessions = 1000
+        pkts = []
+        for i in range(0, max_sessions):
+            src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF)
+            p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+                 IP(src=src, dst=self.pg1.remote_ip4) /
+                 UDP(sport=1025, dport=53))
+            pkts.append(p)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.get_capture(max_sessions)
+
+        sleep(6)
+
+        pkts = []
+        for i in range(0, max_sessions):
+            src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF)
+            p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+                 IP(src=src, dst=self.pg1.remote_ip4) /
+                 UDP(sport=1026, dport=53))
+            pkts.append(p)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.get_capture(max_sessions)
+
+        nsessions = 0
+        users = self.vapi.nat44_user_dump()
+        for user in users:
+            nsessions = nsessions + user.nsessions
+        self.assertLess(nsessions, 2 * max_sessions)
+
     def tearDown(self):
         super(TestNAT44, self).tearDown()
         if not self.vpp_dead:
@@ -3227,6 +3289,7 @@
             self.logger.info(self.vapi.cli("show nat44 sessions detail"))
             self.logger.info(self.vapi.cli("show nat virtual-reassembly"))
             self.logger.info(self.vapi.cli("show nat44 hash tables detail"))
+            self.logger.info(self.vapi.cli("show nat timeouts"))
             self.vapi.cli("nat addr-port-assignment-alg default")
             self.clear_nat44()
             self.vapi.cli("clear logging")
@@ -4909,6 +4972,105 @@
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
 
+    @unittest.skipUnless(running_extended_tests(), "part of extended tests")
+    def test_session_timeout(self):
+        """ NAT44 session timeouts """
+        self.nat44_add_address(self.nat_addr)
+        self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index)
+        self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index,
+                                                  is_inside=0)
+        self.vapi.nat_set_timeouts(icmp=5)
+
+        max_sessions = 1000
+        pkts = []
+        for i in range(0, max_sessions):
+            src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF)
+            p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+                 IP(src=src, dst=self.pg1.remote_ip4) /
+                 ICMP(id=1025, type='echo-request'))
+            pkts.append(p)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.get_capture(max_sessions)
+
+        sleep(10)
+
+        pkts = []
+        for i in range(0, max_sessions):
+            src = "10.10.%u.%u" % ((i & 0xFF00) >> 8, i & 0xFF)
+            p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+                 IP(src=src, dst=self.pg1.remote_ip4) /
+                 ICMP(id=1026, type='echo-request'))
+            pkts.append(p)
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.get_capture(max_sessions)
+
+        nsessions = 0
+        users = self.vapi.nat44_user_dump()
+        for user in users:
+            nsessions = nsessions + user.nsessions
+        self.assertLess(nsessions, 2 * max_sessions)
+
+    @unittest.skipUnless(running_extended_tests(), "part of extended tests")
+    def test_session_limit_per_user(self):
+        """ Maximum sessions per user limit """
+        self.nat44_add_address(self.nat_addr)
+        self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index)
+        self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index,
+                                                  is_inside=0)
+        self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4n,
+                                     src_address=self.pg2.local_ip4n,
+                                     path_mtu=512,
+                                     template_interval=10)
+
+        # get maximum number of translations per user
+        nat44_config = self.vapi.nat_show_config()
+
+        pkts = []
+        for port in range(0, nat44_config.max_translations_per_user):
+            p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+                 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+                 UDP(sport=1025 + port, dport=1025 + port))
+            pkts.append(p)
+
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(len(pkts))
+
+        self.vapi.nat_ipfix(domain_id=self.ipfix_domain_id,
+                            src_port=self.ipfix_src_port)
+
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+             UDP(sport=3001, dport=3002))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.assert_nothing_captured()
+
+        # verify IPFIX logging
+        self.vapi.cli("ipfix flush")  # FIXME this should be an API call
+        sleep(1)
+        capture = self.pg2.get_capture(10)
+        ipfix = IPFIXDecoder()
+        # first load template
+        for p in capture:
+            self.assertTrue(p.haslayer(IPFIX))
+            if p.haslayer(Template):
+                ipfix.add_template(p.getlayer(Template))
+        # verify events in data set
+        for p in capture:
+            if p.haslayer(Data):
+                data = ipfix.decode_data_set(p.getlayer(Set))
+                self.verify_ipfix_max_entries_per_user(
+                    data,
+                    nat44_config.max_translations_per_user,
+                    self.pg0.remote_ip4n)
+
     def tearDown(self):
         super(TestNAT44EndpointDependent, self).tearDown()
         if not self.vpp_dead:
@@ -4918,6 +5080,7 @@
             self.logger.info(self.vapi.cli("show nat44 interface address"))
             self.logger.info(self.vapi.cli("show nat44 sessions detail"))
             self.logger.info(self.vapi.cli("show nat44 hash tables detail"))
+            self.logger.info(self.vapi.cli("show nat timeouts"))
             self.clear_nat44()
             self.vapi.cli("clear logging")
 
@@ -5165,23 +5328,6 @@
                                       "(outside network):", packet))
                 raise
 
-    def verify_ipfix_max_entries_per_user(self, data):
-        """
-        Verify IPFIX maximum entries per user exceeded event
-
-        :param data: Decoded IPFIX data records
-        """
-        self.assertEqual(1, len(data))
-        record = data[0]
-        # natEvent
-        self.assertEqual(ord(record[230]), 13)
-        # natQuotaExceededEvent
-        self.assertEqual('\x03\x00\x00\x00', record[466])
-        # maxEntriesPerUser
-        self.assertEqual('\xe8\x03\x00\x00', record[473])
-        # sourceIPv4Address
-        self.assertEqual(self.pg0.remote_ip4n, record[8])
-
     def test_deterministic_mode(self):
         """ NAT plugin run deterministic mode """
         in_addr = '172.16.255.0'
@@ -5217,14 +5363,14 @@
 
     def test_set_timeouts(self):
         """ Set deterministic NAT timeouts """
-        timeouts_before = self.vapi.nat_det_get_timeouts()
+        timeouts_before = self.vapi.nat_get_timeouts()
 
-        self.vapi.nat_det_set_timeouts(timeouts_before.udp + 10,
-                                       timeouts_before.tcp_established + 10,
-                                       timeouts_before.tcp_transitory + 10,
-                                       timeouts_before.icmp + 10)
+        self.vapi.nat_set_timeouts(timeouts_before.udp + 10,
+                                   timeouts_before.tcp_established + 10,
+                                   timeouts_before.tcp_transitory + 10,
+                                   timeouts_before.icmp + 10)
 
-        timeouts_after = self.vapi.nat_det_get_timeouts()
+        timeouts_after = self.vapi.nat_get_timeouts()
 
         self.assertNotEqual(timeouts_before.udp, timeouts_after.udp)
         self.assertNotEqual(timeouts_before.icmp, timeouts_after.icmp)
@@ -5539,7 +5685,7 @@
                                                   is_inside=0)
 
         self.initiate_tcp_session(self.pg0, self.pg1)
-        self.vapi.nat_det_set_timeouts(5, 5, 5, 5)
+        self.vapi.nat_set_timeouts(5, 5, 5, 5)
         pkts = self.create_stream_in(self.pg0, self.pg1)
         self.pg0.add_stream(pkts)
         self.pg_enable_capture(self.pg_interfaces)
@@ -5616,14 +5762,16 @@
         for p in capture:
             if p.haslayer(Data):
                 data = ipfix.decode_data_set(p.getlayer(Set))
-                self.verify_ipfix_max_entries_per_user(data)
+                self.verify_ipfix_max_entries_per_user(data,
+                                                       1000,
+                                                       self.pg0.remote_ip4n)
 
     def clear_nat_det(self):
         """
         Clear deterministic NAT configuration.
         """
         self.vapi.nat_ipfix(enable=0)
-        self.vapi.nat_det_set_timeouts()
+        self.vapi.nat_set_timeouts()
         deterministic_mappings = self.vapi.nat_det_map_dump()
         for dsm in deterministic_mappings:
             self.vapi.nat_det_add_del_map(dsm.in_addr,
@@ -5642,11 +5790,10 @@
         super(TestDeterministicNAT, self).tearDown()
         if not self.vpp_dead:
             self.logger.info(self.vapi.cli("show nat44 interfaces"))
+            self.logger.info(self.vapi.cli("show nat timeouts"))
             self.logger.info(
                 self.vapi.cli("show nat44 deterministic mappings"))
             self.logger.info(
-                self.vapi.cli("show nat44 deterministic timeouts"))
-            self.logger.info(
                 self.vapi.cli("show nat44 deterministic sessions"))
             self.clear_nat_det()
 
@@ -5854,22 +6001,20 @@
     def test_set_timeouts(self):
         """ Set NAT64 timeouts """
         # verify default values
-        timeouts = self.vapi.nat64_get_timeouts()
+        timeouts = self.vapi.nat_get_timeouts()
         self.assertEqual(timeouts.udp, 300)
         self.assertEqual(timeouts.icmp, 60)
-        self.assertEqual(timeouts.tcp_trans, 240)
-        self.assertEqual(timeouts.tcp_est, 7440)
-        self.assertEqual(timeouts.tcp_incoming_syn, 6)
+        self.assertEqual(timeouts.tcp_transitory, 240)
+        self.assertEqual(timeouts.tcp_established, 7440)
 
         # set and verify custom values
-        self.vapi.nat64_set_timeouts(udp=200, icmp=30, tcp_trans=250,
-                                     tcp_est=7450, tcp_incoming_syn=10)
-        timeouts = self.vapi.nat64_get_timeouts()
+        self.vapi.nat_set_timeouts(udp=200, icmp=30, tcp_transitory=250,
+                                   tcp_established=7450)
+        timeouts = self.vapi.nat_get_timeouts()
         self.assertEqual(timeouts.udp, 200)
         self.assertEqual(timeouts.icmp, 30)
-        self.assertEqual(timeouts.tcp_trans, 250)
-        self.assertEqual(timeouts.tcp_est, 7450)
-        self.assertEqual(timeouts.tcp_incoming_syn, 10)
+        self.assertEqual(timeouts.tcp_transitory, 250)
+        self.assertEqual(timeouts.tcp_established, 7450)
 
     def test_dynamic(self):
         """ NAT64 dynamic translation test """
@@ -6006,7 +6151,7 @@
                                                 self.nat_addr_n)
         self.vapi.nat64_add_del_interface(self.pg0.sw_if_index)
         self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0)
-        self.vapi.nat64_set_timeouts(icmp=5, tcp_trans=5, tcp_est=5)
+        self.vapi.nat_set_timeouts(icmp=5, tcp_transitory=5, tcp_established=5)
 
         pkts = self.create_stream_in_ip6(self.pg0, self.pg1)
         self.pg0.add_stream(pkts)
@@ -6892,7 +7037,7 @@
         self.ipfix_src_port = 4739
         self.ipfix_domain_id = 1
 
-        self.vapi.nat64_set_timeouts()
+        self.vapi.nat_set_timeouts()
 
         interfaces = self.vapi.nat64_interface_dump()
         for intf in interfaces:
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 17c7e9c..c8ca93c 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -1766,13 +1766,13 @@
         """
         return self.api(self.papi.nat_det_map_dump, {})
 
-    def nat_det_set_timeouts(
+    def nat_set_timeouts(
             self,
             udp=300,
             tcp_established=7440,
             tcp_transitory=240,
             icmp=60):
-        """Set values of timeouts for deterministic NAT (in seconds)
+        """Set values of timeouts for NAT sessions (in seconds)
 
         :param udp - UDP timeout (Default value = 300)
         :param tcp_established - TCP established timeout (Default value = 7440)
@@ -1780,18 +1780,18 @@
         :param icmp - ICMP timeout (Default value = 60)
         """
         return self.api(
-            self.papi.nat_det_set_timeouts,
+            self.papi.nat_set_timeouts,
             {'udp': udp,
              'tcp_established': tcp_established,
              'tcp_transitory': tcp_transitory,
              'icmp': icmp})
 
-    def nat_det_get_timeouts(self):
-        """Get values of timeouts for deterministic NAT
+    def nat_get_timeouts(self):
+        """Get values of timeouts for NAT sessions
 
-        :return: Timeouts for deterministic NAT (in seconds)
+        :return: Timeouts for NAT sessions (in seconds)
         """
-        return self.api(self.papi.nat_det_get_timeouts, {})
+        return self.api(self.papi.nat_get_timeouts, {})
 
     def nat_det_close_session_out(
             self,