NAT44: delete user with zero sessions (VPP-1282)

Change-Id: I756e3ad3de9ffe1494221ef95c1943c8591f8f50
Signed-off-by: Matus Fabian <matfabia@cisco.com>
diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c
index be1ddb3..ac0490f 100755
--- a/src/plugins/nat/in2out.c
+++ b/src/plugins/nat/in2out.c
@@ -347,14 +347,6 @@
 
   key1.protocol = key0->protocol;
 
-  u = nat_user_get_or_create (sm, &ip0->src_address, rx_fib_index0,
-                              thread_index);
-  if (!u)
-    {
-      nat_log_warn ("create NAT user failed");
-      return SNAT_IN2OUT_NEXT_DROP;
-    }
-
   /* First try to match static mapping by local address and port */
   if (snat_static_mapping_match (sm, *key0, &key1, 0, 0, 0, 0))
     {
@@ -372,9 +364,18 @@
   else
     is_sm = 1;
 
+  u = nat_user_get_or_create (sm, &ip0->src_address, rx_fib_index0,
+                              thread_index);
+  if (!u)
+    {
+      nat_log_warn ("create NAT user failed");
+      return SNAT_IN2OUT_NEXT_DROP;
+    }
+
   s = nat_session_alloc_or_recycle (sm, u, thread_index);
   if (!s)
     {
+      nat44_delete_user_with_no_session (sm, u, thread_index);
       nat_log_warn ("create NAT session failed");
       return SNAT_IN2OUT_NEXT_DROP;
     }
@@ -2442,6 +2443,7 @@
   s = nat_session_alloc_or_recycle (sm, u, thread_index);
   if (!s)
     {
+      nat44_delete_user_with_no_session (sm, u, thread_index);
       nat_log_warn ("create NAT session failed");
       return SNAT_IN2OUT_NEXT_DROP;
     }
@@ -2924,6 +2926,7 @@
       s = nat_session_alloc_or_recycle (sm, u, thread_index);
       if (!s)
         {
+          nat44_delete_user_with_no_session (sm, u, thread_index);
           nat_log_warn ("create NAT session failed");
           return 0;
         }
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index cdf05fd..79d1113 100755
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -328,6 +328,8 @@
       /* 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
     {
@@ -1086,7 +1088,7 @@
                       nat_free_session_data (sm, s, tsm - sm->per_thread_data);
                       nat44_delete_session (sm, s, tsm - sm->per_thread_data);
 
-                      if (!addr_only)
+                      if (!addr_only && !sm->endpoint_dependent)
                         break;
                     }
                 }
@@ -1192,14 +1194,9 @@
                       nat_free_session_data (sm, s, tsm - sm->per_thread_data);
                       nat44_delete_session (sm, s, tsm - sm->per_thread_data);
 
-                      if (!addr_only)
+                      if (!addr_only && !sm->endpoint_dependent)
                         break;
                     }
-                  if (addr_only && (u->nstaticsessions == 0) && (u->nsessions == 0))
-                    {
-                      pool_put (tsm->users, u);
-                      clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 0);
-                    }
                 }
             }
         }
diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c
index 5887efe..d467861 100644
--- a/src/plugins/nat/nat_api.c
+++ b/src/plugins/nat/nat_api.c
@@ -1195,8 +1195,12 @@
 
   /* *INDENT-OFF* */
   vec_foreach (tsm, sm->per_thread_data)
-    vec_foreach (u, tsm->users)
-      send_nat44_user_details (u, reg, mp->context);
+    {
+      pool_foreach (u, tsm->users,
+      ({
+        send_nat44_user_details (u, reg, mp->context);
+      }));
+    }
   /* *INDENT-ON* */
 }
 
diff --git a/src/plugins/nat/nat_inlines.h b/src/plugins/nat/nat_inlines.h
index 3724986..a069d66 100644
--- a/src/plugins/nat/nat_inlines.h
+++ b/src/plugins/nat/nat_inlines.h
@@ -141,6 +141,26 @@
 }
 
 always_inline void
+nat44_delete_user_with_no_session (snat_main_t * sm, snat_user_t * u,
+				   u32 thread_index)
+{
+  clib_bihash_kv_8_8_t kv;
+  snat_user_key_t u_key;
+  snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
+						       thread_index);
+
+  if (u->nstaticsessions == 0 && u->nsessions == 0)
+    {
+      u_key.addr.as_u32 = u->addr.as_u32;
+      u_key.fib_index = u->fib_index;
+      kv.key = u_key.as_u64;
+      pool_put_index (tsm->list_pool, u->sessions_per_user_list_head_index);
+      pool_put (tsm->users, u);
+      clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 0);
+    }
+}
+
+always_inline void
 nat44_delete_session (snat_main_t * sm, snat_session_t * ses,
 		      u32 thread_index)
 {
@@ -151,6 +171,11 @@
   snat_user_t *u;
 
   nat_log_debug ("session deleted %U", format_snat_session, tsm, ses);
+
+  clib_dlist_remove (tsm->list_pool, ses->per_user_index);
+  pool_put_index (tsm->list_pool, ses->per_user_index);
+  pool_put (tsm->sessions, ses);
+
   u_key.addr = ses->in2out.addr;
   u_key.fib_index = ses->in2out.fib_index;
   kv.key = u_key.as_u64;
@@ -161,10 +186,9 @@
 	u->nstaticsessions--;
       else
 	u->nsessions--;
+
+      nat44_delete_user_with_no_session (sm, u, thread_index);
     }
-  clib_dlist_remove (tsm->list_pool, ses->per_user_index);
-  pool_put_index (tsm->list_pool, ses->per_user_index);
-  pool_put (tsm->sessions, ses);
 }
 
 /** \brief Set TCP session state.
diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c
index 156b728..774ae67 100755
--- a/src/plugins/nat/out2in.c
+++ b/src/plugins/nat/out2in.c
@@ -188,6 +188,7 @@
   s = nat_session_alloc_or_recycle (sm, u, thread_index);
   if (!s)
     {
+      nat44_delete_user_with_no_session (sm, u, thread_index);
       nat_log_warn ("create NAT session failed");
       return 0;
     }
@@ -1599,6 +1600,7 @@
   s = nat_session_alloc_or_recycle (sm, u, thread_index);
   if (!s)
     {
+      nat44_delete_user_with_no_session (sm, u, thread_index);
       nat_log_warn ("create NAT session failed");
       return 0;
     }
@@ -1780,6 +1782,7 @@
       s = nat_session_alloc_or_recycle (sm, u, thread_index);
       if (!s)
         {
+          nat44_delete_user_with_no_session (sm, u, thread_index);
           nat_log_warn ("create NAT session failed");
           return;
         }
@@ -1980,6 +1983,7 @@
       s = nat_session_alloc_or_recycle (sm, u, thread_index);
       if (!s)
         {
+          nat44_delete_user_with_no_session (sm, u, thread_index);
           nat_log_warn ("create NAT session failed");
           return 0;
         }
diff --git a/test/test_nat.py b/test/test_nat.py
index 4bae701..0d723b0 100644
--- a/test/test_nat.py
+++ b/test/test_nat.py
@@ -136,6 +136,7 @@
 
         self.vapi.nat_set_reass()
         self.vapi.nat_set_reass(is_ip6=1)
+        self.verify_no_nat44_user()
 
     def nat44_add_static_mapping(self, local_ip, external_ip='0.0.0.0',
                                  local_port=0, external_port=0, vrf_id=0,
@@ -987,6 +988,11 @@
         # postNAPTDestinationTransportPort
         self.assertEqual(struct.pack("!H", dst_port), record[228])
 
+    def verify_no_nat44_user(self):
+        """ Verify that there is no NAT44 user """
+        users = self.vapi.nat44_user_dump()
+        self.assertEqual(len(users), 0)
+
 
 class TestNAT44(MethodHolder):
     """ NAT44 Test Cases """
@@ -2909,6 +2915,12 @@
         sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0)
         self.assertEqual(nsessions - len(sessions), 2)
 
+        self.vapi.nat44_del_session(sessions[0].inside_ip_address,
+                                    sessions[0].inside_port,
+                                    sessions[0].protocol)
+
+        self.verify_no_nat44_user()
+
     def test_set_get_reass(self):
         """ NAT44 set/get virtual fragmentation reassembly """
         reas_cfg1 = self.vapi.nat_get_reass()