&s->ext_host_nat_addr, s->ext_host_nat_port,
s->nat_proto, s->in2out.fib_index, s->flags, thread_index, 0);
+ per_vrf_sessions_register_session (s, thread_index);
+
return next;
}
s - tsm->sessions);
if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1))
nat_elog_notice ("out2in key add failed");
+
+ per_vrf_sessions_register_session (s, thread_index);
}
/* Update IP checksum */
pool_elt_at_index (tsm->sessions,
ed_value_get_session_index (&value0));
+ if (PREDICT_FALSE (per_vrf_sessions_is_expired (s0, thread_index)))
+ {
+ // session is closed, go slow path
+ nat_free_session_data (sm, s0, thread_index, 0);
+ nat_ed_session_delete (sm, s0, thread_index, 1);
+ next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
+ goto trace0;
+ }
+
if (s0->tcp_closed_timestamp)
{
if (now >= s0->tcp_closed_timestamp)
{
- // session is closed, go slow path
+ // session is closed, go slow path, freed in slow path
next[0] = def_slow;
}
else
snat_main_per_thread_data_t *tsm =
vec_elt_at_index (sm->per_thread_data, thread_index);
+ if (is_ed_session (s))
+ {
+ per_vrf_sessions_unregister_session (s, thread_index);
+ }
+
if (is_fwd_bypass_session (s))
{
if (snat_is_unk_proto_session (s))
vlib_zero_simple_counter (&sm->counters.hairpinning, sw_if_index);
}
+void
+expire_per_vrf_sessions (u32 fib_index)
+{
+ per_vrf_sessions_t *per_vrf_sessions;
+ snat_main_per_thread_data_t *tsm;
+ snat_main_t *sm = &snat_main;
+
+ /* *INDENT-OFF* */
+ 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;
+ }
+ }
+ }
+ /* *INDENT-ON* */
+}
+
+void
+update_per_vrf_sessions_vec (u32 fib_index, int is_del)
+{
+ snat_main_t *sm = &snat_main;
+ nat_fib_t *fib;
+
+ // we don't care if it is outside/inside fib
+ // we just care about their ref_count
+ // if it reaches 0 sessions should expire
+ // because the fib isn't valid for NAT anymore
+
+ vec_foreach (fib, sm->fibs)
+ {
+ if (fib->fib_index == fib_index)
+ {
+ if (is_del)
+ {
+ fib->ref_count--;
+ if (!fib->ref_count)
+ {
+ vec_del1 (sm->fibs, fib - sm->fibs);
+ expire_per_vrf_sessions (fib_index);
+ }
+ return;
+ }
+ else
+ fib->ref_count++;
+ }
+ }
+ if (!is_del)
+ {
+ vec_add2 (sm->fibs, fib, 1);
+ fib->ref_count = 1;
+ fib->fib_index = fib_index;
+ }
+}
+
int
snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del)
{
sm->fq_out2in_index =
vlib_frame_queue_main_init (sm->out2in_node_index, NAT_FQ_NELTS);
+ if (sm->endpoint_dependent)
+ update_per_vrf_sessions_vec (fib_index, is_del);
+
if (!is_inside)
{
/* *INDENT-OFF* */
outside_fib->fib_index = fib_index;
}
}
+
feature_set:
/* *INDENT-OFF* */
pool_foreach (i, sm->interfaces,
u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
sw_if_index);
-
if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))
return VNET_API_ERROR_UNSUPPORTED;
}));
/* *INDENT-ON* */
+ if (sm->endpoint_dependent)
+ update_per_vrf_sessions_vec (fib_index, is_del);
+
if (!is_inside)
{
/* *INDENT-OFF* */
ASSERT (fib_index == fib_index2);
}
+static clib_error_t *
+nat_ip_table_add_del (vnet_main_t * vnm, u32 table_id, u32 is_add)
+{
+ snat_main_t *sm = &snat_main;
+ u32 fib_index;
+
+ if (sm->endpoint_dependent)
+ {
+ // TODO: consider removing all NAT interfaces
+
+ if (!is_add)
+ {
+ fib_index = ip4_fib_index_from_table_id (table_id);
+ if (fib_index != ~0)
+ expire_per_vrf_sessions (fib_index);
+ }
+ }
+ return 0;
+}
+
+VNET_IP_TABLE_ADD_DEL_FUNCTION (nat_ip_table_add_del);
+
+
static clib_error_t *
snat_init (vlib_main_t * vm)
{
sm->translation_memory_size);
clib_bihash_set_kvp_format_fn_16_8 (&tsm->in2out_ed,
format_ed_session_kvp);
+
}
else
{
if (sm->endpoint_dependent)
{
clib_bihash_free_16_8 (&tsm->in2out_ed);
+ vec_free (tsm->per_vrf_sessions_vec);
}
else
{
#define NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT 4
#define NAT_STATIC_MAPPING_FLAG_LB 8
+/* *INDENT-OFF* */
+typedef CLIB_PACKED(struct
+{
+ // number of sessions in this vrf
+ u32 ses_count;
+
+ u32 rx_fib_index;
+ u32 tx_fib_index;
+
+ // is this vrf expired
+ u8 expired;
+}) per_vrf_sessions_t;
+/* *INDENT-ON* */
+
/* *INDENT-OFF* */
typedef CLIB_PACKED(struct
{
/* user index */
u32 user_index;
+
+ /* per vrf sessions index */
+ u32 per_vrf_sessions_index;
+
}) snat_session_t;
/* *INDENT-ON* */
-
typedef struct
{
ip4_address_t addr;
/* *INDENT-ON* */
} snat_address_t;
+typedef struct
+{
+ u32 fib_index;
+ u32 ref_count;
+} nat_fib_t;
+
typedef struct
{
u32 fib_index;
/* real thread index */
u32 thread_index;
+ per_vrf_sessions_t *per_vrf_sessions_vec;
+
} snat_main_per_thread_data_t;
struct snat_main_s;
u16 start_port;
u16 end_port;
+ /* vector of fibs */
+ nat_fib_t *fibs;
+
/* vector of outside fibs */
nat_outside_fib_t *outside_fibs;
u16 port_per_thread,
u32 snat_thread_index);
+void expire_per_vrf_sessions (u32 fib_index);
+
/**
* @brief Match NAT44 static mapping.
*
return s;
}
+// slow path
+static_always_inline void
+per_vrf_sessions_cleanup (u32 thread_index)
+{
+ snat_main_t *sm = &snat_main;
+ snat_main_per_thread_data_t *tsm =
+ vec_elt_at_index (sm->per_thread_data, thread_index);
+ per_vrf_sessions_t *per_vrf_sessions;
+ u32 *to_free = 0, *i;
+
+ vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec)
+ {
+ 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 (vec_len (to_free))
+ {
+ vec_foreach (i, to_free)
+ {
+ vec_del1 (tsm->per_vrf_sessions_vec, *i);
+ }
+ }
+
+ vec_free (to_free);
+}
+
+// slow path
+static_always_inline void
+per_vrf_sessions_register_session (snat_session_t * s, u32 thread_index)
+{
+ snat_main_t *sm = &snat_main;
+ snat_main_per_thread_data_t *tsm =
+ vec_elt_at_index (sm->per_thread_data, thread_index);
+ per_vrf_sessions_t *per_vrf_sessions;
+
+ per_vrf_sessions_cleanup (thread_index);
+
+ // s->per_vrf_sessions_index == ~0 ... reuse of old session
+
+ vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec)
+ {
+ // ignore already expired registrations
+ if (per_vrf_sessions->expired)
+ continue;
+
+ if ((s->in2out.fib_index == per_vrf_sessions->rx_fib_index) &&
+ (s->out2in.fib_index == per_vrf_sessions->tx_fib_index))
+ {
+ goto done;
+ }
+ if ((s->in2out.fib_index == per_vrf_sessions->tx_fib_index) &&
+ (s->out2in.fib_index == per_vrf_sessions->rx_fib_index))
+ {
+ goto done;
+ }
+ }
+
+ // create a new registration
+ vec_add2 (tsm->per_vrf_sessions_vec, per_vrf_sessions, 1);
+ 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;
+ per_vrf_sessions->ses_count++;
+}
+
+// fast path
+static_always_inline void
+per_vrf_sessions_unregister_session (snat_session_t * s, u32 thread_index)
+{
+ snat_main_t *sm = &snat_main;
+ snat_main_per_thread_data_t *tsm;
+ per_vrf_sessions_t *per_vrf_sessions;
+
+ ASSERT (s->per_vrf_sessions_index != ~0);
+
+ 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);
+
+ ASSERT (per_vrf_sessions->ses_count != 0);
+
+ per_vrf_sessions->ses_count--;
+ s->per_vrf_sessions_index = ~0;
+}
+
+// fast path
+static_always_inline u8
+per_vrf_sessions_is_expired (snat_session_t * s, u32 thread_index)
+{
+ snat_main_t *sm = &snat_main;
+ snat_main_per_thread_data_t *tsm;
+ per_vrf_sessions_t *per_vrf_sessions;
+
+ ASSERT (s->per_vrf_sessions_index != ~0);
+
+ 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);
+ return per_vrf_sessions->expired;
+}
+
#endif
return 0;
}
+static clib_error_t *
+nat44_debug_fib_expire_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ clib_error_t *error = 0;
+ u32 fib = ~0;
+
+ /* Get a line of input. */
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "%u", &fib))
+ ;
+ else
+ {
+ error = clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+ expire_per_vrf_sessions (fib);
+done:
+ unformat_free (line_input);
+ return error;
+}
+
+static clib_error_t *
+nat44_debug_fib_registration_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ snat_main_t *sm = &snat_main;
+ snat_main_per_thread_data_t *tsm;
+ per_vrf_sessions_t *per_vrf_sessions;
+
+ vlib_cli_output (vm, "VRF registration debug:");
+ vec_foreach (tsm, sm->per_thread_data)
+ {
+ vlib_cli_output (vm, "thread %u:", tsm->thread_index);
+ vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec)
+ {
+ vlib_cli_output (vm, "rx fib %u tx fib %u ses count %u %s",
+ per_vrf_sessions->rx_fib_index,
+ per_vrf_sessions->tx_fib_index,
+ per_vrf_sessions->ses_count,
+ per_vrf_sessions->expired ? "expired" : "");
+ }
+ }
+ return 0;
+}
+
/* *INDENT-OFF* */
+/*?
+?*/
+VLIB_CLI_COMMAND (nat44_debug_fib_expire_command, static) = {
+ .path = "debug nat44 fib expire",
+ .short_help = "debug nat44 fib expire <fib-index>",
+ .function = nat44_debug_fib_expire_command_fn,
+};
+
+/*?
+?*/
+VLIB_CLI_COMMAND (nat44_debug_fib_registration_command, static) = {
+ .path = "debug nat44 fib registration",
+ .short_help = "debug nat44 fib registration",
+ .function = nat44_debug_fib_registration_command_fn,
+};
+
/*?
* @cliexpar
* @cliexstart{set snat workers}
&s->ext_host_nat_addr, s->ext_host_nat_port,
s->nat_proto, s->in2out.fib_index, s->flags, thread_index, 0);
+ per_vrf_sessions_register_session (s, thread_index);
+
return s;
}
kv.value = s - tsm->sessions;
if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &kv, 1))
nat_elog_notice ("in2out_ed key add failed");
+
+ per_vrf_sessions_register_session (s, thread_index);
}
if (ip->protocol == IP_PROTOCOL_TCP)
ip->protocol, thread_index, s - tsm->sessions);
if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &s_kv, 1))
nat_elog_notice ("in2out key add failed");
+
+ per_vrf_sessions_register_session (s, thread_index);
}
/* Update IP checksum */
}
}
+ // lookup for session
if (clib_bihash_search_16_8 (&sm->out2in_ed, &kv0, &value0))
{
+ // session does not exist go slow path
next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
goto trace0;
}
ed_value_get_session_index (&value0));
skip_lookup:
+
+ if (PREDICT_FALSE (per_vrf_sessions_is_expired (s0, thread_index)))
+ {
+ // session is closed, go slow path
+ nat_free_session_data (sm, s0, thread_index, 0);
+ nat_ed_session_delete (sm, s0, thread_index, 1);
+ next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
+ goto trace0;
+ }
+
if (s0->tcp_closed_timestamp)
{
if (now >= s0->tcp_closed_timestamp)
{
- // session is closed, go slow path
+ // session is closed, go slow path, freed in slow path
next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
}
else
next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH;
goto trace0;
}
- //
old_addr0 = ip0->dst_address.as_u32;
new_addr0 = ip0->dst_address.as_u32 = s0->in2out.addr.as_u32;
is_add=1)
self.vapi.nat44_interface_add_del_feature(
sw_if_index=self.pg0.sw_if_index,
- flags=flags, is_add=1)
+ is_add=1, flags=flags)
self.vapi.nat44_interface_add_del_output_feature(
- is_add=1,
- sw_if_index=self.pg1.sw_if_index)
+ sw_if_index=self.pg1.sw_if_index,
+ is_add=1)
self.vapi.nat44_interface_add_del_feature(
sw_if_index=self.pg5.sw_if_index,
is_add=1)
self.vapi.nat44_interface_add_del_feature(
sw_if_index=self.pg5.sw_if_index,
- flags=flags, is_add=1)
+ is_add=1, flags=flags)
self.vapi.nat44_interface_add_del_feature(
sw_if_index=self.pg6.sw_if_index,
is_add=1)
self.vapi.cli("clear logging")
def show_commands_at_teardown(self):
+ self.logger.info(self.vapi.cli("show errors"))
self.logger.info(self.vapi.cli("show nat44 addresses"))
self.logger.info(self.vapi.cli("show nat44 interfaces"))
self.logger.info(self.vapi.cli("show nat44 static mappings"))
self.logger.info(self.vapi.cli("show nat44 sessions detail"))
self.logger.info(self.vapi.cli("show nat44 hash tables detail"))
self.logger.info(self.vapi.cli("show nat timeouts"))
+ self.logger.info(self.vapi.cli("debug nat44 fib registration"))
class TestNAT44EndpointDependent3(MethodHolder):