NAT: Twice NAT44 (VPP-969) 67/9867/2
authorMatus Fabian <matfabia@cisco.com>
Mon, 18 Dec 2017 13:38:24 +0000 (05:38 -0800)
committerOle Trøan <otroan@employees.org>
Tue, 19 Dec 2017 10:34:07 +0000 (10:34 +0000)
Translation of both source and destination addresses and ports for 1:1 NAT
session initiated from outside network (ExternalIP K8 use case).

Change-Id: Ic0000497cf71619aac996d6d580844f0ea0edc14
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/nat/in2out.c
src/plugins/nat/nat.api
src/plugins/nat/nat.c
src/plugins/nat/nat.h
src/plugins/nat/nat_api.c
src/plugins/nat/out2in.c
test/test_nat.py
test/vpp_papi_provider.py

index 1052451..603abb8 100755 (executable)
@@ -236,7 +236,7 @@ snat_not_translate (snat_main_t * sm, vlib_node_runtime_t *node,
                               &value0))
     {
       /* or is static mappings */
-      if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0))
+      if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
         return 0;
     }
   else
@@ -256,14 +256,8 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
                       u32 thread_index)
 {
   snat_user_t *u;
-  snat_user_key_t user_key;
   snat_session_t *s;
-  clib_bihash_kv_8_8_t kv0, value0;
-  u32 oldest_per_user_translation_list_index;
-  dlist_elt_t * oldest_per_user_translation_list_elt;
-  dlist_elt_t * per_user_translation_list_elt;
-  dlist_elt_t * per_user_list_head_elt;
-  u32 session_index;
+  clib_bihash_kv_8_8_t kv0;
   snat_session_key_t key1;
   u32 address_index = ~0;
   u32 outside_fib_index;
@@ -285,185 +279,44 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
   outside_fib_index = p[0];
 
   key1.protocol = key0->protocol;
-  user_key.addr = ip0->src_address;
-  user_key.fib_index = rx_fib_index0;
-  kv0.key = user_key.as_u64;
 
-  /* Ever heard of the "user" = src ip4 address before? */
-  if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].user_hash,
-                              &kv0, &value0))
+  u = nat_user_get_or_create (sm, &ip0->src_address, rx_fib_index0,
+                              thread_index);
+  if (!u)
     {
-      /* no, make a new one */
-      pool_get (sm->per_thread_data[thread_index].users, u);
-      memset (u, 0, sizeof (*u));
-      u->addr = ip0->src_address;
-      u->fib_index = rx_fib_index0;
-
-      pool_get (sm->per_thread_data[thread_index].list_pool, per_user_list_head_elt);
-
-      u->sessions_per_user_list_head_index = per_user_list_head_elt -
-        sm->per_thread_data[thread_index].list_pool;
-
-      clib_dlist_init (sm->per_thread_data[thread_index].list_pool,
-                       u->sessions_per_user_list_head_index);
-
-      kv0.value = u - sm->per_thread_data[thread_index].users;
-
-      /* add user */
-      clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].user_hash,
-                               &kv0, 1 /* is_add */);
+      clib_warning ("create NAT user failed");
+      return SNAT_IN2OUT_NEXT_DROP;
     }
-  else
+
+  s = nat_session_alloc_or_recycle (sm, u, thread_index);
+  if (!s)
     {
-      u = pool_elt_at_index (sm->per_thread_data[thread_index].users,
-                             value0.value);
+      clib_warning ("create NAT session failed");
+      return SNAT_IN2OUT_NEXT_DROP;
     }
 
-  /* Over quota? Recycle the least recently used dynamic translation */
-  if (u->nsessions >= sm->max_translations_per_user)
+  /* First try to match static mapping by local address and port */
+  if (snat_static_mapping_match (sm, *key0, &key1, 0, 0, 0))
     {
-      /* Remove the oldest dynamic translation */
-      do {
-          oldest_per_user_translation_list_index =
-            clib_dlist_remove_head (sm->per_thread_data[thread_index].list_pool,
-                                    u->sessions_per_user_list_head_index);
-
-          ASSERT (oldest_per_user_translation_list_index != ~0);
-
-          /* add it back to the end of the LRU list */
-          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                              u->sessions_per_user_list_head_index,
-                              oldest_per_user_translation_list_index);
-          /* Get the list element */
-          oldest_per_user_translation_list_elt =
-            pool_elt_at_index (sm->per_thread_data[thread_index].list_pool,
-                               oldest_per_user_translation_list_index);
-
-          /* Get the session index from the list element */
-          session_index = oldest_per_user_translation_list_elt->value;
-
-          /* Get the session */
-          s = pool_elt_at_index (sm->per_thread_data[thread_index].sessions,
-                                 session_index);
-      } while (snat_is_session_static (s));
-
-      if (snat_is_unk_proto_session (s))
-        {
-          clib_bihash_kv_16_8_t up_kv;
-          nat_ed_ses_key_t key;
-
-          /* Remove from lookup tables */
-          key.l_addr = s->in2out.addr;
-          key.r_addr = s->ext_host_addr;
-          key.fib_index = s->in2out.fib_index;
-          key.proto = s->in2out.port;
-          key.rsvd = 0;
-          key.l_port = 0;
-          up_kv.key[0] = key.as_u64[0];
-          up_kv.key[1] = key.as_u64[1];
-          if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &up_kv, 0))
-            clib_warning ("in2out key del failed");
-
-          key.l_addr = s->out2in.addr;
-          key.fib_index = s->out2in.fib_index;
-          up_kv.key[0] = key.as_u64[0];
-          up_kv.key[1] = key.as_u64[1];
-          if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &up_kv, 0))
-            clib_warning ("out2in key del failed");
-        }
-      else
-        {
-          /* Remove in2out, out2in keys */
-          kv0.key = s->in2out.as_u64;
-          if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].in2out,
-                                       &kv0, 0 /* is_add */))
-              clib_warning ("in2out key delete failed");
-          kv0.key = s->out2in.as_u64;
-          if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in,
-                                       &kv0, 0 /* is_add */))
-              clib_warning ("out2in key delete failed");
-
-          /* log NAT event */
-          snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
-                                              s->out2in.addr.as_u32,
-                                              s->in2out.protocol,
-                                              s->in2out.port,
-                                              s->out2in.port,
-                                              s->in2out.fib_index);
-
-          snat_free_outside_address_and_port
-            (sm->addresses, thread_index, &s->out2in, s->outside_address_index);
-        }
-      s->outside_address_index = ~0;
-
+      /* Try to create dynamic translation */
       if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index0,
                                                thread_index, &key1,
                                                &address_index,
                                                sm->port_per_thread,
                                                sm->per_thread_data[thread_index].snat_thread_index))
         {
-          ASSERT(0);
-
           b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
           return SNAT_IN2OUT_NEXT_DROP;
         }
-      s->outside_address_index = address_index;
+      u->nsessions++;
     }
   else
     {
-      u8 static_mapping = 1;
-
-      /* First try to match static mapping by local address and port */
-      if (snat_static_mapping_match (sm, *key0, &key1, 0, 0))
-        {
-          static_mapping = 0;
-          /* Try to create dynamic translation */
-          if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index0,
-                                                   thread_index, &key1,
-                                                   &address_index,
-                                                   sm->port_per_thread,
-                                                   sm->per_thread_data[thread_index].snat_thread_index))
-            {
-              b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
-              return SNAT_IN2OUT_NEXT_DROP;
-            }
-        }
-
-      /* Create a new session */
-      pool_get (sm->per_thread_data[thread_index].sessions, s);
-      memset (s, 0, sizeof (*s));
-
-      s->outside_address_index = address_index;
-
-      if (static_mapping)
-        {
-          u->nstaticsessions++;
-          s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
-        }
-      else
-        {
-          u->nsessions++;
-        }
-
-      /* Create list elts */
-      pool_get (sm->per_thread_data[thread_index].list_pool,
-                per_user_translation_list_elt);
-      clib_dlist_init (sm->per_thread_data[thread_index].list_pool,
-                       per_user_translation_list_elt -
-                       sm->per_thread_data[thread_index].list_pool);
-
-      per_user_translation_list_elt->value =
-        s - sm->per_thread_data[thread_index].sessions;
-      s->per_user_index = per_user_translation_list_elt -
-                          sm->per_thread_data[thread_index].list_pool;
-      s->per_user_list_head_index = u->sessions_per_user_list_head_index;
-
-      clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                          s->per_user_list_head_index,
-                          per_user_translation_list_elt -
-                          sm->per_thread_data[thread_index].list_pool);
-   }
+      u->nstaticsessions++;
+      s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
+    }
 
+  s->outside_address_index = address_index;
   s->in2out = *key0;
   s->out2in = key1;
   s->out2in.protocol = key0->protocol;
@@ -677,7 +530,7 @@ u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node,
     }
   key0.fib_index = rx_fib_index0;
 
-  if (snat_static_mapping_match(sm, key0, &sm0, 0, &is_addr_only))
+  if (snat_static_mapping_match(sm, key0, &sm0, 0, &is_addr_only, 0))
     {
       if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0,
           IP_PROTOCOL_ICMP, rx_fib_index0)))
@@ -873,7 +726,7 @@ snat_hairpinning (snat_main_t *sm,
   kv0.key = key0.as_u64;
 
   /* Check if destination is static mappings */
-  if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0))
+  if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
     {
       new_dst_addr0 = sm0.addr.as_u32;
       new_dst_port0 = sm0.port;
@@ -974,7 +827,7 @@ snat_icmp_hairpinning (snat_main_t *sm,
                                   &value0))
         {
           /* or static mappings */
-          if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0))
+          if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
             {
               new_dst_addr0 = sm0.addr.as_u32;
               vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
@@ -1032,15 +885,12 @@ static inline u32 icmp_in2out_slow_path (snat_main_t *sm,
       s0->last_heard = now;
       s0->total_pkts++;
       s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0);
-      /* Per-user LRU list maintenance for dynamic translations */
-      if (!snat_is_session_static (s0))
-        {
-          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                             s0->per_user_index);
-          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                              s0->per_user_list_head_index,
-                              s0->per_user_index);
-        }
+      /* Per-user LRU list maintenance */
+      clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                         s0->per_user_index);
+      clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                          s0->per_user_list_head_index,
+                          s0->per_user_index);
     }
   return next0;
 }
@@ -1063,7 +913,7 @@ snat_hairpinning_unknown_proto (snat_main_t *sm,
   key.r_addr.as_u32 = ip->src_address.as_u32;
   key.fib_index = sm->outside_fib_index;
   key.proto = ip->protocol;
-  key.rsvd = 0;
+  key.r_port = 0;
   key.l_port = 0;
   s_kv.key[0] = key.as_u64[0];
   s_kv.key[1] = key.as_u64[1];
@@ -1115,11 +965,10 @@ snat_in2out_unknown_proto (snat_main_t *sm,
   snat_session_key_t m_key;
   u32 old_addr, new_addr = 0;
   ip_csum_t sum;
-  snat_user_key_t u_key;
   snat_user_t *u;
-  dlist_elt_t *head, *elt, *oldest;
+  dlist_elt_t *head, *elt;
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
-  u32 elt_index, head_index, ses_index, oldest_index;
+  u32 elt_index, head_index, ses_index;
   snat_session_t * s;
   nat_ed_ses_key_t key;
   u32 address_index = ~0;
@@ -1132,7 +981,7 @@ snat_in2out_unknown_proto (snat_main_t *sm,
   key.r_addr = ip->dst_address;
   key.fib_index = rx_fib_index;
   key.proto = ip->protocol;
-  key.rsvd = 0;
+  key.l_port = 0;
   key.l_port = 0;
   s_kv.key[0] = key.as_u64[0];
   s_kv.key[1] = key.as_u64[1];
@@ -1150,33 +999,12 @@ snat_in2out_unknown_proto (snat_main_t *sm,
           return 0;
         }
 
-      u_key.addr = ip->src_address;
-      u_key.fib_index = rx_fib_index;
-      kv.key = u_key.as_u64;
-
-      /* Ever heard of the "user" = src ip4 address before? */
-      if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
+      u = nat_user_get_or_create (sm, &ip->src_address, rx_fib_index,
+                                  thread_index);
+      if (!u)
         {
-          /* no, make a new one */
-          pool_get (tsm->users, u);
-          memset (u, 0, sizeof (*u));
-          u->addr = ip->src_address;
-          u->fib_index = rx_fib_index;
-
-          pool_get (tsm->list_pool, head);
-          u->sessions_per_user_list_head_index = head - tsm->list_pool;
-
-          clib_dlist_init (tsm->list_pool,
-                           u->sessions_per_user_list_head_index);
-
-          kv.value = u - tsm->users;
-
-          /* add user */
-          clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1);
-        }
-      else
-        {
-          u = pool_elt_at_index (tsm->users, value.value);
+          clib_warning ("create NAT user failed");
+          return 0;
         }
 
       m_key.addr = ip->src_address;
@@ -1244,88 +1072,11 @@ snat_in2out_unknown_proto (snat_main_t *sm,
         }
 
 create_ses:
-      /* Over quota? Recycle the least recently used dynamic translation */
-      if (u->nsessions >= sm->max_translations_per_user && !is_sm)
+      s = nat_session_alloc_or_recycle (sm, u, thread_index);
+      if (!s)
         {
-          /* Remove the oldest dynamic translation */
-          do {
-              oldest_index = clib_dlist_remove_head (
-                tsm->list_pool, u->sessions_per_user_list_head_index);
-
-              ASSERT (oldest_index != ~0);
-
-              /* add it back to the end of the LRU list */
-              clib_dlist_addtail (tsm->list_pool,
-                                  u->sessions_per_user_list_head_index,
-                                  oldest_index);
-              /* Get the list element */
-              oldest = pool_elt_at_index (tsm->list_pool, oldest_index);
-
-              /* Get the session index from the list element */
-              ses_index = oldest->value;
-
-              /* Get the session */
-              s = pool_elt_at_index (tsm->sessions, ses_index);
-          } while (snat_is_session_static (s));
-
-          if (snat_is_unk_proto_session (s))
-            {
-              /* Remove from lookup tables */
-              key.l_addr = s->in2out.addr;
-              key.r_addr = s->ext_host_addr;
-              key.fib_index = s->in2out.fib_index;
-              key.proto = s->in2out.port;
-              s_kv.key[0] = key.as_u64[0];
-              s_kv.key[1] = key.as_u64[1];
-              if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 0))
-                clib_warning ("in2out key del failed");
-
-              key.l_addr = s->out2in.addr;
-              key.fib_index = s->out2in.fib_index;
-              s_kv.key[0] = key.as_u64[0];
-              s_kv.key[1] = key.as_u64[1];
-              if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 0))
-                clib_warning ("out2in key del failed");
-            }
-          else
-            {
-              /* log NAT event */
-              snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
-                                                  s->out2in.addr.as_u32,
-                                                  s->in2out.protocol,
-                                                  s->in2out.port,
-                                                  s->out2in.port,
-                                                  s->in2out.fib_index);
-
-              snat_free_outside_address_and_port (sm->addresses, thread_index,
-                                                  &s->out2in,
-                                                  s->outside_address_index);
-
-              /* Remove in2out, out2in keys */
-              kv.key = s->in2out.as_u64;
-              if (clib_bihash_add_del_8_8 (
-                    &sm->per_thread_data[thread_index].in2out, &kv, 0))
-                clib_warning ("in2out key del failed");
-              kv.key = s->out2in.as_u64;
-              if (clib_bihash_add_del_8_8 (
-                    &sm->per_thread_data[thread_index].out2in, &kv, 0))
-                clib_warning ("out2in key del failed");
-            }
-        }
-      else
-        {
-          /* Create a new session */
-          pool_get (tsm->sessions, s);
-          memset (s, 0, sizeof (*s));
-
-          /* Create list elts */
-          pool_get (tsm->list_pool, elt);
-          clib_dlist_init (tsm->list_pool, elt - tsm->list_pool);
-          elt->value = s - tsm->sessions;
-          s->per_user_index = 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,
-                              s->per_user_index);
+          clib_warning ("create NAT session failed");
+          return 0;
         }
 
       s->ext_host_addr.as_u32 = ip->dst_address.as_u32;
@@ -1410,10 +1161,7 @@ snat_in2out_lb (snat_main_t *sm,
   ip_csum_t sum;
   u32 proto = ip_proto_to_snat_proto (ip->protocol);
   snat_session_key_t e_key, l_key;
-  clib_bihash_kv_8_8_t kv, value;
-  snat_user_key_t u_key;
   snat_user_t *u;
-  dlist_elt_t *head, *elt;
 
   old_addr = ip->src_address.as_u32;
 
@@ -1421,7 +1169,7 @@ snat_in2out_lb (snat_main_t *sm,
   key.r_addr = ip->dst_address;
   key.fib_index = rx_fib_index;
   key.proto = ip->protocol;
-  key.rsvd = 0;
+  key.r_port = udp->dst_port;
   key.l_port = udp->src_port;
   s_kv.key[0] = key.as_u64[0];
   s_kv.key[1] = key.as_u64[1];
@@ -1442,43 +1190,24 @@ snat_in2out_lb (snat_main_t *sm,
       l_key.port = udp->src_port;
       l_key.protocol = proto;
       l_key.fib_index = rx_fib_index;
-      if (snat_static_mapping_match(sm, l_key, &e_key, 0, 0))
+      if (snat_static_mapping_match(sm, l_key, &e_key, 0, 0, 0))
         return 0;
 
-      u_key.addr = ip->src_address;
-      u_key.fib_index = rx_fib_index;
-      kv.key = u_key.as_u64;
-
-      /* Ever heard of the "user" = src ip4 address before? */
-      if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
+      u = nat_user_get_or_create (sm, &ip->src_address, rx_fib_index,
+                                  thread_index);
+      if (!u)
         {
-          /* no, make a new one */
-          pool_get (tsm->users, u);
-          memset (u, 0, sizeof (*u));
-          u->addr = ip->src_address;
-          u->fib_index = rx_fib_index;
-
-          pool_get (tsm->list_pool, head);
-          u->sessions_per_user_list_head_index = head - tsm->list_pool;
-
-          clib_dlist_init (tsm->list_pool,
-                           u->sessions_per_user_list_head_index);
-
-          kv.value = u - tsm->users;
-
-          /* add user */
-          if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
-            clib_warning ("user key add failed");
+          clib_warning ("create NAT user failed");
+          return 0;
         }
-      else
+
+      s = nat_session_alloc_or_recycle (sm, u, thread_index);
+      if (!s)
         {
-          u = pool_elt_at_index (tsm->users, value.value);
+          clib_warning ("create NAT session failed");
+          return 0;
         }
 
-      /* Create a new session */
-      pool_get (tsm->sessions, s);
-      memset (s, 0, sizeof (*s));
-
       s->ext_host_addr.as_u32 = ip->dst_address.as_u32;
       s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
       s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING;
@@ -1487,15 +1216,6 @@ snat_in2out_lb (snat_main_t *sm,
       s->out2in = e_key;
       u->nstaticsessions++;
 
-      /* Create list elts */
-      pool_get (tsm->list_pool, elt);
-      clib_dlist_init (tsm->list_pool, elt - tsm->list_pool);
-      elt->value = s - tsm->sessions;
-      s->per_user_index = 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,
-                          s->per_user_index);
-
       /* Add to lookup tables */
       s_kv.value = s - tsm->sessions;
       if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 1))
@@ -1515,6 +1235,9 @@ snat_in2out_lb (snat_main_t *sm,
   /* Update IP checksum */
   sum = ip->checksum;
   sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address);
+  if (is_twice_nat_session (s))
+    sum = ip_csum_update (sum, ip->dst_address.as_u32,
+                          s->ext_host_addr.as_u32, ip4_header_t, dst_address);
   ip->checksum = ip_csum_fold (sum);
 
   if (PREDICT_TRUE(proto == SNAT_PROTOCOL_TCP))
@@ -1526,11 +1249,26 @@ snat_in2out_lb (snat_main_t *sm,
       sum = tcp->checksum;
       sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address);
       sum = ip_csum_update (sum, old_port, new_port, ip4_header_t, length);
+      if (is_twice_nat_session (s))
+        {
+          sum = ip_csum_update (sum, ip->dst_address.as_u32,
+                                s->ext_host_addr.as_u32, ip4_header_t,
+                                dst_address);
+          sum = ip_csum_update (sum, tcp->dst_port, s->ext_host_port,
+                                ip4_header_t, length);
+          tcp->dst_port = s->ext_host_port;
+          ip->dst_address.as_u32 = s->ext_host_addr.as_u32;
+        }
       tcp->checksum = ip_csum_fold(sum);
     }
   else
     {
       udp->src_port = s->out2in.port;
+      if (is_twice_nat_session (s))
+        {
+          udp->dst_port = s->ext_host_port;
+          ip->dst_address.as_u32 = s->ext_host_addr.as_u32;
+        }
       udp->checksum = 0;
     }
 
@@ -1541,6 +1279,10 @@ snat_in2out_lb (snat_main_t *sm,
   s->last_heard = now;
   s->total_pkts++;
   s->total_bytes += vlib_buffer_length_in_chain (vm, b);
+  /* Per-user LRU list maintenance */
+  clib_dlist_remove (tsm->list_pool, s->per_user_index);
+  clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index,
+                      s->per_user_index);
   return s;
 }
 
@@ -1773,15 +1515,12 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
           s0->last_heard = now;
           s0->total_pkts++;
           s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
-          /* Per-user LRU list maintenance for dynamic translation */
-          if (!snat_is_session_static (s0))
-            {
-              clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                                 s0->per_user_index);
-              clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                                  s0->per_user_list_head_index,
-                                  s0->per_user_index);
-            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                             s0->per_user_index);
+          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                              s0->per_user_list_head_index,
+                              s0->per_user_index);
         trace00:
 
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -1953,15 +1692,12 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
           s1->last_heard = now;
           s1->total_pkts++;
           s1->total_bytes += vlib_buffer_length_in_chain (vm, b1);
-          /* Per-user LRU list maintenance for dynamic translation */
-          if (!snat_is_session_static (s1))
-            {
-              clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                                 s1->per_user_index);
-              clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                                  s1->per_user_list_head_index,
-                                  s1->per_user_index);
-            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                             s1->per_user_index);
+          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                              s1->per_user_list_head_index,
+                              s1->per_user_index);
         trace01:
 
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -2170,15 +1906,12 @@ snat_in2out_node_fn_inline (vlib_main_t * vm,
           s0->last_heard = now;
           s0->total_pkts++;
           s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
-          /* Per-user LRU list maintenance for dynamic translation */
-          if (!snat_is_session_static (s0))
-            {
-              clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                                 s0->per_user_index);
-              clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                                  s0->per_user_list_head_index,
-                                  s0->per_user_index);
-            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                             s0->per_user_index);
+          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                              s0->per_user_list_head_index,
+                              s0->per_user_index);
 
         trace0:
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -2467,7 +2200,7 @@ nat44_reass_hairpinning (snat_main_t *sm,
   udp0 = ip4_next_header (ip0);
 
   /* Check if destination is static mappings */
-  if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0))
+  if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
     {
       new_dst_addr0 = sm0.addr.as_u32;
       new_dst_port0 = sm0.port;
@@ -2710,15 +2443,12 @@ nat44_in2out_reass_node_fn (vlib_main_t * vm,
           s0->last_heard = now;
           s0->total_pkts++;
           s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
-          /* Per-user LRU list maintenance for dynamic translation */
-          if (!snat_is_session_static (s0))
-            {
-              clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                                 s0->per_user_index);
-              clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                                  s0->per_user_list_head_index,
-                                  s0->per_user_index);
-            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                             s0->per_user_index);
+          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                              s0->per_user_list_head_index,
+                              s0->per_user_index);
 
         trace0:
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -3862,7 +3592,6 @@ snat_hairpin_dst_fn (vlib_main_t * vm,
                 }
 
               vnet_buffer (b0)->snat.flags = SNAT_FLAG_HAIRPINNING;
-              clib_warning("is hairpinning");
             }
 
           pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP;
@@ -4080,7 +3809,7 @@ snat_in2out_fast_static_map_fn (vlib_main_t * vm,
           key0.port = udp0->src_port;
           key0.fib_index = rx_fib_index0;
 
-          if (snat_static_mapping_match(sm, key0, &sm0, 0, 0))
+          if (snat_static_mapping_match(sm, key0, &sm0, 0, 0, 0))
             {
               b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION];
               next0= SNAT_IN2OUT_NEXT_DROP;
index d8e0d7e..2a8eeb9 100644 (file)
@@ -13,7 +13,7 @@
  * limitations under the License.
  */
 
-vl_api_version 2.0.0
+vl_api_version 2.1.0
 
 /**
  * @file nat.api
@@ -230,6 +230,7 @@ define nat_reass_details {
     @param first_ip_address - first IPv4 address
     @param last_ip_address - last IPv4 address
     @param vrf_id - VRF id of tenant, ~0 means independent of VRF
+    @param twice_nat - twice NAT address range for extenal hosts
     @param is_add - 1 if add, 0 if delete
 */
 autoreply define nat44_add_del_address_range {
@@ -238,6 +239,7 @@ autoreply define nat44_add_del_address_range {
   u8 first_ip_address[4];
   u8 last_ip_address[4];
   u32 vrf_id;
+  u8 twice_nat;
   u8 is_add;
 };
 
@@ -253,11 +255,13 @@ define nat44_address_dump {
 /** \brief NAT44 address details response
     @param context - sender context, to match reply w/ request
     @param ip_address - IPv4 address
+    @param twice_nat - twice NAT address range for extenal hosts
     @param vrf_id - VRF id of tenant, ~0 means independent of VRF
 */
 define nat44_address_details {
   u32 context;
   u8 ip_address[4];
+  u8 twice_nat;
   u32 vrf_id;
 };
 
@@ -346,6 +350,8 @@ define nat44_interface_output_feature_details {
                                   external_ip_address is ignored, ~0 means not
                                   used)
     @param vfr_id - VRF ID
+    @param twice_nat - if 1 translate external host address and port, only for
+                       1:1 NAPT (addr_only must be 0)
 */
 autoreply define nat44_add_del_static_mapping {
   u32 client_index;
@@ -359,6 +365,7 @@ autoreply define nat44_add_del_static_mapping {
   u16 external_port;
   u32 external_sw_if_index;
   u32 vrf_id;
+  u8 twice_nat;
 };
 
 /** \brief Dump NAT44 static mappings
@@ -380,6 +387,7 @@ define nat44_static_mapping_dump {
     @param external_port - external port number
     @param external_sw_if_index - external interface
     @param vfr_id - VRF ID
+    @param twice_nat - if 1 translate external host address and port
 */
 define nat44_static_mapping_details {
   u32 context;
@@ -391,6 +399,7 @@ define nat44_static_mapping_details {
   u16 external_port;
   u32 external_sw_if_index;
   u32 vrf_id;
+  u8 twice_nat;
 };
 
 /** \brief Add/delete NAT44 identity mapping
@@ -449,13 +458,14 @@ define nat44_identity_mapping_details {
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
     @param is_add - 1 if add, 0 if delete
+    @param twice_nat - twice NAT address for extenal hosts
     @param sw_if_index - software index of the interface
 */
 autoreply define nat44_add_del_interface_addr {
   u32 client_index;
   u32 context;
   u8 is_add;
-  u8 is_inside;
+  u8 twice_nat;
   u32 sw_if_index;
 };
 
@@ -471,10 +481,12 @@ define nat44_interface_addr_dump {
 /** \brief NAT44 pool addresses interfaces details response
     @param context - sender context, to match reply w/ request
     @param sw_if_index - software index of the interface
+    @param twice_nat - twice NAT address for extenal hosts
 */
 define nat44_interface_addr_details {
   u32 context;
   u32 sw_if_index;
+  u8 twice_nat;
 };
 
 /** \brief Dump NAT44 users
@@ -554,6 +566,7 @@ autoreply manual_endian define nat44_add_del_lb_static_mapping {
   u16 external_port;
   u8 protocol;
   u32 vrf_id;
+  u8 twice_nat;
   u8 local_num;
   vl_api_nat44_lb_addr_port_t locals[local_num];
 };
@@ -569,6 +582,7 @@ manual_endian define nat44_lb_static_mapping_details {
   u16 external_port;
   u8 protocol;
   u32 vrf_id;
+  u8 twice_nat;
   u8 local_num;
   vl_api_nat44_lb_addr_port_t locals[local_num];
 };
index 6aa2d81..5294401 100644 (file)
@@ -138,6 +138,208 @@ typedef enum {
   NAT44_CLASSIFY_N_NEXT,
 } nat44_classify_next_t;
 
+void
+nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index)
+{
+  snat_session_key_t key;
+  clib_bihash_kv_8_8_t kv;
+  nat_ed_ses_key_t ed_key;
+  clib_bihash_kv_16_8_t ed_kv;
+  int i;
+  snat_address_t *a;
+  snat_main_per_thread_data_t *tsm =
+    vec_elt_at_index (sm->per_thread_data, thread_index);
+
+  /* Endpoint dependent session lookup tables */
+  if (is_ed_session (s))
+    {
+      ed_key.l_addr = s->out2in.addr;
+      ed_key.r_addr = s->ext_host_addr;
+      ed_key.fib_index = s->out2in.fib_index;
+      if (snat_is_unk_proto_session (s))
+        {
+          ed_key.proto = s->in2out.port;
+          ed_key.r_port = 0;
+          ed_key.l_port = 0;
+        }
+      else
+        {
+          ed_key.proto = snat_proto_to_ip_proto (s->in2out.protocol);
+          ed_key.l_port = s->out2in.port;
+          ed_key.r_port = s->ext_host_port;
+        }
+      ed_kv.key[0] = ed_key.as_u64[0];
+      ed_kv.key[1] = ed_key.as_u64[1];
+      if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &ed_kv, 0))
+        clib_warning ("out2in_ed key del failed");
+
+      ed_key.l_addr = s->in2out.addr;
+      ed_key.fib_index = s->in2out.fib_index;
+      if (!snat_is_unk_proto_session (s))
+        ed_key.l_port = s->in2out.port;
+      if (is_twice_nat_session (s))
+        {
+          ed_key.r_addr = s->ext_host_nat_addr;
+          ed_key.r_port = s->ext_host_nat_port;
+        }
+      ed_kv.key[0] = ed_key.as_u64[0];
+      ed_kv.key[1] = ed_key.as_u64[1];
+      if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0))
+        clib_warning ("in2out_ed key del failed");
+    }
+
+  if (snat_is_unk_proto_session (s))
+    return;
+
+  /* log NAT event */
+  snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
+                                      s->out2in.addr.as_u32,
+                                      s->in2out.protocol,
+                                      s->in2out.port,
+                                      s->out2in.port,
+                                      s->in2out.fib_index);
+
+  /* Twice NAT address and port for external host */
+  if (is_twice_nat_session (s))
+    {
+      for (i = 0; i < vec_len (sm->twice_nat_addresses); i++)
+        {
+          key.protocol = s->in2out.protocol;
+          key.port = s->ext_host_nat_port;
+          a = sm->twice_nat_addresses + i;
+          if (a->addr.as_u32 == s->ext_host_nat_addr.as_u32)
+            {
+              snat_free_outside_address_and_port (sm->twice_nat_addresses,
+                                                  thread_index, &key, i);
+              break;
+            }
+        }
+    }
+
+  if (is_ed_session (s))
+    return;
+
+  /* Session lookup tables */
+  kv.key = s->in2out.as_u64;
+  if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0))
+    clib_warning ("in2out key del failed");
+  kv.key = s->out2in.as_u64;
+  if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
+    clib_warning ("out2in key del failed");
+
+  if (snat_is_session_static (s))
+    return;
+
+  if (s->outside_address_index != ~0)
+    snat_free_outside_address_and_port (sm->addresses, thread_index,
+                                        &s->out2in, s->outside_address_index);
+}
+
+snat_user_t *
+nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr, u32 fib_index,
+                        u32 thread_index)
+{
+  snat_user_t *u = 0;
+  snat_user_key_t user_key;
+  clib_bihash_kv_8_8_t kv, value;
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+  dlist_elt_t * per_user_list_head_elt;
+
+  user_key.addr.as_u32 = addr->as_u32;
+  user_key.fib_index = fib_index;
+  kv.key = user_key.as_u64;
+
+  /* Ever heard of the "user" = src ip4 address before? */
+  if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
+    {
+      /* no, make a new one */
+      pool_get (tsm->users, u);
+      memset (u, 0, sizeof (*u));
+      u->addr.as_u32 = addr->as_u32;
+      u->fib_index = fib_index;
+
+      pool_get (tsm->list_pool, per_user_list_head_elt);
+
+      u->sessions_per_user_list_head_index = per_user_list_head_elt -
+        tsm->list_pool;
+
+      clib_dlist_init (tsm->list_pool, u->sessions_per_user_list_head_index);
+
+      kv.value = u - tsm->users;
+
+      /* add user */
+      if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
+        clib_warning ("user_hash keay add failed");
+    }
+  else
+    {
+      u = pool_elt_at_index (tsm->users, value.value);
+    }
+
+  return u;
+}
+
+snat_session_t *
+nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u, u32 thread_index)
+{
+  snat_session_t *s;
+  snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+  u32 oldest_per_user_translation_list_index, session_index;
+  dlist_elt_t * oldest_per_user_translation_list_elt;
+  dlist_elt_t * per_user_translation_list_elt;
+
+  /* Over quota? Recycle the least recently used translation */
+  if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user)
+    {
+      oldest_per_user_translation_list_index =
+        clib_dlist_remove_head (tsm->list_pool,
+                                u->sessions_per_user_list_head_index);
+
+      ASSERT (oldest_per_user_translation_list_index != ~0);
+
+      /* Add it back to the end of the LRU list */
+      clib_dlist_addtail (tsm->list_pool,
+                          u->sessions_per_user_list_head_index,
+                          oldest_per_user_translation_list_index);
+      /* Get the list element */
+      oldest_per_user_translation_list_elt =
+        pool_elt_at_index (tsm->list_pool,
+                           oldest_per_user_translation_list_index);
+
+      /* Get the session index from the list element */
+      session_index = oldest_per_user_translation_list_elt->value;
+
+      /* Get the session */
+      s = pool_elt_at_index (tsm->sessions, session_index);
+      nat_free_session_data (sm, s, thread_index);
+      s->outside_address_index = ~0;
+      s->flags = 0;
+      s->total_bytes = 0;
+      s->total_pkts = 0;
+    }
+  else
+    {
+      pool_get (tsm->sessions, s);
+      memset (s, 0, sizeof (*s));
+      s->outside_address_index = ~0;
+
+      /* Create list elts */
+      pool_get (tsm->list_pool, per_user_translation_list_elt);
+      clib_dlist_init (tsm->list_pool,
+                       per_user_translation_list_elt - tsm->list_pool);
+
+      per_user_translation_list_elt->value = s - tsm->sessions;
+      s->per_user_index = per_user_translation_list_elt - tsm->list_pool;
+      s->per_user_list_head_index = u->sessions_per_user_list_head_index;
+
+      clib_dlist_addtail (tsm->list_pool,
+                          s->per_user_list_head_index,
+                          per_user_translation_list_elt - tsm->list_pool);
+    }
+
+  return s;
+}
+
 static inline uword
 nat44_classify_node_fn_inline (vlib_main_t * vm,
                                vlib_node_runtime_t * node,
@@ -326,20 +528,25 @@ snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index,
                            FIB_SOURCE_PLUGIN_HI);
 }
 
-void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id)
+void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id,
+                       u8 twice_nat)
 {
   snat_address_t * ap;
   snat_interface_t *i;
   vlib_thread_main_t *tm = vlib_get_thread_main ();
 
   /* Check if address already exists */
-  vec_foreach (ap, sm->addresses)
+  vec_foreach (ap, twice_nat ? sm->twice_nat_addresses : sm->addresses)
     {
       if (ap->addr.as_u32 == addr->as_u32)
         return;
     }
 
-  vec_add2 (sm->addresses, ap, 1);
+  if (twice_nat)
+    vec_add2 (sm->twice_nat_addresses, ap, 1);
+  else
+    vec_add2 (sm->addresses, ap, 1);
+
   ap->addr = *addr;
   if (vrf_id != ~0)
     ap->fib_index =
@@ -354,6 +561,9 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id)
   foreach_snat_protocol
 #undef _
 
+  if (twice_nat)
+    return;
+
   /* Add external address to FIB */
   pool_foreach (i, sm->interfaces,
   ({
@@ -431,12 +641,14 @@ snat_add_static_mapping_when_resolved (snat_main_t * sm,
  * @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 1 translate external host address and port.
  *
  * @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)
+                            u32 sw_if_index, snat_protocol_t proto, int is_add,
+                            u8 twice_nat)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -447,6 +659,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
   uword * p;
   snat_interface_t *interface;
   int i;
+  snat_main_per_thread_data_t *tsm;
 
   /* If the external address is a specific interface address */
   if (sw_if_index != ~0)
@@ -489,6 +702,9 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       if (m)
         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)
         {
@@ -548,6 +764,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       m->addr_only = addr_only;
       m->vrf_id = vrf_id;
       m->fib_index = fib_index;
+      m->twice_nat = twice_nat;
       if (!addr_only)
         {
           m->local_port = l_port;
@@ -555,6 +772,17 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
           m->proto = proto;
         }
 
+      if (sm->workers)
+        {
+          ip4_header_t ip = {
+            .src_address = m->local_addr,
+          };
+          m->worker_index = sm->worker_in2out_cb (&ip, m->fib_index);
+          tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index);
+        }
+      else
+        tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
+
       m_key.addr = m->local_addr;
       m_key.port = m->local_port;
       m_key.protocol = m->proto;
@@ -562,6 +790,14 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       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);
+      if (twice_nat)
+        {
+          m_key.port = clib_host_to_net_u16 (l_port);
+          kv.key = m_key.as_u64;
+          kv.value = ~0ULL;
+          if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 1))
+            clib_warning ("in2out key add failed");
+        }
 
       m_key.addr = m->external_addr;
       m_key.port = m->external_port;
@@ -569,14 +805,15 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
       kv.key = m_key.as_u64;
       kv.value = m - sm->static_mappings;
       clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1);
-
-      if (sm->workers)
+      if (twice_nat)
         {
-          ip4_header_t ip = {
-            .src_address = m->local_addr,
-          };
-          m->worker_index = sm->worker_in2out_cb (&ip, m->fib_index);
+          m_key.port = clib_host_to_net_u16 (e_port);
+          kv.key = m_key.as_u64;
+          kv.value = ~0ULL;
+          if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 1))
+            clib_warning ("out2in key add failed");
         }
+
     }
   else
     {
@@ -613,18 +850,39 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
             }
         }
 
+      if (sm->num_workers > 1)
+        tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index);
+      else
+        tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
+
       m_key.addr = m->local_addr;
       m_key.port = m->local_port;
       m_key.protocol = m->proto;
       m_key.fib_index = m->fib_index;
       kv.key = m_key.as_u64;
       clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0);
+      if (twice_nat)
+        {
+          m_key.port = clib_host_to_net_u16 (m->local_port);
+          kv.key = m_key.as_u64;
+          kv.value = ~0ULL;
+          if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0))
+            clib_warning ("in2out key del failed");
+        }
 
       m_key.addr = m->external_addr;
       m_key.port = m->external_port;
       m_key.fib_index = sm->outside_fib_index;
       kv.key = m_key.as_u64;
       clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0);
+      if (twice_nat)
+        {
+          m_key.port = clib_host_to_net_u16 (m->external_port);
+          kv.key = m_key.as_u64;
+          kv.value = ~0ULL;
+          if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 0))
+            clib_warning ("in2out key del failed");
+        }
 
       /* Delete session(s) for static mapping if exist */
       if (!(sm->static_mapping_only) ||
@@ -633,19 +891,14 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
           snat_user_key_t u_key;
           snat_user_t *u;
           dlist_elt_t * head, * elt;
-          u32 elt_index, head_index, del_elt_index;
+          u32 elt_index, head_index;
           u32 ses_index;
           u64 user_index;
           snat_session_t * s;
-          snat_main_per_thread_data_t *tsm;
 
           u_key.addr = m->local_addr;
           u_key.fib_index = m->fib_index;
           kv.key = u_key.as_u64;
-          if (sm->num_workers > 1)
-            tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index);
-          else
-            tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
           if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
             {
               user_index = value.value;
@@ -660,9 +913,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                   while (ses_index != ~0)
                     {
                       s =  pool_elt_at_index (tsm->sessions, ses_index);
-                      del_elt_index = elt_index;
-                      elt_index = elt->next;
-                      elt = pool_elt_at_index (tsm->list_pool, elt_index);
+                      elt = pool_elt_at_index (tsm->list_pool, elt->next);
                       ses_index = elt->value;
 
                       if (!addr_only)
@@ -672,51 +923,10 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr,
                             continue;
                         }
 
-                      if (snat_is_unk_proto_session (s))
-                        {
-                          clib_bihash_kv_16_8_t up_kv;
-                          nat_ed_ses_key_t up_key;
-                          up_key.l_addr = s->in2out.addr;
-                          up_key.r_addr = s->ext_host_addr;
-                          up_key.fib_index = s->in2out.fib_index;
-                          up_key.proto = s->in2out.port;
-                          up_key.rsvd = 0;
-                          up_key.l_port = 0;
-                          up_kv.key[0] = up_key.as_u64[0];
-                          up_kv.key[1] = up_key.as_u64[1];
-                          if (clib_bihash_add_del_16_8 (&sm->in2out_ed,
-                                                        &up_kv, 0))
-                            clib_warning ("in2out key del failed");
-
-                          up_key.l_addr = s->out2in.addr;
-                          up_key.fib_index = s->out2in.fib_index;
-                          up_kv.key[0] = up_key.as_u64[0];
-                          up_kv.key[1] = up_key.as_u64[1];
-                          if (clib_bihash_add_del_16_8 (&sm->out2in_ed,
-                                                        &up_kv, 0))
-                            clib_warning ("out2in key del failed");
-
-                          goto delete;
-                        }
-                      /* log NAT event */
-                      snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
-                                                          s->out2in.addr.as_u32,
-                                                          s->in2out.protocol,
-                                                          s->in2out.port,
-                                                          s->out2in.port,
-                                                          s->in2out.fib_index);
-
-                      value.key = s->in2out.as_u64;
-                      if (clib_bihash_add_del_8_8 (&tsm->in2out, &value, 0))
-                        clib_warning ("in2out key del failed");
-                      value.key = s->out2in.as_u64;
-                      if (clib_bihash_add_del_8_8 (&tsm->out2in, &value, 0))
-                        clib_warning ("out2in key del failed");
-delete:
+                      nat_free_session_data (sm, s, tsm - sm->per_thread_data);
+                      clib_dlist_remove (tsm->list_pool, s->per_user_index);
+                      pool_put_index (tsm->list_pool, s->per_user_index);
                       pool_put (tsm->sessions, s);
-
-                      clib_dlist_remove (tsm->list_pool, del_elt_index);
-                      pool_put_index (tsm->list_pool, del_elt_index);
                       u->nstaticsessions--;
 
                       if (!addr_only)
@@ -761,7 +971,8 @@ delete:
 
 int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                                      snat_protocol_t proto, u32 vrf_id,
-                                     nat44_lb_addr_port_t *locals, u8 is_add)
+                                     nat44_lb_addr_port_t *locals, u8 is_add,
+                                     u8 twice_nat)
 {
   snat_main_t * sm = &snat_main;
   snat_static_mapping_t *m;
@@ -771,8 +982,12 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
   snat_address_t *a = 0;
   int i;
   nat44_lb_addr_port_t *local;
-  u32 worker_index = 0;
+  u32 worker_index = 0, elt_index, head_index, ses_index;
   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;
 
   m_key.addr = e_addr;
   m_key.port = e_port;
@@ -841,6 +1056,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       m->fib_index = fib_index;
       m->external_port = e_port;
       m->proto = proto;
+      m->twice_nat = twice_nat;
 
       m_key.addr = m->external_addr;
       m_key.port = m->external_port;
@@ -870,7 +1086,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
       kv.value = ~0ULL;
       if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 1))
         {
-          clib_warning ("static_mapping_by_local key add failed");
+          clib_warning ("out2in key add failed");
           return VNET_API_ERROR_UNSPECIFIED;
         }
 
@@ -972,6 +1188,38 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
               clib_warning ("in2out key del failed");
               return VNET_API_ERROR_UNSPECIFIED;
             }
+          /* Delete sessions */
+          u_key.addr = local->addr;
+          u_key.fib_index = m->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 ((s->in2out.addr.as_u32 != local->addr.as_u32) &&
+                          (clib_net_to_host_u16 (s->in2out.port) != local->port))
+                        continue;
+
+                      nat_free_session_data (sm, s, tsm - sm->per_thread_data);
+                      clib_dlist_remove (tsm->list_pool, s->per_user_index);
+                      pool_put_index (tsm->list_pool, s->per_user_index);
+                      pool_put (tsm->sessions, s);
+                      u->nstaticsessions--;
+                    }
+                }
+            }
         }
       vec_free(m->locals);
 
@@ -981,7 +1229,9 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
   return 0;
 }
 
-int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
+int
+snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
+                  u8 twice_nat)
 {
   snat_address_t *a = 0;
   snat_session_t *ses;
@@ -993,13 +1243,14 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
   snat_static_mapping_t *m;
   snat_interface_t *interface;
   int i;
+  snat_address_t *addresses = twice_nat ? sm->twice_nat_addresses : sm->addresses;
 
   /* Find SNAT address */
-  for (i=0; i < vec_len (sm->addresses); i++)
+  for (i=0; i < vec_len (addresses); i++)
     {
-      if (sm->addresses[i].addr.as_u32 == addr.as_u32)
+      if (addresses[i].addr.as_u32 == addr.as_u32)
         {
-          a = sm->addresses + i;
+          a = addresses + i;
           break;
         }
     }
@@ -1014,7 +1265,7 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
             (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->proto, 0);
+                                            m->proto, 0, m->twice_nat);
       }));
     }
   else
@@ -1039,46 +1290,11 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
           pool_foreach (ses, tsm->sessions, ({
             if (ses->out2in.addr.as_u32 == addr.as_u32)
               {
-                if (snat_is_unk_proto_session (ses))
-                  {
-                    clib_bihash_kv_16_8_t up_kv;
-                    nat_ed_ses_key_t up_key;
-                    up_key.l_addr = ses->in2out.addr;
-                    up_key.r_addr = ses->ext_host_addr;
-                    up_key.fib_index = ses->in2out.fib_index;
-                    up_key.proto = ses->in2out.port;
-                    up_key.rsvd = 0;
-                    up_key.l_port = 0;
-                    up_kv.key[0] = up_key.as_u64[0];
-                    up_kv.key[1] = up_key.as_u64[1];
-                    if (clib_bihash_add_del_16_8 (&sm->in2out_ed,
-                                                  &up_kv, 0))
-                      clib_warning ("in2out key del failed");
-
-                    up_key.l_addr = ses->out2in.addr;
-                    up_key.fib_index = ses->out2in.fib_index;
-                    up_kv.key[0] = up_key.as_u64[0];
-                    up_kv.key[1] = up_key.as_u64[1];
-                    if (clib_bihash_add_del_16_8 (&sm->out2in_ed,
-                                                  &up_kv, 0))
-                      clib_warning ("out2in key del failed");
-                  }
-                else
-                  {
-                    /* log NAT event */
-                    snat_ipfix_logging_nat44_ses_delete(ses->in2out.addr.as_u32,
-                                                        ses->out2in.addr.as_u32,
-                                                        ses->in2out.protocol,
-                                                        ses->in2out.port,
-                                                        ses->out2in.port,
-                                                        ses->in2out.fib_index);
-                    kv.key = ses->in2out.as_u64;
-                    clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0);
-                    kv.key = ses->out2in.as_u64;
-                    clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0);
-                  }
-                vec_add1 (ses_to_be_removed, ses - tsm->sessions);
+                ses->outside_address_index = ~0;
+                nat_free_session_data (sm, ses, tsm - sm->per_thread_data);
                 clib_dlist_remove (tsm->list_pool, ses->per_user_index);
+                pool_put_index (tsm->list_pool, ses->per_user_index);
+                vec_add1 (ses_to_be_removed, ses - tsm->sessions);
                 user_key.addr = ses->in2out.addr;
                 user_key.fib_index = ses->in2out.fib_index;
                 kv.key = user_key.as_u64;
@@ -1097,7 +1313,13 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm)
        }
     }
 
-  vec_del1 (sm->addresses, i);
+  if (twice_nat)
+    {
+      vec_del1 (sm->twice_nat_addresses, i);
+      return 0;
+    }
+  else
+    vec_del1 (sm->addresses, i);
 
   /* Delete external address from FIB */
   pool_foreach (interface, sm->interfaces,
@@ -1513,6 +1735,7 @@ void snat_free_outside_address_and_port (snat_address_t * addresses,
  * @param by_external If 0 match by local address otherwise match by external
  *                    address.
  * @param is_addr_only If matched mapping is address only
+ * @param twice_nat If matched mapping is twice NAT.
  *
  * @returns 0 if match found otherwise 1.
  */
@@ -1520,7 +1743,8 @@ int snat_static_mapping_match (snat_main_t * sm,
                                snat_session_key_t match,
                                snat_session_key_t * mapping,
                                u8 by_external,
-                               u8 *is_addr_only)
+                               u8 *is_addr_only,
+                               u8 *twice_nat)
 {
   clib_bihash_kv_8_8_t kv, value;
   snat_static_mapping_t *m;
@@ -1588,6 +1812,9 @@ int snat_static_mapping_match (snat_main_t * sm,
   if (PREDICT_FALSE(is_addr_only != 0))
     *is_addr_only = m->addr_only;
 
+  if (PREDICT_FALSE(twice_nat != 0))
+    *twice_nat = m->twice_nat;
+
   return 0;
 }
 
@@ -1774,6 +2001,7 @@ add_address_command_fn (vlib_main_t * vm,
   int is_add = 1;
   int rv = 0;
   clib_error_t *error = 0;
+  u8 twice_nat = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -1789,6 +2017,8 @@ add_address_command_fn (vlib_main_t * vm,
         ;
       else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
         end_addr = start_addr;
+      else if (unformat (line_input, "twice-nat"))
+        twice_nat = 1;
       else if (unformat (line_input, "del"))
         is_add = 0;
       else
@@ -1827,9 +2057,9 @@ add_address_command_fn (vlib_main_t * vm,
   for (i = 0; i < count; i++)
     {
       if (is_add)
-        snat_add_address (sm, &this_addr, vrf_id);
+        snat_add_address (sm, &this_addr, vrf_id, twice_nat);
       else
-        rv = snat_del_address (sm, this_addr, 0);
+        rv = snat_del_address (sm, this_addr, 0, twice_nat);
 
       switch (rv)
         {
@@ -1855,7 +2085,7 @@ done:
 VLIB_CLI_COMMAND (add_address_command, static) = {
   .path = "nat44 add address",
   .short_help = "nat44 add address <ip4-range-start> [- <ip4-range-end>] "
-                "[tenant-vrf <vrf-id>] [del]",
+                "[tenant-vrf <vrf-id>] [twice-nat] [del]",
   .function = add_address_command_fn,
 };
 
@@ -2028,6 +2258,7 @@ add_static_mapping_command_fn (vlib_main_t * vm,
   int rv;
   snat_protocol_t proto = ~0;
   u8 proto_set = 0;
+  u8 twice_nat = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -2058,6 +2289,8 @@ add_static_mapping_command_fn (vlib_main_t * vm,
         ;
       else if (unformat (line_input, "%U", unformat_snat_protocol, &proto))
         proto_set = 1;
+      else if (unformat (line_input, "twice-nat"))
+        twice_nat = 1;
       else if (unformat (line_input, "del"))
         is_add = 0;
       else
@@ -2068,6 +2301,12 @@ add_static_mapping_command_fn (vlib_main_t * vm,
         }
     }
 
+  if (twice_nat && addr_only)
+    {
+      error = clib_error_return (0, "twice NAT only for 1:1 NAPT");
+      goto done;
+    }
+
   if (!addr_only && !proto_set)
     {
       error = clib_error_return (0, "missing protocol");
@@ -2075,7 +2314,8 @@ add_static_mapping_command_fn (vlib_main_t * vm,
     }
 
   rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port,
-                               vrf_id, addr_only, sw_if_index, proto, is_add);
+                               vrf_id, addr_only, sw_if_index, proto, is_add,
+                               twice_nat);
 
   switch (rv)
     {
@@ -2122,7 +2362,8 @@ VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
   .path = "nat44 add static mapping",
   .function = add_static_mapping_command_fn,
   .short_help =
-    "nat44 add static mapping tcp|udp|icmp local <addr> [<port>] external <addr> [<port>] [vrf <table-id>] [del]",
+    "nat44 add static mapping tcp|udp|icmp local <addr> [<port>] "
+    "external <addr> [<port>] [vrf <table-id>] [twice-nat] [del]",
 };
 
 static clib_error_t *
@@ -2170,7 +2411,8 @@ add_identity_mapping_command_fn (vlib_main_t * vm,
     }
 
   rv = snat_add_static_mapping(addr, addr, (u16) port, (u16) port,
-                               vrf_id, addr_only, sw_if_index, proto, is_add);
+                               vrf_id, addr_only, sw_if_index, proto, is_add,
+                               0);
 
   switch (rv)
     {
@@ -2233,6 +2475,7 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm,
   snat_protocol_t proto;
   u8 proto_set = 0;
   nat44_lb_addr_port_t *locals = 0, local;
+  u8 twice_nat = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -2257,6 +2500,8 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm,
       else if (unformat (line_input, "protocol %U", unformat_snat_protocol,
                          &proto))
         proto_set = 1;
+      else if (unformat (line_input, "twice-nat"))
+        twice_nat = 1;
       else if (unformat (line_input, "del"))
         is_add = 0;
       else
@@ -2280,7 +2525,7 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm,
     }
 
   rv = nat44_add_del_lb_static_mapping (e_addr, (u16) e_port, proto, vrf_id,
-                                        locals, is_add);
+                                        locals, is_add, twice_nat);
 
   switch (rv)
     {
@@ -2311,7 +2556,9 @@ VLIB_CLI_COMMAND (add_lb_static_mapping_command, static) = {
   .path = "nat44 add load-balancing static mapping",
   .function = add_lb_static_mapping_command_fn,
   .short_help =
-    "nat44 add load-balancing static mapping protocol tcp|udp external <addr>:<port> local <addr>:<port> probability <n> [vrf <table-id>] [del]",
+    "nat44 add load-balancing static mapping protocol tcp|udp "
+    "external <addr>:<port> local <addr>:<port> probability <n> [twice-nat] "
+    "[vrf <table-id>] [del]",
 };
 
 static clib_error_t *
@@ -2524,7 +2771,7 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0)
       key.r_addr = ip0->src_address;
       key.fib_index = rx_fib_index0;
       key.proto = ip0->protocol;
-      key.rsvd = 0;
+      key.r_port = 0;
       key.l_port = 0;
       s_kv.key[0] = key.as_u64[0];
       s_kv.key[1] = key.as_u64[1];
@@ -2779,10 +3026,12 @@ u8 * format_snat_session (u8 * s, va_list * args)
   if (snat_is_unk_proto_session (sess))
     {
       s = format (s, "  i2o %U proto %u fib %u\n",
-                  format_ip4_address, &sess->in2out.addr, sess->in2out.port,
+                  format_ip4_address, &sess->in2out.addr,
+                  clib_net_to_host_u16 (sess->in2out.port),
                   sess->in2out.fib_index);
       s = format (s, "    o2i %U proto %u fib %u\n",
-                  format_ip4_address, &sess->out2in.addr, sess->out2in.port,
+                  format_ip4_address, &sess->out2in.addr,
+                  clib_net_to_host_u16 (sess->out2in.port),
                   sess->out2in.fib_index);
     }
   else
@@ -2790,9 +3039,20 @@ u8 * format_snat_session (u8 * s, va_list * args)
       s = format (s, "  i2o %U\n", format_snat_key, &sess->in2out);
       s = format (s, "    o2i %U\n", format_snat_key, &sess->out2in);
     }
-  if (sess->ext_host_addr.as_u32)
-      s = format (s, "       external host %U\n",
-                  format_ip4_address, &sess->ext_host_addr);
+  if (is_twice_nat_session (sess))
+    {
+      s = format (s, "       external host o2i %U:%d i2o %U:%d\n",
+                  format_ip4_address, &sess->ext_host_addr,
+                  clib_net_to_host_u16 (sess->ext_host_port),
+                  format_ip4_address, &sess->ext_host_nat_addr,
+                  clib_net_to_host_u16 (sess->ext_host_nat_port));
+    }
+  else
+    {
+      if (sess->ext_host_addr.as_u32)
+          s = format (s, "       external host %U\n",
+                      format_ip4_address, &sess->ext_host_addr);
+    }
   s = format (s, "       last heard %.2f\n", sess->last_heard);
   s = format (s, "       total pkts %d, total bytes %lld\n",
               sess->total_pkts, sess->total_bytes);
@@ -2802,6 +3062,8 @@ u8 * format_snat_session (u8 * s, va_list * args)
     s = format (s, "       dynamic translation\n");
   if (sess->flags & SNAT_SESSION_FLAG_LOAD_BALANCING)
     s = format (s, "       load-balancing\n");
+  if (is_twice_nat_session (sess))
+    s = format (s, "       twice-nat\n");
 
   return s;
 }
@@ -2852,29 +3114,30 @@ u8 * format_snat_static_mapping (u8 * s, va_list * args)
   nat44_lb_addr_port_t *local;
 
   if (m->addr_only)
-      s = format (s, "local %U external %U vrf %d",
+      s = format (s, "local %U external %U vrf %d %s",
                   format_ip4_address, &m->local_addr,
                   format_ip4_address, &m->external_addr,
-                  m->vrf_id);
+                  m->vrf_id, m->twice_nat ? "twice-nat" : "");
   else
    {
       if (vec_len (m->locals))
         {
-          s = format (s, "%U vrf %d external %U:%d",
+          s = format (s, "%U vrf %d external %U:%d %s",
                       format_snat_protocol, m->proto,
                       m->vrf_id,
-                      format_ip4_address, &m->external_addr, m->external_port);
+                      format_ip4_address, &m->external_addr, m->external_port,
+                      m->twice_nat ? "twice-nat" : "");
           vec_foreach (local, m->locals)
             s = format (s, "\n  local %U:%d probability %d\%",
                         format_ip4_address, &local->addr, local->port,
                         local->probability);
         }
       else
-        s = format (s, "%U local %U:%d external %U:%d vrf %d",
+        s = format (s, "%U local %U:%d external %U:%d vrf %d %s",
                     format_snat_protocol, m->proto,
                     format_ip4_address, &m->local_addr, m->local_port,
                     format_ip4_address, &m->external_addr, m->external_port,
-                    m->vrf_id);
+                    m->vrf_id, m->twice_nat ? "twice-nat" : "");
    }
   return s;
 }
@@ -3002,6 +3265,17 @@ show_snat_command_fn (vlib_main_t * vm,
             }
         }
 
+      if (vec_len (sm->auto_add_sw_if_indices_twice_nat))
+        {
+          vlib_cli_output (vm, "NAT44 twice-nat pool addresses interfaces:");
+          vec_foreach (sw_if_index, sm->auto_add_sw_if_indices_twice_nat)
+            {
+              vlib_cli_output (vm, "%U", format_vnet_sw_interface_name, vnm,
+                               vnet_get_sw_interface (vnm, *sw_if_index));
+            }
+        }
+
+      vlib_cli_output (vm, "NAT44 pool addresses:");
       vec_foreach (ap, sm->addresses)
         {
           vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr);
@@ -3013,6 +3287,21 @@ show_snat_command_fn (vlib_main_t * vm,
 #define _(N, i, n, s) \
           vlib_cli_output (vm, "  %d busy %s ports", ap->busy_##n##_ports, s);
           foreach_snat_protocol
+#undef _
+        }
+
+      vlib_cli_output (vm, "NAT44 twice-nat pool addresses:");
+      vec_foreach (ap, sm->twice_nat_addresses)
+        {
+          vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr);
+          if (ap->fib_index != ~0)
+              vlib_cli_output (vm, "  tenant VRF: %u",
+                               ip4_fib_get(ap->fib_index)->table_id);
+          else
+            vlib_cli_output (vm, "  tenant VRF independent");
+#define _(N, i, n, s) \
+          vlib_cli_output (vm, "  %d busy %s ports", ap->busy_##n##_ports, s);
+          foreach_snat_protocol
 #undef _
         }
     }
@@ -3090,11 +3379,12 @@ show_snat_command_fn (vlib_main_t * vm,
             }
 
           vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions,"
-                           " %d static mappings",
+                           " %d static mappings, %d twice-nat addresses",
                            users_num,
                            vec_len (sm->addresses),
                            sessions_num,
-                           pool_elts (sm->static_mappings));
+                           pool_elts (sm->static_mappings),
+                           vec_len (sm->twice_nat_addresses));
 
           if (verbose > 0)
             {
@@ -3169,87 +3459,104 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im,
   ip4_address_t l_addr;
   int i, j;
   int rv;
+  u8 twice_nat = 0;
+  snat_address_t *addresses = sm->addresses;
 
   for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++)
     {
       if (sw_if_index == sm->auto_add_sw_if_indices[i])
+          goto match;
+    }
+
+  for (i = 0; i < vec_len(sm->auto_add_sw_if_indices_twice_nat); i++)
+    {
+      twice_nat = 1;
+      addresses = sm->twice_nat_addresses;
+      if (sw_if_index == sm->auto_add_sw_if_indices_twice_nat[i])
+          goto match;
+    }
+
+  return;
+
+match:
+  if (!is_delete)
+    {
+      /* Don't trip over lease renewal, static config */
+      for (j = 0; j < vec_len(addresses); j++)
+        if (addresses[j].addr.as_u32 == address->as_u32)
+          return;
+
+      snat_add_address (sm, address, ~0, twice_nat);
+      /* Scan static map resolution vector */
+      for (j = 0; j < vec_len (sm->to_resolve); j++)
         {
-          if (!is_delete)
+          rp = sm->to_resolve + j;
+          /* On this interface? */
+          if (rp->sw_if_index == sw_if_index)
             {
-              /* Don't trip over lease renewal, static config */
-              for (j = 0; j < vec_len(sm->addresses); j++)
-                if (sm->addresses[j].addr.as_u32 == address->as_u32)
-                  return;
-
-              snat_add_address (sm, address, ~0);
-              /* Scan static map resolution vector */
-              for (j = 0; j < vec_len (sm->to_resolve); j++)
-                {
-                  rp = sm->to_resolve + j;
-                  /* On this interface? */
-                  if (rp->sw_if_index == sw_if_index)
-                    {
-                      /* Indetity mapping? */
-                      if (rp->l_addr.as_u32 == 0)
-                        l_addr.as_u32 = address[0].as_u32;
-                      else
-                        l_addr.as_u32 = rp->l_addr.as_u32;
-                      /* Add the static mapping */
-                      rv = snat_add_static_mapping (l_addr,
-                                                    address[0],
-                                                    rp->l_port,
-                                                    rp->e_port,
-                                                    rp->vrf_id,
-                                                    rp->addr_only,
-                                                    ~0 /* sw_if_index */,
-                                                    rp->proto,
-                                                    rp->is_add);
-                      if (rv)
-                        clib_warning ("snat_add_static_mapping returned %d",
-                                      rv);
-                      vec_add1 (indices_to_delete, j);
-                    }
-                }
-              /* If we resolved any of the outstanding static mappings */
-              if (vec_len(indices_to_delete))
-                {
-                  /* Delete them */
-                  for (j = vec_len(indices_to_delete)-1; j >= 0; j--)
-                    vec_delete(sm->to_resolve, 1, j);
-                  vec_free(indices_to_delete);
-                }
-              return;
-            }
-          else
-            {
-              (void) snat_del_address(sm, address[0], 1);
-              return;
+              /* Indetity mapping? */
+              if (rp->l_addr.as_u32 == 0)
+                l_addr.as_u32 = address[0].as_u32;
+              else
+                l_addr.as_u32 = rp->l_addr.as_u32;
+              /* Add the static mapping */
+              rv = snat_add_static_mapping (l_addr,
+                                            address[0],
+                                            rp->l_port,
+                                            rp->e_port,
+                                            rp->vrf_id,
+                                            rp->addr_only,
+                                            ~0 /* sw_if_index */,
+                                            rp->proto,
+                                            rp->is_add,
+                                            0);
+              if (rv)
+                clib_warning ("snat_add_static_mapping returned %d",
+                              rv);
+              vec_add1 (indices_to_delete, j);
             }
         }
+      /* If we resolved any of the outstanding static mappings */
+      if (vec_len(indices_to_delete))
+        {
+          /* Delete them */
+          for (j = vec_len(indices_to_delete)-1; j >= 0; j--)
+            vec_delete(sm->to_resolve, 1, j);
+          vec_free(indices_to_delete);
+        }
+      return;
+    }
+  else
+    {
+      (void) snat_del_address(sm, address[0], 1, twice_nat);
+      return;
     }
 }
 
 
-int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del)
+int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del,
+                                u8 twice_nat)
 {
   ip4_main_t * ip4_main = sm->ip4_main;
   ip4_address_t * first_int_addr;
   snat_static_map_resolve_t *rp;
   u32 *indices_to_delete = 0;
   int i, j;
+  u32 *auto_add_sw_if_indices =
+    twice_nat ? sm->auto_add_sw_if_indices_twice_nat : sm->auto_add_sw_if_indices;
 
   first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index,
                                                 0 /* just want the address*/);
 
-  for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++)
+  for (i = 0; i < vec_len(auto_add_sw_if_indices); i++)
     {
-      if (sm->auto_add_sw_if_indices[i] == sw_if_index)
+      if (auto_add_sw_if_indices[i] == sw_if_index)
         {
           if (is_del)
             {
               /* if have address remove it */
               if (first_int_addr)
-                  (void) snat_del_address (sm, first_int_addr[0], 1);
+                  (void) snat_del_address (sm, first_int_addr[0], 1, twice_nat);
               else
                 {
                   for (j = 0; j < vec_len (sm->to_resolve); j++)
@@ -3265,7 +3572,10 @@ int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del)
                       vec_free(indices_to_delete);
                     }
                 }
-              vec_del1(sm->auto_add_sw_if_indices, i);
+              if (twice_nat)
+                vec_del1(sm->auto_add_sw_if_indices_twice_nat, i);
+              else
+                vec_del1(sm->auto_add_sw_if_indices, i);
             }
           else
             return VNET_API_ERROR_VALUE_EXIST;
@@ -3278,11 +3588,14 @@ int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del)
     return VNET_API_ERROR_NO_SUCH_ENTRY;
 
   /* add to the auto-address list */
-  vec_add1(sm->auto_add_sw_if_indices, sw_if_index);
+  if (twice_nat)
+    vec_add1(sm->auto_add_sw_if_indices_twice_nat, sw_if_index);
+  else
+    vec_add1(sm->auto_add_sw_if_indices, sw_if_index);
 
   /* If the address is already bound - or static - add it now */
   if (first_int_addr)
-      snat_add_address (sm, first_int_addr, ~0);
+      snat_add_address (sm, first_int_addr, ~0, twice_nat);
 
   return 0;
 }
@@ -3298,6 +3611,7 @@ snat_add_interface_address_command_fn (vlib_main_t * vm,
   int rv;
   int is_del = 0;
   clib_error_t *error = 0;
+  u8 twice_nat = 0;
 
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
@@ -3308,6 +3622,8 @@ snat_add_interface_address_command_fn (vlib_main_t * vm,
       if (unformat (line_input, "%U", unformat_vnet_sw_interface,
                     sm->vnet_main, &sw_if_index))
         ;
+      else if (unformat (line_input, "twice-nat"))
+        twice_nat = 1;
       else if (unformat (line_input, "del"))
         is_del = 1;
       else
@@ -3318,7 +3634,7 @@ snat_add_interface_address_command_fn (vlib_main_t * vm,
         }
     }
 
-  rv = snat_add_interface_address (sm, sw_if_index, is_del);
+  rv = snat_add_interface_address (sm, sw_if_index, is_del, twice_nat);
 
   switch (rv)
     {
@@ -3339,7 +3655,7 @@ done:
 
 VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = {
     .path = "nat44 add interface address",
-    .short_help = "nat44 add interface address <interface> [del]",
+    .short_help = "nat44 add interface address <interface> [twice-nat] [del]",
     .function = snat_add_interface_address_command_fn,
 };
 
index e82c230..5a2d085 100644 (file)
@@ -61,10 +61,10 @@ typedef struct {
     {
       ip4_address_t l_addr;
       ip4_address_t r_addr;
-      u32 fib_index;
+      u32 proto:8,
+          fib_index:24;
       u16 l_port;
-      u8 proto;
-      u8 rsvd;
+      u16 r_port;
     };
     u64 as_u64[2];
   };
@@ -128,6 +128,7 @@ typedef enum {
 #define SNAT_SESSION_FLAG_STATIC_MAPPING 1
 #define SNAT_SESSION_FLAG_UNKNOWN_PROTO  2
 #define SNAT_SESSION_FLAG_LOAD_BALANCING 4
+#define SNAT_SESSION_FLAG_TWICE_NAT      8
 
 #define NAT_INTERFACE_FLAG_IS_INSIDE 1
 #define NAT_INTERFACE_FLAG_IS_OUTSIDE 2
@@ -157,6 +158,10 @@ typedef CLIB_PACKED(struct {
   /* External host address and port */
   ip4_address_t ext_host_addr;  /* 68-71 */
   u16 ext_host_port;            /* 72-73 */
+
+  /* External hos address and port after translation */
+  ip4_address_t ext_host_nat_addr; /* 74-77 */
+  u16 ext_host_nat_port;           /* 78-79 */
 }) snat_session_t;
 
 
@@ -211,6 +216,7 @@ typedef struct {
   u16 local_port;
   u16 external_port;
   u8 addr_only;
+  u8 twice_nat;
   u32 vrf_id;
   u32 fib_index;
   snat_protocol_t proto;
@@ -231,6 +237,7 @@ typedef struct {
   u32 vrf_id;
   snat_protocol_t proto;
   int addr_only;
+  int twice_nat;
   int is_add;
 } snat_static_map_resolve_t;
 
@@ -317,8 +324,12 @@ typedef struct snat_main_s {
   u8 psid_length;
   u16 psid;
 
+  /* Vector of twice NAT addresses for extenal hosts */
+  snat_address_t * twice_nat_addresses;
+
   /* sw_if_indices whose intfc addresses should be auto-added */
   u32 * auto_add_sw_if_indices;
+  u32 * auto_add_sw_if_indices_twice_nat;
 
   /* vector of interface address static mappings to resolve. */
   snat_static_map_resolve_t *to_resolve;
@@ -402,7 +413,8 @@ int snat_static_mapping_match (snat_main_t * sm,
                                snat_session_key_t match,
                                snat_session_key_t * mapping,
                                u8 by_external,
-                               u8 *is_addr_only);
+                               u8 *is_addr_only,
+                               u8 *twice_nat);
 
 void snat_add_del_addr_to_fib (ip4_address_t * addr,
                                u8 p_len,
@@ -426,7 +438,25 @@ typedef struct {
     @param s SNAT session
     @return 1 if SNAT session for unknown protocol otherwise 0
 */
-#define snat_is_unk_proto_session(s) s->flags & SNAT_SESSION_FLAG_UNKNOWN_PROTO
+#define snat_is_unk_proto_session(s) (s->flags & SNAT_SESSION_FLAG_UNKNOWN_PROTO)
+
+/** \brief Check if NAT session is twice NAT.
+    @param s NAT session
+    @return 1 if NAT session is twice NAT
+*/
+#define is_twice_nat_session(s) (s->flags & SNAT_SESSION_FLAG_TWICE_NAT)
+
+/** \brief Check if NAT session is load-balancing.
+    @param s NAT session
+    @return 1 if NAT session is load-balancing
+*/
+#define is_lb_session(s) (s->flags & SNAT_SESSION_FLAG_LOAD_BALANCING)
+
+/** \brief Check if NAT session is endpoint dependent.
+    @param s NAT session
+    @return 1 if NAT session is endpoint dependent
+*/
+#define is_ed_session(s) (snat_is_unk_proto_session (s) || is_twice_nat_session (s) || is_lb_session (s))
 
 #define nat_interface_is_inside(i) i->flags & NAT_INTERFACE_FLAG_IS_INSIDE
 #define nat_interface_is_outside(i) i->flags & NAT_INTERFACE_FLAG_IS_OUTSIDE
@@ -502,24 +532,35 @@ u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node,
                           snat_session_key_t *p_value,
                           u8 *p_dont_translate, void *d, void *e);
 void increment_v4_address(ip4_address_t * a);
-void snat_add_address(snat_main_t *sm, ip4_address_t *addr, u32 vrf_id);
-int snat_del_address(snat_main_t *sm, ip4_address_t addr, u8 delete_sm);
+void snat_add_address(snat_main_t *sm, ip4_address_t *addr, u32 vrf_id,
+                      u8 twice_nat);
+int snat_del_address(snat_main_t *sm, ip4_address_t addr, u8 delete_sm,
+                     u8 twice_nat);
 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);
+                            u32 sw_if_index, snat_protocol_t proto, int is_add,
+                            u8 twice_nat);
 clib_error_t * snat_api_init(vlib_main_t * vm, snat_main_t * sm);
 int snat_set_workers (uword * bitmap);
 int snat_interface_add_del(u32 sw_if_index, u8 is_inside, int is_del);
 int snat_interface_add_del_output_feature(u32 sw_if_index, u8 is_inside,
                                           int is_del);
-int snat_add_interface_address(snat_main_t *sm, u32 sw_if_index, int is_del);
+int snat_add_interface_address(snat_main_t *sm, u32 sw_if_index, int is_del,
+                               u8 twice_nat);
 uword unformat_snat_protocol(unformat_input_t * input, va_list * args);
 u8 * format_snat_protocol(u8 * s, va_list * args);
 int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port,
                                      snat_protocol_t proto, u32 vrf_id,
-                                     nat44_lb_addr_port_t *locals, u8 is_add);
+                                     nat44_lb_addr_port_t *locals, u8 is_add,
+                                     u8 twice_nat);
 int nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
                        snat_protocol_t proto, u32 vrf_id, int is_in);
+void nat_free_session_data (snat_main_t * sm, snat_session_t * s,
+                            u32 thread_index);
+snat_user_t * nat_user_get_or_create (snat_main_t *sm, ip4_address_t *addr,
+                                      u32 fib_index, u32 thread_index);
+snat_session_t * nat_session_alloc_or_recycle (snat_main_t *sm, snat_user_t *u,
+                                               u32 thread_index);
 
 static_always_inline u8
 icmp_is_error_message (icmp46_header_t * icmp)
index f1d9ec8..5071609 100644 (file)
@@ -435,9 +435,9 @@ static void
   for (i = 0; i < count; i++)
     {
       if (mp->is_add)
-       snat_add_address (sm, &this_addr, vrf_id);
+       snat_add_address (sm, &this_addr, vrf_id, mp->twice_nat);
       else
-       rv = snat_del_address (sm, this_addr, 0);
+       rv = snat_del_address (sm, this_addr, 0, mp->twice_nat);
 
       if (rv)
        goto send_reply;
@@ -460,12 +460,14 @@ static void *vl_api_nat44_add_del_address_range_t_print
     {
       s = format (s, " - %U ", format_ip4_address, mp->last_ip_address);
     }
+  s = format (s, "twice_nat %d ", mp->twice_nat);
   FINISH;
 }
 
 static void
 send_nat44_address_details (snat_address_t * a,
-                           unix_shared_memory_queue_t * q, u32 context)
+                           unix_shared_memory_queue_t * q, u32 context,
+                           u8 twice_nat)
 {
   vl_api_nat44_address_details_t *rmp;
   snat_main_t *sm = &snat_main;
@@ -481,6 +483,7 @@ send_nat44_address_details (snat_address_t * a,
     }
   else
     rmp->vrf_id = ~0;
+  rmp->twice_nat = twice_nat;
   rmp->context = context;
 
   vl_msg_api_send_shmem (q, (u8 *) & rmp);
@@ -499,7 +502,9 @@ vl_api_nat44_address_dump_t_handler (vl_api_nat44_address_dump_t * mp)
 
   /* *INDENT-OFF* */
   vec_foreach (a, sm->addresses)
-    send_nat44_address_details (a, q, mp->context);
+    send_nat44_address_details (a, q, mp->context, 0);
+  vec_foreach (a, sm->twice_nat_addresses)
+    send_nat44_address_details (a, q, mp->context, 1);
   /* *INDENT-ON* */
 }
 
@@ -702,7 +707,8 @@ static void
 
   rv = snat_add_static_mapping (local_addr, external_addr, local_port,
                                external_port, vrf_id, mp->addr_only,
-                               external_sw_if_index, proto, mp->is_add);
+                               external_sw_if_index, proto, mp->is_add,
+                               mp->twice_nat);
 
   REPLY_MACRO (VL_API_NAT44_ADD_DEL_STATIC_MAPPING_REPLY);
 }
@@ -723,6 +729,8 @@ static void *vl_api_nat44_add_del_static_mapping_t_print
                clib_net_to_host_u16 (mp->local_port),
                clib_net_to_host_u16 (mp->external_port));
 
+  s = format (s, "twice_nat %d ", mp->twice_nat);
+
   if (mp->vrf_id != ~0)
     s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id));
 
@@ -753,6 +761,7 @@ send_nat44_static_mapping_details (snat_static_mapping_t * m,
   rmp->vrf_id = htonl (m->vrf_id);
   rmp->protocol = snat_proto_to_ip_proto (m->proto);
   rmp->context = context;
+  rmp->twice_nat = m->twice_nat;
 
   vl_msg_api_send_shmem (q, (u8 *) & rmp);
 }
@@ -777,6 +786,7 @@ send_nat44_static_map_resolve_details (snat_static_map_resolve_t * m,
   rmp->vrf_id = htonl (m->vrf_id);
   rmp->protocol = snat_proto_to_ip_proto (m->proto);
   rmp->context = context;
+  rmp->twice_nat = m->twice_nat;
 
   vl_msg_api_send_shmem (q, (u8 *) & rmp);
 }
@@ -849,7 +859,7 @@ static void
 
   rv =
     snat_add_static_mapping (addr, addr, port, port, vrf_id, mp->addr_only,
-                            sw_if_index, proto, mp->is_add);
+                            sw_if_index, proto, mp->is_add, 0);
 
   REPLY_MACRO (VL_API_NAT44_ADD_DEL_IDENTITY_MAPPING_REPLY);
 }
@@ -973,7 +983,7 @@ static void
 
   VALIDATE_SW_IF_INDEX (mp);
 
-  rv = snat_add_interface_address (sm, sw_if_index, is_del);
+  rv = snat_add_interface_address (sm, sw_if_index, is_del, mp->twice_nat);
 
   BAD_SW_IF_INDEX_LABEL;
 
@@ -986,9 +996,9 @@ static void *vl_api_nat44_add_del_interface_addr_t_print
   u8 *s;
 
   s = format (0, "SCRIPT: nat44_add_del_interface_addr ");
-  s = format (s, "sw_if_index %d %s",
+  s = format (s, "sw_if_index %d twice_nat %d %s",
              clib_host_to_net_u32 (mp->sw_if_index),
-             mp->is_add ? "" : "del");
+             mp->twice_nat, mp->is_add ? "" : "del");
 
   FINISH;
 }
@@ -996,7 +1006,7 @@ static void *vl_api_nat44_add_del_interface_addr_t_print
 static void
 send_nat44_interface_addr_details (u32 sw_if_index,
                                   unix_shared_memory_queue_t * q,
-                                  u32 context)
+                                  u32 context, u8 twice_nat)
 {
   vl_api_nat44_interface_addr_details_t *rmp;
   snat_main_t *sm = &snat_main;
@@ -1006,6 +1016,7 @@ send_nat44_interface_addr_details (u32 sw_if_index,
   rmp->_vl_msg_id =
     ntohs (VL_API_NAT44_INTERFACE_ADDR_DETAILS + sm->msg_id_base);
   rmp->sw_if_index = ntohl (sw_if_index);
+  rmp->twice_nat = twice_nat;
   rmp->context = context;
 
   vl_msg_api_send_shmem (q, (u8 *) & rmp);
@@ -1025,7 +1036,9 @@ vl_api_nat44_interface_addr_dump_t_handler (vl_api_nat44_interface_addr_dump_t
 
   /* *INDENT-OFF* */
   vec_foreach (i, sm->auto_add_sw_if_indices)
-    send_nat44_interface_addr_details(*i, q, mp->context);
+    send_nat44_interface_addr_details(*i, q, mp->context, 0);
+  vec_foreach (i, sm->auto_add_sw_if_indices_twice_nat)
+    send_nat44_interface_addr_details(*i, q, mp->context, 1);
   /* *INDENT-ON* */
 }
 
@@ -1231,7 +1244,7 @@ static void
     nat44_add_del_lb_static_mapping (e_addr,
                                     clib_net_to_host_u16 (mp->external_port),
                                     proto, clib_net_to_host_u32 (mp->vrf_id),
-                                    locals, mp->is_add);
+                                    locals, mp->is_add, mp->twice_nat);
 
   vec_free (locals);
 
@@ -1244,7 +1257,7 @@ static void *vl_api_nat44_add_del_lb_static_mapping_t_print
   u8 *s;
 
   s = format (0, "SCRIPT: nat44_add_del_lb_static_mapping ");
-  s = format (s, "is_add %d\n", mp->is_add);
+  s = format (s, "is_add %d twice_nat %d", mp->is_add, mp->twice_nat);
 
   FINISH;
 }
@@ -1271,6 +1284,7 @@ send_nat44_lb_static_mapping_details (snat_static_mapping_t * m,
   rmp->protocol = snat_proto_to_ip_proto (m->proto);
   rmp->vrf_id = ntohl (m->vrf_id);
   rmp->context = context;
+  rmp->twice_nat = m->twice_nat;
 
   locals = (vl_api_nat44_lb_addr_port_t *) rmp->locals;
   vec_foreach (ap, m->locals)
index 15f6f55..b5464e0 100755 (executable)
@@ -107,6 +107,7 @@ vlib_node_registration_t nat44_out2in_reass_node;
 #define foreach_snat_out2in_error                       \
 _(UNSUPPORTED_PROTOCOL, "Unsupported protocol")         \
 _(OUT2IN_PACKETS, "Good out2in packets processed")      \
+_(OUT_OF_PORTS, "Out of ports")                         \
 _(BAD_ICMP_TYPE, "unsupported ICMP type")               \
 _(NO_TRANSLATION, "No translation")                     \
 _(MAX_SESSIONS_EXCEEDED, "Maximum sessions exceeded")   \
@@ -158,11 +159,8 @@ create_session_for_static_mapping (snat_main_t *sm,
                                    u32 thread_index)
 {
   snat_user_t *u;
-  snat_user_key_t user_key;
   snat_session_t *s;
-  clib_bihash_kv_8_8_t kv0, value0;
-  dlist_elt_t * per_user_translation_list_elt;
-  dlist_elt_t * per_user_list_head_elt;
+  clib_bihash_kv_8_8_t kv0;
   ip4_header_t *ip0;
   udp_header_t *udp0;
 
@@ -175,68 +173,25 @@ create_session_for_static_mapping (snat_main_t *sm,
   ip0 = vlib_buffer_get_current (b0);
   udp0 = ip4_next_header (ip0);
 
-  user_key.addr = in2out.addr;
-  user_key.fib_index = in2out.fib_index;
-  kv0.key = user_key.as_u64;
-
-  /* Ever heard of the "user" = inside ip4 address before? */
-  if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].user_hash,
-                              &kv0, &value0))
+  u = nat_user_get_or_create (sm, &in2out.addr, in2out.fib_index, thread_index);
+  if (!u)
     {
-      /* no, make a new one */
-      pool_get (sm->per_thread_data[thread_index].users, u);
-      memset (u, 0, sizeof (*u));
-      u->addr = in2out.addr;
-      u->fib_index = in2out.fib_index;
-
-      pool_get (sm->per_thread_data[thread_index].list_pool,
-                per_user_list_head_elt);
-
-      u->sessions_per_user_list_head_index = per_user_list_head_elt -
-        sm->per_thread_data[thread_index].list_pool;
-
-      clib_dlist_init (sm->per_thread_data[thread_index].list_pool,
-                       u->sessions_per_user_list_head_index);
-
-      kv0.value = u - sm->per_thread_data[thread_index].users;
-
-      /* add user */
-      clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].user_hash,
-                               &kv0, 1 /* is_add */);
+      clib_warning ("create NAT user failed");
+      return 0;
     }
-  else
+
+  s = nat_session_alloc_or_recycle (sm, u, thread_index);
+  if (!s)
     {
-      u = pool_elt_at_index (sm->per_thread_data[thread_index].users,
-                             value0.value);
+      clib_warning ("create NAT session failed");
+      return 0;
     }
 
-  pool_get (sm->per_thread_data[thread_index].sessions, s);
-  memset (s, 0, sizeof (*s));
-
   s->outside_address_index = ~0;
   s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
   s->ext_host_addr.as_u32 = ip0->src_address.as_u32;
   s->ext_host_port = udp0->src_port;
   u->nstaticsessions++;
-
-  /* Create list elts */
-  pool_get (sm->per_thread_data[thread_index].list_pool,
-            per_user_translation_list_elt);
-  clib_dlist_init (sm->per_thread_data[thread_index].list_pool,
-                   per_user_translation_list_elt -
-                   sm->per_thread_data[thread_index].list_pool);
-
-  per_user_translation_list_elt->value =
-    s - sm->per_thread_data[thread_index].sessions;
-  s->per_user_index =
-    per_user_translation_list_elt - sm->per_thread_data[thread_index].list_pool;
-  s->per_user_list_head_index = u->sessions_per_user_list_head_index;
-
-  clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                      s->per_user_list_head_index,
-                      per_user_translation_list_elt -
-                      sm->per_thread_data[thread_index].list_pool);
-
   s->in2out = in2out;
   s->out2in = out2in;
   s->in2out.protocol = out2in.protocol;
@@ -249,7 +204,6 @@ create_session_for_static_mapping (snat_main_t *sm,
       clib_warning ("in2out key add failed");
 
   kv0.key = s->out2in.as_u64;
-  kv0.value = s - sm->per_thread_data[thread_index].sessions;
 
   if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0,
                                1 /* is_add */))
@@ -364,7 +318,7 @@ u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node,
     {
       /* Try to match static mapping by external address and port,
          destination address and port in packet */
-      if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only))
+      if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only, 0))
         {
           /* Don't NAT packet aimed at the intfc address */
           if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0,
@@ -463,7 +417,7 @@ u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node,
     }
   key0.fib_index = rx_fib_index0;
 
-  if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only))
+  if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only, 0))
     {
       /* Don't NAT packet aimed at the intfc address */
       if (is_interface_addr(sm, node, sw_if_index0, ip0->dst_address.as_u32))
@@ -636,15 +590,12 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
       s0->last_heard = now;
       s0->total_pkts++;
       s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0);
-      /* Per-user LRU list maintenance for dynamic translation */
-      if (!snat_is_session_static (s0))
-        {
-          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                             s0->per_user_index);
-          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                              s0->per_user_list_head_index,
-                              s0->per_user_index);
-        }
+      /* Per-user LRU list maintenance */
+      clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                         s0->per_user_index);
+      clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                          s0->per_user_list_head_index,
+                          s0->per_user_index);
     }
   return next0;
 }
@@ -668,9 +619,7 @@ snat_out2in_unknown_proto (snat_main_t *sm,
   nat_ed_ses_key_t key;
   snat_session_t * s;
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
-  snat_user_key_t u_key;
   snat_user_t *u;
-  dlist_elt_t *head, *elt;
 
   old_addr = ip->dst_address.as_u32;
 
@@ -678,7 +627,7 @@ snat_out2in_unknown_proto (snat_main_t *sm,
   key.r_addr = ip->src_address;
   key.fib_index = rx_fib_index;
   key.proto = ip->protocol;
-  key.rsvd = 0;
+  key.r_port = 0;
   key.l_port = 0;
   s_kv.key[0] = key.as_u64[0];
   s_kv.key[1] = key.as_u64[1];
@@ -711,38 +660,21 @@ snat_out2in_unknown_proto (snat_main_t *sm,
 
       new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
 
-      u_key.addr = ip->src_address;
-      u_key.fib_index = m->fib_index;
-      kv.key = u_key.as_u64;
-
-      /* Ever heard of the "user" = src ip4 address before? */
-      if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
+      u = nat_user_get_or_create (sm, &ip->src_address, m->fib_index,
+                                  thread_index);
+      if (!u)
         {
-          /* no, make a new one */
-          pool_get (tsm->users, u);
-          memset (u, 0, sizeof (*u));
-          u->addr = ip->src_address;
-          u->fib_index = rx_fib_index;
-
-          pool_get (tsm->list_pool, head);
-          u->sessions_per_user_list_head_index = head - tsm->list_pool;
-
-          clib_dlist_init (tsm->list_pool,
-                           u->sessions_per_user_list_head_index);
-
-          kv.value = u - tsm->users;
-
-          /* add user */
-          clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1);
-        }
-      else
-        {
-          u = pool_elt_at_index (tsm->users, value.value);
+          clib_warning ("create NAT user failed");
+          return 0;
         }
 
       /* Create a new session */
-      pool_get (tsm->sessions, s);
-      memset (s, 0, sizeof (*s));
+      s = nat_session_alloc_or_recycle (sm, u, thread_index);
+      if (!s)
+        {
+          clib_warning ("create NAT session failed");
+          return 0;
+        }
 
       s->ext_host_addr.as_u32 = ip->src_address.as_u32;
       s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO;
@@ -755,15 +687,6 @@ snat_out2in_unknown_proto (snat_main_t *sm,
       s->in2out.port = s->out2in.port = ip->protocol;
       u->nstaticsessions++;
 
-      /* Create list elts */
-      pool_get (tsm->list_pool, elt);
-      clib_dlist_init (tsm->list_pool, elt - tsm->list_pool);
-      elt->value = s - tsm->sessions;
-      s->per_user_index = 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,
-                          s->per_user_index);
-
       /* Add to lookup tables */
       s_kv.value = s - tsm->sessions;
       if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1))
@@ -813,14 +736,14 @@ snat_out2in_lb (snat_main_t *sm,
   snat_session_t *s = 0;
   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
   snat_session_key_t e_key, l_key;
-  clib_bihash_kv_8_8_t kv, value;
   u32 old_addr, new_addr;
   u32 proto = ip_proto_to_snat_proto (ip->protocol);
   u16 new_port, old_port;
   ip_csum_t sum;
-  snat_user_key_t u_key;
   snat_user_t *u;
-  dlist_elt_t *head, *elt;
+  u32 address_index;
+  snat_session_key_t eh_key;
+  u8 twice_nat;
 
   old_addr = ip->dst_address.as_u32;
 
@@ -828,7 +751,7 @@ snat_out2in_lb (snat_main_t *sm,
   key.r_addr = ip->src_address;
   key.fib_index = rx_fib_index;
   key.proto = ip->protocol;
-  key.rsvd = 0;
+  key.r_port = udp->src_port;
   key.l_port = udp->dst_port;
   s_kv.key[0] = key.as_u64[0];
   s_kv.key[1] = key.as_u64[1];
@@ -849,44 +772,26 @@ snat_out2in_lb (snat_main_t *sm,
       e_key.port = udp->dst_port;
       e_key.protocol = proto;
       e_key.fib_index = rx_fib_index;
-      if (snat_static_mapping_match(sm, e_key, &l_key, 1, 0))
+      if (snat_static_mapping_match(sm, e_key, &l_key, 1, 0, &twice_nat))
         return 0;
 
-      u_key.addr = l_key.addr;
-      u_key.fib_index = l_key.fib_index;
-      kv.key = u_key.as_u64;
-
-      /* Ever heard of the "user" = src ip4 address before? */
-      if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
-        {
-          /* no, make a new one */
-          pool_get (tsm->users, u);
-          memset (u, 0, sizeof (*u));
-          u->addr = l_key.addr;
-          u->fib_index = l_key.fib_index;
-
-          pool_get (tsm->list_pool, head);
-          u->sessions_per_user_list_head_index = head - tsm->list_pool;
-
-          clib_dlist_init (tsm->list_pool,
-                           u->sessions_per_user_list_head_index);
-
-          kv.value = u - tsm->users;
+      u = nat_user_get_or_create (sm, &l_key.addr, l_key.fib_index,
+                                  thread_index);
+      if (!u)
+      {
+        clib_warning ("create NAT user failed");
+        return 0;
+      }
 
-          /* add user */
-          if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
-            clib_warning ("user key add failed");
-        }
-      else
+      s = nat_session_alloc_or_recycle (sm, u, thread_index);
+      if (!s)
         {
-          u = pool_elt_at_index (tsm->users, value.value);
+          clib_warning ("create NAT session failed");
+          return 0;
         }
 
-      /* Create a new session */
-      pool_get (tsm->sessions, s);
-      memset (s, 0, sizeof (*s));
-
       s->ext_host_addr.as_u32 = ip->src_address.as_u32;
+      s->ext_host_port = udp->src_port;
       s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
       s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING;
       s->outside_address_index = ~0;
@@ -894,20 +799,27 @@ snat_out2in_lb (snat_main_t *sm,
       s->in2out = l_key;
       u->nstaticsessions++;
 
-      /* Create list elts */
-      pool_get (tsm->list_pool, elt);
-      clib_dlist_init (tsm->list_pool, elt - tsm->list_pool);
-      elt->value = s - tsm->sessions;
-      s->per_user_index = 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,
-                          s->per_user_index);
-
       /* Add to lookup tables */
       s_kv.value = s - tsm->sessions;
       if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1))
         clib_warning ("out2in-ed key add failed");
 
+      if (twice_nat)
+        {
+          eh_key.protocol = proto;
+          if (snat_alloc_outside_address_and_port (sm->twice_nat_addresses, 0,
+                                                   thread_index, &eh_key,
+                                                   &address_index,
+                                                   sm->port_per_thread,
+                                                   sm->per_thread_data[thread_index].snat_thread_index))
+            {
+              b->error = node->errors[SNAT_OUT2IN_ERROR_OUT_OF_PORTS];
+              return 0;
+            }
+          key.r_addr.as_u32 = s->ext_host_nat_addr.as_u32 = eh_key.addr.as_u32;
+          key.r_port = s->ext_host_nat_port = eh_key.port;
+          s->flags |= SNAT_SESSION_FLAG_TWICE_NAT;
+        }
       key.l_addr = l_key.addr;
       key.fib_index = l_key.fib_index;
       key.l_port = l_key.port;
@@ -922,6 +834,10 @@ snat_out2in_lb (snat_main_t *sm,
   /* Update IP checksum */
   sum = ip->checksum;
   sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
+  if (is_twice_nat_session (s))
+    sum = ip_csum_update (sum, ip->src_address.as_u32,
+                          s->ext_host_nat_addr.as_u32, ip4_header_t,
+                          src_address);
   ip->checksum = ip_csum_fold (sum);
 
   if (PREDICT_TRUE(proto == SNAT_PROTOCOL_TCP))
@@ -933,11 +849,26 @@ snat_out2in_lb (snat_main_t *sm,
       sum = tcp->checksum;
       sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
       sum = ip_csum_update (sum, old_port, new_port, ip4_header_t, length);
+      if (is_twice_nat_session (s))
+        {
+          sum = ip_csum_update (sum, ip->src_address.as_u32,
+                                s->ext_host_nat_addr.as_u32, ip4_header_t,
+                                dst_address);
+          sum = ip_csum_update (sum, tcp->src_port, s->ext_host_nat_port,
+                                ip4_header_t, length);
+          tcp->src_port = s->ext_host_nat_port;
+          ip->src_address.as_u32 = s->ext_host_nat_addr.as_u32;
+        }
       tcp->checksum = ip_csum_fold(sum);
     }
   else
     {
       udp->dst_port = s->in2out.port;
+      if (is_twice_nat_session (s))
+        {
+          udp->src_port = s->ext_host_nat_port;
+          ip->src_address.as_u32 = s->ext_host_nat_addr.as_u32;
+        }
       udp->checksum = 0;
     }
 
@@ -947,6 +878,11 @@ snat_out2in_lb (snat_main_t *sm,
   s->last_heard = now;
   s->total_pkts++;
   s->total_bytes += vlib_buffer_length_in_chain (vm, b);
+  /* Per-user LRU list maintenance */
+  clib_dlist_remove (tsm->list_pool, s->per_user_index);
+  clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index,
+                      s->per_user_index);
+
   return s;
 }
 
@@ -1079,7 +1015,7 @@ snat_out2in_node_fn (vlib_main_t * vm,
             {
               /* Try to match static mapping by external address and port,
                  destination address and port in packet */
-              if (snat_static_mapping_match(sm, key0, &sm0, 1, 0))
+              if (snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
                 {
                   b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
                   /*
@@ -1158,15 +1094,12 @@ snat_out2in_node_fn (vlib_main_t * vm,
           s0->last_heard = now;
           s0->total_pkts++;
           s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
-          /* Per-user LRU list maintenance for dynamic translation */
-          if (!snat_is_session_static (s0))
-            {
-              clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                                 s0->per_user_index);
-              clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                                  s0->per_user_list_head_index,
-                                  s0->per_user_index);
-            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                             s0->per_user_index);
+          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                              s0->per_user_list_head_index,
+                              s0->per_user_index);
         trace0:
 
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -1240,7 +1173,7 @@ snat_out2in_node_fn (vlib_main_t * vm,
             {
               /* Try to match static mapping by external address and port,
                  destination address and port in packet */
-              if (snat_static_mapping_match(sm, key1, &sm1, 1, 0))
+              if (snat_static_mapping_match(sm, key1, &sm1, 1, 0, 0))
                 {
                   b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
                   /*
@@ -1319,15 +1252,12 @@ snat_out2in_node_fn (vlib_main_t * vm,
           s1->last_heard = now;
           s1->total_pkts++;
           s1->total_bytes += vlib_buffer_length_in_chain (vm, b1);
-          /* Per-user LRU list maintenance for dynamic translation */
-          if (!snat_is_session_static (s1))
-            {
-              clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                                 s1->per_user_index);
-              clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                                  s1->per_user_list_head_index,
-                                  s1->per_user_index);
-            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                             s1->per_user_index);
+          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                              s1->per_user_list_head_index,
+                              s1->per_user_index);
         trace1:
 
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -1437,7 +1367,7 @@ snat_out2in_node_fn (vlib_main_t * vm,
             {
               /* Try to match static mapping by external address and port,
                  destination address and port in packet */
-              if (snat_static_mapping_match(sm, key0, &sm0, 1, 0))
+              if (snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
                 {
                   b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
                   /*
@@ -1517,15 +1447,12 @@ snat_out2in_node_fn (vlib_main_t * vm,
           s0->last_heard = now;
           s0->total_pkts++;
           s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
-          /* Per-user LRU list maintenance for dynamic translation */
-          if (!snat_is_session_static (s0))
-            {
-              clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                                 s0->per_user_index);
-              clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                                  s0->per_user_list_head_index,
-                                  s0->per_user_index);
-            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                             s0->per_user_index);
+          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                              s0->per_user_list_head_index,
+                              s0->per_user_index);
         trace00:
 
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -1676,7 +1603,7 @@ nat44_out2in_reass_node_fn (vlib_main_t * vm,
                 {
                   /* Try to match static mapping by external address and port,
                      destination address and port in packet */
-                  if (snat_static_mapping_match(sm, key0, &sm0, 1, 0))
+                  if (snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
                     {
                       b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
                       /*
@@ -1769,15 +1696,12 @@ nat44_out2in_reass_node_fn (vlib_main_t * vm,
           s0->last_heard = now;
           s0->total_pkts++;
           s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
-          /* Per-user LRU list maintenance for dynamic translation */
-          if (!snat_is_session_static (s0))
-            {
-              clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
-                                 s0->per_user_index);
-              clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
-                                  s0->per_user_list_head_index,
-                                  s0->per_user_index);
-            }
+          /* Per-user LRU list maintenance */
+          clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
+                             s0->per_user_index);
+          clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
+                              s0->per_user_list_head_index,
+                              s0->per_user_index);
 
         trace0:
           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
@@ -2739,7 +2663,7 @@ snat_out2in_fast_node_fn (vlib_main_t * vm,
           key0.port = udp0->dst_port;
           key0.fib_index = rx_fib_index0;
 
-          if (snat_static_mapping_match(sm, key0, &sm0, 1, 0))
+          if (snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0))
             {
               b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
               goto trace00;
index 3616329..1f87bff 100644 (file)
@@ -737,7 +737,9 @@ class TestNAT44(MethodHolder):
 
         interfaces = self.vapi.nat44_interface_addr_dump()
         for intf in interfaces:
-            self.vapi.nat44_add_interface_addr(intf.sw_if_index, is_add=0)
+            self.vapi.nat44_add_interface_addr(intf.sw_if_index,
+                                               twice_nat=intf.twice_nat,
+                                               is_add=0)
 
         self.vapi.nat_ipfix(enable=0, src_port=self.ipfix_src_port,
                             domain_id=self.ipfix_domain_id)
@@ -770,6 +772,7 @@ class TestNAT44(MethodHolder):
                 addr_only=sm.addr_only,
                 vrf_id=sm.vrf_id,
                 protocol=sm.protocol,
+                twice_nat=sm.twice_nat,
                 is_add=0)
 
         lb_static_mappings = self.vapi.nat44_lb_static_mapping_dump()
@@ -778,7 +781,8 @@ class TestNAT44(MethodHolder):
                 lb_sm.external_addr,
                 lb_sm.external_port,
                 lb_sm.protocol,
-                lb_sm.vrf_id,
+                vrf_id=lb_sm.vrf_id,
+                twice_nat=lb_sm.twice_nat,
                 is_add=0,
                 local_num=0,
                 locals=[])
@@ -798,6 +802,7 @@ class TestNAT44(MethodHolder):
         for addr in adresses:
             self.vapi.nat44_add_del_address_range(addr.ip_address,
                                                   addr.ip_address,
+                                                  twice_nat=addr.twice_nat,
                                                   is_add=0)
 
         self.vapi.nat_set_reass()
@@ -806,7 +811,7 @@ class TestNAT44(MethodHolder):
     def nat44_add_static_mapping(self, local_ip, external_ip='0.0.0.0',
                                  local_port=0, external_port=0, vrf_id=0,
                                  is_add=1, external_sw_if_index=0xFFFFFFFF,
-                                 proto=0):
+                                 proto=0, twice_nat=0):
         """
         Add/delete NAT44 static mapping
 
@@ -818,6 +823,7 @@ class TestNAT44(MethodHolder):
         :param is_add: 1 if add, 0 if delete (Default add)
         :param external_sw_if_index: External interface instead of IP address
         :param proto: IP protocol (Mandatory if port specified)
+        :param twice_nat: 1 if translate external host address and port
         """
         addr_only = 1
         if local_port and external_port:
@@ -833,18 +839,21 @@ class TestNAT44(MethodHolder):
             addr_only,
             vrf_id,
             proto,
+            twice_nat,
             is_add)
 
-    def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF):
+    def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF, twice_nat=0):
         """
         Add/delete NAT44 address
 
         :param ip: IP address
         :param is_add: 1 if add, 0 if delete (Default add)
+        :param twice_nat: twice NAT address for extenal hosts
         """
         nat_addr = socket.inet_pton(socket.AF_INET, ip)
         self.vapi.nat44_add_del_address_range(nat_addr, nat_addr, is_add,
-                                              vrf_id=vrf_id)
+                                              vrf_id=vrf_id,
+                                              twice_nat=twice_nat)
 
     def test_dynamic(self):
         """ NAT44 dynamic translation test """
@@ -2857,6 +2866,164 @@ class TestNAT44(MethodHolder):
             self.logger.error(ppp("Unexpected or invalid packet:", p))
             raise
 
+    def test_twice_nat(self):
+        """ Twice NAT44 """
+        twice_nat_addr = '10.0.1.3'
+        port_in = 8080
+        port_out = 80
+        eh_port_out = 4567
+        eh_port_in = 0
+        self.nat44_add_address(self.nat_addr)
+        self.nat44_add_address(twice_nat_addr, twice_nat=1)
+        self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr,
+                                      port_in, port_out, proto=IP_PROTOS.tcp,
+                                      twice_nat=1)
+        self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index)
+        self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index,
+                                                  is_inside=0)
+
+        p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+             IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+             TCP(sport=eh_port_out, dport=port_out))
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(1)
+        p = capture[0]
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.dst, self.pg0.remote_ip4)
+            self.assertEqual(ip.src, twice_nat_addr)
+            self.assertEqual(tcp.dport, port_in)
+            self.assertNotEqual(tcp.sport, eh_port_out)
+            eh_port_in = tcp.sport
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
+        p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
+             IP(src=self.pg0.remote_ip4, dst=twice_nat_addr) /
+             TCP(sport=port_in, dport=eh_port_in))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(1)
+        p = capture[0]
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.dst, self.pg1.remote_ip4)
+            self.assertEqual(ip.src, self.nat_addr)
+            self.assertEqual(tcp.dport, eh_port_out)
+            self.assertEqual(tcp.sport, port_out)
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
+    def test_twice_nat_lb(self):
+        """ Twice NAT44 local service load balancing """
+        external_addr_n = socket.inet_pton(socket.AF_INET, self.nat_addr)
+        twice_nat_addr = '10.0.1.3'
+        local_port = 8080
+        external_port = 80
+        eh_port_out = 4567
+        eh_port_in = 0
+        server1 = self.pg0.remote_hosts[0]
+        server2 = self.pg0.remote_hosts[1]
+
+        locals = [{'addr': server1.ip4n,
+                   'port': local_port,
+                   'probability': 50},
+                  {'addr': server2.ip4n,
+                   'port': local_port,
+                   'probability': 50}]
+
+        self.nat44_add_address(self.nat_addr)
+        self.nat44_add_address(twice_nat_addr, twice_nat=1)
+
+        self.vapi.nat44_add_del_lb_static_mapping(external_addr_n,
+                                                  external_port,
+                                                  IP_PROTOS.tcp,
+                                                  twice_nat=1,
+                                                  local_num=len(locals),
+                                                  locals=locals)
+        self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index)
+        self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index,
+                                                  is_inside=0)
+
+        p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
+             IP(src=self.pg1.remote_ip4, dst=self.nat_addr) /
+             TCP(sport=eh_port_out, dport=external_port))
+        self.pg1.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg0.get_capture(1)
+        p = capture[0]
+        server = None
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.src, twice_nat_addr)
+            self.assertIn(ip.dst, [server1.ip4, server2.ip4])
+            if ip.dst == server1.ip4:
+                server = server1
+            else:
+                server = server2
+            self.assertNotEqual(tcp.sport, eh_port_out)
+            eh_port_in = tcp.sport
+            self.assertEqual(tcp.dport, local_port)
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
+        p = (Ether(src=server.mac, dst=self.pg0.local_mac) /
+             IP(src=server.ip4, dst=twice_nat_addr) /
+             TCP(sport=local_port, dport=eh_port_in))
+        self.pg0.add_stream(p)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        capture = self.pg1.get_capture(1)
+        p = capture[0]
+        try:
+            ip = p[IP]
+            tcp = p[TCP]
+            self.assertEqual(ip.src, self.nat_addr)
+            self.assertEqual(ip.dst, self.pg1.remote_ip4)
+            self.assertEqual(tcp.sport, external_port)
+            self.assertEqual(tcp.dport, eh_port_out)
+            self.check_tcp_checksum(p)
+            self.check_ip_checksum(p)
+        except:
+            self.logger.error(ppp("Unexpected or invalid packet:", p))
+            raise
+
+    def test_twice_nat_interface_addr(self):
+        """ Acquire twice NAT44 addresses from interface """
+        self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index, twice_nat=1)
+
+        # no address in NAT pool
+        adresses = self.vapi.nat44_address_dump()
+        self.assertEqual(0, len(adresses))
+
+        # configure interface address and check NAT address pool
+        self.pg7.config_ip4()
+        adresses = self.vapi.nat44_address_dump()
+        self.assertEqual(1, len(adresses))
+        self.assertEqual(adresses[0].ip_address[0:4], self.pg7.local_ip4n)
+        self.assertEqual(adresses[0].twice_nat, 1)
+
+        # remove interface address and check NAT address pool
+        self.pg7.unconfig_ip4()
+        adresses = self.vapi.nat44_address_dump()
+        self.assertEqual(0, len(adresses))
+
     def tearDown(self):
         super(TestNAT44, self).tearDown()
         if not self.vpp_dead:
index 338ca27..3644d3c 100644 (file)
@@ -1222,6 +1222,7 @@ class VppPapiProvider(object):
             addr_only=1,
             vrf_id=0,
             protocol=0,
+            twice_nat=0,
             is_add=1):
         """Add/delete NAT44 static mapping
 
@@ -1233,6 +1234,7 @@ class VppPapiProvider(object):
         :param addr_only: 1 if address only mapping, 0 if address and port
         :param vrf_id: VRF ID
         :param protocol: IP protocol (Default value = 0)
+        :param twice_nat: 1 if translate external host address and port
         :param is_add: 1 if add, 0 if delete (Default value = 1)
         """
         return self.api(
@@ -1245,7 +1247,8 @@ class VppPapiProvider(object):
              'external_port': external_port,
              'external_sw_if_index': external_sw_if_index,
              'vrf_id': vrf_id,
-             'protocol': protocol})
+             'protocol': protocol,
+             'twice_nat': twice_nat})
 
     def nat44_add_del_identity_mapping(
             self,
@@ -1281,12 +1284,14 @@ class VppPapiProvider(object):
             first_ip_address,
             last_ip_address,
             is_add=1,
-            vrf_id=0xFFFFFFFF):
+            vrf_id=0xFFFFFFFF,
+            twice_nat=0):
         """Add/del NAT44 address range
 
         :param first_ip_address: First IP address
         :param last_ip_address: Last IP address
         :param vrf_id: VRF id for the address range
+        :param twice_nat: twice NAT address for extenal hosts
         :param is_add: 1 if add, 0 if delete (Default value = 1)
         """
         return self.api(
@@ -1294,6 +1299,7 @@ class VppPapiProvider(object):
             {'first_ip_address': first_ip_address,
              'last_ip_address': last_ip_address,
              'vrf_id': vrf_id,
+             'twice_nat': twice_nat,
              'is_add': is_add})
 
     def nat44_address_dump(self):
@@ -1335,14 +1341,19 @@ class VppPapiProvider(object):
     def nat44_add_interface_addr(
             self,
             sw_if_index,
+            twice_nat=0,
             is_add=1):
         """Add/del NAT44 address from interface
 
         :param sw_if_index: Software index of the interface
+        :param twice_nat: twice NAT address for extenal hosts
         :param is_add: 1 if add, 0 if delete (Default value = 1)
         """
-        return self.api(self.papi.nat44_add_del_interface_addr,
-                        {'is_add': is_add, 'sw_if_index': sw_if_index})
+        return self.api(
+            self.papi.nat44_add_del_interface_addr,
+            {'is_add': is_add,
+             'sw_if_index': sw_if_index,
+             'twice_nat': twice_nat})
 
     def nat44_interface_addr_dump(self):
         """Dump NAT44 addresses interfaces
@@ -1396,11 +1407,13 @@ class VppPapiProvider(object):
             external_port,
             protocol,
             vrf_id=0,
+            twice_nat=0,
             local_num=0,
             locals=[],
             is_add=1):
         """Add/delete NAT44 load balancing static mapping
 
+        :param twice_nat: 1 if translate external host address and port
         :param is_add - 1 if add, 0 if delete
         """
         return self.api(
@@ -1410,6 +1423,7 @@ class VppPapiProvider(object):
              'external_port': external_port,
              'protocol': protocol,
              'vrf_id': vrf_id,
+             'twice_nat': twice_nat,
              'local_num': local_num,
              'locals': locals})