#include <nat/nat64.h>
#include <nat/nat66.h>
#include <nat/nat_inlines.h>
+#include <nat/nat44/inlines.h>
#include <nat/nat_affinity.h>
#include <nat/nat_syslog.h>
#include <nat/nat_ha.h>
#include <vnet/fib/fib_table.h>
#include <vnet/fib/ip4_fib.h>
#include <vnet/ip/reass/ip4_sv_reass.h>
+#include <vppinfra/bihash_16_8.h>
#include <vpp/app/version.h>
if (snat_is_session_static (s))
return;
+ ed_bihash_kv_t bihash_key;
+ clib_memset (&bihash_key, 0, sizeof (bihash_key));
+ bihash_key.k.dst_address = s->ext_host_addr.as_u32;
+ bihash_key.k.dst_port = s->ext_host_port;
+ bihash_key.k.src_address = s->out2in.addr.as_u32;
+ bihash_key.k.src_port = s->out2in.port;
+ bihash_key.k.protocol = s->out2in.protocol;
+ clib_bihash_add_del_16_8 (&sm->ed_ext_ports, &bihash_key.kv,
+ 0 /* is_add */ );
+
+ snat_free_outside_address_and_port (sm->addresses, thread_index,
+ &s->out2in);
+}
+
+void
+nat44_free_session_data (snat_main_t * sm, snat_session_t * s,
+ u32 thread_index, u8 is_ha)
+{
+ snat_session_key_t key;
+ nat_ed_ses_key_t ed_key;
+ clib_bihash_kv_16_8_t ed_kv;
+ snat_main_per_thread_data_t *tsm =
+ vec_elt_at_index (sm->per_thread_data, thread_index);
+
+ if (is_fwd_bypass_session (s))
+ {
+ 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->in2out.port;
+ ed_key.r_port = s->ext_host_port;
+ }
+
+ ed_key.l_addr = s->in2out.addr;
+ ed_key.r_addr = s->ext_host_addr;
+ ed_key.fib_index = 0;
+ ed_kv.key[0] = ed_key.as_u64[0];
+ ed_kv.key[1] = ed_key.as_u64[1];
+
+ if (PREDICT_FALSE
+ (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)))
+ nat_elog_warn ("in2out_ed key del failed");
+ return;
+ }
+
+ /* session lookup tables */
+ if (is_affinity_sessions (s))
+ nat_affinity_unlock (s->ext_host_addr, s->out2in.addr,
+ s->in2out.protocol, s->out2in.port);
+ 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 (PREDICT_FALSE (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &ed_kv, 0)))
+ nat_elog_warn ("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 (PREDICT_FALSE (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)))
+ nat_elog_warn ("in2out_ed key del failed");
+
+ if (!is_ha)
+ {
+ nat_syslog_nat44_sdel (s->user_index, s->in2out.fib_index,
+ &s->in2out.addr, s->in2out.port,
+ &s->ext_host_nat_addr, s->ext_host_nat_port,
+ &s->out2in.addr, s->out2in.port,
+ &s->ext_host_addr, s->ext_host_port,
+ s->in2out.protocol, is_twice_nat_session (s));
+ }
+
+ if (snat_is_unk_proto_session (s))
+ return;
+
+ // is this correct ?
+ if (!is_ha)
+ {
+ snat_ipfix_logging_nat44_ses_delete (thread_index,
+ s->in2out.addr.as_u32,
+ s->out2in.addr.as_u32,
+ s->in2out.protocol,
+ s->in2out.port,
+ s->out2in.port,
+ s->in2out.fib_index);
+ nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
+ s->ext_host_port, s->out2in.protocol, s->out2in.fib_index,
+ thread_index);
+ }
+
+ /* Twice NAT address and port for external host */
+ if (is_twice_nat_session (s))
+ {
+ key.protocol = s->in2out.protocol;
+ key.port = s->ext_host_nat_port;
+ key.addr.as_u32 = s->ext_host_nat_addr.as_u32;
+ snat_free_outside_address_and_port (sm->twice_nat_addresses,
+ thread_index, &key);
+ }
+
+ if (snat_is_session_static (s))
+ return;
+
+ ed_bihash_kv_t bihash_key;
+ clib_memset (&bihash_key, 0, sizeof (bihash_key));
+ bihash_key.k.dst_address = s->ext_host_addr.as_u32;
+ bihash_key.k.dst_port = s->ext_host_port;
+ bihash_key.k.src_address = s->out2in.addr.as_u32;
+ bihash_key.k.src_port = s->out2in.port;
+ bihash_key.k.protocol = s->out2in.protocol;
+ clib_bihash_add_del_16_8 (&sm->ed_ext_ports, &bihash_key.kv,
+ 0 /* is_add */ );
+
+ // should be called for every dynamic session
snat_free_outside_address_and_port (sm->addresses, thread_index,
&s->out2in);
}
+
snat_user_t *
nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index,
u32 thread_index)
/* no, make a new one */
pool_get (tsm->users, u);
clib_memset (u, 0, sizeof (*u));
+
+ u->min_session_timeout = 0;
+
u->addr.as_u32 = addr->as_u32;
u->fib_index = fib_index;
/* add user */
if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
- nat_elog_warn ("user_hash keay add failed");
+ {
+ nat_elog_warn ("user_hash key add failed");
+ nat44_delete_user_with_no_session (sm, u, thread_index);
+ return NULL;
+ }
vlib_set_simple_counter (&sm->total_users, thread_index, 0,
pool_elts (tsm->users));
s->per_user_list_head_index,
per_user_translation_list_elt - tsm->list_pool);
+ dlist_elt_t *global_lru_list_elt;
+ pool_get (tsm->global_lru_pool, global_lru_list_elt);
+ global_lru_list_elt->value = s - tsm->sessions;
+ s->global_lru_index = global_lru_list_elt - tsm->global_lru_pool;
+ clib_dlist_addtail (tsm->global_lru_pool, tsm->global_lru_head_index,
+ s->global_lru_index);
+ s->last_lru_update = now;
+
s->user_index = u - tsm->users;
vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
pool_elts (tsm->sessions));
nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index,
f64 now)
{
- snat_session_t *s;
+ snat_session_t *s = NULL;
snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
- dlist_elt_t *per_user_translation_list_elt, *oldest_elt;
- u32 oldest_index;
+
+ dlist_elt_t *oldest_elt;
u64 sess_timeout_time;
+ u32 oldest_index;
+ // no sessions
if (PREDICT_FALSE (!(u->nsessions) && !(u->nstaticsessions)))
goto alloc_new;
+ // no free sessions
+ if (PREDICT_FALSE
+ ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user))
+ {
+ if (nat44_max_translations_per_user_cleanup (u, thread_index, now))
+ goto alloc_new;
+
+ nat_elog_addr (SNAT_LOG_WARNING, "[warn] max translations per user",
+ clib_net_to_host_u32 (u->addr.as_u32));
+ snat_ipfix_logging_max_entries_per_user (thread_index,
+ sm->max_translations_per_user,
+ u->addr.as_u32);
+ return 0;
+ }
+
+ /* first try to reuse an expired session from this ip */
oldest_index =
clib_dlist_remove_head (tsm->list_pool,
u->sessions_per_user_list_head_index);
s = pool_elt_at_index (tsm->sessions, oldest_elt->value);
sess_timeout_time = s->last_heard + (f64) nat44_session_get_timeout (sm, s);
- if (now >= sess_timeout_time)
+ if (now >= sess_timeout_time ||
+ (s->tcp_close_timestamp && now >= s->tcp_close_timestamp))
{
+ // reuse old session
clib_dlist_addtail (tsm->list_pool,
u->sessions_per_user_list_head_index, oldest_index);
- nat_free_session_data (sm, s, thread_index, 0);
- if (snat_is_session_static (s))
- u->nstaticsessions--;
- else
- u->nsessions--;
- s->flags = 0;
- s->total_bytes = 0;
- s->total_pkts = 0;
- s->state = 0;
- s->ext_host_addr.as_u32 = 0;
- s->ext_host_port = 0;
- s->ext_host_nat_addr.as_u32 = 0;
- s->ext_host_nat_port = 0;
+ s = nat44_session_reuse_old (sm, u, s, thread_index, now);
+ s->last_lru_update = now;
}
else
{
clib_dlist_addhead (tsm->list_pool,
u->sessions_per_user_list_head_index, oldest_index);
- if ((u->nsessions + u->nstaticsessions) >=
- sm->max_translations_per_user)
- {
- nat_elog_addr (SNAT_LOG_WARNING, "[warn] max translations per user",
- clib_net_to_host_u32 (u->addr.as_u32));
- snat_ipfix_logging_max_entries_per_user
- (thread_index, sm->max_translations_per_user, u->addr.as_u32);
- return 0;
- }
- else
+ s = NULL;
+ }
+
+alloc_new:
+ /* try to free an expired session from global LRU list */
+ if (!s)
+ {
+ oldest_index = clib_dlist_remove_head (tsm->global_lru_pool,
+ tsm->global_lru_head_index);
+ if (~0 != oldest_index)
{
- alloc_new:
- pool_get (tsm->sessions, s);
- clib_memset (s, 0, sizeof (*s));
-
- /* Create list elts */
- pool_get (tsm->list_pool, per_user_translation_list_elt);
- clib_dlist_init (tsm->list_pool,
- per_user_translation_list_elt - tsm->list_pool);
-
- per_user_translation_list_elt->value = s - tsm->sessions;
- s->per_user_index = per_user_translation_list_elt - tsm->list_pool;
- s->per_user_list_head_index = u->sessions_per_user_list_head_index;
-
- clib_dlist_addtail (tsm->list_pool,
- s->per_user_list_head_index,
- per_user_translation_list_elt - tsm->list_pool);
- }
+ oldest_elt = pool_elt_at_index (tsm->global_lru_pool, oldest_index);
+ s = pool_elt_at_index (tsm->sessions, oldest_elt->value);
+ sess_timeout_time =
+ s->last_heard + (f64) nat44_session_get_timeout (sm, s);
+ if (now >= sess_timeout_time
+ || (s->tcp_close_timestamp && now >= s->tcp_close_timestamp))
+ {
+ nat_free_session_data (sm, s, thread_index, 0);
+ nat44_ed_delete_session (sm, s, thread_index, 0);
+ }
+ else
+ {
+ clib_dlist_addhead (tsm->global_lru_pool,
+ tsm->global_lru_head_index, oldest_index);
+ }
+ s = NULL;
+ }
+ }
+ if (!s)
+ {
+ s = nat44_session_alloc_new (tsm, u, now);
vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
pool_elts (tsm->sessions));
}
-
- s->ha_last_refreshed = now;
-
return s;
}
else
ap->fib_index = ~0;
#define _(N, i, n, s) \
- clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535); \
+ clib_memset(ap->busy_##n##_port_refcounts, 0, sizeof(ap->busy_##n##_port_refcounts));\
ap->busy_##n##_ports = 0; \
ap->busy_##n##_ports_per_thread = 0;\
vec_validate_init_empty (ap->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0);
{
#define _(N, j, n, s) \
case SNAT_PROTOCOL_##N: \
- if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \
+ if (a->busy_##n##_port_refcounts[e_port]) \
return VNET_API_ERROR_INVALID_VALUE; \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \
+ ++a->busy_##n##_port_refcounts[e_port]; \
if (e_port > 1024) \
{ \
a->busy_##n##_ports++; \
{
#define _(N, j, n, s) \
case SNAT_PROTOCOL_##N: \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \
+ --a->busy_##n##_port_refcounts[e_port]; \
if (e_port > 1024) \
{ \
a->busy_##n##_ports--; \
{
#define _(N, j, n, s) \
case SNAT_PROTOCOL_##N: \
- if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \
+ if (a->busy_##n##_port_refcounts[e_port]) \
return VNET_API_ERROR_INVALID_VALUE; \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \
+ ++a->busy_##n##_port_refcounts[e_port]; \
if (e_port > 1024) \
{ \
a->busy_##n##_ports++; \
{
#define _(N, j, n, s) \
case SNAT_PROTOCOL_##N: \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \
+ --a->busy_##n##_port_refcounts[e_port]; \
if (e_port > 1024) \
{ \
a->busy_##n##_ports--; \
}
#define _(N, i, n, s) \
- clib_bitmap_free (a->busy_##n##_port_bitmap); \
vec_free (a->busy_##n##_ports_per_thread);
foreach_snat_protocol
#undef _
sm->fq_in2out_output_index = ~0;
sm->fq_out2in_index = ~0;
-
sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
sm->forwarding_enabled = 0;
{
#define _(N, i, n, s) \
case SNAT_PROTOCOL_##N: \
- ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
- port_host_byte_order) == 1); \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
- port_host_byte_order, 0); \
+ ASSERT (a->busy_##n##_port_refcounts[port_host_byte_order] >= 1); \
+ --a->busy_##n##_port_refcounts[port_host_byte_order]; \
a->busy_##n##_ports--; \
a->busy_##n##_ports_per_thread[thread_index]--; \
break;
{
#define _(N, j, n, s) \
case SNAT_PROTOCOL_##N: \
- if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, port_host_byte_order)) \
+ if (a->busy_##n##_port_refcounts[port_host_byte_order]) \
return VNET_API_ERROR_INSTANCE_IN_USE; \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, port_host_byte_order, 1); \
+ ++a->busy_##n##_port_refcounts[port_host_byte_order]; \
a->busy_##n##_ports_per_thread[thread_index]++; \
a->busy_##n##_ports++; \
return 0;
portnum = (port_per_thread * \
snat_thread_index) + \
snat_random_port(1, port_per_thread) + 1024; \
- if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
+ if (a->busy_##n##_port_refcounts[portnum]) \
continue; \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
+ --a->busy_##n##_port_refcounts[portnum]; \
a->busy_##n##_ports_per_thread[thread_index]++; \
a->busy_##n##_ports++; \
k->addr = a->addr; \
portnum = (port_per_thread * \
snat_thread_index) + \
snat_random_port(1, port_per_thread) + 1024; \
- if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
+ if (a->busy_##n##_port_refcounts[portnum]) \
continue; \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
+ ++a->busy_##n##_port_refcounts[portnum]; \
a->busy_##n##_ports_per_thread[thread_index]++; \
a->busy_##n##_ports++; \
k->addr = a->addr; \
A = snat_random_port(1, pow2_mask(sm->psid_offset)); \
j = snat_random_port(0, pow2_mask(m)); \
portnum = A | (sm->psid << sm->psid_offset) | (j << (16 - m)); \
- if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
+ if (a->busy_##n##_port_refcounts[portnum]) \
continue; \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
+ ++a->busy_##n##_port_refcounts[portnum]; \
a->busy_##n##_ports++; \
k->addr = a->addr; \
k->port = clib_host_to_net_u16 (portnum); \
while (1) \
{ \
portnum = snat_random_port(sm->start_port, sm->end_port); \
- if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
+ if (a->busy_##n##_port_refcounts[portnum]) \
continue; \
- clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
+ ++a->busy_##n##_port_refcounts[portnum]; \
a->busy_##n##_ports++; \
k->addr = a->addr; \
k->port = clib_host_to_net_u16 (portnum); \
u8 static_mapping_only = 0;
u8 static_mapping_connection_tracking = 0;
+ // configurable timeouts
u32 udp_timeout = SNAT_UDP_TIMEOUT;
u32 icmp_timeout = SNAT_ICMP_TIMEOUT;
-
u32 tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
u32 tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT;
/* optionally configurable timeouts for testing purposes */
sm->udp_timeout = udp_timeout;
- sm->icmp_timeout = icmp_timeout;
sm->tcp_transitory_timeout = tcp_transitory_timeout;
sm->tcp_established_timeout = tcp_established_timeout;
+ sm->icmp_timeout = icmp_timeout;
+
+ sm->min_timeout = nat44_minimal_timeout (sm);
sm->user_buckets = user_buckets;
sm->user_memory_size = user_memory_size;
sm->worker_in2out_cb = snat_get_worker_in2out_cb;
sm->worker_out2in_cb = snat_get_worker_out2in_cb;
- sm->handoff_out2in_index = snat_in2out_node.index;
- sm->handoff_in2out_index = snat_out2in_node.index;
+ sm->handoff_out2in_index = snat_out2in_node.index;
+ sm->handoff_in2out_index = snat_in2out_node.index;
sm->handoff_in2out_output_index = snat_in2out_output_node.index;
sm->in2out_node_index = snat_in2out_node.index;
vec_foreach (tsm, sm->per_thread_data)
{
tsm->min_session_timeout = 0;
+
+ tsm->cleared = 0;
+ tsm->cleanup_runs = 0;
+ tsm->cleanup_timeout = 0;
+
+ pool_alloc (tsm->sessions, sm->max_translations);
+ pool_alloc (tsm->list_pool, sm->max_translations);
+ pool_alloc (tsm->global_lru_pool, sm->max_translations);
+
+ dlist_elt_t *head;
+ pool_get (tsm->global_lru_pool, head);
+ tsm->global_lru_head_index = head - tsm->global_lru_pool;
+ clib_dlist_init (tsm->global_lru_pool,
+ tsm->global_lru_head_index);
+
if (sm->endpoint_dependent)
{
clib_bihash_init_16_8 (&tsm->in2out_ed, "in2out-ed",
translation_memory_size);
clib_bihash_set_kvp_format_fn_16_8 (&tsm->out2in_ed,
format_ed_session_kvp);
+ clib_bihash_init_16_8
+ (&sm->ed_ext_ports, "ed-nat-5-tuple-port-overload-hash",
+ translation_buckets, translation_memory_size);
}
else
{