diff options
author | Klement Sekera <ksekera@cisco.com> | 2020-05-04 09:56:58 +0000 |
---|---|---|
committer | Ole Trøan <otroan@employees.org> | 2020-05-13 11:39:30 +0000 |
commit | a3a830484fa6b6487f7f3d991a52cb6b090a8be8 (patch) | |
tree | 3b833401c46da712228b0c1194b9f6c3b80b0ffa | |
parent | bc752e432ff3e252f6e1a3f0372c9532f14909cd (diff) |
nat: fix LRU blocked by inactive session
This fixes a situation where long-lived inactive session blocks LRU
list. Solution is to have multiple LRU lists based on session type.
This helps because session timeout is same for all sessions of same
type.
Type: fix
Signed-off-by: Klement Sekera <ksekera@cisco.com>
Change-Id: I5e54b2aab73b23911d6518d42e8c3f166c69a38c
-rw-r--r-- | src/plugins/nat/in2out_ed.c | 25 | ||||
-rwxr-xr-x | src/plugins/nat/nat.c | 105 | ||||
-rw-r--r-- | src/plugins/nat/nat.h | 28 | ||||
-rw-r--r-- | src/plugins/nat/nat44/ed_inlines.h | 144 | ||||
-rw-r--r-- | src/plugins/nat/nat44/inlines.h | 15 | ||||
-rw-r--r-- | src/plugins/nat/nat44_cli.c | 33 | ||||
-rw-r--r-- | src/plugins/nat/nat_api.c | 3 | ||||
-rw-r--r-- | src/plugins/nat/nat_inlines.h | 73 | ||||
-rw-r--r-- | src/plugins/nat/out2in_ed.c | 21 | ||||
-rw-r--r-- | src/plugins/nat/test/test_nat.py | 105 |
10 files changed, 391 insertions, 161 deletions
diff --git a/src/plugins/nat/in2out_ed.c b/src/plugins/nat/in2out_ed.c index f8682d0f3c2..45d9fd0b32c 100644 --- a/src/plugins/nat/in2out_ed.c +++ b/src/plugins/nat/in2out_ed.c @@ -31,6 +31,7 @@ #include <nat/nat44/inlines.h> #include <nat/nat_syslog.h> #include <nat/nat_ha.h> +#include <nat/nat44/ed_inlines.h> static char *nat_in2out_ed_error_strings[] = { #define _(sym,string) string, @@ -155,7 +156,7 @@ nat44_i2o_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void *arg) snat_free_outside_address_and_port (sm->addresses, ctx->thread_index, &s->out2in); delete: - nat44_ed_delete_session (sm, s, ctx->thread_index, 1); + nat_ed_session_delete (sm, s, ctx->thread_index, 1); return 1; } @@ -323,7 +324,7 @@ slow_path_ed (snat_main_t * sm, if (PREDICT_FALSE (nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) { - if (!nat_global_lru_free_one (sm, thread_index, now)) + if (!nat_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); @@ -342,7 +343,7 @@ slow_path_ed (snat_main_t * sm, if (snat_static_mapping_match (sm, key0, &key1, 0, 0, 0, &lb, 0, &identity_nat)) { - s = nat_ed_session_alloc (sm, thread_index, now); + s = nat_ed_session_alloc (sm, thread_index, now, proto); if (!s) { nat_elog_warn ("create NAT session failed"); @@ -386,7 +387,7 @@ slow_path_ed (snat_main_t * sm, nat_elog_notice ("addresses exhausted"); b->error = node->errors[NAT_IN2OUT_ED_ERROR_OUT_OF_PORTS]; nat_free_session_data (sm, s, thread_index, 0); - nat44_ed_delete_session (sm, s, thread_index, 1); + nat_ed_session_delete (sm, s, thread_index, 1); return NAT_NEXT_DROP; } key1.addr = allocated_addr; @@ -399,7 +400,7 @@ slow_path_ed (snat_main_t * sm, *sessionp = s; return next; } - s = nat_ed_session_alloc (sm, thread_index, now); + s = nat_ed_session_alloc (sm, thread_index, now, proto); if (!s) { nat_elog_warn ("create NAT session failed"); @@ -601,7 +602,7 @@ nat44_ed_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip, if (nat44_is_ses_closed (s)) { nat_free_session_data (sm, s, thread_index, 0); - nat44_ed_delete_session (sm, s, thread_index, 1); + nat_ed_session_delete (sm, s, thread_index, 1); } else s->flags |= SNAT_SESSION_FLAG_OUTPUT_FEATURE; @@ -857,7 +858,7 @@ nat44_ed_in2out_unknown_proto (snat_main_t * sm, } create_ses: - s = nat_ed_session_alloc (sm, thread_index, now); + s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol); if (!s) { b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_USER_SESS_EXCEEDED]; @@ -1026,9 +1027,9 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm, } s0 = pool_elt_at_index (tsm->sessions, value0.value); - if (s0->tcp_close_timestamp) + if (s0->tcp_closed_timestamp) { - if (now >= s0->tcp_close_timestamp) + if (now >= s0->tcp_closed_timestamp) { // session is closed, go slow path next0 = def_slow; @@ -1049,7 +1050,7 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm, if (now >= sess_timeout_time) { nat_free_session_data (sm, s0, thread_index, 0); - nat44_ed_delete_session (sm, s0, thread_index, 1); + nat_ed_session_delete (sm, s0, thread_index, 1); // session is closed, go slow path next0 = def_slow; goto trace0; @@ -1307,10 +1308,10 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t * vm, { s0 = pool_elt_at_index (tsm->sessions, value0.value); - if (s0->tcp_close_timestamp && now >= s0->tcp_close_timestamp) + if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp) { nat_free_session_data (sm, s0, thread_index, 0); - nat44_ed_delete_session (sm, s0, thread_index, 1); + nat_ed_session_delete (sm, s0, thread_index, 1); s0 = NULL; } } diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 1fb3a7ff397..ee35a57a3ce 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -34,6 +34,7 @@ #include <vnet/fib/ip4_fib.h> #include <vnet/ip/reass/ip4_sv_reass.h> #include <vppinfra/bihash_16_8.h> +#include <nat/nat44/ed_inlines.h> #include <vpp/app/version.h> @@ -593,14 +594,6 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u, 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)); @@ -611,58 +604,6 @@ 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, u32 thread_index, f64 now) -{ - snat_session_t *s; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - - nat_global_lru_free_one (sm, thread_index, now); - - pool_get (tsm->sessions, s); - clib_memset (s, 0, sizeof (*s)); - - nat44_global_lru_insert (tsm, s, now); - - s->ha_last_refreshed = now; - vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, - pool_elts (tsm->sessions)); - return s; -} - void snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index, int is_add) @@ -912,7 +853,7 @@ snat_ed_static_mapping_del_sessions (snat_main_t * sm, vec_foreach (ses_index, indexes_to_free) { s = pool_elt_at_index (tsm->sessions, *ses_index); - nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); } vec_free (indexes_to_free); } @@ -1617,7 +1558,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, continue; nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); }); })); /* *INDENT-ON* */ @@ -1749,7 +1690,7 @@ nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port, continue; nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); }); /* *INDENT-ON* */ @@ -1866,7 +1807,7 @@ snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm, vec_foreach (ses_index, ses_to_be_removed) { ses = pool_elt_at_index (tsm->sessions, ses_index[0]); - nat44_ed_delete_session (sm, ses, tsm - sm->per_thread_data, 1); + nat_ed_session_delete (sm, ses, tsm - sm->per_thread_data, 1); } }else{ vec_foreach (ses_index, ses_to_be_removed) @@ -3657,6 +3598,11 @@ nat_ha_sadd_cb (ip4_address_t * in_addr, u16 in_port, if (!s) return; + if (sm->endpoint_dependent) + { + nat_ed_lru_insert (tsm, s, now, proto); + } + s->last_heard = now; s->flags = flags; s->ext_host_addr.as_u32 = eh_addr->as_u32; @@ -3806,7 +3752,7 @@ nat_ha_sadd_ed_cb (ip4_address_t * in_addr, u16 in_port, return; } - s = nat_ed_session_alloc (sm, thread_index, now); + s = nat_ed_session_alloc (sm, thread_index, now, proto); if (!s) return; @@ -3925,12 +3871,29 @@ nat44_db_init (snat_main_per_thread_data_t * tsm) snat_main_t *sm = &snat_main; pool_alloc (tsm->sessions, sm->max_translations); - pool_alloc (tsm->global_lru_pool, sm->max_translations); + pool_alloc (tsm->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); + + pool_get (tsm->lru_pool, head); + tsm->tcp_trans_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->tcp_trans_lru_head_index); + + pool_get (tsm->lru_pool, head); + tsm->tcp_estab_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->tcp_estab_lru_head_index); + + pool_get (tsm->lru_pool, head); + tsm->udp_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->udp_lru_head_index); + + pool_get (tsm->lru_pool, head); + tsm->icmp_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->icmp_lru_head_index); + + pool_get (tsm->lru_pool, head); + tsm->unk_proto_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->unk_proto_lru_head_index); if (sm->endpoint_dependent) { @@ -3970,7 +3933,7 @@ nat44_db_free (snat_main_per_thread_data_t * tsm) snat_main_t *sm = &snat_main; pool_free (tsm->sessions); - pool_free (tsm->global_lru_pool); + pool_free (tsm->lru_pool); if (sm->endpoint_dependent) { @@ -4503,7 +4466,7 @@ nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port, return VNET_API_ERROR_UNSPECIFIED; s = pool_elt_at_index (tsm->sessions, value.value); nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); return 0; } diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index e4f7a06ead8..7a4d020dcde 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -264,8 +264,10 @@ typedef CLIB_PACKED(struct u32 per_user_index; u32 per_user_list_head_index; + /* head of LRU list in which this session is tracked */ + u32 lru_head_index; /* index in global LRU list */ - u32 global_lru_index; + u32 lru_index; f64 last_lru_update; /* Last heard timer */ @@ -290,7 +292,7 @@ typedef CLIB_PACKED(struct u8 state; u32 i2o_fin_seq; u32 o2i_fin_seq; - u32 tcp_close_timestamp; + u64 tcp_closed_timestamp; /* user index */ u32 user_index; @@ -468,8 +470,12 @@ typedef struct dlist_elt_t *list_pool; /* LRU session list - head is stale, tail is fresh */ - dlist_elt_t *global_lru_pool; - u32 global_lru_head_index; + dlist_elt_t *lru_pool; + u32 tcp_trans_lru_head_index; + u32 tcp_estab_lru_head_index; + u32 udp_lru_head_index; + u32 icmp_lru_head_index; + u32 unk_proto_lru_head_index; /* NAT thread index */ u32 snat_thread_index; @@ -1311,18 +1317,6 @@ snat_session_t *nat_session_alloc_or_recycle (snat_main_t * sm, u32 thread_index, f64 now); /** - * @brief Allocate NAT endpoint-dependent session - * - * @param sm snat global configuration data - * @param thread_index thread index - * @param now time now - * - * @return session data structure on success otherwise zero value - */ -snat_session_t *nat_ed_session_alloc (snat_main_t * sm, u32 thread_index, - f64 now); - -/** * @brief Set address and port assignment algorithm for MAP-E CE * * @param psid Port Set Identifier value @@ -1431,8 +1425,6 @@ 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/ed_inlines.h b/src/plugins/nat/nat44/ed_inlines.h new file mode 100644 index 00000000000..37212f36bf5 --- /dev/null +++ b/src/plugins/nat/nat44/ed_inlines.h @@ -0,0 +1,144 @@ +/* + * simple nat plugin + * + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __included_ed_inlines_h__ +#define __included_ed_inlines_h__ + +#include <float.h> +#include <vppinfra/clib.h> +#include <nat/nat.h> +#include <nat/nat_inlines.h> + +static_always_inline int +nat_ed_lru_insert (snat_main_per_thread_data_t * tsm, + snat_session_t * s, f64 now, u8 proto) +{ + dlist_elt_t *lru_list_elt; + pool_get (tsm->lru_pool, lru_list_elt); + s->lru_index = lru_list_elt - tsm->lru_pool; + switch (proto) + { + case IP_PROTOCOL_UDP: + s->lru_head_index = tsm->udp_lru_head_index; + break; + case IP_PROTOCOL_TCP: + s->lru_head_index = tsm->tcp_trans_lru_head_index; + break; + case IP_PROTOCOL_ICMP: + s->lru_head_index = tsm->icmp_lru_head_index; + break; + default: + s->lru_head_index = tsm->unk_proto_lru_head_index; + break; + } + clib_dlist_addtail (tsm->lru_pool, s->lru_head_index, s->lru_index); + lru_list_elt->value = s - tsm->sessions; + s->last_lru_update = now; + return 1; +} + +always_inline void +nat_ed_session_delete (snat_main_t * sm, snat_session_t * ses, + u32 thread_index, int lru_delete + /* delete from global LRU list */ ) +{ + snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, + thread_index); + + if (lru_delete) + { + clib_dlist_remove (tsm->lru_pool, ses->lru_index); + } + pool_put_index (tsm->lru_pool, ses->lru_index); + pool_put (tsm->sessions, ses); + vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, + pool_elts (tsm->sessions)); + +} + +static_always_inline int +nat_lru_free_one_with_head (snat_main_t * sm, int thread_index, + f64 now, u32 head_index) +{ + snat_session_t *s = NULL; + dlist_elt_t *oldest_elt; + f64 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->lru_pool, head_index); + if (~0 != oldest_index) + { + oldest_elt = pool_elt_at_index (tsm->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_closed_timestamp && now >= s->tcp_closed_timestamp)) + { + nat_free_session_data (sm, s, thread_index, 0); + nat_ed_session_delete (sm, s, thread_index, 0); + return 1; + } + else + { + clib_dlist_addhead (tsm->lru_pool, head_index, oldest_index); + } + } + return 0; +} + +static_always_inline int +nat_lru_free_one (snat_main_t * sm, int thread_index, f64 now) +{ + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + int rc = 0; +#define _(p) \ + if ((rc = nat_lru_free_one_with_head (sm, thread_index, now, \ + tsm->p##_lru_head_index))) \ + { \ + return rc; \ + } + _(tcp_trans); + _(udp); + _(unk_proto); + _(icmp); + _(tcp_estab); +#undef _ + return 0; +} + +static_always_inline snat_session_t * +nat_ed_session_alloc (snat_main_t * sm, u32 thread_index, f64 now, u8 proto) +{ + snat_session_t *s; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + nat_lru_free_one (sm, thread_index, now); + + pool_get (tsm->sessions, s); + clib_memset (s, 0, sizeof (*s)); + + nat_ed_lru_insert (tsm, s, now, proto); + + s->ha_last_refreshed = now; + vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, + pool_elts (tsm->sessions)); + return s; +} + +#endif diff --git a/src/plugins/nat/nat44/inlines.h b/src/plugins/nat/nat44/inlines.h index a5118ea2fa6..62121dfb0db 100644 --- a/src/plugins/nat/nat44/inlines.h +++ b/src/plugins/nat/nat44/inlines.h @@ -59,25 +59,12 @@ nat44_session_reuse_old (snat_main_t * sm, snat_user_t * u, s->ext_host_port = 0; s->ext_host_nat_addr.as_u32 = 0; s->ext_host_nat_port = 0; - s->tcp_close_timestamp = 0; + s->tcp_closed_timestamp = 0; s->ha_last_refreshed = now; return s; } static_always_inline void -nat44_global_lru_insert (snat_main_per_thread_data_t * tsm, - snat_session_t * s, f64 now) -{ - dlist_elt_t *lru_list_elt; - pool_get (tsm->global_lru_pool, lru_list_elt); - s->global_lru_index = lru_list_elt - tsm->global_lru_pool; - clib_dlist_addtail (tsm->global_lru_pool, tsm->global_lru_head_index, - s->global_lru_index); - lru_list_elt->value = s - tsm->sessions; - s->last_lru_update = now; -} - -static_always_inline void nat44_sessions_clear () { snat_main_t *sm = &snat_main; diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c index 8c800d5c373..796e3495594 100644 --- a/src/plugins/nat/nat44_cli.c +++ b/src/plugins/nat/nat44_cli.c @@ -688,9 +688,9 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input, tcp_sessions++; if (s->state) { - if (s->tcp_close_timestamp) + if (s->tcp_closed_timestamp) { - if (now >= s->tcp_close_timestamp) + if (now >= s->tcp_closed_timestamp) { ++transitory_closed; } @@ -734,9 +734,9 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input, tcp_sessions++; if (s->state) { - if (s->tcp_close_timestamp) + if (s->tcp_closed_timestamp) { - if (now >= s->tcp_close_timestamp) + if (now >= s->tcp_closed_timestamp) { ++transitory_closed; } @@ -758,6 +758,31 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input, })); /* *INDENT-ON* */ count = pool_elts (tsm->sessions); + if (sm->endpoint_dependent) + { + dlist_elt_t *oldest_elt; + u32 oldest_index; +#define _(n, d) \ + oldest_index = \ + clib_dlist_remove_head (tsm->lru_pool, tsm->n##_lru_head_index); \ + if (~0 != oldest_index) \ + { \ + oldest_elt = pool_elt_at_index (tsm->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); \ + vlib_cli_output (vm, d " LRU min session timeout %llu (now %llu)", \ + sess_timeout_time, now); \ + clib_dlist_addhead (tsm->lru_pool, tsm->n##_lru_head_index, \ + oldest_index); \ + } + _(tcp_estab, "established tcp"); + _(tcp_trans, "transitory tcp"); + _(udp, "udp"); + _(unk_proto, "unknown protocol"); + _(icmp, "icmp"); +#undef _ + } } vlib_cli_output (vm, "total timed out sessions: %u", timed_out); diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index 74f24d85cf8..37af77d11c1 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -31,6 +31,7 @@ #include <nat/nat_msg_enum.h> #include <vnet/fib/fib_table.h> #include <vnet/ip/ip_types_api.h> +#include <nat/nat44/ed_inlines.h> #define vl_api_nat44_add_del_lb_static_mapping_t_endian vl_noop_handler #define vl_api_nat44_nat44_lb_static_mapping_details_t_endian vl_noop_handler @@ -2058,7 +2059,7 @@ static void { s = pool_elt_at_index(tsm->sessions, ses_index[0]); nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat44_ed_delete_session (sm, s, tsm - sm->per_thread_data, 1); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); } }else{ vec_foreach (ses_index, ses_to_be_removed) diff --git a/src/plugins/nat/nat_inlines.h b/src/plugins/nat/nat_inlines.h index e5ac421a6f9..d37cb10b6fd 100644 --- a/src/plugins/nat/nat_inlines.h +++ b/src/plugins/nat/nat_inlines.h @@ -261,8 +261,11 @@ nat44_delete_session (snat_main_t * sm, snat_session_t * ses, clib_dlist_remove (tsm->list_pool, ses->per_user_index); pool_put_index (tsm->list_pool, ses->per_user_index); - clib_dlist_remove (tsm->global_lru_pool, ses->global_lru_index); - pool_put_index (tsm->global_lru_pool, ses->global_lru_index); + if (sm->endpoint_dependent) + { + clib_dlist_remove (tsm->lru_pool, ses->lru_index); + pool_put_index (tsm->lru_pool, ses->lru_index); + } pool_put (tsm->sessions, ses); vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, pool_elts (tsm->sessions)); @@ -280,25 +283,6 @@ nat44_delete_session (snat_main_t * sm, snat_session_t * ses, } } -always_inline void -nat44_ed_delete_session (snat_main_t * sm, snat_session_t * ses, - u32 thread_index, int global_lru_delete - /* delete from global LRU list */ ) -{ - snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, - thread_index); - - if (global_lru_delete) - { - clib_dlist_remove (tsm->global_lru_pool, ses->global_lru_index); - } - pool_put_index (tsm->global_lru_pool, ses->global_lru_index); - pool_put (tsm->sessions, ses); - vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, - pool_elts (tsm->sessions)); - -} - /** \brief Set TCP session state. @return 1 if session was closed, otherwise 0 */ @@ -307,6 +291,7 @@ nat44_set_tcp_session_state_i2o (snat_main_t * sm, f64 now, snat_session_t * ses, vlib_buffer_t * b, u32 thread_index) { + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; u8 tcp_flags = vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags; u32 tcp_ack_number = vnet_buffer (b)->ip.reass.tcp_ack_number; u32 tcp_seq_number = vnet_buffer (b)->ip.reass.tcp_seq_number; @@ -331,10 +316,23 @@ nat44_set_tcp_session_state_i2o (snat_main_t * sm, f64 now, ses->state |= NAT44_SES_O2I_FIN_ACK; if (nat44_is_ses_closed (ses)) { // if session is now closed, save the timestamp - ses->tcp_close_timestamp = now + sm->tcp_transitory_timeout; + ses->tcp_closed_timestamp = now + sm->tcp_transitory_timeout; + ses->last_lru_update = now; } } } + + // move the session to proper LRU + if (ses->state) + { + ses->lru_head_index = tsm->tcp_trans_lru_head_index; + } + else + { + ses->lru_head_index = tsm->tcp_estab_lru_head_index; + } + clib_dlist_remove (tsm->lru_pool, ses->lru_index); + clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index); return 0; } @@ -344,6 +342,7 @@ nat44_set_tcp_session_state_o2i (snat_main_t * sm, f64 now, u32 tcp_ack_number, u32 tcp_seq_number, u32 thread_index) { + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST)) ses->state = NAT44_SES_RST; if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST)) @@ -364,9 +363,21 @@ nat44_set_tcp_session_state_o2i (snat_main_t * sm, f64 now, ses->state |= NAT44_SES_I2O_FIN_ACK; if (nat44_is_ses_closed (ses)) { // if session is now closed, save the timestamp - ses->tcp_close_timestamp = now + sm->tcp_transitory_timeout; + ses->tcp_closed_timestamp = now + sm->tcp_transitory_timeout; + ses->last_lru_update = now; } } + // move the session to proper LRU + if (ses->state) + { + ses->lru_head_index = tsm->tcp_trans_lru_head_index; + } + else + { + ses->lru_head_index = tsm->tcp_estab_lru_head_index; + } + clib_dlist_remove (tsm->lru_pool, ses->lru_index); + clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index); return 0; } @@ -411,7 +422,7 @@ always_inline void nat44_session_update_lru (snat_main_t * sm, snat_session_t * s, u32 thread_index) { - /* don't update too often - timeout is in a magnitude of seconds anyway */ + /* don't update too often - timeout is in magnitude of seconds anyway */ if (s->last_heard > s->last_lru_update + 1) { if (!sm->endpoint_dependent) @@ -421,13 +432,13 @@ nat44_session_update_lru (snat_main_t * sm, snat_session_t * s, clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, s->per_user_list_head_index, s->per_user_index); } - - clib_dlist_remove (sm->per_thread_data[thread_index].global_lru_pool, - s->global_lru_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].global_lru_pool, - sm-> - per_thread_data[thread_index].global_lru_head_index, - s->global_lru_index); + else + { + clib_dlist_remove (sm->per_thread_data[thread_index].lru_pool, + s->lru_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].lru_pool, + s->lru_head_index, s->lru_index); + } s->last_lru_update = s->last_heard; } } diff --git a/src/plugins/nat/out2in_ed.c b/src/plugins/nat/out2in_ed.c index 9db82e00ab4..e597191d4d2 100644 --- a/src/plugins/nat/out2in_ed.c +++ b/src/plugins/nat/out2in_ed.c @@ -31,6 +31,7 @@ #include <nat/nat44/inlines.h> #include <nat/nat_syslog.h> #include <nat/nat_ha.h> +#include <nat/nat44/ed_inlines.h> static char *nat_out2in_ed_error_strings[] = { #define _(sym,string) string, @@ -181,7 +182,7 @@ nat44_o2i_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void *arg) snat_free_outside_address_and_port (sm->addresses, ctx->thread_index, &s->out2in); delete: - nat44_ed_delete_session (sm, s, ctx->thread_index, 1); + nat_ed_session_delete (sm, s, ctx->thread_index, 1); return 1; } @@ -216,7 +217,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm, return 0; } - s = nat_ed_session_alloc (sm, thread_index, now); + s = nat_ed_session_alloc (sm, thread_index, now, e_key.protocol); if (!s) { b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_USER_SESS_EXCEEDED]; @@ -260,7 +261,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm, tsm->snat_thread_index)) { b->error = node->errors[NAT_OUT2IN_ED_ERROR_OUT_OF_PORTS]; - nat44_ed_delete_session (sm, s, thread_index, 1); + nat_ed_session_delete (sm, s, thread_index, 1); if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 0)) nat_elog_notice ("out2in-ed key del failed"); return 0; @@ -369,7 +370,7 @@ create_bypass_for_fwd (snat_main_t * sm, vlib_buffer_t * b, ip4_header_t * ip, (sm, rx_fib_index, thread_index))) return; - s = nat_ed_session_alloc (sm, thread_index, now); + s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol); if (!s) { nat_elog_warn ("create NAT session failed"); @@ -597,7 +598,7 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm, new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; /* Create a new session */ - s = nat_ed_session_alloc (sm, thread_index, now); + s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol); if (!s) { b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_USER_SESS_EXCEEDED]; @@ -739,9 +740,9 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, } s0 = pool_elt_at_index (tsm->sessions, value0.value); - if (s0->tcp_close_timestamp) + if (s0->tcp_closed_timestamp) { - if (now >= s0->tcp_close_timestamp) + if (now >= s0->tcp_closed_timestamp) { // session is closed, go slow path next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH; @@ -763,7 +764,7 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, { // session is closed, go slow path nat_free_session_data (sm, s0, thread_index, 0); - nat44_ed_delete_session (sm, s0, thread_index, 1); + nat_ed_session_delete (sm, s0, thread_index, 1); next0 = NAT_NEXT_OUT2IN_ED_SLOW_PATH; goto trace0; } @@ -1023,10 +1024,10 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, { s0 = pool_elt_at_index (tsm->sessions, value0.value); - if (s0->tcp_close_timestamp && now >= s0->tcp_close_timestamp) + if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp) { nat_free_session_data (sm, s0, thread_index, 0); - nat44_ed_delete_session (sm, s0, thread_index, 1); + nat_ed_session_delete (sm, s0, thread_index, 1); s0 = NULL; } } diff --git a/src/plugins/nat/test/test_nat.py b/src/plugins/nat/test/test_nat.py index a8e2af763d0..2ba7a9b78f3 100644 --- a/src/plugins/nat/test/test_nat.py +++ b/src/plugins/nat/test/test_nat.py @@ -7059,6 +7059,111 @@ class TestNAT44EndpointDependent(MethodHolder): self.logger.info(self.vapi.cli("show nat timeouts")) +class TestNAT44EndpointDependent2(MethodHolder): + """ Endpoint-Dependent mapping and filtering extra test cases """ + + translation_buckets = 5 + + @classmethod + def setUpConstants(cls): + super(TestNAT44EndpointDependent2, cls).setUpConstants() + cls.vpp_cmdline.extend([ + "nat", "{", "endpoint-dependent", + "translation hash buckets %d" % cls.translation_buckets, + "}" + ]) + + @classmethod + def setUpClass(cls): + super(TestNAT44EndpointDependent2, cls).setUpClass() + cls.vapi.cli("set log class nat level debug") + + cls.nat_addr = '10.0.0.3' + + cls.create_pg_interfaces(range(2)) + + for i in cls.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + def setUp(self): + super(TestNAT44EndpointDependent2, self).setUp() + self.vapi.nat_set_timeouts( + udp=1, tcp_established=7440, tcp_transitory=30, icmp=1) + self.nat44_add_address(self.nat_addr) + flags = self.config_flags.NAT_IS_INSIDE + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) + self.vapi.nat44_interface_add_del_feature( + sw_if_index=self.pg1.sw_if_index, is_add=1) + + @classmethod + def tearDownClass(cls): + super(TestNAT44EndpointDependent2, cls).tearDownClass() + + def init_tcp_session(self, in_if, out_if, sport, ext_dport): + # SYN packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=sport, dport=ext_dport, flags="S")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = out_if.get_capture(1) + p = capture[0] + tcp_port_out = p[TCP].sport + + # SYN + ACK packet out->in + p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) / + IP(src=out_if.remote_ip4, dst=self.nat_addr) / + TCP(sport=ext_dport, dport=tcp_port_out, flags="SA")) + out_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + in_if.get_capture(1) + + # ACK packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=sport, dport=ext_dport, flags="A")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + out_if.get_capture(1) + + return tcp_port_out + + def test_lru_cleanup(self): + """ LRU cleanup algorithm """ + tcp_port_out = self.init_tcp_session(self.pg0, self.pg1, 2000, 80) + max_translations = 10 * self.translation_buckets + pkts = [] + for i in range(0, max_translations - 1): + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / + UDP(sport=7000+i, dport=80)) + pkts.append(p) + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(len(pkts)) + self.sleep(1.5, "wait for timeouts") + + pkts = [] + for i in range(0, max_translations - 1): + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / + ICMP(id=8000+i, type='echo-request')) + pkts.append(p) + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(len(pkts)) + + class TestNAT44Out2InDPO(MethodHolder): """ NAT44 Test Cases using out2in DPO """ |