summaryrefslogtreecommitdiffstats
path: root/src/plugins/nat/in2out_ed.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/nat/in2out_ed.c')
-rw-r--r--src/plugins/nat/in2out_ed.c116
1 files changed, 108 insertions, 8 deletions
diff --git a/src/plugins/nat/in2out_ed.c b/src/plugins/nat/in2out_ed.c
index 6ca1e6ecb4d..0ca1dd89ca8 100644
--- a/src/plugins/nat/in2out_ed.c
+++ b/src/plugins/nat/in2out_ed.c
@@ -107,6 +107,16 @@ nat44_i2o_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void *arg)
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;
@@ -182,6 +192,96 @@ icmp_in2out_ed_slow_path (snat_main_t * sm, vlib_buffer_t * b0,
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,
@@ -234,16 +334,16 @@ slow_path_ed (snat_main_t * sm,
(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))
{
if (cleared || !nat44_out_of_ports_cleanup (thread_index, now) ||
- snat_alloc_outside_address_and_port (sm->addresses,
- rx_fib_index, thread_index,
- &key1, sm->port_per_thread,
- tsm->snat_thread_index))
+ 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];