+void
+nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index,
+ u8 is_ha)
+{
+ clib_bihash_kv_8_8_t kv;
+ u8 proto;
+ u16 r_port, l_port;
+ ip4_address_t *l_addr, *r_addr;
+ u32 fib_index = 0;
+ clib_bihash_kv_16_8_t ed_kv;
+ snat_main_per_thread_data_t *tsm =
+ vec_elt_at_index (sm->per_thread_data, thread_index);
+
+ if (is_fwd_bypass_session (s))
+ {
+ if (snat_is_unk_proto_session (s))
+ {
+ init_ed_k (&ed_kv, s->in2out.addr, 0, s->ext_host_addr, 0, 0,
+ s->in2out.port);
+ }
+ else
+ {
+ l_port = s->in2out.port;
+ r_port = s->ext_host_port;
+ l_addr = &s->in2out.addr;
+ r_addr = &s->ext_host_addr;
+ proto = nat_proto_to_ip_proto (s->nat_proto);
+ init_ed_k (&ed_kv, *l_addr, l_port, *r_addr, r_port, fib_index,
+ proto);
+ }
+ if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))
+ nat_elog_warn ("in2out_ed key del failed");
+ return;
+ }
+
+ /* session lookup tables */
+ if (is_ed_session (s))
+ {
+ if (is_affinity_sessions (s))
+ nat_affinity_unlock (s->ext_host_addr, s->out2in.addr,
+ s->nat_proto, s->out2in.port);
+ l_addr = &s->out2in.addr;
+ r_addr = &s->ext_host_addr;
+ fib_index = s->out2in.fib_index;
+ if (snat_is_unk_proto_session (s))
+ {
+ proto = s->in2out.port;
+ r_port = 0;
+ l_port = 0;
+ }
+ else
+ {
+ proto = nat_proto_to_ip_proto (s->nat_proto);
+ l_port = s->out2in.port;
+ r_port = s->ext_host_port;
+ }
+ init_ed_k (&ed_kv, *l_addr, l_port, *r_addr, r_port, fib_index, proto);
+ if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &ed_kv, 0))
+ nat_elog_warn ("out2in_ed key del failed");
+ l_addr = &s->in2out.addr;
+ fib_index = s->in2out.fib_index;
+ if (!snat_is_unk_proto_session (s))
+ l_port = s->in2out.port;
+ if (is_twice_nat_session (s))
+ {
+ r_addr = &s->ext_host_nat_addr;
+ r_port = s->ext_host_nat_port;
+ }
+ init_ed_k (&ed_kv, *l_addr, l_port, *r_addr, r_port, fib_index, proto);
+ if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))
+ nat_elog_warn ("in2out_ed key del failed");
+
+ if (!is_ha)
+ nat_syslog_nat44_sdel (s->user_index, s->in2out.fib_index,
+ &s->in2out.addr, s->in2out.port,
+ &s->ext_host_nat_addr, s->ext_host_nat_port,
+ &s->out2in.addr, s->out2in.port,
+ &s->ext_host_addr, s->ext_host_port,
+ s->nat_proto, is_twice_nat_session (s));
+ }
+ else
+ {
+ init_nat_i2o_k (&kv, s);
+ if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0))
+ nat_elog_warn ("in2out key del failed");
+ init_nat_o2i_k (&kv, s);
+ if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
+ nat_elog_warn ("out2in key del failed");
+
+ if (!is_ha)
+ nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
+ &s->in2out.addr, s->in2out.port,
+ &s->out2in.addr, s->out2in.port,
+ s->nat_proto);
+ }
+
+ if (snat_is_unk_proto_session (s))
+ return;
+
+ if (!is_ha)
+ {
+ /* log NAT event */
+ snat_ipfix_logging_nat44_ses_delete (thread_index,
+ s->in2out.addr.as_u32,
+ s->out2in.addr.as_u32,
+ s->nat_proto,
+ s->in2out.port,
+ s->out2in.port,
+ s->in2out.fib_index);
+
+ nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
+ s->ext_host_port, s->nat_proto, s->out2in.fib_index,
+ thread_index);
+ }
+
+ /* Twice NAT address and port for external host */
+ if (is_twice_nat_session (s))
+ {
+ snat_free_outside_address_and_port (sm->twice_nat_addresses,
+ thread_index,
+ &s->ext_host_nat_addr,
+ s->ext_host_nat_port, s->nat_proto);
+ }
+
+ if (snat_is_session_static (s))
+ return;
+
+ snat_free_outside_address_and_port (sm->addresses, thread_index,
+ &s->out2in.addr, s->out2in.port,
+ s->nat_proto);
+}
+
+int
+nat44_set_session_limit (u32 session_limit, u32 vrf_id)
+{
+ snat_main_t *sm = &snat_main;
+ u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
+ u32 len = vec_len (sm->max_translations_per_fib);
+
+ if (len <= fib_index)
+ {
+ vec_validate (sm->max_translations_per_fib, fib_index + 1);
+
+ for (; len < vec_len (sm->max_translations_per_fib); len++)
+ sm->max_translations_per_fib[len] = sm->max_translations_per_thread;
+ }
+
+ sm->max_translations_per_fib[fib_index] = session_limit;
+ return 0;
+}
+
+void
+nat44_free_session_data (snat_main_t * sm, snat_session_t * s,
+ u32 thread_index, u8 is_ha)
+{
+ u8 proto;
+ u16 r_port, l_port;
+ ip4_address_t *l_addr, *r_addr;
+ u32 fib_index;
+ clib_bihash_kv_16_8_t ed_kv;
+ snat_main_per_thread_data_t *tsm =
+ vec_elt_at_index (sm->per_thread_data, thread_index);
+
+ if (is_fwd_bypass_session (s))
+ {
+ if (snat_is_unk_proto_session (s))
+ {
+ proto = s->in2out.port;
+ r_port = 0;
+ l_port = 0;
+ }
+ else
+ {
+ proto = nat_proto_to_ip_proto (s->nat_proto);
+ l_port = s->in2out.port;
+ r_port = s->ext_host_port;
+ }
+
+ l_addr = &s->in2out.addr;
+ r_addr = &s->ext_host_addr;
+ fib_index = 0;
+ init_ed_k (&ed_kv, *l_addr, l_port, *r_addr, r_port, fib_index, proto);
+
+ if (PREDICT_FALSE
+ (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)))
+ nat_elog_warn ("in2out_ed key del failed");
+ return;
+ }
+
+ /* session lookup tables */
+ if (is_affinity_sessions (s))
+ nat_affinity_unlock (s->ext_host_addr, s->out2in.addr,
+ s->nat_proto, s->out2in.port);
+ l_addr = &s->out2in.addr;
+ r_addr = &s->ext_host_addr;
+ fib_index = s->out2in.fib_index;
+ if (snat_is_unk_proto_session (s))
+ {
+ proto = s->in2out.port;
+ r_port = 0;
+ l_port = 0;
+ }
+ else
+ {
+ proto = nat_proto_to_ip_proto (s->nat_proto);
+ l_port = s->out2in.port;
+ r_port = s->ext_host_port;
+ }
+ init_ed_k (&ed_kv, *l_addr, l_port, *r_addr, r_port, fib_index, proto);
+
+ if (PREDICT_FALSE (clib_bihash_add_del_16_8 (&sm->out2in_ed, &ed_kv, 0)))
+ nat_elog_warn ("out2in_ed key del failed");
+
+ l_addr = &s->in2out.addr;
+ fib_index = s->in2out.fib_index;
+
+ if (!snat_is_unk_proto_session (s))
+ l_port = s->in2out.port;
+
+ if (is_twice_nat_session (s))
+ {
+ r_addr = &s->ext_host_nat_addr;
+ r_port = s->ext_host_nat_port;
+ }
+ init_ed_k (&ed_kv, *l_addr, l_port, *r_addr, r_port, fib_index, proto);
+
+ if (PREDICT_FALSE (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)))
+ nat_elog_warn ("in2out_ed key del failed");
+
+ if (!is_ha)
+ {
+ nat_syslog_nat44_sdel (s->user_index, s->in2out.fib_index,
+ &s->in2out.addr, s->in2out.port,
+ &s->ext_host_nat_addr, s->ext_host_nat_port,
+ &s->out2in.addr, s->out2in.port,
+ &s->ext_host_addr, s->ext_host_port,
+ s->nat_proto, is_twice_nat_session (s));
+ }
+
+ if (snat_is_unk_proto_session (s))
+ return;
+
+ if (!is_ha)
+ {
+ snat_ipfix_logging_nat44_ses_delete (thread_index,
+ s->in2out.addr.as_u32,
+ s->out2in.addr.as_u32,
+ s->nat_proto,
+ s->in2out.port,
+ s->out2in.port,
+ s->in2out.fib_index);
+ nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
+ s->ext_host_port, s->nat_proto, s->out2in.fib_index,
+ thread_index);
+ }
+
+ /* Twice NAT address and port for external host */
+ if (is_twice_nat_session (s))
+ {
+ snat_free_outside_address_and_port (sm->twice_nat_addresses,
+ thread_index,
+ &s->ext_host_nat_addr,
+ s->ext_host_nat_port, s->nat_proto);
+ }
+
+ if (snat_is_session_static (s))
+ return;
+
+ snat_free_outside_address_and_port (sm->addresses, thread_index,
+ &s->out2in.addr, s->out2in.port,
+ s->nat_proto);
+}
+
+
+snat_user_t *
+nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index,
+ u32 thread_index)
+{
+ snat_user_t *u = 0;
+ snat_user_key_t user_key;
+ clib_bihash_kv_8_8_t kv, value;
+ snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+ dlist_elt_t *per_user_list_head_elt;
+
+ user_key.addr.as_u32 = addr->as_u32;
+ user_key.fib_index = fib_index;
+ kv.key = user_key.as_u64;
+
+ /* Ever heard of the "user" = src ip4 address before? */
+ if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
+ {
+ if (pool_elts (tsm->users) >= sm->max_users_per_thread)
+ {
+ vlib_increment_simple_counter (&sm->user_limit_reached,
+ thread_index, 0, 1);
+ nat_elog_warn ("maximum user limit reached");
+ return NULL;
+ }
+ /* no, make a new one */
+ pool_get (tsm->users, u);
+ clib_memset (u, 0, sizeof (*u));
+
+ u->addr.as_u32 = addr->as_u32;
+ u->fib_index = fib_index;
+
+ pool_get (tsm->list_pool, per_user_list_head_elt);
+
+ u->sessions_per_user_list_head_index = per_user_list_head_elt -
+ tsm->list_pool;
+
+ clib_dlist_init (tsm->list_pool, u->sessions_per_user_list_head_index);
+
+ kv.value = u - tsm->users;
+
+ /* add user */
+ if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
+ {
+ nat_elog_warn ("user_hash key add failed");
+ nat44_delete_user_with_no_session (sm, u, thread_index);
+ return NULL;
+ }
+
+ vlib_set_simple_counter (&sm->total_users, thread_index, 0,
+ pool_elts (tsm->users));
+ }
+ else
+ {
+ u = pool_elt_at_index (tsm->users, value.value);
+ }
+
+ return u;
+}
+
+snat_session_t *
+nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u,
+ u32 thread_index, f64 now)
+{
+ snat_session_t *s;
+ snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+ u32 oldest_per_user_translation_list_index, session_index;
+ dlist_elt_t *oldest_per_user_translation_list_elt;
+ dlist_elt_t *per_user_translation_list_elt;
+
+ /* Over quota? Recycle the least recently used translation */
+ if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user)
+ {
+ oldest_per_user_translation_list_index =
+ clib_dlist_remove_head (tsm->list_pool,
+ u->sessions_per_user_list_head_index);
+
+ ASSERT (oldest_per_user_translation_list_index != ~0);
+
+ /* Add it back to the end of the LRU list */
+ clib_dlist_addtail (tsm->list_pool,
+ u->sessions_per_user_list_head_index,
+ oldest_per_user_translation_list_index);
+ /* Get the list element */
+ oldest_per_user_translation_list_elt =
+ pool_elt_at_index (tsm->list_pool,
+ oldest_per_user_translation_list_index);
+
+ /* Get the session index from the list element */
+ session_index = oldest_per_user_translation_list_elt->value;
+
+ /* Get the session */
+ s = pool_elt_at_index (tsm->sessions, session_index);
+ nat_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;
+ }
+ else
+ {
+ 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->user_index = u - tsm->users;
+ vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
+ pool_elts (tsm->sessions));
+ }
+
+ s->ha_last_refreshed = now;
+
+ return s;
+}
+