nat: transitory timeout for TCP CLOSED state 62/25762/12
authorKlement Sekera <ksekera@cisco.com>
Tue, 10 Mar 2020 11:32:54 +0000 (12:32 +0100)
committerOle Trøan <otroan@employees.org>
Thu, 26 Mar 2020 14:56:09 +0000 (14:56 +0000)
Wait transitory timeout seconds before moving internal state of TCP
session to CLOSED state per RFC 7857. This patch implements this
functionality for endpoint-dependent NAT.

Type: improvement
Signed-off-by: Klement Sekera <ksekera@cisco.com>
Change-Id: I4491d831cd9edf63fae520a516cdbe590bac85db

src/plugins/nat/in2out_ed.c
src/plugins/nat/nat.c
src/plugins/nat/nat.h
src/plugins/nat/nat44/inlines.h
src/plugins/nat/nat44_cli.c
src/plugins/nat/nat_det_in2out.c
src/plugins/nat/nat_inlines.h
src/plugins/nat/out2in_ed.c
src/plugins/nat/test/test_nat.py

index 4e7432d..4be7637 100644 (file)
@@ -455,7 +455,8 @@ nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip,
        {
          if (ip->protocol == IP_PROTOCOL_TCP)
            {
-             if (nat44_set_tcp_session_state_i2o (sm, s, b, thread_index))
+             if (nat44_set_tcp_session_state_i2o
+                 (sm, now, s, b, thread_index))
                return 1;
            }
          /* Accounting */
@@ -850,6 +851,7 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm,
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   u32 tcp_packets = 0, udp_packets = 0, icmp_packets = 0, other_packets =
     0, def_slow;
+  u32 tcp_closed_drops = 0;
 
   def_slow = is_output_feature ? NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH :
     NAT_NEXT_IN2OUT_ED_SLOW_PATH;
@@ -953,6 +955,23 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm,
            }
          s0 = pool_elt_at_index (tsm->sessions, value0.value);
 
+         if (s0->tcp_close_timestamp)
+           {
+             if (now >= s0->tcp_close_timestamp)
+               {
+                 // session is closed, go slow path
+                 next0 = def_slow;
+               }
+             else
+               {
+                 // session in transitory timeout, drop
+                 ++tcp_closed_drops;
+                 b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TCP_CLOSED];
+                 next0 = NAT_NEXT_DROP;
+               }
+             goto trace0;
+           }
+
          // drop if session expired
          u64 sess_timeout_time;
          sess_timeout_time = s0->last_heard +
@@ -967,7 +986,6 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm,
              next0 = NAT_NEXT_DROP;
              goto trace0;
            }
-         //
 
          b0->flags |= VNET_BUFFER_F_IS_NATED;
 
@@ -1017,7 +1035,8 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm,
                  tcp0->checksum = ip_csum_fold (sum0);
                }
              tcp_packets++;
-             if (nat44_set_tcp_session_state_i2o (sm, s0, b0, thread_index))
+             if (nat44_set_tcp_session_state_i2o
+                 (sm, now, s0, b0, thread_index))
                goto trace0;
            }
          else if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment
@@ -1216,9 +1235,20 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t * vm,
                      vnet_buffer (b0)->ip.reass.l4_src_port,
                      vnet_buffer (b0)->ip.reass.l4_dst_port);
 
-         if (clib_bihash_search_16_8 (&tsm->in2out_ed, &kv0, &value0))
+         if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &kv0, &value0))
            {
+             s0 = pool_elt_at_index (tsm->sessions, value0.value);
 
+             if (s0->tcp_close_timestamp && now >= s0->tcp_close_timestamp)
+               {
+                 nat_free_session_data (sm, s0, thread_index, 0);
+                 nat44_delete_session (sm, s0, thread_index);
+                 s0 = NULL;
+               }
+           }
+
+         if (!s0)
+           {
              if (is_output_feature)
                {
                  if (PREDICT_FALSE
@@ -1260,11 +1290,6 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t * vm,
                goto trace0;
 
            }
-         else
-           {
-             s0 = pool_elt_at_index (tsm->sessions, value0.value);
-           }
-
 
          b0->flags |= VNET_BUFFER_F_IS_NATED;
 
@@ -1314,7 +1339,8 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t * vm,
                  tcp0->checksum = ip_csum_fold (sum0);
                }
              tcp_packets++;
-             if (nat44_set_tcp_session_state_i2o (sm, s0, b0, thread_index))
+             if (nat44_set_tcp_session_state_i2o
+                 (sm, now, s0, b0, thread_index))
                goto trace0;
            }
          else if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment
index 1f63237..ffa94a1 100755 (executable)
@@ -619,7 +619,8 @@ nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
   s = pool_elt_at_index (tsm->sessions, oldest_elt->value);
 
   sess_timeout_time = s->last_heard + (f64) nat44_session_get_timeout (sm, s);
-  if (now >= sess_timeout_time)
+  if (now >= sess_timeout_time ||
+      (s->tcp_close_timestamp && now >= s->tcp_close_timestamp))
     {
       // reuse old session
       clib_dlist_addtail (tsm->list_pool,
index 8df3b9a..8361101 100644 (file)
@@ -228,6 +228,7 @@ _(MAX_FRAG, "maximum fragments per reassembly exceeded")\
 _(CANNOT_CREATE_USER, "cannot create NAT user")         \
 _(NON_SYN, "non-SYN packet try to create session")      \
 _(TCP_PACKETS, "TCP packets")                           \
+_(TCP_CLOSED, "drops due to TCP in transitory timeout") \
 _(UDP_PACKETS, "UDP packets")                           \
 _(ICMP_PACKETS, "ICMP packets")                         \
 _(OTHER_PACKETS, "other protocol packets")              \
@@ -258,6 +259,7 @@ _(MAX_FRAG, "maximum fragments per reassembly exceeded")\
 _(CANNOT_CREATE_USER, "cannot create NAT user")         \
 _(NON_SYN, "non-SYN packet try to create session")      \
 _(TCP_PACKETS, "TCP packets")                           \
+_(TCP_CLOSED, "drops due to TCP in transitory timeout") \
 _(UDP_PACKETS, "UDP packets")                           \
 _(ICMP_PACKETS, "ICMP packets")                         \
 _(OTHER_PACKETS, "other protocol packets")              \
@@ -341,6 +343,7 @@ typedef CLIB_PACKED(struct
   u8 state;
   u32 i2o_fin_seq;
   u32 o2i_fin_seq;
+  u32 tcp_close_timestamp;
 
   /* user index */
   u32 user_index;
@@ -746,23 +749,14 @@ extern vlib_node_registration_t nat_pre_out2in_node;
 extern vlib_node_registration_t snat_in2out_node;
 extern vlib_node_registration_t snat_in2out_output_node;
 extern vlib_node_registration_t snat_out2in_node;
-extern vlib_node_registration_t snat_in2out_fast_node;
-extern vlib_node_registration_t snat_out2in_fast_node;
 extern vlib_node_registration_t snat_in2out_worker_handoff_node;
 extern vlib_node_registration_t snat_in2out_output_worker_handoff_node;
 extern vlib_node_registration_t snat_out2in_worker_handoff_node;
 extern vlib_node_registration_t snat_det_in2out_node;
 extern vlib_node_registration_t snat_det_out2in_node;
-extern vlib_node_registration_t snat_hairpin_dst_node;
-extern vlib_node_registration_t snat_hairpin_src_node;
 extern vlib_node_registration_t nat44_ed_in2out_node;
 extern vlib_node_registration_t nat44_ed_in2out_output_node;
 extern vlib_node_registration_t nat44_ed_out2in_node;
-extern vlib_node_registration_t nat44_ed_hairpin_dst_node;
-extern vlib_node_registration_t nat44_ed_hairpin_src_node;
-extern vlib_node_registration_t nat44_ed_in2out_worker_handoff_node;
-extern vlib_node_registration_t nat44_ed_in2out_output_worker_handoff_node;
-extern vlib_node_registration_t nat44_ed_out2in_worker_handoff_node;
 
 extern fib_source_t nat_fib_src_hi;
 extern fib_source_t nat_fib_src_low;
index 7cc2475..fcaf573 100644 (file)
@@ -61,7 +61,7 @@ nat44_session_reuse_old (snat_main_t * sm, snat_user_t * u,
   s->ext_host_port = 0;
   s->ext_host_nat_addr.as_u32 = 0;
   s->ext_host_nat_port = 0;
-  //
+  s->tcp_close_timestamp = 0;
   s->ha_last_refreshed = now;
   return s;
 }
@@ -193,6 +193,12 @@ nat44_user_session_cleanup (snat_user_t * u, u32 thread_index, f64 now)
       sess_timeout_time = s->last_heard +
        (f64) nat44_session_get_timeout (sm, s);
 
+      if (s->tcp_close_timestamp)
+       {
+         sess_timeout_time =
+           clib_min (sess_timeout_time, s->tcp_close_timestamp);
+       }
+
       if (now < sess_timeout_time)
        continue;
 
index 45e0069..cdf94a8 100644 (file)
@@ -664,6 +664,8 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
 
   u32 timed_out = 0;
   u32 transitory = 0;
+  u32 transitory_wait_closed = 0;
+  u32 transitory_closed = 0;
   u32 established = 0;
 
   if (sm->num_workers > 1)
@@ -686,7 +688,23 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
               case SNAT_PROTOCOL_TCP:
                 tcp_sessions++;
                 if (s->state)
-                  transitory++;
+                  {
+                    if (s->tcp_close_timestamp)
+                      {
+                        if (now >= s->tcp_close_timestamp)
+                          {
+                            ++transitory_closed;
+                          }
+                        else
+                          {
+                            ++transitory_wait_closed;
+                          }
+                      }
+                    else
+                      {
+                        transitory++;
+                      }
+                  }
                 else
                   established++;
                 break;
@@ -731,7 +749,23 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
           case SNAT_PROTOCOL_TCP:
             tcp_sessions++;
             if (s->state)
-              transitory++;
+              {
+                if (s->tcp_close_timestamp)
+                  {
+                    if (now >= s->tcp_close_timestamp)
+                      {
+                        ++transitory_closed;
+                      }
+                    else
+                      {
+                        ++transitory_wait_closed;
+                      }
+                  }
+                else
+                  {
+                    transitory++;
+                  }
+              }
             else
               established++;
             break;
@@ -761,6 +795,10 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
   vlib_cli_output (vm, "total tcp sessions: %u", tcp_sessions);
   vlib_cli_output (vm, "total tcp established sessions: %u", established);
   vlib_cli_output (vm, "total tcp transitory sessions: %u", transitory);
+  vlib_cli_output (vm, "total tcp transitory (WAIT-CLOSED) sessions: %u",
+                  transitory_wait_closed);
+  vlib_cli_output (vm, "total tcp transitory (CLOSED) sessions: %u",
+                  transitory_closed);
   vlib_cli_output (vm, "total udp sessions: %u", udp_sessions);
   vlib_cli_output (vm, "total icmp sessions: %u", icmp_sessions);
   return 0;
index 384a1eb..22b7637 100644 (file)
@@ -95,8 +95,8 @@ u32
 icmp_match_in2out_det (snat_main_t * sm, vlib_node_runtime_t * node,
                       u32 thread_index, vlib_buffer_t * b0,
                       ip4_header_t * ip0, u8 * p_proto,
-                      snat_session_key_t * p_value,
-                      u8 * p_dont_translate, void *d, void *e)
+                      snat_session_key_t * p_value, u8 * p_dont_translate,
+                      void *d, void *e)
 {
   icmp46_header_t *icmp0;
   u32 sw_if_index0;
index f693032..bdcfd39 100644 (file)
@@ -322,8 +322,9 @@ nat44_delete_session (snat_main_t * sm, snat_session_t * ses,
     @return 1 if session was closed, otherwise 0
 */
 always_inline int
-nat44_set_tcp_session_state_i2o (snat_main_t * sm, snat_session_t * ses,
-                                vlib_buffer_t * b, u32 thread_index)
+nat44_set_tcp_session_state_i2o (snat_main_t * sm, f64 now,
+                                snat_session_t * ses, vlib_buffer_t * b,
+                                u32 thread_index)
 {
   u8 tcp_flags = vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags;
   u32 tcp_ack_number = vnet_buffer (b)->ip.reass.tcp_ack_number;
@@ -345,22 +346,22 @@ nat44_set_tcp_session_state_i2o (snat_main_t * sm, snat_session_t * ses,
   if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_O2I_FIN))
     {
       if (clib_net_to_host_u32 (tcp_ack_number) > ses->o2i_fin_seq)
-       ses->state |= NAT44_SES_O2I_FIN_ACK;
-    }
-  if (nat44_is_ses_closed (ses)
-      && !(ses->flags & SNAT_SESSION_FLAG_OUTPUT_FEATURE))
-    {
-      nat_free_session_data (sm, ses, thread_index, 0);
-      nat44_delete_session (sm, ses, thread_index);
-      return 1;
+       {
+         ses->state |= NAT44_SES_O2I_FIN_ACK;
+         if (nat44_is_ses_closed (ses))
+           {                   // if session is now closed, save the timestamp
+             ses->tcp_close_timestamp = now + sm->tcp_transitory_timeout;
+           }
+       }
     }
   return 0;
 }
 
 always_inline int
-nat44_set_tcp_session_state_o2i (snat_main_t * sm, snat_session_t * ses,
-                                u8 tcp_flags, u32 tcp_ack_number,
-                                u32 tcp_seq_number, u32 thread_index)
+nat44_set_tcp_session_state_o2i (snat_main_t * sm, f64 now,
+                                snat_session_t * ses, u8 tcp_flags,
+                                u32 tcp_ack_number, u32 tcp_seq_number,
+                                u32 thread_index)
 {
   if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST))
     ses->state = NAT44_SES_RST;
@@ -380,12 +381,10 @@ nat44_set_tcp_session_state_o2i (snat_main_t * sm, snat_session_t * ses,
     {
       if (clib_net_to_host_u32 (tcp_ack_number) > ses->i2o_fin_seq)
        ses->state |= NAT44_SES_I2O_FIN_ACK;
-    }
-  if (nat44_is_ses_closed (ses))
-    {
-      nat_free_session_data (sm, ses, thread_index, 0);
-      nat44_delete_session (sm, ses, thread_index);
-      return 1;
+      if (nat44_is_ses_closed (ses))
+       {                       // if session is now closed, save the timestamp
+         ses->tcp_close_timestamp = now + sm->tcp_transitory_timeout;
+       }
     }
   return 0;
 }
index fbb7d06..6943614 100644 (file)
@@ -414,7 +414,8 @@ create_bypass_for_fwd (snat_main_t * sm, vlib_buffer_t * b, ip4_header_t * ip,
     {
       tcp_header_t *tcp = ip4_next_header (ip);
       if (nat44_set_tcp_session_state_o2i
-         (sm, s, tcp->flags, tcp->ack_number, tcp->seq_number, thread_index))
+         (sm, now, s, tcp->flags, tcp->ack_number, tcp->seq_number,
+          thread_index))
        return;
     }
 
@@ -682,6 +683,7 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   u32 tcp_packets = 0, udp_packets = 0, icmp_packets = 0, other_packets =
     0, fragments = 0;
+  u32 tcp_closed_drops = 0;
 
   stats_node_index = sm->ed_out2in_node_index;
 
@@ -766,6 +768,23 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
            }
          s0 = pool_elt_at_index (tsm->sessions, value0.value);
 
+         if (s0->tcp_close_timestamp)
+           {
+             if (now >= s0->tcp_close_timestamp)
+               {
+                 // session is closed, go slow path
+                 next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
+               }
+             else
+               {
+                 // session in transitory timeout, drop
+                 b0->error = node->errors[NAT_OUT2IN_ED_ERROR_TCP_CLOSED];
+                 ++tcp_closed_drops;
+                 next0 = NAT_NEXT_DROP;
+               }
+             goto trace0;
+           }
+
          // drop if session expired
          u64 sess_timeout_time;
          sess_timeout_time = s0->last_heard +
@@ -827,7 +846,8 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
                }
              tcp_packets++;
              if (nat44_set_tcp_session_state_o2i
-                 (sm, s0, vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
+                 (sm, now, s0,
+                  vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
                   vnet_buffer (b0)->ip.reass.tcp_ack_number,
                   vnet_buffer (b0)->ip.reass.tcp_seq_number, thread_index))
                goto trace0;
@@ -1029,7 +1049,20 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
                      vnet_buffer (b0)->ip.reass.l4_dst_port,
                      vnet_buffer (b0)->ip.reass.l4_src_port);
 
-         if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv0, &value0))
+         s0 = NULL;
+         if (!clib_bihash_search_16_8 (&tsm->out2in_ed, &kv0, &value0))
+           {
+             s0 = pool_elt_at_index (tsm->sessions, value0.value);
+
+             if (s0->tcp_close_timestamp && now >= s0->tcp_close_timestamp)
+               {
+                 nat_free_session_data (sm, s0, thread_index, 0);
+                 nat44_delete_session (sm, s0, thread_index);
+                 s0 = NULL;
+               }
+           }
+
+         if (!s0)
            {
              /* Try to match static mapping by external address and port,
                 destination address and port in packet */
@@ -1108,10 +1141,6 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
                  goto trace0;
                }
            }
-         else
-           {
-             s0 = pool_elt_at_index (tsm->sessions, value0.value);
-           }
 
          old_addr0 = ip0->dst_address.as_u32;
          new_addr0 = ip0->dst_address.as_u32 = s0->in2out.addr.as_u32;
@@ -1158,7 +1187,8 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
                }
              tcp_packets++;
              if (nat44_set_tcp_session_state_o2i
-                 (sm, s0, vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
+                 (sm, now, s0,
+                  vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags,
                   vnet_buffer (b0)->ip.reass.tcp_ack_number,
                   vnet_buffer (b0)->ip.reass.tcp_seq_number, thread_index))
                goto trace0;
index a38cdc9..d5d4128 100644 (file)
@@ -1155,12 +1155,8 @@ class MethodHolder(VppTestCase):
         self.port_in = random.randint(1025, 65535)
 
         # in2out
-        pkts = self.create_stream_frag(self.pg0,
-                                       self.pg1.remote_ip4,
-                                       self.port_in,
-                                       20,
-                                       data,
-                                       proto)
+        pkts = self.create_stream_frag(self.pg0, self.pg1.remote_ip4,
+                                       self.port_in, 20, data, proto)
         self.pg0.add_stream(pkts)
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
@@ -1197,13 +1193,8 @@ class MethodHolder(VppTestCase):
         else:
             sport = p[layer].id
             dport = 0
-        pkts = self.create_stream_frag(self.pg1,
-                                       dst_addr,
-                                       sport,
-                                       dport,
-                                       data,
-                                       proto,
-                                       echo_reply=True)
+        pkts = self.create_stream_frag(self.pg1, dst_addr, sport, dport, data,
+                                       proto, echo_reply=True)
         self.pg1.add_stream(pkts)
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
@@ -1229,12 +1220,9 @@ class MethodHolder(VppTestCase):
 
         for i in range(2):
             # out2in
-            pkts = self.create_stream_frag(self.pg0,
-                                           self.server_out_addr,
-                                           self.port_in,
-                                           self.server_out_port,
-                                           data,
-                                           proto)
+            pkts = self.create_stream_frag(self.pg0, self.server_out_addr,
+                                           self.port_in, self.server_out_port,
+                                           data, proto)
             self.pg0.add_stream(pkts)
             self.pg_enable_capture(self.pg_interfaces)
             self.pg_start()
@@ -1251,19 +1239,12 @@ class MethodHolder(VppTestCase):
 
             # in2out
             if proto != IP_PROTOS.icmp:
-                pkts = self.create_stream_frag(self.pg1,
-                                               self.pg0.remote_ip4,
+                pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4,
                                                self.server_in_port,
-                                               p[layer].sport,
-                                               data,
-                                               proto)
+                                               p[layer].sport, data, proto)
             else:
-                pkts = self.create_stream_frag(self.pg1,
-                                               self.pg0.remote_ip4,
-                                               p[layer].id,
-                                               0,
-                                               data,
-                                               proto,
+                pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4,
+                                               p[layer].id, 0, data, proto,
                                                echo_reply=True)
             self.pg1.add_stream(pkts)
             self.pg_enable_capture(self.pg_interfaces)
@@ -1319,12 +1300,8 @@ class MethodHolder(VppTestCase):
 
         for i in range(2):
             # in2out
-            pkts = self.create_stream_frag(self.pg0,
-                                           self.pg1.remote_ip4,
-                                           self.port_in,
-                                           20,
-                                           data,
-                                           proto)
+            pkts = self.create_stream_frag(self.pg0, self.pg1.remote_ip4,
+                                           self.port_in, 20, data, proto)
             pkts.reverse()
             self.pg0.add_stream(pkts)
             self.pg_enable_capture(self.pg_interfaces)
@@ -1362,13 +1339,8 @@ class MethodHolder(VppTestCase):
             else:
                 sport = p[layer].id
                 dport = 0
-            pkts = self.create_stream_frag(self.pg1,
-                                           dst_addr,
-                                           sport,
-                                           dport,
-                                           data,
-                                           proto,
-                                           echo_reply=True)
+            pkts = self.create_stream_frag(self.pg1, dst_addr, sport, dport,
+                                           data, proto, echo_reply=True)
             pkts.reverse()
             self.pg1.add_stream(pkts)
             self.pg_enable_capture(self.pg_interfaces)
@@ -1395,12 +1367,9 @@ class MethodHolder(VppTestCase):
 
         for i in range(2):
             # out2in
-            pkts = self.create_stream_frag(self.pg0,
-                                           self.server_out_addr,
-                                           self.port_in,
-                                           self.server_out_port,
-                                           data,
-                                           proto)
+            pkts = self.create_stream_frag(self.pg0, self.server_out_addr,
+                                           self.port_in, self.server_out_port,
+                                           data, proto)
             pkts.reverse()
             self.pg0.add_stream(pkts)
             self.pg_enable_capture(self.pg_interfaces)
@@ -1419,19 +1388,12 @@ class MethodHolder(VppTestCase):
 
             # in2out
             if proto != IP_PROTOS.icmp:
-                pkts = self.create_stream_frag(self.pg1,
-                                               self.pg0.remote_ip4,
+                pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4,
                                                self.server_in_port,
-                                               p[layer].sport,
-                                               data,
-                                               proto)
+                                               p[layer].sport, data, proto)
             else:
-                pkts = self.create_stream_frag(self.pg1,
-                                               self.pg0.remote_ip4,
-                                               p[layer].id,
-                                               0,
-                                               data,
-                                               proto,
+                pkts = self.create_stream_frag(self.pg1, self.pg0.remote_ip4,
+                                               p[layer].id, 0, data, proto,
                                                echo_reply=True)
             pkts.reverse()
             self.pg1.add_stream(pkts)
@@ -4425,6 +4387,11 @@ class TestNAT44EndpointDependent(MethodHolder):
         cls.pg8.config_ip4()
         cls.pg8.resolve_arp()
 
+    def setUp(self):
+        super(TestNAT44EndpointDependent, self).setUp()
+        self.vapi.nat_set_timeouts(
+            udp=300, tcp_established=7440, tcp_transitory=240, icmp=60)
+
     @classmethod
     def tearDownClass(cls):
         super(TestNAT44EndpointDependent, cls).tearDownClass()
@@ -5979,6 +5946,9 @@ class TestNAT44EndpointDependent(MethodHolder):
         sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
         start_sessnum = len(sessions)
 
+        self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
+                                   tcp_transitory=2, icmp=5)
+
         self.initiate_tcp_session(self.pg0, self.pg1)
 
         # FIN packet in -> out
@@ -6022,8 +5992,55 @@ class TestNAT44EndpointDependent(MethodHolder):
         self.pg_start()
         self.pg1.get_capture(1)
 
-        sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4,
-                                                     0)
+        sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+        self.assertEqual(len(sessions) - start_sessnum, 1)
+
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
+        out2in_drops = stats[0]
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
+        in2out_drops = stats[0]
+
+        # extra FIN packet out -> in - this should be dropped
+        p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+             IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+             TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
+                 flags="FA", seq=300, ack=101))
+
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg0.assert_nothing_captured()
+
+        # extra ACK packet in -> out - this should be dropped
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+             TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+                 flags="A", seq=101, ack=301))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.assert_nothing_captured()
+
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
+        self.assertEqual(stats[0] - out2in_drops, 1)
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
+        self.assertEqual(stats[0] - in2out_drops, 1)
+
+        self.sleep(3)
+        # extra ACK packet in -> out - this will cause session to be wiped
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+             TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+                 flags="A", seq=101, ack=301))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.assert_nothing_captured()
+        sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
         self.assertEqual(len(sessions) - start_sessnum, 0)
 
     def test_tcp_session_close_out(self):
@@ -6048,6 +6065,9 @@ class TestNAT44EndpointDependent(MethodHolder):
         sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
         start_sessnum = len(sessions)
 
+        self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
+                                   tcp_transitory=2, icmp=5)
+
         self.initiate_tcp_session(self.pg0, self.pg1)
 
         # FIN packet out -> in
@@ -6081,8 +6101,55 @@ class TestNAT44EndpointDependent(MethodHolder):
         self.pg_start()
         self.pg0.get_capture(1)
 
-        sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4,
-                                                     0)
+        sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+        self.assertEqual(len(sessions) - start_sessnum, 1)
+
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
+        out2in_drops = stats[0]
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
+        in2out_drops = stats[0]
+
+        # extra FIN packet out -> in - this should be dropped
+        p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+             IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+             TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
+                 flags="FA", seq=300, ack=101))
+
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg0.assert_nothing_captured()
+
+        # extra ACK packet in -> out - this should be dropped
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+             TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+                 flags="A", seq=101, ack=301))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.assert_nothing_captured()
+
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
+        self.assertEqual(stats[0] - out2in_drops, 1)
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
+        self.assertEqual(stats[0] - in2out_drops, 1)
+
+        self.sleep(3)
+        # extra ACK packet in -> out - this will cause session to be wiped
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+             TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+                 flags="A", seq=101, ack=301))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.assert_nothing_captured()
+        sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
         self.assertEqual(len(sessions) - start_sessnum, 0)
 
     def test_tcp_session_close_simultaneous(self):
@@ -6107,6 +6174,9 @@ class TestNAT44EndpointDependent(MethodHolder):
         sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
         start_sessnum = len(sessions)
 
+        self.vapi.nat_set_timeouts(udp=300, tcp_established=7440,
+                                   tcp_transitory=2, icmp=5)
+
         self.initiate_tcp_session(self.pg0, self.pg1)
 
         # FIN packet in -> out
@@ -6149,8 +6219,55 @@ class TestNAT44EndpointDependent(MethodHolder):
         self.pg_start()
         self.pg0.get_capture(1)
 
-        sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4,
-                                                     0)
+        sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
+        self.assertEqual(len(sessions) - start_sessnum, 1)
+
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
+        out2in_drops = stats[0]
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
+        in2out_drops = stats[0]
+
+        # extra FIN packet out -> in - this should be dropped
+        p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+             IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+             TCP(sport=self.tcp_external_port, dport=self.tcp_port_out,
+                 flags="FA", seq=300, ack=101))
+
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg0.assert_nothing_captured()
+
+        # extra ACK packet in -> out - this should be dropped
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+             TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+                 flags="A", seq=101, ack=301))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.assert_nothing_captured()
+
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-out2in/drops due to TCP in transitory timeout')
+        self.assertEqual(stats[0] - out2in_drops, 1)
+        stats = self.statistics.get_counter(
+            '/err/nat44-ed-in2out/drops due to TCP in transitory timeout')
+        self.assertEqual(stats[0] - in2out_drops, 1)
+
+        self.sleep(3)
+        # extra ACK packet in -> out - this will cause session to be wiped
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) /
+             TCP(sport=self.tcp_port_in, dport=self.tcp_external_port,
+                 flags="A", seq=101, ack=301))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.assert_nothing_captured()
+        sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0)
         self.assertEqual(len(sessions) - start_sessnum, 0)
 
     def test_one_armed_nat44_static(self):