NAT: total users and sessions gauges (VPP-1484)
[vpp.git] / src / plugins / nat / nat.c
index efca440..c2c812d 100755 (executable)
@@ -29,6 +29,7 @@
 #include <nat/nat_reass.h>
 #include <nat/nat_inlines.h>
 #include <nat/nat_affinity.h>
+#include <nat/nat_syslog.h>
 #include <vnet/fib/fib_table.h>
 #include <vnet/fib/ip4_fib.h>
 
@@ -236,6 +237,13 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
       ed_kv.key[1] = ed_key.as_u64[1];
       if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))
        nat_log_warn ("in2out_ed key del failed");
+
+      nat_syslog_nat44_sdel (s->user_index, s->in2out.fib_index,
+                            &s->in2out.addr, s->in2out.port,
+                            &s->ext_host_nat_addr, s->ext_host_nat_port,
+                            &s->out2in.addr, s->out2in.port,
+                            &s->ext_host_addr, s->ext_host_port,
+                            s->in2out.protocol, is_twice_nat_session (s));
     }
   else
     {
@@ -245,6 +253,11 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
       kv.key = s->out2in.as_u64;
       if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
        nat_log_warn ("out2in key del failed");
+
+      nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
+                              &s->in2out.addr, s->in2out.port,
+                              &s->out2in.addr, s->out2in.port,
+                              s->in2out.protocol);
     }
 
   if (snat_is_unk_proto_session (s))
@@ -293,7 +306,7 @@ nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index,
     {
       /* no, make a new one */
       pool_get (tsm->users, u);
-      memset (u, 0, sizeof (*u));
+      clib_memset (u, 0, sizeof (*u));
       u->addr.as_u32 = addr->as_u32;
       u->fib_index = fib_index;
 
@@ -309,6 +322,9 @@ nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index,
       /* add user */
       if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
        nat_log_warn ("user_hash keay add failed");
+
+      vlib_set_simple_counter (&sm->total_users, thread_index, 0,
+                              pool_elts (tsm->users));
     }
   else
     {
@@ -368,7 +384,7 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
   else
     {
       pool_get (tsm->sessions, s);
-      memset (s, 0, sizeof (*s));
+      clib_memset (s, 0, sizeof (*s));
 
       /* Create list elts */
       pool_get (tsm->list_pool, per_user_translation_list_elt);
@@ -382,42 +398,88 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
       clib_dlist_addtail (tsm->list_pool,
                          s->per_user_list_head_index,
                          per_user_translation_list_elt - tsm->list_pool);
+
+      s->user_index = u - tsm->users;
+      vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
+                              pool_elts (tsm->sessions));
     }
 
   return s;
 }
 
 snat_session_t *
-nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index)
+nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
+                     f64 now)
 {
   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;
+  dlist_elt_t *per_user_translation_list_elt, *oldest_elt;
+  u32 oldest_index;
+  u64 sess_timeout_time;
 
-  if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user)
+  if (PREDICT_FALSE (!(u->nsessions) && !(u->nstaticsessions)))
+    goto alloc_new;
+
+  oldest_index =
+    clib_dlist_remove_head (tsm->list_pool,
+                           u->sessions_per_user_list_head_index);
+  oldest_elt = pool_elt_at_index (tsm->list_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)
     {
-      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;
+      clib_dlist_addtail (tsm->list_pool,
+                         u->sessions_per_user_list_head_index, oldest_index);
+      nat_free_session_data (sm, s, thread_index);
+      if (snat_is_session_static (s))
+       u->nstaticsessions--;
+      else
+       u->nsessions--;
+      s->flags = 0;
+      s->total_bytes = 0;
+      s->total_pkts = 0;
+      s->state = 0;
+      s->ext_host_addr.as_u32 = 0;
+      s->ext_host_port = 0;
+      s->ext_host_nat_addr.as_u32 = 0;
+      s->ext_host_nat_port = 0;
     }
+  else
+    {
+      clib_dlist_addhead (tsm->list_pool,
+                         u->sessions_per_user_list_head_index, oldest_index);
+      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;
+       }
+      else
+       {
+       alloc_new:
+         pool_get (tsm->sessions, s);
+         clib_memset (s, 0, sizeof (*s));
+
+         /* 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);
+       }
 
-  pool_get (tsm->sessions, s);
-  memset (s, 0, sizeof (*s));
-
-  /* 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);
+      vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
+                              pool_elts (tsm->sessions));
+    }
 
   return s;
 }
@@ -522,6 +584,10 @@ is_snat_address_used_in_static_mapping (snat_main_t * sm, ip4_address_t addr)
   /* *INDENT-OFF* */
   pool_foreach (m, sm->static_mappings,
   ({
+      if (is_addr_only_static_mapping (m) ||
+          is_out2in_only_static_mapping (m) ||
+          is_identity_static_mapping (m))
+        continue;
       if (m->external_addr.as_u32 == addr.as_u32)
         return 1;
   }));
@@ -547,7 +613,9 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
                                       u16 e_port,
                                       u32 vrf_id,
                                       snat_protocol_t proto,
-                                      int addr_only, int is_add, u8 * tag)
+                                      int addr_only, int is_add, u8 * tag,
+                                      int twice_nat, int out2in_only,
+                                      int identity_nat)
 {
   snat_static_map_resolve_t *rp;
 
@@ -560,6 +628,9 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
   rp->proto = proto;
   rp->addr_only = addr_only;
   rp->is_add = is_add;
+  rp->twice_nat = twice_nat;
+  rp->out2in_only = out2in_only;
+  rp->identity_nat = identity_nat;
   rp->tag = vec_dup (tag);
 }
 
@@ -577,34 +648,12 @@ get_thread_idx_by_port (u16 e_port)
   return thread_idx;
 }
 
-/**
- * @brief Add static mapping.
- *
- * Create static mapping between local addr+port and external addr+port.
- *
- * @param l_addr Local IPv4 address.
- * @param e_addr External IPv4 address.
- * @param l_port Local port number.
- * @param e_port External port number.
- * @param vrf_id VRF ID.
- * @param addr_only If 0 address port and pair mapping, otherwise address only.
- * @param sw_if_index External port instead of specific IP address.
- * @param is_add If 0 delete static mapping, otherwise add.
- * @param twice_nat If value is TWICE_NAT then translate external host address
- *                  and port.
- *                  If value is TWICE_NAT_SELF then translate external host
- *                  address and port whenever external host address equals
- *                  local address of internal host.
- * @param out2in_only If 1 rule match only out2in direction
- * @param tag - opaque string tag
- *
- * @returns
- */
 int
 snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
                         u16 l_port, u16 e_port, u32 vrf_id, int addr_only,
                         u32 sw_if_index, snat_protocol_t proto, int is_add,
-                        twice_nat_type_t twice_nat, u8 out2in_only, u8 * tag)
+                        twice_nat_type_t twice_nat, u8 out2in_only, u8 * tag,
+                        u8 identity_nat)
 {
   snat_main_t *sm = &snat_main;
   snat_static_mapping_t *m;
@@ -612,7 +661,6 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
   clib_bihash_kv_8_8_t kv, value;
   snat_address_t *a = 0;
   u32 fib_index = ~0;
-  uword *p;
   snat_interface_t *interface;
   int i;
   snat_main_per_thread_data_t *tsm;
@@ -624,6 +672,8 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
   u64 user_index;
   snat_session_t *s;
   snat_static_map_resolve_t *rp, *rp_match = 0;
+  nat44_lb_addr_port_t *local;
+  u32 find = ~0;
 
   if (!sm->endpoint_dependent)
     {
@@ -666,7 +716,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
 
          snat_add_static_mapping_when_resolved
            (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto,
-            addr_only, is_add, tag);
+            addr_only, is_add, tag, twice_nat, out2in_only, identity_nat);
 
          /* DHCP resolution required? */
          if (first_int_addr == 0)
@@ -713,19 +763,42 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
   if (is_add)
     {
       if (m)
-       return VNET_API_ERROR_VALUE_EXIST;
+       {
+         if (is_identity_static_mapping (m))
+           {
+              /* *INDENT-OFF* */
+              pool_foreach (local, m->locals,
+              ({
+                if (local->vrf_id == vrf_id)
+                  return VNET_API_ERROR_VALUE_EXIST;
+              }));
+              /* *INDENT-ON* */
+             pool_get (m->locals, local);
+             local->vrf_id = vrf_id;
+             local->fib_index =
+               fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
+                                                  FIB_SOURCE_PLUGIN_LOW);
+             m_key.addr = m->local_addr;
+             m_key.port = m->local_port;
+             m_key.protocol = m->proto;
+             m_key.fib_index = local->fib_index;
+             kv.key = m_key.as_u64;
+             kv.value = m - sm->static_mappings;
+             clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
+             return 0;
+           }
+         else
+           return VNET_API_ERROR_VALUE_EXIST;
+       }
 
       if (twice_nat && addr_only)
        return VNET_API_ERROR_UNSUPPORTED;
 
       /* Convert VRF id to FIB index */
       if (vrf_id != ~0)
-       {
-         p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id);
-         if (!p)
-           return VNET_API_ERROR_NO_SUCH_FIB;
-         fib_index = p[0];
-       }
+       fib_index =
+         fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
+                                            FIB_SOURCE_PLUGIN_LOW);
       /* If not specified use inside VRF id from SNAT plugin startup config */
       else
        {
@@ -733,7 +806,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
          vrf_id = sm->inside_vrf_id;
        }
 
-      if (!out2in_only)
+      if (!(out2in_only || identity_nat))
        {
          m_key.addr = l_addr;
          m_key.port = addr_only ? 0 : l_port;
@@ -802,15 +875,27 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
        }
 
       pool_get (sm->static_mappings, m);
-      memset (m, 0, sizeof (*m));
+      clib_memset (m, 0, sizeof (*m));
       m->tag = vec_dup (tag);
       m->local_addr = l_addr;
       m->external_addr = e_addr;
-      m->addr_only = addr_only;
-      m->vrf_id = vrf_id;
-      m->fib_index = fib_index;
       m->twice_nat = twice_nat;
-      m->out2in_only = out2in_only;
+      if (out2in_only)
+       m->flags |= NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY;
+      if (addr_only)
+       m->flags |= NAT_STATIC_MAPPING_FLAG_ADDR_ONLY;
+      if (identity_nat)
+       {
+         m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT;
+         pool_get (m->locals, local);
+         local->vrf_id = vrf_id;
+         local->fib_index = fib_index;
+       }
+      else
+       {
+         m->vrf_id = vrf_id;
+         m->fib_index = fib_index;
+       }
       if (!addr_only)
        {
          m->local_port = l_port;
@@ -832,7 +917,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
       m_key.addr = m->local_addr;
       m_key.port = m->local_port;
       m_key.protocol = m->proto;
-      m_key.fib_index = m->fib_index;
+      m_key.fib_index = fib_index;
       kv.key = m_key.as_u64;
       kv.value = m - sm->static_mappings;
       if (!out2in_only)
@@ -897,6 +982,28 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
            return VNET_API_ERROR_NO_SUCH_ENTRY;
        }
 
+      if (identity_nat)
+       {
+         if (vrf_id == ~0)
+           vrf_id = sm->inside_vrf_id;
+
+          /* *INDENT-OFF* */
+          pool_foreach (local, m->locals,
+          ({
+           if (local->vrf_id == vrf_id)
+              find = local - m->locals;
+         }));
+          /* *INDENT-ON* */
+         if (find == ~0)
+           return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+         local = pool_elt_at_index (m->locals, find);
+         fib_index = local->fib_index;
+         pool_put (m->locals, local);
+       }
+      else
+       fib_index = m->fib_index;
+
       /* Free external address port */
       if (!(addr_only || sm->static_mapping_only || out2in_only))
        {
@@ -935,23 +1042,17 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
       m_key.addr = m->local_addr;
       m_key.port = m->local_port;
       m_key.protocol = m->proto;
-      m_key.fib_index = m->fib_index;
+      m_key.fib_index = fib_index;
       kv.key = m_key.as_u64;
       if (!out2in_only)
        clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0);
 
-      m_key.addr = m->external_addr;
-      m_key.port = m->external_port;
-      m_key.fib_index = 0;
-      kv.key = m_key.as_u64;
-      clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0);
-
       /* Delete session(s) for static mapping if exist */
       if (!(sm->static_mapping_only) ||
          (sm->static_mapping_only && sm->static_mapping_connection_tracking))
        {
          u_key.addr = m->local_addr;
-         u_key.fib_index = m->fib_index;
+         u_key.fib_index = fib_index;
          kv.key = u_key.as_u64;
          if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
            {
@@ -995,6 +1096,16 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
            }
        }
 
+      fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW);
+      if (pool_elts (m->locals))
+       return 0;
+
+      m_key.addr = m->external_addr;
+      m_key.port = m->external_port;
+      m_key.fib_index = 0;
+      kv.key = m_key.as_u64;
+      clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0);
+
       vec_free (m->tag);
       vec_free (m->workers);
       /* Delete static mapping from pool */
@@ -1108,14 +1219,15 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
        }
 
       pool_get (sm->static_mappings, m);
-      memset (m, 0, sizeof (*m));
+      clib_memset (m, 0, sizeof (*m));
       m->tag = vec_dup (tag);
       m->external_addr = e_addr;
-      m->addr_only = 0;
       m->external_port = e_port;
       m->proto = proto;
       m->twice_nat = twice_nat;
-      m->out2in_only = out2in_only;
+      m->flags |= NAT_STATIC_MAPPING_FLAG_LB;
+      if (out2in_only)
+       m->flags |= NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY;
       m->affinity = affinity;
 
       if (affinity)
@@ -1154,7 +1266,8 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
            }
          locals[i].prefix = (i == 0) ? locals[i].probability :
            (locals[i - 1].prefix + locals[i].probability);
-         vec_add1 (m->locals, locals[i]);
+         pool_get (m->locals, local);
+         *local = locals[i];
          if (sm->num_workers > 1)
            {
              ip4_header_t ip = {
@@ -1182,6 +1295,9 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       if (!m)
        return VNET_API_ERROR_NO_SUCH_ENTRY;
 
+      if (!is_lb_static_mapping (m))
+       return VNET_API_ERROR_INVALID_VALUE;
+
       /* Free external address port */
       if (!(sm->static_mapping_only || out2in_only))
        {
@@ -1224,8 +1340,8 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
        }
 
       /* *INDENT-OFF* */
-      vec_foreach (local, m->locals)
-        {
+      pool_foreach (local, m->locals,
+      ({
           fib_table_unlock (local->fib_index, FIB_PROTOCOL_IP4,
                             FIB_SOURCE_PLUGIN_LOW);
           m_key.addr = local->addr;
@@ -1254,7 +1370,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
 
           /* Delete sessions */
           u_key.addr = local->addr;
-          u_key.fib_index = m->fib_index;
+          u_key.fib_index = local->fib_index;
           kv.key = u_key.as_u64;
           if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
             {
@@ -1284,11 +1400,11 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                     }
                 }
             }
-        }
+      }));
       /* *INDENT-ON* */
       if (m->affinity)
        nat_affinity_flush_service (m->affinity_per_service_list_head_index);
-      vec_free (m->locals);
+      pool_free (m->locals);
       vec_free (m->tag);
       vec_free (m->workers);
 
@@ -1298,6 +1414,187 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
   return 0;
 }
 
+int
+nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port,
+                                      ip4_address_t l_addr, u16 l_port,
+                                      snat_protocol_t proto, u32 vrf_id,
+                                      u8 probability, u8 is_add)
+{
+  snat_main_t *sm = &snat_main;
+  snat_static_mapping_t *m = 0;
+  snat_session_key_t m_key;
+  clib_bihash_kv_8_8_t kv, value;
+  nat44_lb_addr_port_t *local, *prev_local, *match_local = 0;
+  snat_main_per_thread_data_t *tsm;
+  snat_user_key_t u_key;
+  snat_user_t *u;
+  snat_session_t *s;
+  dlist_elt_t *head, *elt;
+  u32 elt_index, head_index, ses_index, *locals = 0;
+  uword *bitmap = 0;
+  int i;
+
+  if (!sm->endpoint_dependent)
+    return VNET_API_ERROR_FEATURE_DISABLED;
+
+  m_key.addr = e_addr;
+  m_key.port = e_port;
+  m_key.protocol = proto;
+  m_key.fib_index = 0;
+  kv.key = m_key.as_u64;
+  if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
+    m = pool_elt_at_index (sm->static_mappings, value.value);
+
+  if (!m)
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+  if (!is_lb_static_mapping (m))
+    return VNET_API_ERROR_INVALID_VALUE;
+
+  /* *INDENT-OFF* */
+  pool_foreach (local, m->locals,
+  ({
+    if ((local->addr.as_u32 == l_addr.as_u32) && (local->port == l_port) &&
+        (local->vrf_id == vrf_id))
+      {
+        match_local = local;
+        break;
+      }
+  }));
+  /* *INDENT-ON* */
+
+  if (is_add)
+    {
+      if (match_local)
+       return VNET_API_ERROR_VALUE_EXIST;
+
+      pool_get (m->locals, local);
+      clib_memset (local, 0, sizeof (*local));
+      local->addr.as_u32 = l_addr.as_u32;
+      local->port = l_port;
+      local->probability = probability;
+      local->vrf_id = vrf_id;
+      local->fib_index =
+       fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id,
+                                          FIB_SOURCE_PLUGIN_LOW);
+
+      if (!is_out2in_only_static_mapping (m))
+       {
+         m_key.addr = l_addr;
+         m_key.port = l_port;
+         m_key.fib_index = local->fib_index;
+         kv.key = m_key.as_u64;
+         kv.value = m - sm->static_mappings;
+         if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1))
+           nat_log_err ("static_mapping_by_local key add failed");
+       }
+    }
+  else
+    {
+      if (!match_local)
+       return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+      if (pool_elts (m->locals) < 3)
+       return VNET_API_ERROR_UNSPECIFIED;
+
+      fib_table_unlock (match_local->fib_index, FIB_PROTOCOL_IP4,
+                       FIB_SOURCE_PLUGIN_LOW);
+
+      if (!is_out2in_only_static_mapping (m))
+       {
+         m_key.addr = l_addr;
+         m_key.port = l_port;
+         m_key.fib_index = match_local->fib_index;
+         kv.key = m_key.as_u64;
+         if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0))
+           nat_log_err ("static_mapping_by_local key del failed");
+       }
+
+      if (sm->num_workers > 1)
+       {
+         ip4_header_t ip = {
+           .src_address = local->addr,
+         };
+         tsm = vec_elt_at_index (sm->per_thread_data,
+                                 sm->worker_in2out_cb (&ip, m->fib_index));
+       }
+      else
+       tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
+
+      /* Delete sessions */
+      u_key.addr = match_local->addr;
+      u_key.fib_index = match_local->fib_index;
+      kv.key = u_key.as_u64;
+      if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
+       {
+         u = pool_elt_at_index (tsm->users, value.value);
+         if (u->nstaticsessions)
+           {
+             head_index = u->sessions_per_user_list_head_index;
+             head = pool_elt_at_index (tsm->list_pool, head_index);
+             elt_index = head->next;
+             elt = pool_elt_at_index (tsm->list_pool, elt_index);
+             ses_index = elt->value;
+             while (ses_index != ~0)
+               {
+                 s = pool_elt_at_index (tsm->sessions, ses_index);
+                 elt = pool_elt_at_index (tsm->list_pool, elt->next);
+                 ses_index = elt->value;
+
+                 if (!(is_lb_session (s)))
+                   continue;
+
+                 if ((s->in2out.addr.as_u32 != match_local->addr.as_u32) ||
+                     (clib_net_to_host_u16 (s->in2out.port) !=
+                      match_local->port))
+                   continue;
+
+                 nat_free_session_data (sm, s, tsm - sm->per_thread_data);
+                 nat44_delete_session (sm, s, tsm - sm->per_thread_data);
+               }
+           }
+       }
+
+      pool_put (m->locals, match_local);
+    }
+
+  vec_free (m->workers);
+
+  /* *INDENT-OFF* */
+  pool_foreach (local, m->locals,
+  ({
+    vec_add1 (locals, local - m->locals);
+    if (sm->num_workers > 1)
+      {
+        ip4_header_t ip;
+        ip.src_address.as_u32 = local->addr.as_u32,
+        bitmap = clib_bitmap_set (bitmap,
+                                  sm->worker_in2out_cb (&ip, local->fib_index),
+                                  1);
+      }
+  }));
+  /* *INDENT-ON* */
+
+  local = pool_elt_at_index (m->locals, locals[0]);
+  local->prefix = local->probability;
+  for (i = 1; i < vec_len (locals); i++)
+    {
+      local = pool_elt_at_index (m->locals, locals[i]);
+      prev_local = pool_elt_at_index (m->locals, locals[i - 1]);
+      local->prefix = local->probability + prev_local->prefix;
+    }
+
+  /* Assign workers */
+  if (sm->num_workers > 1)
+    {
+      /* *INDENT-OFF* */
+      clib_bitmap_foreach (i, bitmap, ({ vec_add1(m->workers, i); }));
+      /* *INDENT-ON* */
+    }
+
+  return 0;
+}
+
 int
 snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm,
                  u8 twice_nat)
@@ -1332,9 +1629,9 @@ snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm,
           if (m->external_addr.as_u32 == addr.as_u32)
             (void) snat_add_static_mapping (m->local_addr, m->external_addr,
                                             m->local_port, m->external_port,
-                                            m->vrf_id, m->addr_only, ~0,
+                                            m->vrf_id, is_addr_only_static_mapping(m), ~0,
                                             m->proto, 0, m->twice_nat,
-                                            m->out2in_only, m->tag);
+                                            is_out2in_only_static_mapping(m), m->tag, is_identity_static_mapping(m));
       }));
       /* *INDENT-ON* */
     }
@@ -1646,7 +1943,7 @@ fib:
 
   pool_foreach (m, sm->static_mappings,
   ({
-    if (!(m->addr_only) || (m->local_addr.as_u32 == m->external_addr.as_u32))
+    if (!(is_addr_only_static_mapping(m)) || (m->local_addr.as_u32 == m->external_addr.as_u32))
       continue;
 
     snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
@@ -1774,7 +2071,7 @@ fib:
 
   pool_foreach (m, sm->static_mappings,
   ({
-    if (!(m->addr_only)  || (m->local_addr.as_u32 == m->external_addr.as_u32))
+    if (!((is_addr_only_static_mapping(m)))  || (m->local_addr.as_u32 == m->external_addr.as_u32))
       continue;
 
     snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del);
@@ -1918,6 +2215,16 @@ snat_init (vlib_main_t * vm)
 
   nat_dpo_module_init ();
 
+  /* Init counters */
+  sm->total_users.name = "total-users";
+  sm->total_users.stat_segment_name = "/nat44/total-users";
+  vlib_validate_simple_counter (&sm->total_users, 0);
+  vlib_zero_simple_counter (&sm->total_users, 0);
+  sm->total_sessions.name = "total-sessions";
+  sm->total_sessions.stat_segment_name = "/nat44/total-sessions";
+  vlib_validate_simple_counter (&sm->total_sessions, 0);
+  vlib_zero_simple_counter (&sm->total_sessions, 0);
+
   /* Init IPFIX logging */
   snat_ipfix_logging_init (vm);
 
@@ -1981,14 +2288,16 @@ snat_static_mapping_match (snat_main_t * sm,
                           u8 by_external,
                           u8 * is_addr_only,
                           twice_nat_type_t * twice_nat,
-                          lb_nat_type_t * lb, ip4_address_t * ext_host_addr)
+                          lb_nat_type_t * lb, ip4_address_t * ext_host_addr,
+                          u8 * is_identity_nat)
 {
   clib_bihash_kv_8_8_t kv, value;
   snat_static_mapping_t *m;
   snat_session_key_t m_key;
   clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local;
-  u32 rand, lo = 0, hi, mid;
+  u32 rand, lo = 0, hi, mid, *tmp = 0, i;
   u8 backend_index;
+  nat44_lb_addr_port_t *local;
 
   m_key.fib_index = match.fib_index;
   if (by_external)
@@ -2017,7 +2326,7 @@ snat_static_mapping_match (snat_main_t * sm,
 
   if (by_external)
     {
-      if (vec_len (m->locals))
+      if (is_lb_static_mapping (m))
        {
          if (PREDICT_FALSE (lb != 0))
            *lb = m->affinity ? AFFINITY_LB_NAT : LB_NAT;
@@ -2028,42 +2337,52 @@ snat_static_mapping_match (snat_main_t * sm,
                                              &backend_index))
                goto get_local;
 
-             mapping->addr = m->locals[backend_index].addr;
-             mapping->port =
-               clib_host_to_net_u16 (m->locals[backend_index].port);
-             mapping->fib_index = m->locals[backend_index].fib_index;
+             local = pool_elt_at_index (m->locals, backend_index);
+             mapping->addr = local->addr;
+             mapping->port = clib_host_to_net_u16 (local->port);
+             mapping->fib_index = local->fib_index;
              goto end;
            }
        get_local:
-         hi = vec_len (m->locals) - 1;
-         rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix);
+          /* *INDENT-OFF* */
+          pool_foreach_index (i, m->locals,
+          ({
+            vec_add1 (tmp, i);
+          }));
+          /* *INDENT-ON* */
+         hi = vec_len (tmp) - 1;
+         local = pool_elt_at_index (m->locals, tmp[hi]);
+         rand = 1 + (random_u32 (&sm->random_seed) % local->prefix);
          while (lo < hi)
            {
              mid = ((hi - lo) >> 1) + lo;
-             (rand > m->locals[mid].prefix) ? (lo = mid + 1) : (hi = mid);
+             local = pool_elt_at_index (m->locals, tmp[mid]);
+             (rand > local->prefix) ? (lo = mid + 1) : (hi = mid);
            }
-         if (!(m->locals[lo].prefix >= rand))
+         local = pool_elt_at_index (m->locals, tmp[lo]);
+         if (!(local->prefix >= rand))
            return 1;
          if (PREDICT_FALSE (sm->num_workers > 1))
            {
              ip4_header_t ip = {
-               .src_address = m->locals[lo].addr,
+               .src_address = local->addr,
              };
              if (sm->worker_in2out_cb (&ip, m->fib_index) !=
                  vlib_get_thread_index ())
                goto get_local;
            }
-         mapping->addr = m->locals[lo].addr;
-         mapping->port = clib_host_to_net_u16 (m->locals[lo].port);
-         mapping->fib_index = m->locals[lo].fib_index;
+         mapping->addr = local->addr;
+         mapping->port = clib_host_to_net_u16 (local->port);
+         mapping->fib_index = local->fib_index;
          if (m->affinity)
            {
              if (nat_affinity_create_and_lock (ext_host_addr[0], match.addr,
                                                match.protocol, match.port,
-                                               lo, m->affinity,
+                                               tmp[lo], m->affinity,
                                                m->affinity_per_service_list_head_index))
                nat_log_info ("create affinity record failed");
            }
+         vec_free (tmp);
        }
       else
        {
@@ -2072,7 +2391,7 @@ snat_static_mapping_match (snat_main_t * sm,
          mapping->fib_index = m->fib_index;
          mapping->addr = m->local_addr;
          /* Address only mapping doesn't change port */
-         mapping->port = m->addr_only ? match.port
+         mapping->port = is_addr_only_static_mapping (m) ? match.port
            : clib_host_to_net_u16 (m->local_port);
        }
       mapping->protocol = m->proto;
@@ -2081,18 +2400,21 @@ snat_static_mapping_match (snat_main_t * sm,
     {
       mapping->addr = m->external_addr;
       /* Address only mapping doesn't change port */
-      mapping->port = m->addr_only ? match.port
+      mapping->port = is_addr_only_static_mapping (m) ? match.port
        : clib_host_to_net_u16 (m->external_port);
       mapping->fib_index = sm->outside_fib_index;
     }
 
 end:
   if (PREDICT_FALSE (is_addr_only != 0))
-    *is_addr_only = m->addr_only;
+    *is_addr_only = is_addr_only_static_mapping (m);
 
   if (PREDICT_FALSE (twice_nat != 0))
     *twice_nat = m->twice_nat;
 
+  if (PREDICT_FALSE (is_identity_nat != 0))
+    *is_identity_nat = is_identity_static_mapping (m);
+
   return 0;
 }
 
@@ -2585,7 +2907,7 @@ nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index)
          (&sm->static_mapping_by_external, &kv, &value))
        {
          m = pool_elt_at_index (sm->static_mappings, value.value);
-         if (!vec_len (m->locals))
+         if (!is_lb_static_mapping (m))
            return m->workers[0];
 
          hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) +
@@ -2883,7 +3205,8 @@ match:
                                rp->e_port,
                                rp->vrf_id,
                                rp->addr_only, ~0 /* sw_if_index */ ,
-                               rp->proto, !is_delete, 0, 0, rp->tag);
+                               rp->proto, !is_delete, rp->twice_nat,
+                               rp->out2in_only, rp->tag, rp->identity_nat);
   if (rv)
     nat_log_notice ("snat_add_static_mapping returned %d", rv);
 }
@@ -2952,7 +3275,9 @@ match:
                                            rp->addr_only,
                                            ~0 /* sw_if_index */ ,
                                            rp->proto,
-                                           rp->is_add, 0, 0, rp->tag);
+                                           rp->is_add, rp->twice_nat,
+                                           rp->out2in_only, rp->tag,
+                                           rp->identity_nat);
              if (rv)
                nat_log_notice ("snat_add_static_mapping returned %d", rv);
            }