if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &ed_kv, 0))
nat_elog_warn ("out2in_ed key del failed");
+ 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 */ );
+
if (snat_is_unk_proto_session (s))
goto delete;
return next0;
}
+static_always_inline u16
+snat_random_port (u16 min, u16 max)
+{
+ snat_main_t *sm = &snat_main;
+ return min + random_u32 (&sm->random_seed) /
+ (random_u32_max () / (max - min + 1) + 1);
+}
+
+static int
+nat_alloc_addr_and_port_ed (snat_address_t * addresses, u32 fib_index,
+ u32 thread_index, nat_ed_ses_key_t * key,
+ snat_session_key_t * key1, u16 port_per_thread,
+ u32 snat_thread_index)
+{
+ int i;
+ snat_address_t *a, *ga = 0;
+ u32 portnum;
+
+ const u16 port_thread_offset = (port_per_thread * snat_thread_index) + 1024;
+ ed_bihash_kv_t bihash_key;
+ clib_memset (&bihash_key, 0, sizeof (bihash_key));
+ bihash_key.k.dst_address = key->r_addr.as_u32;
+ bihash_key.k.dst_port = key->r_port;
+ bihash_key.k.protocol = key1->protocol;
+
+ for (i = 0; i < vec_len (addresses); i++)
+ {
+ a = addresses + i;
+ switch (key1->protocol)
+ {
+#define _(N, j, n, s) \
+ case SNAT_PROTOCOL_##N: \
+ if (a->fib_index == fib_index) \
+ { \
+ bihash_key.k.src_address = a->addr.as_u32; \
+ u16 port = snat_random_port (1, port_per_thread); \
+ u16 attempts = port_per_thread; \
+ while (attempts > 0) \
+ { \
+ --attempts; \
+ portnum = port_thread_offset + port; \
+ bihash_key.k.src_port = clib_host_to_net_u16 (portnum); \
+ int rv = clib_bihash_add_del_16_8 ( \
+ &snat_main.ed_ext_ports, &bihash_key.kv, 2 /* is_add */); \
+ if (0 == rv) \
+ { \
+ ++a->busy_##n##_port_refcounts[portnum]; \
+ a->busy_##n##_ports_per_thread[thread_index]++; \
+ a->busy_##n##_ports++; \
+ key1->addr = a->addr; \
+ key1->port = clib_host_to_net_u16 (portnum); \
+ return 0; \
+ } \
+ port = (port + 1) % port_per_thread; \
+ } \
+ } \
+ else if (a->fib_index == ~0) \
+ { \
+ ga = a; \
+ } \
+ break;
+
+ foreach_snat_protocol;
+ default:
+ nat_elog_info ("unknown protocol");
+ return 1;
+ }
+ }
+
+ if (ga)
+ {
+ /* fake fib_index to reuse macro */
+ fib_index = ~0;
+ a = ga;
+ switch (key1->protocol)
+ {
+ foreach_snat_protocol;
+ default:
+ nat_elog_info ("unknown protocol");
+ return 1;
+ }
+ }
+
+#undef _
+
+ /* Totally out of translations to use... */
+ snat_ipfix_logging_addresses_exhausted (thread_index, 0);
+ return 1;
+}
+
static u32
slow_path_ed (snat_main_t * sm,
vlib_buffer_t * b,
};
nat44_is_idle_session_ctx_t ctx;
- nat44_session_try_cleanup (&key->l_addr, rx_fib_index, thread_index, now);
+ u32 cleared = 0;
if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index)))
{
- b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED];
- nat_ipfix_logging_max_sessions (thread_index, sm->max_translations);
- nat_elog_notice ("maximum sessions exceeded");
- return NAT_NEXT_DROP;
+ if (PREDICT_FALSE
+ (!(cleared = nat44_users_cleanup (thread_index, now))))
+ {
+ b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED];
+ nat_ipfix_logging_max_sessions (thread_index, sm->max_translations);
+ nat_elog_notice ("maximum sessions exceeded");
+ return NAT_NEXT_DROP;
+ }
}
key0.addr = key->l_addr;
key1.protocol = key0.protocol = proto;
key0.fib_index = rx_fib_index;
key1.fib_index = sm->outside_fib_index;
+
/* First try to match static mapping by local address and port */
if (snat_static_mapping_match
(sm, key0, &key1, 0, 0, 0, &lb, 0, &identity_nat))
{
/* Try to create dynamic translation */
- if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index,
- thread_index, &key1,
- sm->port_per_thread,
- tsm->snat_thread_index))
+ if (nat_alloc_addr_and_port_ed (sm->addresses, rx_fib_index,
+ thread_index, key, &key1,
+ sm->port_per_thread,
+ tsm->snat_thread_index))
{
- nat_elog_notice ("addresses exhausted");
- b->error = node->errors[NAT_IN2OUT_ED_ERROR_OUT_OF_PORTS];
- return NAT_NEXT_DROP;
+ if (cleared || !nat44_out_of_ports_cleanup (thread_index, now) ||
+ nat_alloc_addr_and_port_ed (sm->addresses, rx_fib_index,
+ thread_index, key, &key1,
+ sm->port_per_thread,
+ tsm->snat_thread_index))
+ {
+ nat_elog_notice ("addresses exhausted");
+ b->error = node->errors[NAT_IN2OUT_ED_ERROR_OUT_OF_PORTS];
+ return NAT_NEXT_DROP;
+ }
}
}
else
*sessionp = s;
return next;
}
-
is_sm = 1;
}
- if (proto == SNAT_PROTOCOL_TCP)
+ if (PREDICT_TRUE (proto == SNAT_PROTOCOL_TCP))
{
- if (!tcp_flags_is_init
- (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
+ if (PREDICT_FALSE
+ (!tcp_flags_is_init
+ (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags)))
{
b->error = node->errors[NAT_IN2OUT_ED_ERROR_NON_SYN];
+ if (!is_sm)
+ snat_free_outside_address_and_port (sm->addresses,
+ thread_index, &key1);
return NAT_NEXT_DROP;
}
}
if (!is_sm)
snat_free_outside_address_and_port (sm->addresses,
thread_index, &key1);
+ b->error = node->errors[NAT_IN2OUT_ED_ERROR_CANNOT_CREATE_USER];
return NAT_NEXT_DROP;
}
if (!is_sm)
snat_free_outside_address_and_port (sm->addresses,
thread_index, &key1);
+ b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_USER_SESS_EXCEEDED];
return NAT_NEXT_DROP;
}
{
if (ip->protocol == IP_PROTOCOL_TCP)
{
- if (nat44_set_tcp_session_state_i2o (sm, s, b, thread_index))
+ if (nat44_set_tcp_session_state_i2o
+ (sm, now, s, b, thread_index))
return 1;
}
/* Accounting */
thread_index);
if (!u)
{
+ b->error = node->errors[NAT_IN2OUT_ED_ERROR_CANNOT_CREATE_USER];
nat_elog_warn ("create NAT user failed");
return 0;
}
s = nat_ed_session_alloc (sm, u, thread_index, now);
if (!s)
{
+ b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_USER_SESS_EXCEEDED];
nat44_delete_user_with_no_session (sm, u, thread_index);
nat_elog_warn ("create NAT session failed");
return 0;
next0 = def_slow;
goto trace0;
}
-
s0 = pool_elt_at_index (tsm->sessions, value0.value);
+ if (s0->tcp_close_timestamp)
+ {
+ if (now >= s0->tcp_close_timestamp)
+ {
+ // session is closed, go slow path
+ next0 = def_slow;
+ }
+ else
+ {
+ // session in transitory timeout, drop
+ b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TCP_CLOSED];
+ next0 = NAT_NEXT_DROP;
+ }
+ goto trace0;
+ }
+
+ // drop if session expired
+ u64 sess_timeout_time;
+ sess_timeout_time = s0->last_heard +
+ (f64) nat44_session_get_timeout (sm, s0);
+ if (now >= sess_timeout_time)
+ {
+ nat_free_session_data (sm, s0, thread_index, 0);
+ nat44_delete_session (sm, s0, thread_index);
+ // session is closed, go slow path
+ next0 = def_slow;
+ goto trace0;
+ }
+
b0->flags |= VNET_BUFFER_F_IS_NATED;
if (!is_output_feature)
tcp0->checksum = ip_csum_fold (sum0);
}
tcp_packets++;
- if (nat44_set_tcp_session_state_i2o (sm, s0, b0, thread_index))
+ if (nat44_set_tcp_session_state_i2o
+ (sm, now, s0, b0, thread_index))
goto trace0;
}
else if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment
vnet_buffer (b0)->ip.reass.l4_src_port,
vnet_buffer (b0)->ip.reass.l4_dst_port);
- if (clib_bihash_search_16_8 (&tsm->in2out_ed, &kv0, &value0))
+ if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &kv0, &value0))
{
+ s0 = pool_elt_at_index (tsm->sessions, value0.value);
+
+ if (s0->tcp_close_timestamp && now >= s0->tcp_close_timestamp)
+ {
+ nat_free_session_data (sm, s0, thread_index, 0);
+ nat44_delete_session (sm, s0, thread_index);
+ s0 = NULL;
+ }
+ }
+ if (!s0)
+ {
if (is_output_feature)
{
if (PREDICT_FALSE
goto trace0;
}
- else
- {
- s0 = pool_elt_at_index (tsm->sessions, value0.value);
- }
-
b0->flags |= VNET_BUFFER_F_IS_NATED;
tcp0->checksum = ip_csum_fold (sum0);
}
tcp_packets++;
- if (nat44_set_tcp_session_state_i2o (sm, s0, b0, thread_index))
+ if (nat44_set_tcp_session_state_i2o
+ (sm, now, s0, b0, thread_index))
goto trace0;
}
else if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment