X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fnat%2Fnat.c;h=3856add419f717d939c5e40e4869c8ae2771979c;hb=34931eb47;hp=8115a578e526ac1f64da678497ad9e3adfac8d1f;hpb=b7b929931a07fbb27b43d5cd105f366c3e29807e;p=vpp.git diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 8115a578e52..3856add419f 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -174,7 +176,8 @@ VLIB_PLUGIN_REGISTER () = { /* *INDENT-ON* */ void -nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) +nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index, + u8 is_ha) { snat_session_key_t key; clib_bihash_kv_8_8_t kv; @@ -236,6 +239,14 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) ed_kv.key[1] = ed_key.as_u64[1]; if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)) nat_log_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->in2out.protocol, is_twice_nat_session (s)); } else { @@ -245,17 +256,32 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) kv.key = s->out2in.as_u64; if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0)) nat_log_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->in2out.protocol); } if (snat_is_unk_proto_session (s)) return; - /* log NAT event */ - snat_ipfix_logging_nat44_ses_delete (s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->in2out.protocol, - s->in2out.port, - s->out2in.port, s->in2out.fib_index); + 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->in2out.protocol, + 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->out2in.protocol, s->out2in.fib_index, + thread_index); + } /* Twice NAT address and port for external host */ if (is_twice_nat_session (s)) @@ -309,6 +335,9 @@ nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index, /* add user */ if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1)) nat_log_warn ("user_hash keay add failed"); + + vlib_set_simple_counter (&sm->total_users, thread_index, 0, + pool_elts (tsm->users)); } else { @@ -320,7 +349,7 @@ nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index, snat_session_t * nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u, - u32 thread_index) + u32 thread_index, f64 now) { snat_session_t *s; snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; @@ -351,7 +380,7 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u, /* Get the session */ s = pool_elt_at_index (tsm->sessions, session_index); - nat_free_session_data (sm, s, thread_index); + nat_free_session_data (sm, s, thread_index, 0); if (snat_is_session_static (s)) u->nstaticsessions--; else @@ -382,8 +411,14 @@ nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u, 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; } @@ -397,64 +432,72 @@ nat_ed_session_alloc (snat_main_t * sm, snat_user_t * u, u32 thread_index, u32 oldest_index; u64 sess_timeout_time; - if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user) + if (PREDICT_FALSE (!(u->nsessions) && !(u->nstaticsessions))) + goto alloc_new; + + oldest_index = + clib_dlist_remove_head (tsm->list_pool, + u->sessions_per_user_list_head_index); + oldest_elt = pool_elt_at_index (tsm->list_pool, oldest_index); + s = pool_elt_at_index (tsm->sessions, oldest_elt->value); + sess_timeout_time = s->last_heard + (f64) nat44_session_get_timeout (sm, s); + if (now >= sess_timeout_time) { - oldest_index = - clib_dlist_remove_head (tsm->list_pool, - u->sessions_per_user_list_head_index); - oldest_elt = pool_elt_at_index (tsm->list_pool, oldest_index); - s = pool_elt_at_index (tsm->sessions, oldest_elt->value); - sess_timeout_time = - s->last_heard + (f64) nat44_session_get_timeout (sm, s); - if (now >= sess_timeout_time) - { - clib_dlist_addtail (tsm->list_pool, - u->sessions_per_user_list_head_index, - oldest_index); - nat_free_session_data (sm, s, thread_index); - 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; - } + clib_dlist_addtail (tsm->list_pool, + u->sessions_per_user_list_head_index, oldest_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 + { + clib_dlist_addhead (tsm->list_pool, + u->sessions_per_user_list_head_index, oldest_index); + if ((u->nsessions + u->nstaticsessions) >= + sm->max_translations_per_user) { - clib_dlist_addhead (tsm->list_pool, - u->sessions_per_user_list_head_index, - oldest_index); nat_log_warn ("max translations per user %U", format_ip4_address, &u->addr); snat_ipfix_logging_max_entries_per_user - (sm->max_translations_per_user, u->addr.as_u32); + (thread_index, sm->max_translations_per_user, u->addr.as_u32); return 0; } - } - else - { - pool_get (tsm->sessions, s); - clib_memset (s, 0, sizeof (*s)); + else + { + alloc_new: + 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); + /* 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; + 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); + clib_dlist_addtail (tsm->list_pool, + s->per_user_list_head_index, + per_user_translation_list_elt - tsm->list_pool); + } + + vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, + pool_elts (tsm->sessions)); } + + s->ha_last_refreshed = now; + return s; } @@ -558,6 +601,10 @@ is_snat_address_used_in_static_mapping (snat_main_t * sm, ip4_address_t addr) /* *INDENT-OFF* */ pool_foreach (m, sm->static_mappings, ({ + if (is_addr_only_static_mapping (m) || + is_out2in_only_static_mapping (m) || + is_identity_static_mapping (m)) + continue; if (m->external_addr.as_u32 == addr.as_u32) return 1; })); @@ -643,7 +690,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, snat_session_t *s; snat_static_map_resolve_t *rp, *rp_match = 0; nat44_lb_addr_port_t *local; - u8 find = 0; + u32 find = ~0; if (!sm->endpoint_dependent) { @@ -737,13 +784,13 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (is_identity_static_mapping (m)) { /* *INDENT-OFF* */ - vec_foreach (local, m->locals) - { - if (local->vrf_id == vrf_id) - return VNET_API_ERROR_VALUE_EXIST; - } + pool_foreach (local, m->locals, + ({ + if (local->vrf_id == vrf_id) + return VNET_API_ERROR_VALUE_EXIST; + })); /* *INDENT-ON* */ - vec_add2 (m->locals, local, 1); + pool_get (m->locals, local); local->vrf_id = vrf_id; local->fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, @@ -857,7 +904,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (identity_nat) { m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT; - vec_add2 (m->locals, local, 1); + pool_get (m->locals, local); local->vrf_id = vrf_id; local->fib_index = fib_index; } @@ -932,7 +979,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, continue; nat_free_session_data (sm, s, - tsm - sm->per_thread_data); + tsm - sm->per_thread_data, 0); nat44_delete_session (sm, s, tsm - sm->per_thread_data); if (!addr_only && !sm->endpoint_dependent) @@ -954,19 +1001,22 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (identity_nat) { - for (i = 0; i < vec_len (m->locals); i++) - { - if (m->locals[i].vrf_id == vrf_id) - { - find = 1; - break; - } - } - if (!find) + if (vrf_id == ~0) + vrf_id = sm->inside_vrf_id; + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + if (local->vrf_id == vrf_id) + find = local - m->locals; + })); + /* *INDENT-ON* */ + if (find == ~0) return VNET_API_ERROR_NO_SUCH_ENTRY; - fib_index = m->locals[i].fib_index; - vec_del1 (m->locals, i); + local = pool_elt_at_index (m->locals, find); + fib_index = local->fib_index; + pool_put (m->locals, local); } else fib_index = m->fib_index; @@ -1053,7 +1103,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, continue; nat_free_session_data (sm, s, - tsm - sm->per_thread_data); + tsm - sm->per_thread_data, 0); nat44_delete_session (sm, s, tsm - sm->per_thread_data); if (!addr_only && !sm->endpoint_dependent) @@ -1064,7 +1114,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, } fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW); - if (vec_len (m->locals)) + if (pool_elts (m->locals)) return 0; m_key.addr = m->external_addr; @@ -1233,7 +1283,8 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } locals[i].prefix = (i == 0) ? locals[i].probability : (locals[i - 1].prefix + locals[i].probability); - vec_add1 (m->locals, locals[i]); + pool_get (m->locals, local); + *local = locals[i]; if (sm->num_workers > 1) { ip4_header_t ip = { @@ -1306,8 +1357,8 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } /* *INDENT-OFF* */ - vec_foreach (local, m->locals) - { + pool_foreach (local, m->locals, + ({ fib_table_unlock (local->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW); m_key.addr = local->addr; @@ -1336,7 +1387,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, /* Delete sessions */ u_key.addr = local->addr; - u_key.fib_index = m->fib_index; + u_key.fib_index = local->fib_index; kv.key = u_key.as_u64; if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { @@ -1361,16 +1412,16 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, (clib_net_to_host_u16 (s->in2out.port) != local->port)) continue; - nat_free_session_data (sm, s, tsm - sm->per_thread_data); + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); nat44_delete_session (sm, s, tsm - sm->per_thread_data); } } } - } + })); /* *INDENT-ON* */ if (m->affinity) nat_affinity_flush_service (m->affinity_per_service_list_head_index); - vec_free (m->locals); + pool_free (m->locals); vec_free (m->tag); vec_free (m->workers); @@ -1380,6 +1431,189 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, return 0; } +int +nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port, + ip4_address_t l_addr, u16 l_port, + snat_protocol_t proto, u32 vrf_id, + u8 probability, u8 is_add) +{ + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m = 0; + snat_session_key_t m_key; + clib_bihash_kv_8_8_t kv, value; + nat44_lb_addr_port_t *local, *prev_local, *match_local = 0; + snat_main_per_thread_data_t *tsm; + snat_user_key_t u_key; + snat_user_t *u; + snat_session_t *s; + dlist_elt_t *head, *elt; + u32 elt_index, head_index, ses_index, *locals = 0; + uword *bitmap = 0; + int i; + + if (!sm->endpoint_dependent) + return VNET_API_ERROR_FEATURE_DISABLED; + + m_key.addr = e_addr; + m_key.port = e_port; + m_key.protocol = proto; + m_key.fib_index = 0; + kv.key = m_key.as_u64; + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (!m) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (!is_lb_static_mapping (m)) + return VNET_API_ERROR_INVALID_VALUE; + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + if ((local->addr.as_u32 == l_addr.as_u32) && (local->port == l_port) && + (local->vrf_id == vrf_id)) + { + match_local = local; + break; + } + })); + /* *INDENT-ON* */ + + if (is_add) + { + if (match_local) + return VNET_API_ERROR_VALUE_EXIST; + + pool_get (m->locals, local); + clib_memset (local, 0, sizeof (*local)); + local->addr.as_u32 = l_addr.as_u32; + local->port = l_port; + local->probability = probability; + local->vrf_id = vrf_id; + local->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + FIB_SOURCE_PLUGIN_LOW); + + if (!is_out2in_only_static_mapping (m)) + { + m_key.addr = l_addr; + m_key.port = l_port; + m_key.fib_index = local->fib_index; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1)) + nat_log_err ("static_mapping_by_local key add failed"); + } + } + else + { + if (!match_local) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (pool_elts (m->locals) < 3) + return VNET_API_ERROR_UNSPECIFIED; + + fib_table_unlock (match_local->fib_index, FIB_PROTOCOL_IP4, + FIB_SOURCE_PLUGIN_LOW); + + if (!is_out2in_only_static_mapping (m)) + { + m_key.addr = l_addr; + m_key.port = l_port; + m_key.fib_index = match_local->fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0)) + nat_log_err ("static_mapping_by_local key del failed"); + } + + if (sm->num_workers > 1) + { + ip4_header_t ip = { + .src_address = local->addr, + }; + tsm = vec_elt_at_index (sm->per_thread_data, + sm->worker_in2out_cb (&ip, m->fib_index)); + } + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + /* Delete sessions */ + u_key.addr = match_local->addr; + u_key.fib_index = match_local->fib_index; + kv.key = u_key.as_u64; + if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) + { + u = pool_elt_at_index (tsm->users, value.value); + if (u->nstaticsessions) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + while (ses_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, ses_index); + elt = pool_elt_at_index (tsm->list_pool, elt->next); + ses_index = elt->value; + + if (!(is_lb_session (s))) + continue; + + if ((s->in2out.addr.as_u32 != match_local->addr.as_u32) || + (clib_net_to_host_u16 (s->in2out.port) != + match_local->port)) + continue; + + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); + nat44_delete_session (sm, s, tsm - sm->per_thread_data); + } + } + } + + pool_put (m->locals, match_local); + } + + vec_free (m->workers); + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + vec_add1 (locals, local - m->locals); + if (sm->num_workers > 1) + { + ip4_header_t ip; + ip.src_address.as_u32 = local->addr.as_u32, + bitmap = clib_bitmap_set (bitmap, + sm->worker_in2out_cb (&ip, local->fib_index), + 1); + } + })); + /* *INDENT-ON* */ + + ASSERT (vec_len (locals) > 1); + + local = pool_elt_at_index (m->locals, locals[0]); + local->prefix = local->probability; + for (i = 1; i < vec_len (locals); i++) + { + local = pool_elt_at_index (m->locals, locals[i]); + prev_local = pool_elt_at_index (m->locals, locals[i - 1]); + local->prefix = local->probability + prev_local->prefix; + } + + /* Assign workers */ + if (sm->num_workers > 1) + { + /* *INDENT-OFF* */ + clib_bitmap_foreach (i, bitmap, ({ vec_add1(m->workers, i); })); + /* *INDENT-ON* */ + } + + return 0; +} + int snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm, u8 twice_nat) @@ -1442,7 +1676,7 @@ snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm, pool_foreach (ses, tsm->sessions, ({ if (ses->out2in.addr.as_u32 == addr.as_u32) { - nat_free_session_data (sm, ses, tsm - sm->per_thread_data); + nat_free_session_data (sm, ses, tsm - sm->per_thread_data, 0); vec_add1 (ses_to_be_removed, ses - tsm->sessions); } })); @@ -1551,7 +1785,7 @@ snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) { if (is_del) { - outside_fib->refcount--; + outside_fib->refcount--; if (!outside_fib->refcount) vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); } @@ -1751,6 +1985,10 @@ snat_interface_add_del_output_feature (u32 sw_if_index, snat_interface_t *i; snat_address_t *ap; snat_static_mapping_t *m; + nat_outside_fib_t *outside_fib; + u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index); + if (sm->deterministic || (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))) @@ -1764,6 +2002,34 @@ snat_interface_add_del_output_feature (u32 sw_if_index, })); /* *INDENT-ON* */ + if (!is_inside) + { + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + if (outside_fib->fib_index == fib_index) + { + if (is_del) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); + } + else + outside_fib->refcount++; + goto feature_set; + } + } + /* *INDENT-ON* */ + if (!is_del) + { + vec_add2 (sm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = fib_index; + } + } + +feature_set: if (is_inside) { if (sm->endpoint_dependent) @@ -1894,6 +2160,65 @@ snat_set_workers (uword * bitmap) return 0; } +static void +snat_update_outside_fib (u32 sw_if_index, u32 new_fib_index, + u32 old_fib_index) +{ + snat_main_t *sm = &snat_main; + nat_outside_fib_t *outside_fib; + snat_interface_t *i; + u8 is_add = 1; + + if (new_fib_index == old_fib_index) + return; + + if (!vec_len (sm->outside_fibs)) + return; + + pool_foreach (i, sm->interfaces, ( + { + if (i->sw_if_index == sw_if_index) + { + if (!(nat_interface_is_outside (i))) + return;} + } + )); + vec_foreach (outside_fib, sm->outside_fibs) + { + if (outside_fib->fib_index == old_fib_index) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); + break; + } + } + + vec_foreach (outside_fib, sm->outside_fibs) + { + if (outside_fib->fib_index == new_fib_index) + { + outside_fib->refcount++; + is_add = 0; + break; + } + } + + if (is_add) + { + vec_add2 (sm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = new_fib_index; + } +} + +static void +snat_ip4_table_bind (ip4_main_t * im, + uword opaque, + u32 sw_if_index, u32 new_fib_index, u32 old_fib_index) +{ + snat_update_outside_fib (sw_if_index, new_fib_index, old_fib_index); +} static void snat_ip4_add_del_interface_address_cb (ip4_main_t * im, @@ -1931,7 +2256,7 @@ snat_init (vlib_main_t * vm) uword *bitmap = 0; u32 i; ip4_add_del_interface_address_callback_t cb4; - vlib_node_t *error_drop_node; + vlib_node_t *node; sm->vlib_main = vm; sm->vnet_main = vnet_get_main (); @@ -1953,10 +2278,63 @@ snat_init (vlib_main_t * vm) sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT; sm->forwarding_enabled = 0; sm->log_class = vlib_log_register_class ("nat", 0); - error_drop_node = vlib_get_node_by_name (vm, (u8 *) "error-drop"); - sm->error_node_index = error_drop_node->index; sm->mss_clamping = 0; + node = vlib_get_node_by_name (vm, (u8 *) "error-drop"); + sm->error_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out"); + sm->in2out_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-output"); + sm->in2out_output_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-fast"); + sm->in2out_fast_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-slowpath"); + sm->in2out_slowpath_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-output-slowpath"); + sm->in2out_slowpath_output_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-reass"); + sm->in2out_reass_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out"); + sm->ed_in2out_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-slowpath"); + sm->ed_in2out_slowpath_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-reass"); + sm->ed_in2out_reass_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-out2in"); + sm->out2in_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-out2in-fast"); + sm->out2in_fast_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-out2in-reass"); + sm->out2in_reass_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in"); + sm->ed_out2in_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in-slowpath"); + sm->ed_out2in_slowpath_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in-reass"); + sm->ed_out2in_reass_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-det-in2out"); + sm->det_in2out_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-det-out2in"); + sm->det_out2in_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpinning"); + sm->hairpinning_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpin-dst"); + sm->hairpin_dst_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpin-src"); + sm->hairpin_src_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-hairpinning"); + sm->ed_hairpinning_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-hairpin-dst"); + sm->ed_hairpin_dst_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-hairpin-src"); + sm->ed_hairpin_src_node_index = node->index; + p = hash_get_mem (tm->thread_registrations_by_name, "workers"); if (p) { @@ -2000,6 +2378,16 @@ snat_init (vlib_main_t * vm) nat_dpo_module_init (); + /* Init counters */ + sm->total_users.name = "total-users"; + sm->total_users.stat_segment_name = "/nat44/total-users"; + vlib_validate_simple_counter (&sm->total_users, 0); + vlib_zero_simple_counter (&sm->total_users, 0); + sm->total_sessions.name = "total-sessions"; + sm->total_sessions.stat_segment_name = "/nat44/total-sessions"; + vlib_validate_simple_counter (&sm->total_sessions, 0); + vlib_zero_simple_counter (&sm->total_sessions, 0); + /* Init IPFIX logging */ snat_ipfix_logging_init (vm); @@ -2010,7 +2398,12 @@ snat_init (vlib_main_t * vm) dslite_init (vm); - nat66_init (); + nat66_init (vm); + + ip4_table_bind_callback_t cbt4 = { + .function = snat_ip4_table_bind, + }; + vec_add1 (ip4_main.table_bind_callbacks, cbt4); /* Init virtual fragmenentation reassembly */ return nat_reass_init (vm); @@ -2056,6 +2449,42 @@ snat_free_outside_address_and_port (snat_address_t * addresses, } } +static int +nat_set_outside_address_and_port (snat_address_t * addresses, + u32 thread_index, snat_session_key_t * k) +{ + snat_address_t *a = 0; + u32 address_index; + u16 port_host_byte_order = clib_net_to_host_u16 (k->port); + + for (address_index = 0; address_index < vec_len (addresses); + address_index++) + { + if (addresses[address_index].addr.as_u32 != k->addr.as_u32) + continue; + + a = addresses + address_index; + switch (k->protocol) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, port_host_byte_order)) \ + return VNET_API_ERROR_INSTANCE_IN_USE; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, port_host_byte_order, 1); \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + return 0; + foreach_snat_protocol +#undef _ + default: + nat_log_info ("unknown protocol"); + return 1; + } + } + + return VNET_API_ERROR_NO_SUCH_ENTRY; +} + int snat_static_mapping_match (snat_main_t * sm, snat_session_key_t match, @@ -2070,8 +2499,9 @@ snat_static_mapping_match (snat_main_t * sm, snat_static_mapping_t *m; snat_session_key_t m_key; clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local; - u32 rand, lo = 0, hi, mid; + u32 rand, lo = 0, hi, mid, *tmp = 0, i; u8 backend_index; + nat44_lb_addr_port_t *local; m_key.fib_index = match.fib_index; if (by_external) @@ -2111,42 +2541,52 @@ snat_static_mapping_match (snat_main_t * sm, &backend_index)) goto get_local; - mapping->addr = m->locals[backend_index].addr; - mapping->port = - clib_host_to_net_u16 (m->locals[backend_index].port); - mapping->fib_index = m->locals[backend_index].fib_index; + local = pool_elt_at_index (m->locals, backend_index); + mapping->addr = local->addr; + mapping->port = clib_host_to_net_u16 (local->port); + mapping->fib_index = local->fib_index; goto end; } get_local: - hi = vec_len (m->locals) - 1; - rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix); + /* *INDENT-OFF* */ + pool_foreach_index (i, m->locals, + ({ + vec_add1 (tmp, i); + })); + /* *INDENT-ON* */ + hi = vec_len (tmp) - 1; + local = pool_elt_at_index (m->locals, tmp[hi]); + rand = 1 + (random_u32 (&sm->random_seed) % local->prefix); while (lo < hi) { mid = ((hi - lo) >> 1) + lo; - (rand > m->locals[mid].prefix) ? (lo = mid + 1) : (hi = mid); + local = pool_elt_at_index (m->locals, tmp[mid]); + (rand > local->prefix) ? (lo = mid + 1) : (hi = mid); } - if (!(m->locals[lo].prefix >= rand)) + local = pool_elt_at_index (m->locals, tmp[lo]); + if (!(local->prefix >= rand)) return 1; if (PREDICT_FALSE (sm->num_workers > 1)) { ip4_header_t ip = { - .src_address = m->locals[lo].addr, + .src_address = local->addr, }; if (sm->worker_in2out_cb (&ip, m->fib_index) != vlib_get_thread_index ()) goto get_local; } - mapping->addr = m->locals[lo].addr; - mapping->port = clib_host_to_net_u16 (m->locals[lo].port); - mapping->fib_index = m->locals[lo].fib_index; + mapping->addr = local->addr; + mapping->port = clib_host_to_net_u16 (local->port); + mapping->fib_index = local->fib_index; if (m->affinity) { if (nat_affinity_create_and_lock (ext_host_addr[0], match.addr, match.protocol, match.port, - lo, m->affinity, + tmp[lo], m->affinity, m->affinity_per_service_list_head_index)) nat_log_info ("create affinity record failed"); } + vec_free (tmp); } else { @@ -2287,7 +2727,7 @@ nat_alloc_addr_and_port_default (snat_address_t * addresses, } /* Totally out of translations to use... */ - snat_ipfix_logging_addresses_exhausted (0); + snat_ipfix_logging_addresses_exhausted (thread_index, 0); return 1; } @@ -2337,7 +2777,7 @@ nat_alloc_addr_and_port_mape (snat_address_t * addresses, exhausted: /* Totally out of translations to use... */ - snat_ipfix_logging_addresses_exhausted (0); + snat_ipfix_logging_addresses_exhausted (thread_index, 0); return 1; } @@ -2385,7 +2825,7 @@ nat_alloc_addr_and_port_range (snat_address_t * addresses, exhausted: /* Totally out of translations to use... */ - snat_ipfix_logging_addresses_exhausted (0); + snat_ipfix_logging_addresses_exhausted (thread_index, 0); return 1; } @@ -2527,20 +2967,47 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) if (PREDICT_FALSE (nat_reass_is_drop_frag (0))) return vlib_get_thread_index (); - if (PREDICT_TRUE (!ip4_is_first_fragment (ip0))) - { - nat_reass_ip4_t *reass; + nat_reass_ip4_t *reass; + reass = nat_ip4_reass_find (ip0->src_address, ip0->dst_address, + ip0->fragment_id, ip0->protocol); - reass = nat_ip4_reass_find (ip0->src_address, ip0->dst_address, - ip0->fragment_id, ip0->protocol); + if (reass && (reass->thread_index != (u32) ~ 0)) + return reass->thread_index; - if (reass && (reass->thread_index != (u32) ~ 0)) - return reass->thread_index; - else - return vlib_get_thread_index (); + if (ip4_is_first_fragment (ip0)) + { + reass = + nat_ip4_reass_create (ip0->src_address, ip0->dst_address, + ip0->fragment_id, ip0->protocol); + if (!reass) + goto no_reass; + + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + m_key.addr = ip0->dst_address; + m_key.port = clib_net_to_host_u16 (port); + m_key.protocol = proto; + m_key.fib_index = rx_fib_index0; + kv.key = m_key.as_u64; + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + reass->thread_index = m->workers[0]; + return reass->thread_index; + } + } + reass->thread_index = sm->first_worker_index; + reass->thread_index += + sm->workers[(clib_net_to_host_u16 (port) - 1024) / + sm->port_per_thread]; + return reass->thread_index; } + else + return vlib_get_thread_index (); } +no_reass: /* unknown protocol */ if (PREDICT_FALSE (proto == ~0)) { @@ -2692,6 +3159,334 @@ nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index) return next_worker_index; } +void +nat_ha_sadd_cb (ip4_address_t * in_addr, u16 in_port, + ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, + ip4_address_t * ehn_addr, u16 ehn_port, u8 proto, + u32 fib_index, u16 flags, u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_session_key_t key; + snat_user_t *u; + snat_session_t *s; + clib_bihash_kv_8_8_t kv; + f64 now = vlib_time_now (sm->vlib_main); + nat_outside_fib_t *outside_fib; + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + snat_main_per_thread_data_t *tsm; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = { + .ip4.as_u32 = eh_addr->as_u32, + }, + }; + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + key.protocol = proto; + + if (!(flags & SNAT_SESSION_FLAG_STATIC_MAPPING)) + { + if (nat_set_outside_address_and_port + (sm->addresses, thread_index, &key)) + return; + } + + u = nat_user_get_or_create (sm, in_addr, fib_index, thread_index); + if (!u) + return; + + s = nat_session_alloc_or_recycle (sm, u, thread_index, now); + if (!s) + return; + + s->last_heard = now; + s->flags = flags; + s->ext_host_addr.as_u32 = eh_addr->as_u32; + s->ext_host_port = eh_port; + user_session_increment (sm, u, snat_is_session_static (s)); + switch (vec_len (sm->outside_fibs)) + { + case 0: + key.fib_index = sm->outside_fib_index; + break; + case 1: + key.fib_index = sm->outside_fibs[0].fib_index; + break; + default: + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + if (fib_entry_get_resolving_interface (fei) != ~0) + { + key.fib_index = outside_fib->fib_index; + break; + } + } + } + /* *INDENT-ON* */ + break; + } + s->out2in = key; + kv.key = key.as_u64; + kv.value = s - tsm->sessions; + if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 1)) + nat_log_warn ("out2in key add failed"); + + key.addr.as_u32 = in_addr->as_u32; + key.port = in_port; + key.fib_index = fib_index; + s->in2out = key; + kv.key = key.as_u64; + if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 1)) + nat_log_warn ("in2out key add failed"); +} + +void +nat_ha_sdel_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, u32 fib_index, + u32 ti) +{ + snat_main_t *sm = &snat_main; + snat_session_key_t key; + clib_bihash_kv_8_8_t kv, value; + u32 thread_index; + snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + if (sm->num_workers > 1) + thread_index = + sm->first_worker_index + + (sm->workers[(clib_net_to_host_u16 (out_port) - + 1024) / sm->port_per_thread]); + else + thread_index = sm->num_workers; + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + key.protocol = proto; + key.fib_index = fib_index; + kv.key = key.as_u64; + if (clib_bihash_search_8_8 (&tsm->out2in, &kv, &value)) + return; + + s = pool_elt_at_index (tsm->sessions, value.value); + nat_free_session_data (sm, s, thread_index, 1); + nat44_delete_session (sm, s, thread_index); +} + +void +nat_ha_sref_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, u32 fib_index, + u32 total_pkts, u64 total_bytes, u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_session_key_t key; + clib_bihash_kv_8_8_t kv, value; + snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + key.protocol = proto; + key.fib_index = fib_index; + kv.key = key.as_u64; + if (clib_bihash_search_8_8 (&tsm->out2in, &kv, &value)) + return; + + s = pool_elt_at_index (tsm->sessions, value.value); + s->total_pkts = total_pkts; + s->total_bytes = total_bytes; +} + +void +nat_ha_sadd_ed_cb (ip4_address_t * in_addr, u16 in_port, + ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, + ip4_address_t * ehn_addr, u16 ehn_port, u8 proto, + u32 fib_index, u16 flags, u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_session_key_t key; + snat_user_t *u; + snat_session_t *s; + clib_bihash_kv_16_8_t kv; + f64 now = vlib_time_now (sm->vlib_main); + nat_outside_fib_t *outside_fib; + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + snat_main_per_thread_data_t *tsm; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = { + .ip4.as_u32 = eh_addr->as_u32, + }, + }; + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + key.protocol = proto; + + if (!(flags & SNAT_SESSION_FLAG_STATIC_MAPPING)) + { + if (nat_set_outside_address_and_port + (sm->addresses, thread_index, &key)) + return; + } + + key.addr.as_u32 = ehn_addr->as_u32; + key.port = ehn_port; + if (flags & SNAT_SESSION_FLAG_TWICE_NAT) + { + if (nat_set_outside_address_and_port + (sm->twice_nat_addresses, thread_index, &key)) + return; + } + + u = nat_user_get_or_create (sm, in_addr, fib_index, thread_index); + if (!u) + return; + + s = nat_ed_session_alloc (sm, u, thread_index, now); + if (!s) + return; + + s->last_heard = now; + s->flags = flags; + s->ext_host_nat_addr.as_u32 = s->ext_host_addr.as_u32 = eh_addr->as_u32; + s->ext_host_nat_port = s->ext_host_port = eh_port; + if (is_twice_nat_session (s)) + { + s->ext_host_nat_addr.as_u32 = ehn_addr->as_u32; + s->ext_host_nat_port = ehn_port; + } + user_session_increment (sm, u, snat_is_session_static (s)); + switch (vec_len (sm->outside_fibs)) + { + case 0: + key.fib_index = sm->outside_fib_index; + break; + case 1: + key.fib_index = sm->outside_fibs[0].fib_index; + break; + default: + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + if (fib_entry_get_resolving_interface (fei) != ~0) + { + key.fib_index = outside_fib->fib_index; + break; + } + } + } + /* *INDENT-ON* */ + break; + } + key.addr.as_u32 = out_addr->as_u32; + key.port = out_port; + s->out2in = key; + kv.value = s - tsm->sessions; + + key.addr.as_u32 = in_addr->as_u32; + key.port = in_port; + key.fib_index = fib_index; + s->in2out = key; + + make_ed_kv (&kv, in_addr, &s->ext_host_nat_addr, + snat_proto_to_ip_proto (proto), fib_index, in_port, + s->ext_host_nat_port); + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &kv, 1)) + nat_log_warn ("in2out key add failed"); + + make_ed_kv (&kv, out_addr, eh_addr, snat_proto_to_ip_proto (proto), + s->out2in.fib_index, out_port, eh_port); + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 1)) + nat_log_warn ("out2in key add failed"); +} + +void +nat_ha_sdel_ed_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, + u32 fib_index, u32 ti) +{ + snat_main_t *sm = &snat_main; + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t kv, value; + u32 thread_index; + snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + if (sm->num_workers > 1) + thread_index = + sm->first_worker_index + + (sm->workers[(clib_net_to_host_u16 (out_port) - + 1024) / sm->port_per_thread]); + else + thread_index = sm->num_workers; + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.l_addr.as_u32 = out_addr->as_u32; + key.l_port = out_port; + key.r_addr.as_u32 = eh_addr->as_u32; + key.r_port = eh_port; + key.proto = proto; + key.fib_index = fib_index; + kv.key[0] = key.as_u64[0]; + kv.key[1] = key.as_u64[1]; + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv, &value)) + return; + + s = pool_elt_at_index (tsm->sessions, value.value); + nat_free_session_data (sm, s, thread_index, 1); + nat44_delete_session (sm, s, thread_index); +} + +void +nat_ha_sref_ed_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, + u32 fib_index, u32 total_pkts, u64 total_bytes, + u32 thread_index) +{ + snat_main_t *sm = &snat_main; + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t kv, value; + snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + + key.l_addr.as_u32 = out_addr->as_u32; + key.l_port = out_port; + key.r_addr.as_u32 = eh_addr->as_u32; + key.r_port = eh_port; + key.proto = proto; + key.fib_index = fib_index; + kv.key[0] = key.as_u64[0]; + kv.key[1] = key.as_u64[1]; + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv, &value)) + return; + + s = pool_elt_at_index (tsm->sessions, value.value); + s->total_pkts = total_pkts; + s->total_bytes = total_bytes; +} + static clib_error_t * snat_config (vlib_main_t * vm, unformat_input_t * input) { @@ -2829,6 +3624,8 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) sm->icmp_match_in2out_cb = icmp_match_in2out_ed; sm->icmp_match_out2in_cb = icmp_match_out2in_ed; nat_affinity_init (vm); + nat_ha_init (vm, nat_ha_sadd_ed_cb, nat_ha_sdel_ed_cb, + nat_ha_sref_ed_cb); } else { @@ -2839,6 +3636,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) sm->out2in_node_index = snat_out2in_node.index; sm->icmp_match_in2out_cb = icmp_match_in2out_slow; sm->icmp_match_out2in_cb = icmp_match_out2in_slow; + nat_ha_init (vm, nat_ha_sadd_cb, nat_ha_sdel_cb, nat_ha_sref_cb); } if (!static_mapping_only || (static_mapping_only && static_mapping_connection_tracking)) @@ -3159,7 +3957,7 @@ nat44_del_session (snat_main_t * sm, ip4_address_t * addr, u16 port, return VNET_API_ERROR_UNSPECIFIED; s = pool_elt_at_index (tsm->sessions, value.value); - nat_free_session_data (sm, s, tsm - sm->per_thread_data); + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); nat44_delete_session (sm, s, tsm - sm->per_thread_data); return 0; } @@ -3206,7 +4004,7 @@ nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port, if (pool_is_free_index (tsm->sessions, value.value)) return VNET_API_ERROR_UNSPECIFIED; s = pool_elt_at_index (tsm->sessions, value.value); - nat_free_session_data (sm, s, tsm - sm->per_thread_data); + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); nat44_delete_session (sm, s, tsm - sm->per_thread_data); return 0; }