aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xsrc/plugins/nat/in2out.c475
-rw-r--r--src/plugins/nat/nat.api18
-rw-r--r--src/plugins/nat/nat.c700
-rw-r--r--src/plugins/nat/nat.h61
-rw-r--r--src/plugins/nat/nat_api.c40
-rwxr-xr-xsrc/plugins/nat/out2in.c310
-rw-r--r--test/test_nat.py177
-rw-r--r--test/vpp_papi_provider.py22
8 files changed, 1011 insertions, 792 deletions
diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c
index 1052451ed0a..603abb8ff38 100755
--- a/src/plugins/nat/in2out.c
+++ b/src/plugins/nat/in2out.c
@@ -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;
diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api
index d8e0d7eb812..2a8eeb9489f 100644
--- a/src/plugins/nat/nat.api
+++ b/src/plugins/nat/nat.api
@@ -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];
};
diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c
index 6aa2d81f073..52944014ab0 100644
--- a/src/plugins/nat/nat.c
+++ b/src/plugins/nat/nat.c
@@ -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);
@@ -3015,6 +3289,21 @@ show_snat_command_fn (vlib_main_t * vm,
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 _
+ }
}
if (sm->num_workers > 1)
@@ -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,
};
diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h
index e82c23026cb..5a2d0855e9b 100644
--- a/src/plugins/nat/nat.h
+++ b/src/plugins/nat/nat.h
@@ -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)
diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c
index f1d9ec83af3..5071609c0b9 100644
--- a/src/plugins/nat/nat_api.c
+++ b/src/plugins/nat/nat_api.c
@@ -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)
diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c
index 15f6f556d73..b5464e0ad41 100755
--- a/src/plugins/nat/out2in.c
+++ b/src/plugins/nat/out2in.c
@@ -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;
diff --git a/test/test_nat.py b/test/test_nat.py
index 36163297961..1f87bffab57 100644
--- a/test/test_nat.py
+++ b/test/test_nat.py
@@ -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:
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 338ca27eaa1..3644d3c6779 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -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})