From 69de9fadbfd2e6e256a5133513e002712705ded3 Mon Sep 17 00:00:00 2001 From: Klement Sekera Date: Thu, 9 Apr 2020 13:31:27 +0200 Subject: nat: ED: reduce number of hash tables used Use out2in_ed hash table for port overloading tracking instead of global table. This reduces number of hash insertions in slowpath. Type: improvement Change-Id: Iad4e897d52033beb7f6d76a7ddb596eef586c6cb Signed-off-by: Klement Sekera --- src/plugins/nat/in2out_ed.c | 301 +++++++++++++++++++++++++------------------- src/plugins/nat/nat.c | 85 +++++-------- src/plugins/nat/nat.h | 25 +--- src/plugins/nat/nat44_cli.c | 1 - src/plugins/nat/out2in_ed.c | 10 -- 5 files changed, 210 insertions(+), 212 deletions(-) (limited to 'src/plugins/nat') diff --git a/src/plugins/nat/in2out_ed.c b/src/plugins/nat/in2out_ed.c index a70369aae4e..295958927cd 100644 --- a/src/plugins/nat/in2out_ed.c +++ b/src/plugins/nat/in2out_ed.c @@ -107,16 +107,6 @@ 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; @@ -201,57 +191,57 @@ snat_random_port (u16 min, u16 max) } static int -nat_alloc_addr_and_port_ed (snat_address_t * addresses, u32 fib_index, +nat_ed_alloc_addr_and_port (snat_main_t * sm, 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) + u32 snat_thread_index, + snat_session_t * s, + clib_bihash_kv_16_8_t * out2in_ed_kv) { int i; snat_address_t *a, *ga = 0; u32 portnum; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; 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++) + for (i = 0; i < vec_len (sm->addresses); i++) { - a = addresses + i; + a = sm->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; \ - } \ +#define _(N, j, n, unused) \ + case SNAT_PROTOCOL_##N: \ + if (a->fib_index == fib_index) \ + { \ + u16 port = snat_random_port (1, port_per_thread); \ + u16 attempts = port_per_thread; \ + while (attempts > 0) \ + { \ + --attempts; \ + portnum = port_thread_offset + port; \ + make_ed_kv (out2in_ed_kv, &a->addr, &key->r_addr, key->proto, \ + s->out2in.fib_index, clib_host_to_net_u16 (portnum), \ + key->r_port); \ + out2in_ed_kv->value = s - tsm->sessions; \ + int rv = clib_bihash_add_del_16_8 (&tsm->out2in_ed, out2in_ed_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; @@ -290,8 +280,8 @@ slow_path_ed (snat_main_t * sm, snat_session_t ** sessionp, vlib_node_runtime_t * node, u32 next, u32 thread_index, f64 now) { - snat_session_t *s = 0; - snat_user_t *u; + snat_session_t *s = NULL; + snat_user_t *u = NULL; snat_session_key_t key0, key1; lb_nat_type_t lb = 0, is_sm = 0; snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; @@ -299,6 +289,8 @@ slow_path_ed (snat_main_t * sm, u32 proto = ip_proto_to_snat_proto (key->proto); nat_outside_fib_t *outside_fib; fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + clib_bihash_kv_16_8_t out2in_ed_kv; + bool out2in_ed_inserted = false; u8 identity_nat; fib_prefix_t pfx = { .fp_proto = FIB_PROTOCOL_IP4, @@ -309,12 +301,26 @@ slow_path_ed (snat_main_t * sm, }; nat44_is_idle_session_ctx_t ctx; + if (PREDICT_TRUE (proto == SNAT_PROTOCOL_TCP)) + { + 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]; + return NAT_NEXT_DROP; + } + } + 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 (!nat_global_lru_free_one (sm, 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; @@ -327,16 +333,61 @@ slow_path_ed (snat_main_t * sm, if (snat_static_mapping_match (sm, key0, &key1, 0, 0, 0, &lb, 0, &identity_nat)) { + u = + nat_user_get_or_create (sm, &key->l_addr, rx_fib_index, thread_index); + if (!u) + { + nat_elog_warn ("create NAT user failed"); + b->error = node->errors[NAT_IN2OUT_ED_ERROR_CANNOT_CREATE_USER]; + goto drop; + } + + s = nat_ed_session_alloc (sm, u, thread_index, now); + if (!s) + { + nat_elog_warn ("create NAT session failed"); + b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_USER_SESS_EXCEEDED]; + goto drop; + } + switch (vec_len (sm->outside_fibs)) + { + case 0: + s->out2in.fib_index = sm->outside_fib_index; + break; + case 1: + s->out2in.fib_index = sm->outside_fibs[0].fib_index; + break; + default: + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + if (fib_entry_get_resolving_interface (fei) != ~0) + { + s->out2in.fib_index = outside_fib->fib_index; + break; + } + } + } + /* *INDENT-ON* */ + break; + } + /* Try to create dynamic translation */ - if (nat_alloc_addr_and_port_ed (sm->addresses, rx_fib_index, + if (nat_ed_alloc_addr_and_port (sm, rx_fib_index, thread_index, key, &key1, sm->port_per_thread, - tsm->snat_thread_index)) + tsm->snat_thread_index, s, + &out2in_ed_kv)) { nat_elog_notice ("addresses exhausted"); b->error = node->errors[NAT_IN2OUT_ED_ERROR_OUT_OF_PORTS]; - return NAT_NEXT_DROP; + goto drop; } + + out2in_ed_inserted = true; } else { @@ -346,48 +397,63 @@ slow_path_ed (snat_main_t * sm, return next; } is_sm = 1; - } + u = + nat_user_get_or_create (sm, &key->l_addr, rx_fib_index, thread_index); + if (!u) + { + nat_elog_warn ("create NAT user failed"); + b->error = node->errors[NAT_IN2OUT_ED_ERROR_CANNOT_CREATE_USER]; + goto drop; + } - if (PREDICT_TRUE (proto == SNAT_PROTOCOL_TCP)) - { - if (PREDICT_FALSE - (!tcp_flags_is_init - (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))) + s = nat_ed_session_alloc (sm, u, thread_index, now); + if (!s) { - 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; + nat44_delete_user_with_no_session (sm, u, thread_index); + nat_elog_warn ("create NAT session failed"); + b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_USER_SESS_EXCEEDED]; + goto drop; + } + switch (vec_len (sm->outside_fibs)) + { + case 0: + s->out2in.fib_index = sm->outside_fib_index; + break; + case 1: + s->out2in.fib_index = sm->outside_fibs[0].fib_index; + break; + default: + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + if (fib_entry_get_resolving_interface (fei) != ~0) + { + s->out2in.fib_index = outside_fib->fib_index; + break; + } + } + } + /* *INDENT-ON* */ + break; } - } - u = nat_user_get_or_create (sm, &key->l_addr, rx_fib_index, thread_index); - if (!u) - { - nat_elog_warn ("create NAT user failed"); - 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; - } + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - s = nat_ed_session_alloc (sm, u, thread_index, now); - if (!s) - { - nat44_delete_user_with_no_session (sm, u, thread_index); - nat_elog_warn ("create NAT session failed"); - 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; + + make_ed_kv (&out2in_ed_kv, &key1.addr, &key->r_addr, key->proto, + s->out2in.fib_index, key1.port, key->r_port); + out2in_ed_kv.value = s - tsm->sessions; + if (clib_bihash_add_or_overwrite_stale_16_8 + (&tsm->out2in_ed, &out2in_ed_kv, nat44_o2i_ed_is_idle_session_cb, + &ctx)) + nat_elog_notice ("out2in-ed key add failed"); + out2in_ed_inserted = true; } user_session_increment (sm, u, is_sm); - if (is_sm) - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; if (lb) s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; @@ -397,33 +463,6 @@ slow_path_ed (snat_main_t * sm, s->out2in = key1; s->out2in.protocol = key0.protocol; - switch (vec_len (sm->outside_fibs)) - { - case 0: - s->out2in.fib_index = sm->outside_fib_index; - break; - case 1: - s->out2in.fib_index = sm->outside_fibs[0].fib_index; - break; - default: - /* *INDENT-OFF* */ - vec_foreach (outside_fib, sm->outside_fibs) - { - fei = fib_table_lookup (outside_fib->fib_index, &pfx); - if (FIB_NODE_INDEX_INVALID != fei) - { - if (fib_entry_get_resolving_interface (fei) != ~0) - { - s->out2in.fib_index = outside_fib->fib_index; - break; - } - } - } - /* *INDENT-ON* */ - break; - } - - /* Add to lookup tables */ kv->value = s - tsm->sessions; ctx.now = now; ctx.thread_index = thread_index; @@ -432,14 +471,6 @@ slow_path_ed (snat_main_t * sm, &ctx)) nat_elog_notice ("in2out-ed key add failed"); - make_ed_kv (kv, &key1.addr, &key->r_addr, key->proto, s->out2in.fib_index, - key1.port, key->r_port); - kv->value = s - tsm->sessions; - if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->out2in_ed, kv, - nat44_o2i_ed_is_idle_session_cb, - &ctx)) - nat_elog_notice ("out2in-ed key add failed"); - *sessionp = s; /* log NAT event */ @@ -464,6 +495,22 @@ slow_path_ed (snat_main_t * sm, thread_index, 0); return next; +drop: + if (out2in_ed_inserted) + { + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &out2in_ed_kv, 0)) + nat_elog_notice ("out2in-ed key del failed"); + } + if (s) + { + nat_free_session_data (sm, s, thread_index, 0); + nat44_ed_delete_session (sm, s, thread_index, 1); + } + if (u) + { + nat44_delete_user_with_no_session (sm, u, thread_index); + } + return NAT_NEXT_DROP; } static_always_inline int diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 30238f9d62f..d27887e80e6 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -323,17 +323,6 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index, 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; - if (sm->ed_ext_ports.instantiated) - 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); } @@ -459,17 +448,6 @@ nat44_free_session_data (snat_main_t * sm, snat_session_t * s, 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; - if (sm->ed_ext_ports.instantiated) - 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); @@ -611,6 +589,39 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u, return s; } +int +nat_global_lru_free_one (snat_main_t * sm, int thread_index, f64 now) +{ + snat_session_t *s = NULL; + dlist_elt_t *oldest_elt; + u64 sess_timeout_time; + u32 oldest_index; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + oldest_index = clib_dlist_remove_head (tsm->global_lru_pool, + tsm->global_lru_head_index); + if (~0 != oldest_index) + { + 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); + return 1; + } + else + { + clib_dlist_addhead (tsm->global_lru_pool, + tsm->global_lru_head_index, oldest_index); + } + } + return 0; +} + snat_session_t * nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index, f64 now) @@ -663,34 +674,9 @@ nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index, } 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) - { - 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) { + nat_global_lru_free_one (sm, thread_index, now); s = nat44_session_alloc_new (tsm, u, now); vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, pool_elts (tsm->sessions)); @@ -4125,9 +4111,6 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) 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 { diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index be82ced4318..4b9f24308f5 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -555,27 +555,6 @@ typedef int (nat_alloc_out_addr_and_port_function_t) (snat_address_t * u16 port_per_thread, u32 snat_thread_index); -typedef struct ed_bihash_key_s -{ - u32 src_address; - u32 dst_address; - u16 src_port; - u16 dst_port; - u8 protocol; -} ed_bihash_key_t; - -typedef struct ed_bihash_kv_s -{ - union - { - ed_bihash_key_t k; - clib_bihash_kv_16_8_t kv; - }; -} ed_bihash_kv_t; - -STATIC_ASSERT (STRUCT_SIZE_OF (ed_bihash_kv_t, k) <= - STRUCT_SIZE_OF (ed_bihash_kv_t, kv.key), - "ed key needs to fit in bihash key"); typedef struct snat_main_s { @@ -729,8 +708,6 @@ typedef struct snat_main_s ip4_main_t *ip4_main; ip_lookup_main_t *ip4_lookup_main; api_main_t *api_main; - - clib_bihash_16_8_t ed_ext_ports; } snat_main_t; typedef struct @@ -1465,6 +1442,8 @@ typedef struct u16 src_port, dst_port; } tcp_udp_header_t; +int nat_global_lru_free_one (snat_main_t * sm, int thread_index, f64 now); + #endif /* __included_nat_h__ */ /* * fd.io coding-style-patch-verification: ON diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c index 82c0433501c..333b3854845 100644 --- a/src/plugins/nat/nat44_cli.c +++ b/src/plugins/nat/nat44_cli.c @@ -245,7 +245,6 @@ nat44_show_hash_commnad_fn (vlib_main_t * vm, unformat_input_t * input, { vlib_cli_output (vm, "%U", format_bihash_16_8, &nam->affinity_hash, verbose); - vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->ed_ext_ports, 0); } return 0; } diff --git a/src/plugins/nat/out2in_ed.c b/src/plugins/nat/out2in_ed.c index 5b70b0c672a..e4f01303b4d 100644 --- a/src/plugins/nat/out2in_ed.c +++ b/src/plugins/nat/out2in_ed.c @@ -131,16 +131,6 @@ nat44_o2i_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void *arg) if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)) nat_elog_warn ("in2out_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; -- cgit 1.2.3-korg