nat: twice-nat static mapping pool address

Let twice-nat static mapping pick specific
address from the twice-nat pool.

Type: improvement

Change-Id: Iadaa036af2fa3b0e6e9a68ff6e68b4bbe1650eb1
Signed-off-by: Filip Varga <fivarga@cisco.com>
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index 796d9d0..61a36ec 100644
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -692,7 +692,8 @@
 				       nat_protocol_t proto,
 				       int addr_only, int is_add, u8 * tag,
 				       int twice_nat, int out2in_only,
-				       int identity_nat)
+				       int identity_nat,
+				       ip4_address_t pool_addr, int exact)
 {
   snat_static_map_resolve_t *rp;
 
@@ -709,6 +710,8 @@
   rp->out2in_only = out2in_only;
   rp->identity_nat = identity_nat;
   rp->tag = vec_dup (tag);
+  rp->pool_addr = pool_addr;
+  rp->exact = exact;
 }
 
 static u32
@@ -829,7 +832,7 @@
 			 u16 l_port, u16 e_port, u32 vrf_id, int addr_only,
 			 u32 sw_if_index, nat_protocol_t proto, int is_add,
 			 twice_nat_type_t twice_nat, u8 out2in_only, u8 * tag,
-			 u8 identity_nat)
+			 u8 identity_nat, ip4_address_t pool_addr, int exact)
 {
   snat_main_t *sm = &snat_main;
   snat_static_mapping_t *m;
@@ -891,7 +894,8 @@
 
 	  snat_add_static_mapping_when_resolved
 	    (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto,
-	     addr_only, is_add, tag, twice_nat, out2in_only, identity_nat);
+	     addr_only, is_add, tag, twice_nat, out2in_only,
+	     identity_nat, pool_addr, exact);
 
 	  /* DHCP resolution required? */
 	  if (first_int_addr == 0)
@@ -1046,6 +1050,13 @@
       m->local_addr = l_addr;
       m->external_addr = e_addr;
       m->twice_nat = twice_nat;
+
+      if (twice_nat == TWICE_NAT && exact)
+	{
+	  m->flags |= NAT_STATIC_MAPPING_FLAG_EXACT_ADDRESS;
+	  m->pool_addr = pool_addr;
+	}
+
       if (out2in_only)
 	m->flags |= NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY;
       if (addr_only)
@@ -1673,15 +1684,21 @@
 
   if (delete_sm)
     {
+      ip4_address_t pool_addr = { 0 };
       /* *INDENT-OFF* */
       pool_foreach (m, sm->static_mappings,
       ({
           if (m->external_addr.as_u32 == addr.as_u32)
             (void) snat_add_static_mapping (m->local_addr, m->external_addr,
                                             m->local_port, m->external_port,
-                                            m->vrf_id, is_addr_only_static_mapping(m), ~0,
-                                            m->proto, 0, m->twice_nat,
-                                            is_out2in_only_static_mapping(m), m->tag, is_identity_static_mapping(m));
+                                            m->vrf_id,
+                                            is_addr_only_static_mapping(m), ~0,
+                                            m->proto, 0 /* is_add */,
+                                            m->twice_nat,
+                                            is_out2in_only_static_mapping(m),
+                                            m->tag,
+                                            is_identity_static_mapping(m),
+                                            pool_addr, 0);
       }));
       /* *INDENT-ON* */
     }
@@ -2801,16 +2818,29 @@
 			   u8 * is_addr_only,
 			   twice_nat_type_t * twice_nat,
 			   lb_nat_type_t * lb, ip4_address_t * ext_host_addr,
-			   u8 * is_identity_nat)
+			   u8 * is_identity_nat, snat_static_mapping_t ** out)
 {
   clib_bihash_kv_8_8_t kv, value;
+  clib_bihash_8_8_t *mapping_hash;
   snat_static_mapping_t *m;
-  clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local;
   u32 rand, lo = 0, hi, mid, *tmp = 0, i;
-  u8 backend_index;
   nat44_lb_addr_port_t *local;
+  u8 backend_index;
 
-  if (by_external)
+  if (!by_external)
+    {
+      mapping_hash = &sm->static_mapping_by_local;
+      init_nat_k (&kv, match_addr, match_port, match_fib_index,
+		  match_protocol);
+      if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
+	{
+	  /* Try address only mapping */
+	  init_nat_k (&kv, match_addr, 0, match_fib_index, 0);
+	  if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
+	    return 1;
+	}
+    }
+  else
     {
       mapping_hash = &sm->static_mapping_by_external;
       init_nat_k (&kv, match_addr, match_port, 0, match_protocol);
@@ -2821,20 +2851,6 @@
 	  if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
 	    return 1;
 	}
-
-    }
-  else
-    {
-      init_nat_k (&kv, match_addr, match_port, match_fib_index,
-		  match_protocol);
-      if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
-	{
-	  /* Try address only mapping */
-	  init_nat_k (&kv, match_addr, 0, match_fib_index, 0);
-	  if (clib_bihash_search_8_8 (mapping_hash, &kv, &value))
-	    return 1;
-	}
-
     }
 
   m = pool_elt_at_index (sm->static_mappings, value.value);
@@ -2943,6 +2959,9 @@
   if (PREDICT_FALSE (is_identity_nat != 0))
     *is_identity_nat = is_identity_static_mapping (m);
 
+  if (out != 0)
+    *out = m;
+
   return 0;
 }
 
@@ -4358,7 +4377,8 @@
 				rp->vrf_id,
 				rp->addr_only, ~0 /* sw_if_index */ ,
 				rp->proto, !is_delete, rp->twice_nat,
-				rp->out2in_only, rp->tag, rp->identity_nat);
+				rp->out2in_only, rp->tag, rp->identity_nat,
+				rp->pool_addr, rp->exact);
   if (rv)
     nat_elog_notice_X1 ("snat_add_static_mapping returned %d", "i4", rv);
 }
@@ -4429,7 +4449,8 @@
 					    rp->proto,
 					    rp->is_add, rp->twice_nat,
 					    rp->out2in_only, rp->tag,
-					    rp->identity_nat);
+					    rp->identity_nat,
+					    rp->pool_addr, rp->exact);
 	      if (rv)
 		nat_elog_notice_X1 ("snat_add_static_mapping returned %d",
 				    "i4", rv);