#include <nat/dslite.h>
#include <nat/nat_reass.h>
#include <nat/nat_inlines.h>
+#include <nat/nat_affinity.h>
#include <vnet/fib/fib_table.h>
#include <vnet/fib/ip4_fib.h>
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);
/* session lookup tables */
if (is_ed_session (s))
{
+ 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;
ed_kv.key[1] = ed_key.as_u64[1];
if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &ed_kv, 0))
nat_log_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))
/* 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;
- }
- }
+ 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))
if (s->outside_address_index != ~0)
snat_free_outside_address_and_port (sm->addresses, thread_index,
- &s->out2in, s->outside_address_index);
+ &s->out2in);
}
snat_user_t *
/* add user */
if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
nat_log_warn ("user_hash keay add failed");
-
- clib_warning("%U %d", format_ip4_address, addr, fib_index);
}
else
{
return s;
}
+snat_session_t *
+nat_ed_session_alloc (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];
+ dlist_elt_t * per_user_translation_list_elt;
+
+ if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user)
+ {
+ nat_log_warn ("max translations per user %U", format_ip4_address, &u->addr);
+ snat_ipfix_logging_max_entries_per_user (sm->max_translations_per_user,
+ u->addr.as_u32);
+ return 0;
+ }
+
+ 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;
+}
+
typedef struct {
u8 next_in2out;
} nat44_classify_trace_t;
rp->is_add = is_add;
rp->tag = vec_dup (tag);
}
+
+static u32 get_thread_idx_by_port(u16 e_port)
+{
+ snat_main_t * sm = &snat_main;
+ u32 thread_idx = sm->num_workers;
+ if (sm->num_workers > 1)
+ {
+ thread_idx = sm->first_worker_index + sm->workers[(e_port - 1024) / sm->port_per_thread];
+ }
+ return thread_idx;
+}
/**
* @brief Add static mapping.
if (e_port > 1024) \
{ \
a->busy_##n##_ports++; \
- a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]++; \
+ a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \
} \
break;
foreach_snat_protocol
if (e_port > 1024) \
{ \
a->busy_##n##_ports--; \
- a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]--; \
+ a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \
} \
break;
foreach_snat_protocol
snat_protocol_t proto,
nat44_lb_addr_port_t *locals, u8 is_add,
twice_nat_type_t twice_nat, u8 out2in_only,
- u8 *tag)
+ u8 *tag, u32 affinity)
{
snat_main_t * sm = &snat_main;
snat_static_mapping_t *m;
if (e_port > 1024) \
{ \
a->busy_##n##_ports++; \
- a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]++; \
+ a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \
} \
break;
foreach_snat_protocol
m->proto = proto;
m->twice_nat = twice_nat;
m->out2in_only = out2in_only;
+ m->affinity = affinity;
+
+ if (affinity)
+ m->affinity_per_service_list_head_index =
+ nat_affinity_get_per_service_list_head_index();
+ else
+ m->affinity_per_service_list_head_index = ~0;
m_key.addr = m->external_addr;
m_key.port = m->external_port;
if (e_port > 1024) \
{ \
a->busy_##n##_ports--; \
- a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]--; \
+ a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \
} \
break;
foreach_snat_protocol
}
}
}
+ if (m->affinity)
+ nat_affinity_flush_service (m->affinity_per_service_list_head_index);
vec_free(m->locals);
vec_free(m->tag);
vec_free(m->workers);
sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT;
sm->icmp_timeout = SNAT_ICMP_TIMEOUT;
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;
sm->log_class = vlib_log_register_class ("nat", 0);
error_drop_node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
sm->error_node_index = error_drop_node->index;
+ sm->mss_clamping = 0;
p = hash_get_mem (tm->thread_registrations_by_name, "workers");
if (p)
void snat_free_outside_address_and_port (snat_address_t * addresses,
u32 thread_index,
- snat_session_key_t * k,
- u32 address_index)
+ snat_session_key_t * k)
{
snat_address_t *a;
+ u32 address_index;
u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
+ for (address_index = 0; address_index < vec_len (addresses); address_index++)
+ {
+ if (addresses[address_index].addr.as_u32 == k->addr.as_u32)
+ break;
+ }
+
ASSERT (address_index < vec_len (addresses));
a = addresses + address_index;
u8 by_external,
u8 *is_addr_only,
twice_nat_type_t *twice_nat,
- u8 *lb)
+ lb_nat_type_t *lb,
+ ip4_address_t * ext_host_addr)
{
clib_bihash_kv_8_8_t kv, value;
snat_static_mapping_t *m;
snat_session_key_t m_key;
clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local;
u32 rand, lo = 0, hi, mid;
+ u8 backend_index;
m_key.fib_index = match.fib_index;
if (by_external)
{
if (vec_len (m->locals))
{
+ if (PREDICT_FALSE(lb != 0))
+ *lb = m->affinity ? AFFINITY_LB_NAT : LB_NAT;
+ if (m->affinity)
+ {
+ if (nat_affinity_find_and_lock (ext_host_addr[0], match.addr,
+ match.protocol, match.port, &backend_index))
+ goto get_local;
+
+ mapping->addr = m->locals[backend_index].addr;
+ mapping->port = clib_host_to_net_u16 (m->locals[backend_index].port);
+ mapping->fib_index = m->locals[backend_index].fib_index;
+ goto end;
+ }
get_local:
hi = vec_len (m->locals) - 1;
rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix);
mapping->addr = m->locals[lo].addr;
mapping->port = clib_host_to_net_u16 (m->locals[lo].port);
mapping->fib_index = m->locals[lo].fib_index;
+ if (m->affinity)
+ {
+ if (nat_affinity_create_and_lock (ext_host_addr[0], match.addr,
+ match.protocol, match.port, lo, m->affinity,
+ m->affinity_per_service_list_head_index))
+ nat_log_info ("create affinity record failed");
+ }
}
else
{
+ if (PREDICT_FALSE(lb != 0))
+ *lb = NO_LB_NAT;
mapping->fib_index = m->fib_index;
mapping->addr = m->local_addr;
/* Address only mapping doesn't change port */
mapping->fib_index = sm->outside_fib_index;
}
+end:
if (PREDICT_FALSE(is_addr_only != 0))
*is_addr_only = m->addr_only;
if (PREDICT_FALSE(twice_nat != 0))
*twice_nat = m->twice_nat;
- if (PREDICT_FALSE(lb != 0))
- *lb = vec_len (m->locals) > 0;
-
return 0;
}
return 1;
}
+static int
+nat_alloc_addr_and_port_range (snat_address_t * addresses,
+ u32 fib_index,
+ u32 thread_index,
+ snat_session_key_t * k,
+ u32 * address_indexp,
+ u16 port_per_thread,
+ u32 snat_thread_index)
+{
+ snat_main_t *sm = &snat_main;
+ snat_address_t *a = addresses;
+ u16 portnum, ports;
+
+ ports = sm->end_port - sm->start_port + 1;
+
+ if (!vec_len (addresses))
+ goto exhausted;
+
+ switch (k->protocol)
+ {
+#define _(N, i, n, s) \
+ case SNAT_PROTOCOL_##N: \
+ if (a->busy_##n##_ports < ports) \
+ { \
+ while (1) \
+ { \
+ portnum = snat_random_port(sm->start_port, sm->end_port); \
+ if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
+ continue; \
+ clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
+ a->busy_##n##_ports++; \
+ k->addr = a->addr; \
+ k->port = clib_host_to_net_u16 (portnum); \
+ *address_indexp = i; \
+ return 0; \
+ } \
+ } \
+ break;
+ foreach_snat_protocol
+#undef _
+ default:
+ nat_log_info ("unknown protocol");
+ return 1;
+ }
+
+exhausted:
+ /* Totally out of translations to use... */
+ snat_ipfix_logging_addresses_exhausted(0);
+ return 1;
+}
+
void
nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add)
{
return s;
}
+u8 *
+format_nat_addr_and_port_alloc_alg (u8 * s, va_list * args)
+{
+ u32 i = va_arg (*args, u32);
+ u8 *t = 0;
+
+ switch (i)
+ {
+#define _(v, N, s) case NAT_ADDR_AND_PORT_ALLOC_ALG_##N: t = (u8 *) s; break;
+ foreach_nat_addr_and_port_alloc_alg
+#undef _
+ default:
+ s = format (s, "unknown");
+ return s;
+ }
+ s = format (s, "%s", t);
+ return s;
+}
+
u8 * format_snat_key (u8 * s, va_list * args);
+u8 * format_static_mapping_key (u8 * s, va_list * args);
u8 *
format_session_kvp (u8 * s, va_list * args)
k.as_u64 = v->key;
- s = format (s, "%U static-mapping-index %llu", format_snat_key, &k, v->value);
+ s = format (s, "%U static-mapping-index %llu",
+ format_static_mapping_key, &k, v->value);
return s;
}
format_unformat_error, input);
}
+ if (sm->deterministic && sm->endpoint_dependent)
+ return clib_error_return (
+ 0, "deterministic and endpoint-dependent modes are mutually exclusive");
+
+ if (static_mapping_only && (sm->deterministic || sm->endpoint_dependent))
+ return clib_error_return (
+ 0, "static mapping only mode available only for simple nat");
+
+ if (sm->out2in_dpo && (sm->deterministic || sm->endpoint_dependent))
+ return clib_error_return (
+ 0, "out2in dpo mode available only for simple nat");
+
/* for show commands, etc. */
sm->translation_buckets = translation_buckets;
sm->translation_memory_size = translation_memory_size;
sm->out2in_node_index = nat44_ed_out2in_node.index;
sm->icmp_match_in2out_cb = icmp_match_in2out_ed;
sm->icmp_match_out2in_cb = icmp_match_out2in_ed;
+ nat_affinity_init (vm);
}
else
{
return s;
}
+u8 * format_static_mapping_key (u8 * s, va_list * args)
+{
+ snat_session_key_t * key = va_arg (*args, snat_session_key_t *);
+
+ s = format (s, "%U proto %U port %d fib %d",
+ format_ip4_address, &key->addr,
+ format_snat_protocol, key->protocol,
+ key->port, key->fib_index);
+ return s;
+}
+
u8 * format_snat_session (u8 * s, va_list * args)
{
snat_main_per_thread_data_t * sm = va_arg (*args, snat_main_per_thread_data_t *);
{
snat_main_t *sm = &snat_main;
+ sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE;
sm->alloc_addr_and_port = nat_alloc_addr_and_port_mape;
sm->psid = psid;
sm->psid_offset = psid_offset;
sm->psid_length = psid_length;
}
+void
+nat_set_alloc_addr_and_port_range (u16 start_port, u16 end_port)
+{
+ snat_main_t *sm = &snat_main;
+
+ sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE;
+ sm->alloc_addr_and_port = nat_alloc_addr_and_port_range;
+ sm->start_port = start_port;
+ sm->end_port = end_port;
+}
+
void
nat_set_alloc_addr_and_port_default (void)
{
snat_main_t *sm = &snat_main;
+ sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
sm->alloc_addr_and_port = nat_alloc_addr_and_port_default;
}