diff options
author | Jing Peng <jing@meter.com> | 2022-07-15 15:12:29 -0400 |
---|---|---|
committer | Ole Tr�an <otroan@employees.org> | 2022-11-07 08:00:23 +0000 |
commit | 61fdfd51d1dd5bd4f3bc02b332b265c28e879221 (patch) | |
tree | 414b4893346d8dccddf44723fdccd802dee7db2b | |
parent | 9ff833d8f4ccccc475f9a302b8f70eed49963472 (diff) |
nat: fix per-vrf session bookkeeping
Each NAT44 ED session has a per_vrf_sessions_index referencing
an element in the thread-local vector per_vrf_sessions_vec.
However this index can be possibly invalidated by vec_del1() in
per_vrf_sessions_cleanup(), before a session is registered.
Such a stale index can cause an assertion failure in function
per_vrf_sessions_is_expired() when we use it to locate the
per_vrf_sessions object.
A possible sequence to reproduce is:
1. Create two NAT44 ED sessions s1, s2 so that two per_vrf_sessions are created:
index 0: between VRF pair 10 and 11 (expired=0, ses_count=1)
index 1: between VRF pair 20 and 21 (expired=0, ses_count=1)
For the sessions we have:
s1->per_vrf_sessions_index == 0
s2->per_vrf_sessions_index == 1
2. Delete the first session via CLI, now the two per_vrf_sessions become:
index 0: between VRF pair 10 and 11 (expired=0, ses_count=0)
index 1: between VRF pair 20 and 21 (expired=0, ses_count=1)
For the sessions we have:
s2->per_vrf_sessions_index == 1
3. Delete the VRF 11:
index 0: between VRF pair 10 and 11 (expired=1, ses_count=0)
index 1: between VRF pair 20 and 21 (expired=0, ses_count=1)
For the sessions we have:
s2->per_vrf_sessions_index == 1
4. Create a new session s3 between VRF pair 20 and 21 so that the first
per_vrf_sessions will be deleted:
index 0: between VRF pair 20 and 21 (expired=0, ses_count=2)
For the sessions we have:
s2->per_vrf_sessions_index == 1
s3->per_vrf_sessions_index == 0
Here, note that the actual index of per_vrf_session is changed due
to vec_del1(). The new session is added after the cleanup so it gets
the correct index. But the index held by the existing session is not
updated.
5. Trigger the fast path of the session s2. To achieve this, session
s2 could be created in step 1 by
ping -i20 -Iiface_in_vrf_10 1.1.1.1
and steps 2-4 should then be performed within the 20-second interval.
This patch fixes this by changing per_vrf_sessions_vec to a pool so
that indicies are kept intact.
Type: fix
Signed-off-by: Jing Peng <jing@meter.com>
Change-Id: I4c08f9bfd50134bcb5f08e50ad61af2bddbcb645
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed.c | 28 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed.h | 2 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_inlines.h | 30 |
3 files changed, 26 insertions, 34 deletions
diff --git a/src/plugins/nat/nat44-ed/nat44_ed.c b/src/plugins/nat/nat44-ed/nat44_ed.c index ed93f6cc55f..133c39e28c3 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed.c +++ b/src/plugins/nat/nat44-ed/nat44_ed.c @@ -1633,19 +1633,19 @@ expire_per_vrf_sessions (u32 fib_index) vec_foreach (tsm, sm->per_thread_data) { - vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) - { - if ((per_vrf_sessions->rx_fib_index == fib_index) || - (per_vrf_sessions->tx_fib_index == fib_index)) - { - per_vrf_sessions->expired = 1; - } - } + pool_foreach (per_vrf_sessions, tsm->per_vrf_sessions_pool) + { + if ((per_vrf_sessions->rx_fib_index == fib_index) || + (per_vrf_sessions->tx_fib_index == fib_index)) + { + per_vrf_sessions->expired = 1; + } + } } } void -update_per_vrf_sessions_vec (u32 fib_index, int is_del) +update_per_vrf_sessions_pool (u32 fib_index, int is_del) { snat_main_t *sm = &snat_main; nat_fib_t *fib; @@ -1788,7 +1788,7 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside) fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); - update_per_vrf_sessions_vec (fib_index, 0 /*is_del*/); + update_per_vrf_sessions_pool (fib_index, 0 /*is_del*/); if (!is_inside) { @@ -1903,7 +1903,7 @@ nat44_ed_del_interface (u32 sw_if_index, u8 is_inside) fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); - update_per_vrf_sessions_vec (fib_index, 1 /*is_del*/); + update_per_vrf_sessions_pool (fib_index, 1 /*is_del*/); if (!is_inside) { @@ -2002,7 +2002,7 @@ nat44_ed_add_output_interface (u32 sw_if_index) fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); - update_per_vrf_sessions_vec (fib_index, 0 /*is_del*/); + update_per_vrf_sessions_pool (fib_index, 0 /*is_del*/); outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index); if (outside_fib) @@ -2092,7 +2092,7 @@ nat44_ed_del_output_interface (u32 sw_if_index) fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); - update_per_vrf_sessions_vec (fib_index, 1 /*is_del*/); + update_per_vrf_sessions_pool (fib_index, 1 /*is_del*/); outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index); if (outside_fib) @@ -3287,7 +3287,7 @@ nat44_ed_worker_db_free (snat_main_per_thread_data_t *tsm) { pool_free (tsm->lru_pool); pool_free (tsm->sessions); - vec_free (tsm->per_vrf_sessions_vec); + pool_free (tsm->per_vrf_sessions_pool); } void diff --git a/src/plugins/nat/nat44-ed/nat44_ed.h b/src/plugins/nat/nat44-ed/nat44_ed.h index bc69648dc3f..706511475cf 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed.h +++ b/src/plugins/nat/nat44-ed/nat44_ed.h @@ -469,7 +469,7 @@ typedef struct /* real thread index */ u32 thread_index; - per_vrf_sessions_t *per_vrf_sessions_vec; + per_vrf_sessions_t *per_vrf_sessions_pool; } snat_main_per_thread_data_t; diff --git a/src/plugins/nat/nat44-ed/nat44_ed_inlines.h b/src/plugins/nat/nat44-ed/nat44_ed_inlines.h index b99d152da26..04e5236b7f9 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_inlines.h +++ b/src/plugins/nat/nat44-ed/nat44_ed_inlines.h @@ -414,23 +414,16 @@ per_vrf_sessions_cleanup (u32 thread_index) per_vrf_sessions_t *per_vrf_sessions; u32 *to_free = 0, *i; - vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) + pool_foreach (per_vrf_sessions, tsm->per_vrf_sessions_pool) { - if (per_vrf_sessions->expired) - { - if (per_vrf_sessions->ses_count == 0) - { - vec_add1 (to_free, per_vrf_sessions - tsm->per_vrf_sessions_vec); - } - } + if (per_vrf_sessions->expired && per_vrf_sessions->ses_count == 0) + vec_add1 (to_free, per_vrf_sessions - tsm->per_vrf_sessions_pool); } - if (vec_len (to_free)) + vec_foreach (i, to_free) { - vec_foreach (i, to_free) - { - vec_del1 (tsm->per_vrf_sessions_vec, *i); - } + per_vrf_sessions = pool_elt_at_index (tsm->per_vrf_sessions_pool, *i); + pool_put (tsm->per_vrf_sessions_pool, per_vrf_sessions); } vec_free (to_free); @@ -449,7 +442,7 @@ per_vrf_sessions_register_session (snat_session_t *s, u32 thread_index) // s->per_vrf_sessions_index == ~0 ... reuse of old session - vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) + pool_foreach (per_vrf_sessions, tsm->per_vrf_sessions_pool) { // ignore already expired registrations if (per_vrf_sessions->expired) @@ -468,14 +461,13 @@ per_vrf_sessions_register_session (snat_session_t *s, u32 thread_index) } // create a new registration - vec_add2 (tsm->per_vrf_sessions_vec, per_vrf_sessions, 1); + pool_get (tsm->per_vrf_sessions_pool, per_vrf_sessions); clib_memset (per_vrf_sessions, 0, sizeof (*per_vrf_sessions)); - per_vrf_sessions->rx_fib_index = s->in2out.fib_index; per_vrf_sessions->tx_fib_index = s->out2in.fib_index; done: - s->per_vrf_sessions_index = per_vrf_sessions - tsm->per_vrf_sessions_vec; + s->per_vrf_sessions_index = per_vrf_sessions - tsm->per_vrf_sessions_pool; per_vrf_sessions->ses_count++; } @@ -491,7 +483,7 @@ per_vrf_sessions_unregister_session (snat_session_t *s, u32 thread_index) tsm = vec_elt_at_index (sm->per_thread_data, thread_index); per_vrf_sessions = - vec_elt_at_index (tsm->per_vrf_sessions_vec, s->per_vrf_sessions_index); + pool_elt_at_index (tsm->per_vrf_sessions_pool, s->per_vrf_sessions_index); ASSERT (per_vrf_sessions->ses_count != 0); @@ -511,7 +503,7 @@ per_vrf_sessions_is_expired (snat_session_t *s, u32 thread_index) tsm = vec_elt_at_index (sm->per_thread_data, thread_index); per_vrf_sessions = - vec_elt_at_index (tsm->per_vrf_sessions_vec, s->per_vrf_sessions_index); + pool_elt_at_index (tsm->per_vrf_sessions_pool, s->per_vrf_sessions_index); return per_vrf_sessions->expired; } |