nat: fix LRU blocked by inactive session 69/26869/11
authorKlement Sekera <ksekera@cisco.com>
Mon, 4 May 2020 09:56:58 +0000 (09:56 +0000)
committerOle Trøan <otroan@employees.org>
Wed, 13 May 2020 11:39:30 +0000 (11:39 +0000)
This fixes a situation where long-lived inactive session blocks LRU
list. Solution is to have multiple LRU lists based on session type.
This helps because session timeout is same for all sessions of same
type.

Type: fix

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

src/plugins/nat/in2out_ed.c
src/plugins/nat/nat.c
src/plugins/nat/nat.h
src/plugins/nat/nat44/ed_inlines.h [new file with mode: 0644]
src/plugins/nat/nat44/inlines.h
src/plugins/nat/nat44_cli.c
src/plugins/nat/nat_api.c
src/plugins/nat/nat_inlines.h
src/plugins/nat/out2in_ed.c
src/plugins/nat/test/test_nat.py

index f8682d0..45d9fd0 100644 (file)
@@ -31,6 +31,7 @@
 #include <nat/nat44/inlines.h>
 #include <nat/nat_syslog.h>
 #include <nat/nat_ha.h>
+#include <nat/nat44/ed_inlines.h>
 
 static char *nat_in2out_ed_error_strings[] = {
 #define _(sym,string) string,
@@ -155,7 +156,7 @@ nat44_i2o_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void *arg)
       snat_free_outside_address_and_port (sm->addresses, ctx->thread_index,
                                          &s->out2in);
     delete:
-      nat44_ed_delete_session (sm, s, ctx->thread_index, 1);
+      nat_ed_session_delete (sm, s, ctx->thread_index, 1);
       return 1;
     }
 
@@ -323,7 +324,7 @@ slow_path_ed (snat_main_t * sm,
   if (PREDICT_FALSE
       (nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index)))
     {
-      if (!nat_global_lru_free_one (sm, thread_index, now))
+      if (!nat_lru_free_one (sm, thread_index, now))
        {
          b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED];
          nat_ipfix_logging_max_sessions (thread_index, sm->max_translations);
@@ -342,7 +343,7 @@ slow_path_ed (snat_main_t * sm,
   if (snat_static_mapping_match
       (sm, key0, &key1, 0, 0, 0, &lb, 0, &identity_nat))
     {
-      s = nat_ed_session_alloc (sm, thread_index, now);
+      s = nat_ed_session_alloc (sm, thread_index, now, proto);
       if (!s)
        {
          nat_elog_warn ("create NAT session failed");
@@ -386,7 +387,7 @@ slow_path_ed (snat_main_t * sm,
          nat_elog_notice ("addresses exhausted");
          b->error = node->errors[NAT_IN2OUT_ED_ERROR_OUT_OF_PORTS];
          nat_free_session_data (sm, s, thread_index, 0);
-         nat44_ed_delete_session (sm, s, thread_index, 1);
+         nat_ed_session_delete (sm, s, thread_index, 1);
          return NAT_NEXT_DROP;
        }
       key1.addr = allocated_addr;
@@ -399,7 +400,7 @@ slow_path_ed (snat_main_t * sm,
          *sessionp = s;
          return next;
        }
-      s = nat_ed_session_alloc (sm, thread_index, now);
+      s = nat_ed_session_alloc (sm, thread_index, now, proto);
       if (!s)
        {
          nat_elog_warn ("create NAT session failed");
@@ -601,7 +602,7 @@ nat44_ed_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip,
       if (nat44_is_ses_closed (s))
        {
          nat_free_session_data (sm, s, thread_index, 0);
-         nat44_ed_delete_session (sm, s, thread_index, 1);
+         nat_ed_session_delete (sm, s, thread_index, 1);
        }
       else
        s->flags |= SNAT_SESSION_FLAG_OUTPUT_FEATURE;
@@ -857,7 +858,7 @@ nat44_ed_in2out_unknown_proto (snat_main_t * sm,
        }
 
     create_ses:
-      s = nat_ed_session_alloc (sm, thread_index, now);
+      s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol);
       if (!s)
        {
          b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_USER_SESS_EXCEEDED];
@@ -1026,9 +1027,9 @@ 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 (s0->tcp_closed_timestamp)
            {
-             if (now >= s0->tcp_close_timestamp)
+             if (now >= s0->tcp_closed_timestamp)
                {
                  // session is closed, go slow path
                  next0 = def_slow;
@@ -1049,7 +1050,7 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm,
          if (now >= sess_timeout_time)
            {
              nat_free_session_data (sm, s0, thread_index, 0);
-             nat44_ed_delete_session (sm, s0, thread_index, 1);
+             nat_ed_session_delete (sm, s0, thread_index, 1);
              // session is closed, go slow path
              next0 = def_slow;
              goto trace0;
@@ -1307,10 +1308,10 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t * vm,
            {
              s0 = pool_elt_at_index (tsm->sessions, value0.value);
 
-             if (s0->tcp_close_timestamp && now >= s0->tcp_close_timestamp)
+             if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp)
                {
                  nat_free_session_data (sm, s0, thread_index, 0);
-                 nat44_ed_delete_session (sm, s0, thread_index, 1);
+                 nat_ed_session_delete (sm, s0, thread_index, 1);
                  s0 = NULL;
                }
            }
index 1fb3a7f..ee35a57 100755 (executable)
@@ -34,6 +34,7 @@
 #include <vnet/fib/ip4_fib.h>
 #include <vnet/ip/reass/ip4_sv_reass.h>
 #include <vppinfra/bihash_16_8.h>
+#include <nat/nat44/ed_inlines.h>
 
 #include <vpp/app/version.h>
 
@@ -593,14 +594,6 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
                          s->per_user_list_head_index,
                          per_user_translation_list_elt - tsm->list_pool);
 
-      dlist_elt_t *global_lru_list_elt;
-      pool_get (tsm->global_lru_pool, global_lru_list_elt);
-      global_lru_list_elt->value = s - tsm->sessions;
-      s->global_lru_index = global_lru_list_elt - tsm->global_lru_pool;
-      clib_dlist_addtail (tsm->global_lru_pool, tsm->global_lru_head_index,
-                         s->global_lru_index);
-      s->last_lru_update = now;
-
       s->user_index = u - tsm->users;
       vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
                               pool_elts (tsm->sessions));
@@ -611,58 +604,6 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
   return s;
 }
 
-int
-nat_global_lru_free_one (snat_main_t * sm, int thread_index, f64 now)
-{
-  snat_session_t *s = NULL;
-  dlist_elt_t *oldest_elt;
-  u64 sess_timeout_time;
-  u32 oldest_index;
-  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
-  oldest_index = clib_dlist_remove_head (tsm->global_lru_pool,
-                                        tsm->global_lru_head_index);
-  if (~0 != oldest_index)
-    {
-      oldest_elt = pool_elt_at_index (tsm->global_lru_pool, oldest_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
-         || (s->tcp_close_timestamp && now >= s->tcp_close_timestamp))
-       {
-         nat_free_session_data (sm, s, thread_index, 0);
-         nat44_ed_delete_session (sm, s, thread_index, 0);
-         return 1;
-       }
-      else
-       {
-         clib_dlist_addhead (tsm->global_lru_pool,
-                             tsm->global_lru_head_index, oldest_index);
-       }
-    }
-  return 0;
-}
-
-snat_session_t *
-nat_ed_session_alloc (snat_main_t * sm, u32 thread_index, f64 now)
-{
-  snat_session_t *s;
-  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
-
-  nat_global_lru_free_one (sm, thread_index, now);
-
-  pool_get (tsm->sessions, s);
-  clib_memset (s, 0, sizeof (*s));
-
-  nat44_global_lru_insert (tsm, s, now);
-
-  s->ha_last_refreshed = now;
-  vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
-                          pool_elts (tsm->sessions));
-  return s;
-}
-
 void
 snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index,
                          int is_add)
@@ -912,7 +853,7 @@ snat_ed_static_mapping_del_sessions (snat_main_t * sm,
   vec_foreach (ses_index, indexes_to_free)
   {
     s = pool_elt_at_index (tsm->sessions, *ses_index);
-    nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1);
+    nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
   }
   vec_free (indexes_to_free);
 }
@@ -1617,7 +1558,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
               continue;
 
             nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
-            nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1);
+            nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
           });
       }));
       /* *INDENT-ON* */
@@ -1749,7 +1690,7 @@ nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port,
           continue;
 
         nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
-        nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1);
+        nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
       });
       /* *INDENT-ON* */
 
@@ -1866,7 +1807,7 @@ snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm,
              vec_foreach (ses_index, ses_to_be_removed)
                {
                  ses = pool_elt_at_index (tsm->sessions, ses_index[0]);
-                 nat44_ed_delete_session (sm, ses, tsm - sm->per_thread_data, 1);
+                 nat_ed_session_delete (sm, ses, tsm - sm->per_thread_data, 1);
                }
          }else{
              vec_foreach (ses_index, ses_to_be_removed)
@@ -3657,6 +3598,11 @@ nat_ha_sadd_cb (ip4_address_t * in_addr, u16 in_port,
   if (!s)
     return;
 
+  if (sm->endpoint_dependent)
+    {
+      nat_ed_lru_insert (tsm, s, now, proto);
+    }
+
   s->last_heard = now;
   s->flags = flags;
   s->ext_host_addr.as_u32 = eh_addr->as_u32;
@@ -3806,7 +3752,7 @@ nat_ha_sadd_ed_cb (ip4_address_t * in_addr, u16 in_port,
        return;
     }
 
-  s = nat_ed_session_alloc (sm, thread_index, now);
+  s = nat_ed_session_alloc (sm, thread_index, now, proto);
   if (!s)
     return;
 
@@ -3925,12 +3871,29 @@ nat44_db_init (snat_main_per_thread_data_t * tsm)
   snat_main_t *sm = &snat_main;
 
   pool_alloc (tsm->sessions, sm->max_translations);
-  pool_alloc (tsm->global_lru_pool, sm->max_translations);
+  pool_alloc (tsm->lru_pool, sm->max_translations);
 
   dlist_elt_t *head;
-  pool_get (tsm->global_lru_pool, head);
-  tsm->global_lru_head_index = head - tsm->global_lru_pool;
-  clib_dlist_init (tsm->global_lru_pool, tsm->global_lru_head_index);
+
+  pool_get (tsm->lru_pool, head);
+  tsm->tcp_trans_lru_head_index = head - tsm->lru_pool;
+  clib_dlist_init (tsm->lru_pool, tsm->tcp_trans_lru_head_index);
+
+  pool_get (tsm->lru_pool, head);
+  tsm->tcp_estab_lru_head_index = head - tsm->lru_pool;
+  clib_dlist_init (tsm->lru_pool, tsm->tcp_estab_lru_head_index);
+
+  pool_get (tsm->lru_pool, head);
+  tsm->udp_lru_head_index = head - tsm->lru_pool;
+  clib_dlist_init (tsm->lru_pool, tsm->udp_lru_head_index);
+
+  pool_get (tsm->lru_pool, head);
+  tsm->icmp_lru_head_index = head - tsm->lru_pool;
+  clib_dlist_init (tsm->lru_pool, tsm->icmp_lru_head_index);
+
+  pool_get (tsm->lru_pool, head);
+  tsm->unk_proto_lru_head_index = head - tsm->lru_pool;
+  clib_dlist_init (tsm->lru_pool, tsm->unk_proto_lru_head_index);
 
   if (sm->endpoint_dependent)
     {
@@ -3970,7 +3933,7 @@ nat44_db_free (snat_main_per_thread_data_t * tsm)
   snat_main_t *sm = &snat_main;
 
   pool_free (tsm->sessions);
-  pool_free (tsm->global_lru_pool);
+  pool_free (tsm->lru_pool);
 
   if (sm->endpoint_dependent)
     {
@@ -4503,7 +4466,7 @@ nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port,
     return VNET_API_ERROR_UNSPECIFIED;
   s = pool_elt_at_index (tsm->sessions, value.value);
   nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
-  nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1);
+  nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
   return 0;
 }
 
index e4f7a06..7a4d020 100644 (file)
@@ -264,8 +264,10 @@ typedef CLIB_PACKED(struct
   u32 per_user_index;
   u32 per_user_list_head_index;
 
+  /* head of LRU list in which this session is tracked */
+  u32 lru_head_index;
   /* index in global LRU list */
-  u32 global_lru_index;
+  u32 lru_index;
   f64 last_lru_update;
 
   /* Last heard timer */
@@ -290,7 +292,7 @@ typedef CLIB_PACKED(struct
   u8 state;
   u32 i2o_fin_seq;
   u32 o2i_fin_seq;
-  u32 tcp_close_timestamp;
+  u64 tcp_closed_timestamp;
 
   /* user index */
   u32 user_index;
@@ -468,8 +470,12 @@ typedef struct
   dlist_elt_t *list_pool;
 
   /* LRU session list - head is stale, tail is fresh */
-  dlist_elt_t *global_lru_pool;
-  u32 global_lru_head_index;
+  dlist_elt_t *lru_pool;
+  u32 tcp_trans_lru_head_index;
+  u32 tcp_estab_lru_head_index;
+  u32 udp_lru_head_index;
+  u32 icmp_lru_head_index;
+  u32 unk_proto_lru_head_index;
 
   /* NAT thread index */
   u32 snat_thread_index;
@@ -1310,18 +1316,6 @@ snat_session_t *nat_session_alloc_or_recycle (snat_main_t * sm,
                                              snat_user_t * u,
                                              u32 thread_index, f64 now);
 
-/**
- * @brief Allocate NAT endpoint-dependent session
- *
- * @param sm           snat global configuration data
- * @param thread_index thread index
- * @param now          time now
- *
- * @return session data structure on success otherwise zero value
- */
-snat_session_t *nat_ed_session_alloc (snat_main_t * sm, u32 thread_index,
-                                     f64 now);
-
 /**
  * @brief Set address and port assignment algorithm for MAP-E CE
  *
@@ -1431,8 +1425,6 @@ typedef struct
   u16 src_port, dst_port;
 } tcp_udp_header_t;
 
-int nat_global_lru_free_one (snat_main_t * sm, int thread_index, f64 now);
-
 #endif /* __included_nat_h__ */
 /*
  * fd.io coding-style-patch-verification: ON
diff --git a/src/plugins/nat/nat44/ed_inlines.h b/src/plugins/nat/nat44/ed_inlines.h
new file mode 100644 (file)
index 0000000..37212f3
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * simple nat plugin
+ *
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_ed_inlines_h__
+#define __included_ed_inlines_h__
+
+#include <float.h>
+#include <vppinfra/clib.h>
+#include <nat/nat.h>
+#include <nat/nat_inlines.h>
+
+static_always_inline int
+nat_ed_lru_insert (snat_main_per_thread_data_t * tsm,
+                  snat_session_t * s, f64 now, u8 proto)
+{
+  dlist_elt_t *lru_list_elt;
+  pool_get (tsm->lru_pool, lru_list_elt);
+  s->lru_index = lru_list_elt - tsm->lru_pool;
+  switch (proto)
+    {
+    case IP_PROTOCOL_UDP:
+      s->lru_head_index = tsm->udp_lru_head_index;
+      break;
+    case IP_PROTOCOL_TCP:
+      s->lru_head_index = tsm->tcp_trans_lru_head_index;
+      break;
+    case IP_PROTOCOL_ICMP:
+      s->lru_head_index = tsm->icmp_lru_head_index;
+      break;
+    default:
+      s->lru_head_index = tsm->unk_proto_lru_head_index;
+      break;
+    }
+  clib_dlist_addtail (tsm->lru_pool, s->lru_head_index, s->lru_index);
+  lru_list_elt->value = s - tsm->sessions;
+  s->last_lru_update = now;
+  return 1;
+}
+
+always_inline void
+nat_ed_session_delete (snat_main_t * sm, snat_session_t * ses,
+                      u32 thread_index, int lru_delete
+                      /* delete from global LRU list */ )
+{
+  snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
+                                                      thread_index);
+
+  if (lru_delete)
+    {
+      clib_dlist_remove (tsm->lru_pool, ses->lru_index);
+    }
+  pool_put_index (tsm->lru_pool, ses->lru_index);
+  pool_put (tsm->sessions, ses);
+  vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
+                          pool_elts (tsm->sessions));
+
+}
+
+static_always_inline int
+nat_lru_free_one_with_head (snat_main_t * sm, int thread_index,
+                           f64 now, u32 head_index)
+{
+  snat_session_t *s = NULL;
+  dlist_elt_t *oldest_elt;
+  f64 sess_timeout_time;
+  u32 oldest_index;
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+  oldest_index = clib_dlist_remove_head (tsm->lru_pool, head_index);
+  if (~0 != oldest_index)
+    {
+      oldest_elt = pool_elt_at_index (tsm->lru_pool, oldest_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
+         || (s->tcp_closed_timestamp && now >= s->tcp_closed_timestamp))
+       {
+         nat_free_session_data (sm, s, thread_index, 0);
+         nat_ed_session_delete (sm, s, thread_index, 0);
+         return 1;
+       }
+      else
+       {
+         clib_dlist_addhead (tsm->lru_pool, head_index, oldest_index);
+       }
+    }
+  return 0;
+}
+
+static_always_inline int
+nat_lru_free_one (snat_main_t * sm, int thread_index, f64 now)
+{
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+  int rc = 0;
+#define _(p)                                                       \
+  if ((rc = nat_lru_free_one_with_head (sm, thread_index, now,     \
+                                        tsm->p##_lru_head_index))) \
+    {                                                              \
+      return rc;                                                   \
+    }
+  _(tcp_trans);
+  _(udp);
+  _(unk_proto);
+  _(icmp);
+  _(tcp_estab);
+#undef _
+  return 0;
+}
+
+static_always_inline snat_session_t *
+nat_ed_session_alloc (snat_main_t * sm, u32 thread_index, f64 now, u8 proto)
+{
+  snat_session_t *s;
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+
+  nat_lru_free_one (sm, thread_index, now);
+
+  pool_get (tsm->sessions, s);
+  clib_memset (s, 0, sizeof (*s));
+
+  nat_ed_lru_insert (tsm, s, now, proto);
+
+  s->ha_last_refreshed = now;
+  vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
+                          pool_elts (tsm->sessions));
+  return s;
+}
+
+#endif
index a5118ea..62121df 100644 (file)
@@ -59,24 +59,11 @@ 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->tcp_closed_timestamp = 0;
   s->ha_last_refreshed = now;
   return s;
 }
 
-static_always_inline void
-nat44_global_lru_insert (snat_main_per_thread_data_t * tsm,
-                        snat_session_t * s, f64 now)
-{
-  dlist_elt_t *lru_list_elt;
-  pool_get (tsm->global_lru_pool, lru_list_elt);
-  s->global_lru_index = lru_list_elt - tsm->global_lru_pool;
-  clib_dlist_addtail (tsm->global_lru_pool, tsm->global_lru_head_index,
-                     s->global_lru_index);
-  lru_list_elt->value = s - tsm->sessions;
-  s->last_lru_update = now;
-}
-
 static_always_inline void
 nat44_sessions_clear ()
 {
index 8c800d5..796e349 100644 (file)
@@ -688,9 +688,9 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
                 tcp_sessions++;
                 if (s->state)
                   {
-                    if (s->tcp_close_timestamp)
+                    if (s->tcp_closed_timestamp)
                       {
-                        if (now >= s->tcp_close_timestamp)
+                        if (now >= s->tcp_closed_timestamp)
                           {
                             ++transitory_closed;
                           }
@@ -734,9 +734,9 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
             tcp_sessions++;
             if (s->state)
               {
-                if (s->tcp_close_timestamp)
+                if (s->tcp_closed_timestamp)
                   {
-                    if (now >= s->tcp_close_timestamp)
+                    if (now >= s->tcp_closed_timestamp)
                       {
                         ++transitory_closed;
                       }
@@ -758,6 +758,31 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input,
       }));
       /* *INDENT-ON* */
       count = pool_elts (tsm->sessions);
+      if (sm->endpoint_dependent)
+       {
+         dlist_elt_t *oldest_elt;
+         u32 oldest_index;
+#define _(n, d)                                                          \
+  oldest_index =                                                         \
+      clib_dlist_remove_head (tsm->lru_pool, tsm->n##_lru_head_index);   \
+  if (~0 != oldest_index)                                                \
+    {                                                                    \
+      oldest_elt = pool_elt_at_index (tsm->lru_pool, oldest_index);      \
+      s = pool_elt_at_index (tsm->sessions, oldest_elt->value);          \
+      sess_timeout_time =                                                \
+          s->last_heard + (f64)nat44_session_get_timeout (sm, s);        \
+      vlib_cli_output (vm, d " LRU min session timeout %llu (now %llu)", \
+                       sess_timeout_time, now);                          \
+      clib_dlist_addhead (tsm->lru_pool, tsm->n##_lru_head_index,        \
+                          oldest_index);                                 \
+    }
+         _(tcp_estab, "established tcp");
+         _(tcp_trans, "transitory tcp");
+         _(udp, "udp");
+         _(unk_proto, "unknown protocol");
+         _(icmp, "icmp");
+#undef _
+       }
     }
 
   vlib_cli_output (vm, "total timed out sessions: %u", timed_out);
index 74f24d8..37af77d 100644 (file)
@@ -31,6 +31,7 @@
 #include <nat/nat_msg_enum.h>
 #include <vnet/fib/fib_table.h>
 #include <vnet/ip/ip_types_api.h>
+#include <nat/nat44/ed_inlines.h>
 
 #define vl_api_nat44_add_del_lb_static_mapping_t_endian vl_noop_handler
 #define vl_api_nat44_nat44_lb_static_mapping_details_t_endian vl_noop_handler
@@ -2058,7 +2059,7 @@ static void
              {
                s = pool_elt_at_index(tsm->sessions, ses_index[0]);
                nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
-               nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1);
+               nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1);
              }
        }else{
            vec_foreach (ses_index, ses_to_be_removed)
index e5ac421..d37cb10 100644 (file)
@@ -261,8 +261,11 @@ nat44_delete_session (snat_main_t * sm, snat_session_t * ses,
 
   clib_dlist_remove (tsm->list_pool, ses->per_user_index);
   pool_put_index (tsm->list_pool, ses->per_user_index);
-  clib_dlist_remove (tsm->global_lru_pool, ses->global_lru_index);
-  pool_put_index (tsm->global_lru_pool, ses->global_lru_index);
+  if (sm->endpoint_dependent)
+    {
+      clib_dlist_remove (tsm->lru_pool, ses->lru_index);
+      pool_put_index (tsm->lru_pool, ses->lru_index);
+    }
   pool_put (tsm->sessions, ses);
   vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
                           pool_elts (tsm->sessions));
@@ -280,25 +283,6 @@ nat44_delete_session (snat_main_t * sm, snat_session_t * ses,
     }
 }
 
-always_inline void
-nat44_ed_delete_session (snat_main_t * sm, snat_session_t * ses,
-                        u32 thread_index, int global_lru_delete
-                        /* delete from global LRU list */ )
-{
-  snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
-                                                      thread_index);
-
-  if (global_lru_delete)
-    {
-      clib_dlist_remove (tsm->global_lru_pool, ses->global_lru_index);
-    }
-  pool_put_index (tsm->global_lru_pool, ses->global_lru_index);
-  pool_put (tsm->sessions, ses);
-  vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
-                          pool_elts (tsm->sessions));
-
-}
-
 /** \brief Set TCP session state.
     @return 1 if session was closed, otherwise 0
 */
@@ -307,6 +291,7 @@ nat44_set_tcp_session_state_i2o (snat_main_t * sm, f64 now,
                                 snat_session_t * ses, vlib_buffer_t * b,
                                 u32 thread_index)
 {
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[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;
   u32 tcp_seq_number = vnet_buffer (b)->ip.reass.tcp_seq_number;
@@ -331,10 +316,23 @@ nat44_set_tcp_session_state_i2o (snat_main_t * sm, f64 now,
          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;
+             ses->tcp_closed_timestamp = now + sm->tcp_transitory_timeout;
+             ses->last_lru_update = now;
            }
        }
     }
+
+  // move the session to proper LRU
+  if (ses->state)
+    {
+      ses->lru_head_index = tsm->tcp_trans_lru_head_index;
+    }
+  else
+    {
+      ses->lru_head_index = tsm->tcp_estab_lru_head_index;
+    }
+  clib_dlist_remove (tsm->lru_pool, ses->lru_index);
+  clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index);
   return 0;
 }
 
@@ -344,6 +342,7 @@ nat44_set_tcp_session_state_o2i (snat_main_t * sm, f64 now,
                                 u32 tcp_ack_number, u32 tcp_seq_number,
                                 u32 thread_index)
 {
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST))
     ses->state = NAT44_SES_RST;
   if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST))
@@ -364,9 +363,21 @@ nat44_set_tcp_session_state_o2i (snat_main_t * sm, f64 now,
        ses->state |= NAT44_SES_I2O_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;
+         ses->tcp_closed_timestamp = now + sm->tcp_transitory_timeout;
+         ses->last_lru_update = now;
        }
     }
+  // move the session to proper LRU
+  if (ses->state)
+    {
+      ses->lru_head_index = tsm->tcp_trans_lru_head_index;
+    }
+  else
+    {
+      ses->lru_head_index = tsm->tcp_estab_lru_head_index;
+    }
+  clib_dlist_remove (tsm->lru_pool, ses->lru_index);
+  clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index);
   return 0;
 }
 
@@ -411,7 +422,7 @@ always_inline void
 nat44_session_update_lru (snat_main_t * sm, snat_session_t * s,
                          u32 thread_index)
 {
-  /* don't update too often - timeout is in magnitude of seconds anyway */
+  /* don't update too often - timeout is in magnitude of seconds anyway */
   if (s->last_heard > s->last_lru_update + 1)
     {
       if (!sm->endpoint_dependent)
@@ -421,13 +432,13 @@ nat44_session_update_lru (snat_main_t * sm, snat_session_t * s,
          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
                              s->per_user_list_head_index, s->per_user_index);
        }
-
-      clib_dlist_remove (sm->per_thread_data[thread_index].global_lru_pool,
-                        s->global_lru_index);
-      clib_dlist_addtail (sm->per_thread_data[thread_index].global_lru_pool,
-                         sm->
-                         per_thread_data[thread_index].global_lru_head_index,
-                         s->global_lru_index);
+      else
+       {
+         clib_dlist_remove (sm->per_thread_data[thread_index].lru_pool,
+                            s->lru_index);
+         clib_dlist_addtail (sm->per_thread_data[thread_index].lru_pool,
+                             s->lru_head_index, s->lru_index);
+       }
       s->last_lru_update = s->last_heard;
     }
 }
index 9db82e0..e597191 100644 (file)
@@ -31,6 +31,7 @@
 #include <nat/nat44/inlines.h>
 #include <nat/nat_syslog.h>
 #include <nat/nat_ha.h>
+#include <nat/nat44/ed_inlines.h>
 
 static char *nat_out2in_ed_error_strings[] = {
 #define _(sym,string) string,
@@ -181,7 +182,7 @@ nat44_o2i_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void *arg)
       snat_free_outside_address_and_port (sm->addresses, ctx->thread_index,
                                          &s->out2in);
     delete:
-      nat44_ed_delete_session (sm, s, ctx->thread_index, 1);
+      nat_ed_session_delete (sm, s, ctx->thread_index, 1);
       return 1;
     }
 
@@ -216,7 +217,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
       return 0;
     }
 
-  s = nat_ed_session_alloc (sm, thread_index, now);
+  s = nat_ed_session_alloc (sm, thread_index, now, e_key.protocol);
   if (!s)
     {
       b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_USER_SESS_EXCEEDED];
@@ -260,7 +261,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
                                               tsm->snat_thread_index))
        {
          b->error = node->errors[NAT_OUT2IN_ED_ERROR_OUT_OF_PORTS];
-         nat44_ed_delete_session (sm, s, thread_index, 1);
+         nat_ed_session_delete (sm, s, thread_index, 1);
          if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 0))
            nat_elog_notice ("out2in-ed key del failed");
          return 0;
@@ -369,7 +370,7 @@ create_bypass_for_fwd (snat_main_t * sm, vlib_buffer_t * b, ip4_header_t * ip,
           (sm, rx_fib_index, thread_index)))
        return;
 
-      s = nat_ed_session_alloc (sm, thread_index, now);
+      s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol);
       if (!s)
        {
          nat_elog_warn ("create NAT session failed");
@@ -597,7 +598,7 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm,
       new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
 
       /* Create a new session */
-      s = nat_ed_session_alloc (sm, thread_index, now);
+      s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol);
       if (!s)
        {
          b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_USER_SESS_EXCEEDED];
@@ -739,9 +740,9 @@ 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 (s0->tcp_closed_timestamp)
            {
-             if (now >= s0->tcp_close_timestamp)
+             if (now >= s0->tcp_closed_timestamp)
                {
                  // session is closed, go slow path
                  next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
@@ -763,7 +764,7 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm,
            {
              // session is closed, go slow path
              nat_free_session_data (sm, s0, thread_index, 0);
-             nat44_ed_delete_session (sm, s0, thread_index, 1);
+             nat_ed_session_delete (sm, s0, thread_index, 1);
              next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
              goto trace0;
            }
@@ -1023,10 +1024,10 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm,
            {
              s0 = pool_elt_at_index (tsm->sessions, value0.value);
 
-             if (s0->tcp_close_timestamp && now >= s0->tcp_close_timestamp)
+             if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp)
                {
                  nat_free_session_data (sm, s0, thread_index, 0);
-                 nat44_ed_delete_session (sm, s0, thread_index, 1);
+                 nat_ed_session_delete (sm, s0, thread_index, 1);
                  s0 = NULL;
                }
            }
index a8e2af7..2ba7a9b 100644 (file)
@@ -7059,6 +7059,111 @@ class TestNAT44EndpointDependent(MethodHolder):
         self.logger.info(self.vapi.cli("show nat timeouts"))
 
 
+class TestNAT44EndpointDependent2(MethodHolder):
+    """ Endpoint-Dependent mapping and filtering extra test cases """
+
+    translation_buckets = 5
+
+    @classmethod
+    def setUpConstants(cls):
+        super(TestNAT44EndpointDependent2, cls).setUpConstants()
+        cls.vpp_cmdline.extend([
+            "nat", "{", "endpoint-dependent",
+            "translation hash buckets %d" % cls.translation_buckets,
+            "}"
+        ])
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestNAT44EndpointDependent2, cls).setUpClass()
+        cls.vapi.cli("set log class nat level debug")
+
+        cls.nat_addr = '10.0.0.3'
+
+        cls.create_pg_interfaces(range(2))
+
+        for i in cls.pg_interfaces:
+            i.admin_up()
+            i.config_ip4()
+            i.resolve_arp()
+
+    def setUp(self):
+        super(TestNAT44EndpointDependent2, self).setUp()
+        self.vapi.nat_set_timeouts(
+            udp=1, tcp_established=7440, tcp_transitory=30, icmp=1)
+        self.nat44_add_address(self.nat_addr)
+        flags = self.config_flags.NAT_IS_INSIDE
+        self.vapi.nat44_interface_add_del_feature(
+            sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1)
+        self.vapi.nat44_interface_add_del_feature(
+            sw_if_index=self.pg1.sw_if_index, is_add=1)
+
+    @classmethod
+    def tearDownClass(cls):
+        super(TestNAT44EndpointDependent2, cls).tearDownClass()
+
+    def init_tcp_session(self, in_if, out_if, sport, ext_dport):
+        # SYN packet in->out
+        p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) /
+             IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) /
+             TCP(sport=sport, dport=ext_dport, flags="S"))
+        in_if.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = out_if.get_capture(1)
+        p = capture[0]
+        tcp_port_out = p[TCP].sport
+
+        # SYN + ACK packet out->in
+        p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) /
+             IP(src=out_if.remote_ip4, dst=self.nat_addr) /
+             TCP(sport=ext_dport, dport=tcp_port_out, flags="SA"))
+        out_if.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        in_if.get_capture(1)
+
+        # ACK packet in->out
+        p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) /
+             IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) /
+             TCP(sport=sport, dport=ext_dport, flags="A"))
+        in_if.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        out_if.get_capture(1)
+
+        return tcp_port_out
+
+    def test_lru_cleanup(self):
+        """ LRU cleanup algorithm """
+        tcp_port_out = self.init_tcp_session(self.pg0, self.pg1, 2000, 80)
+        max_translations = 10 * self.translation_buckets
+        pkts = []
+        for i in range(0, max_translations - 1):
+            p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+                 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) /
+                 UDP(sport=7000+i, dport=80))
+            pkts.append(p)
+
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        self.pg1.get_capture(len(pkts))
+        self.sleep(1.5, "wait for timeouts")
+
+        pkts = []
+        for i in range(0, max_translations - 1):
+            p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+                 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) /
+                 ICMP(id=8000+i, 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(len(pkts))
+
+
 class TestNAT44Out2InDPO(MethodHolder):
     """ NAT44 Test Cases using out2in DPO """