diff options
author | Filip Varga <fivarga@cisco.com> | 2020-02-25 14:31:33 +0100 |
---|---|---|
committer | Ole Trøan <otroan@employees.org> | 2020-03-13 11:17:13 +0000 |
commit | a73f2d6f53c224668bd6bbea1a980ee4313c794f (patch) | |
tree | c40965985639940beb294e8688aedbe30e47a548 /src/plugins/nat/nat44 | |
parent | c27b43673237c3971c1c170646b531728e0d8eb1 (diff) |
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 <fivarga@cisco.com>
Change-Id: I78897585a2a57291fad5db6d457941aa0a0457bd
Diffstat (limited to 'src/plugins/nat/nat44')
-rw-r--r-- | src/plugins/nat/nat44/inlines.h | 218 |
1 files changed, 149 insertions, 69 deletions
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__ */ |