From a73f2d6f53c224668bd6bbea1a980ee4313c794f Mon Sep 17 00:00:00 2001 From: Filip Varga Date: Tue, 25 Feb 2020 14:31:33 +0100 Subject: nat: timed out session scavenging upgrade Patch changes the behavior of session scavenging and fixes multiple nat issues. Allows proper session clearing and removes issue with lingering sessions in session db. Patch also updates and fixes CLI/API calls for better readability of session state metrics. Fixes security issue that would allow attacker to reuse timed out session in both directions (in2out/out2in). Type: improvement Signed-off-by: Filip Varga Change-Id: I78897585a2a57291fad5db6d457941aa0a0457bd --- src/plugins/nat/nat44/inlines.h | 218 +++++++++++++++++++++++++++------------- 1 file changed, 149 insertions(+), 69 deletions(-) (limited to 'src/plugins/nat/nat44') diff --git a/src/plugins/nat/nat44/inlines.h b/src/plugins/nat/nat44/inlines.h index 0c8487b473d..7cc24750423 100644 --- a/src/plugins/nat/nat44/inlines.h +++ b/src/plugins/nat/nat44/inlines.h @@ -31,13 +31,66 @@ nat44_maximum_sessions_exceeded (snat_main_t * sm, u32 thread_index) return 0; } -static_always_inline void -nat44_session_cleanup (snat_session_t * s, u32 thread_index) +static_always_inline f64 +nat44_minimal_timeout (snat_main_t * sm) { - snat_main_t *sm = &snat_main; + f64 min_timeout; + + min_timeout = clib_min (sm->udp_timeout, sm->icmp_timeout); + min_timeout = clib_min (min_timeout, sm->icmp_timeout); + min_timeout = clib_min (min_timeout, sm->tcp_transitory_timeout); + min_timeout = clib_min (min_timeout, sm->tcp_established_timeout); + + return min_timeout; +} + +static_always_inline snat_session_t * +nat44_session_reuse_old (snat_main_t * sm, snat_user_t * u, + snat_session_t * s, u32 thread_index, f64 now) +{ + nat44_free_session_data (sm, s, thread_index, 0); + if (snat_is_session_static (s)) + u->nstaticsessions--; + else + u->nsessions--; + s->flags = 0; + s->total_bytes = 0; + s->total_pkts = 0; + s->state = 0; + s->ext_host_addr.as_u32 = 0; + s->ext_host_port = 0; + s->ext_host_nat_addr.as_u32 = 0; + s->ext_host_nat_port = 0; + // + s->ha_last_refreshed = now; + return s; +} + + +static_always_inline snat_session_t * +nat44_session_alloc_new (snat_main_per_thread_data_t * tsm, snat_user_t * u, + f64 now) +{ + snat_session_t *s; + dlist_elt_t *per_user_translation_list_elt; - nat_free_session_data (sm, s, thread_index, 0); - nat44_delete_session (sm, s, thread_index); + pool_get (tsm->sessions, s); + clib_memset (s, 0, sizeof (*s)); + /* 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); + + s->ha_last_refreshed = now; + return s; } static_always_inline void @@ -59,7 +112,10 @@ nat44_user_del_sessions (snat_user_t * u, u32 thread_index) { s = pool_elt_at_index (tsm->sessions, elt->value); elt = pool_elt_at_index (tsm->list_pool, elt->next); - nat44_session_cleanup (s, thread_index); + + nat44_free_session_data (sm, s, thread_index, 0); + // TODO: needs refactoring as in nat44_user_session_cleanup + nat44_delete_session (sm, s, thread_index); } } @@ -108,9 +164,10 @@ nat44_user_del (ip4_address_t * addr, u32 fib_index) return rv; } -static_always_inline void -nat44_user_try_cleanup (snat_user_t * u, u32 thread_index, f64 now) +static_always_inline u32 +nat44_user_session_cleanup (snat_user_t * u, u32 thread_index, f64 now) { + u32 cleared = 0; dlist_elt_t *elt; snat_session_t *s; u64 sess_timeout_time; @@ -118,6 +175,10 @@ nat44_user_try_cleanup (snat_user_t * u, u32 thread_index, f64 now) snat_main_t *sm = &snat_main; snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + if (now < u->min_session_timeout) + goto done; + u->min_session_timeout = now + sm->min_timeout; + // get head elt = pool_elt_at_index (tsm->list_pool, u->sessions_per_user_list_head_index); @@ -133,102 +194,121 @@ nat44_user_try_cleanup (snat_user_t * u, u32 thread_index, f64 now) (f64) nat44_session_get_timeout (sm, s); if (now < sess_timeout_time) - { - tsm->min_session_timeout = - clib_min (sess_timeout_time, tsm->min_session_timeout); - continue; - } + continue; + + // do cleanup of this call (refactor for ED NAT44 only) + nat44_free_session_data (sm, s, thread_index, 0); + + clib_dlist_remove (tsm->list_pool, s->per_user_index); + pool_put_index (tsm->list_pool, s->per_user_index); + pool_put (tsm->sessions, s); + vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, + pool_elts (tsm->sessions)); - nat44_session_cleanup (s, thread_index); + if (snat_is_session_static (s)) + u->nstaticsessions--; + else + u->nsessions--; + + cleared++; } +done: + return cleared; } -static_always_inline void -nat44_session_try_cleanup (ip4_address_t * addr, - u32 fib_index, u32 thread_index, f64 now) +static_always_inline u32 +nat44_users_cleanup (u32 thread_index, f64 now) { + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + + u32 cleared = 0; + + snat_user_key_t u_key; + clib_bihash_kv_8_8_t kv; + snat_user_t *u = 0; - snat_user_key_t user_key; - clib_bihash_kv_8_8_t kv, value; + u32 pool_index; - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); - user_key.addr.as_u32 = addr->as_u32; - user_key.fib_index = fib_index; - kv.key = user_key.as_u64; + if (now < tsm->min_session_timeout) + goto done; + tsm->min_session_timeout = now + sm->min_timeout; + // consider + tsm->cleanup_timeout = tsm->min_session_timeout; - // lookup user for this traffic - if (PREDICT_FALSE (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))) + pool_index = ~0; + do { - // there is still place and a new user can be created - if (PREDICT_TRUE (pool_elts (tsm->sessions) < sm->max_translations)) - return; + pool_index = pool_next_index (tsm->users, pool_index); + if (pool_index == ~0) + break; + u = pool_elt_at_index (tsm->users, pool_index); - if (now >= tsm->min_session_timeout) - { - tsm->min_session_timeout = ~0; - // there is no place so we try to cleanup all users in this thread - /* *INDENT-OFF* */ - pool_foreach (u, tsm->users, - ({ nat44_user_try_cleanup (u, thread_index, now); })); - /* *INDENT-ON* */ - if (~0 == tsm->min_session_timeout) - { - tsm->min_session_timeout = 0; - } - } - return; - } + cleared += nat44_user_session_cleanup (u, thread_index, now); - if (now >= tsm->min_session_timeout) - { - tsm->min_session_timeout = ~0; - // each time user creates a new session we try to cleanup expired sessions - nat44_user_try_cleanup (pool_elt_at_index (tsm->users, value.value), - thread_index, now); - if (~0 == tsm->min_session_timeout) + if (u->nstaticsessions == 0 && u->nsessions == 0) { - tsm->min_session_timeout = 0; + u_key.addr.as_u32 = u->addr.as_u32; + u_key.fib_index = u->fib_index; + kv.key = u_key.as_u64; + + // delete user + pool_put_index (tsm->list_pool, + u->sessions_per_user_list_head_index); + pool_put (tsm->users, u); + clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 0); + + // update total users counter + vlib_set_simple_counter (&sm->total_users, thread_index, 0, + pool_elts (tsm->users)); } } + while (1); + tsm->cleared += cleared; + tsm->cleanup_runs++; +done: + return cleared; } -static_always_inline void -nat44_force_session_cleanup (void) +static_always_inline u32 +nat44_out_of_ports_cleanup (u32 thread_index, f64 now) { - snat_user_t *u = 0; + return nat44_users_cleanup (thread_index, now); +} + +static_always_inline u32 +nat44_max_translations_per_user_cleanup (snat_user_t * u, u32 thread_index, + f64 now) +{ + return nat44_user_session_cleanup (u, thread_index, now); +} +static_always_inline u32 +nat44_force_users_cleanup (void) +{ snat_main_t *sm = &snat_main; snat_main_per_thread_data_t *tsm; - vlib_main_t *vm = vlib_get_main (); - f64 now = vlib_time_now (vm); - - // TODO: consider own timeouts + f64 now = vlib_time_now (vlib_get_main ()); + u32 cleared = 0; if (sm->num_workers > 1) { /* *INDENT-OFF* */ vec_foreach (tsm, sm->per_thread_data) { - pool_foreach (u, tsm->users, - ({ - nat44_user_try_cleanup (u, tsm->thread_index, now); - })); + cleared += nat44_users_cleanup (tsm->thread_index, now); } /* *INDENT-ON* */ } else { tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - /* *INDENT-OFF* */ - pool_foreach (u, tsm->users, - ({ - nat44_user_try_cleanup (u, tsm->thread_index, now); - })); - /* *INDENT-ON* */ + cleared += nat44_users_cleanup (tsm->thread_index, now); } + return cleared; } #endif /* included_nat44_inlines_h__ */ -- cgit 1.2.3-korg