+ if (!(nat44_ei_is_addr_only_static_mapping (m)) ||
+ (m->local_addr.as_u32 == m->external_addr.as_u32))
+ continue;
+
+ nat44_ei_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index,
+ !is_del);
+ }
+
+ return 0;
+}
+
+int
+nat44_ei_interface_add_del_output_feature (u32 sw_if_index, u8 is_inside,
+ int is_del)
+{
+ nat44_ei_main_t *nm = &nat44_ei_main;
+ nat44_ei_interface_t *i;
+ nat44_ei_address_t *ap;
+ nat44_ei_static_mapping_t *m;
+ nat44_ei_outside_fib_t *outside_fib;
+ u32 fib_index =
+ fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index);
+
+ fail_if_disabled ();
+
+ if (nm->static_mapping_only && !(nm->static_mapping_connection_tracking))
+ {
+ nat44_ei_log_err ("error unsupported");
+ return VNET_API_ERROR_UNSUPPORTED;
+ }
+
+ pool_foreach (i, nm->interfaces)
+ {
+ if (i->sw_if_index == sw_if_index)
+ {
+ nat44_ei_log_err ("error interface already configured");
+ return VNET_API_ERROR_VALUE_EXIST;
+ }
+ }
+
+ if (!is_inside)
+ {
+ vec_foreach (outside_fib, nm->outside_fibs)
+ {
+ if (outside_fib->fib_index == fib_index)
+ {
+ if (is_del)
+ {
+ outside_fib->refcount--;
+ if (!outside_fib->refcount)
+ vec_del1 (nm->outside_fibs,
+ outside_fib - nm->outside_fibs);
+ }
+ else
+ outside_fib->refcount++;
+ goto feature_set;
+ }
+ }
+ if (!is_del)
+ {
+ vec_add2 (nm->outside_fibs, outside_fib, 1);
+ outside_fib->refcount = 1;
+ outside_fib->fib_index = fib_index;
+ }
+ }
+
+feature_set:
+ if (is_inside)
+ {
+ int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del);
+ if (rv)
+ return rv;
+ rv =
+ ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del);
+ if (rv)
+ return rv;
+ vnet_feature_enable_disable ("ip4-unicast", "nat44-ei-hairpin-dst",
+ sw_if_index, !is_del, 0, 0);
+ vnet_feature_enable_disable ("ip4-output", "nat44-ei-hairpin-src",
+ sw_if_index, !is_del, 0, 0);
+ goto fq;
+ }
+
+ if (nm->num_workers > 1)
+ {
+ int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del);
+ if (rv)
+ return rv;
+ rv =
+ ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del);
+ if (rv)
+ return rv;
+ vnet_feature_enable_disable ("ip4-unicast",
+ "nat44-ei-out2in-worker-handoff",
+ sw_if_index, !is_del, 0, 0);
+ vnet_feature_enable_disable ("ip4-output",
+ "nat44-ei-in2out-output-worker-handoff",
+ sw_if_index, !is_del, 0, 0);
+ }
+ else
+ {
+ int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del);
+ if (rv)
+ return rv;
+ rv =
+ ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del);
+ if (rv)
+ return rv;
+ vnet_feature_enable_disable ("ip4-unicast", "nat44-ei-out2in",
+ sw_if_index, !is_del, 0, 0);
+ vnet_feature_enable_disable ("ip4-output", "nat44-ei-in2out-output",
+ sw_if_index, !is_del, 0, 0);
+ }
+
+fq:
+ if (nm->fq_in2out_output_index == ~0 && nm->num_workers > 1)
+ nm->fq_in2out_output_index =
+ vlib_frame_queue_main_init (nm->in2out_output_node_index, 0);
+
+ if (nm->fq_out2in_index == ~0 && nm->num_workers > 1)
+ nm->fq_out2in_index =
+ vlib_frame_queue_main_init (nm->out2in_node_index, 0);
+
+ pool_foreach (i, nm->output_feature_interfaces)
+ {
+ if (i->sw_if_index == sw_if_index)
+ {
+ if (is_del)
+ pool_put (nm->output_feature_interfaces, i);
+ else
+ return VNET_API_ERROR_VALUE_EXIST;
+
+ goto fib;
+ }
+ }
+
+ if (is_del)
+ {
+ nat44_ei_log_err ("error interface couldn't be found");
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+ }
+
+ pool_get (nm->output_feature_interfaces, i);
+ i->sw_if_index = sw_if_index;
+ i->flags = 0;
+ nat_validate_interface_counters (nm, sw_if_index);
+ if (is_inside)
+ i->flags |= NAT44_EI_INTERFACE_FLAG_IS_INSIDE;
+ else
+ i->flags |= NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE;
+
+ /* Add/delete external addresses to FIB */
+fib:
+ if (is_inside)
+ return 0;
+
+ vec_foreach (ap, nm->addresses)
+ nat44_ei_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, !is_del);
+
+ pool_foreach (m, nm->static_mappings)
+ {
+ if (!((nat44_ei_is_addr_only_static_mapping (m))) ||
+ (m->local_addr.as_u32 == m->external_addr.as_u32))
+ continue;
+
+ nat44_ei_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index,
+ !is_del);
+ }
+
+ return 0;
+}
+
+int
+nat44_ei_plugin_disable ()
+{
+ nat44_ei_main_t *nm = &nat44_ei_main;
+ nat44_ei_interface_t *i, *vec;
+ int error = 0;
+
+ // first unregister all nodes from interfaces
+ vec = vec_dup (nm->interfaces);
+ vec_foreach (i, vec)
+ {
+ if (nat44_ei_interface_is_inside (i))
+ error = nat44_ei_interface_add_del (i->sw_if_index, 1, 1);
+ if (nat44_ei_interface_is_outside (i))
+ error = nat44_ei_interface_add_del (i->sw_if_index, 0, 1);
+
+ if (error)
+ {
+ nat44_ei_log_err ("error occurred while removing interface %u",
+ i->sw_if_index);
+ }
+ }
+ vec_free (vec);
+ nm->interfaces = 0;
+
+ vec = vec_dup (nm->output_feature_interfaces);
+ vec_foreach (i, vec)
+ {
+ if (nat44_ei_interface_is_inside (i))
+ error =
+ nat44_ei_interface_add_del_output_feature (i->sw_if_index, 1, 1);
+ if (nat44_ei_interface_is_outside (i))
+ error =
+ nat44_ei_interface_add_del_output_feature (i->sw_if_index, 0, 1);
+
+ if (error)
+ {
+ nat44_ei_log_err ("error occurred while removing interface %u",
+ i->sw_if_index);
+ }
+ }
+ vec_free (vec);
+ nm->output_feature_interfaces = 0;
+
+ nat_ha_disable ();
+ nat44_ei_db_free ();
+
+ nat44_ei_addresses_free (&nm->addresses);
+
+ vec_free (nm->to_resolve);
+ vec_free (nm->auto_add_sw_if_indices);
+
+ nm->to_resolve = 0;
+ nm->auto_add_sw_if_indices = 0;
+
+ nm->forwarding_enabled = 0;
+
+ nm->enabled = 0;
+ clib_memset (&nm->rconfig, 0, sizeof (nm->rconfig));
+
+ return error;
+}
+
+int
+nat44_ei_set_outside_address_and_port (nat44_ei_address_t *addresses,
+ u32 thread_index, ip4_address_t addr,
+ u16 port, nat_protocol_t protocol)
+{
+ nat44_ei_main_t *nm = &nat44_ei_main;
+ nat44_ei_address_t *a = 0;
+ u32 address_index;
+ u16 port_host_byte_order = clib_net_to_host_u16 (port);
+
+ for (address_index = 0; address_index < vec_len (addresses); address_index++)
+ {
+ if (addresses[address_index].addr.as_u32 != addr.as_u32)
+ continue;
+
+ a = addresses + address_index;
+ switch (protocol)
+ {
+#define _(N, j, n, s) \
+ case NAT_PROTOCOL_##N: \
+ if (a->busy_##n##_port_refcounts[port_host_byte_order]) \
+ return VNET_API_ERROR_INSTANCE_IN_USE; \
+ ++a->busy_##n##_port_refcounts[port_host_byte_order]; \
+ a->busy_##n##_ports_per_thread[thread_index]++; \
+ a->busy_##n##_ports++; \
+ return 0;
+ foreach_nat_protocol
+#undef _
+ default : nat_elog_info (nm, "unknown protocol");
+ return 1;
+ }
+ }
+
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+}
+
+void
+nat44_ei_add_del_address_dpo (ip4_address_t addr, u8 is_add)
+{
+ nat44_ei_main_t *nm = &nat44_ei_main;
+ dpo_id_t dpo_v4 = DPO_INVALID;
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_addr.ip4.as_u32 = addr.as_u32,
+ };
+
+ if (is_add)
+ {
+ nat_dpo_create (DPO_PROTO_IP4, 0, &dpo_v4);
+ fib_table_entry_special_dpo_add (0, &pfx, nm->fib_src_hi,
+ FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4);
+ dpo_reset (&dpo_v4);
+ }
+ else
+ {
+ fib_table_entry_special_remove (0, &pfx, nm->fib_src_hi);
+ }
+}
+
+void
+nat44_ei_free_outside_address_and_port (nat44_ei_address_t *addresses,
+ u32 thread_index, ip4_address_t *addr,
+ u16 port, nat_protocol_t protocol)
+{
+ nat44_ei_main_t *nm = &nat44_ei_main;
+ nat44_ei_address_t *a;
+ u32 address_index;
+ u16 port_host_byte_order = clib_net_to_host_u16 (port);
+
+ for (address_index = 0; address_index < vec_len (addresses); address_index++)
+ {
+ if (addresses[address_index].addr.as_u32 == addr->as_u32)
+ break;
+ }
+
+ ASSERT (address_index < vec_len (addresses));
+
+ a = addresses + address_index;
+
+ switch (protocol)
+ {
+#define _(N, i, n, s) \
+ case NAT_PROTOCOL_##N: \
+ ASSERT (a->busy_##n##_port_refcounts[port_host_byte_order] >= 1); \
+ --a->busy_##n##_port_refcounts[port_host_byte_order]; \
+ a->busy_##n##_ports--; \
+ a->busy_##n##_ports_per_thread[thread_index]--; \
+ break;
+ foreach_nat_protocol
+#undef _
+ default : nat_elog_info (nm, "unknown protocol");
+ return;
+ }
+}
+
+void
+nat44_ei_free_session_data_v2 (nat44_ei_main_t *nm, nat44_ei_session_t *s,
+ u32 thread_index, u8 is_ha)
+{
+ clib_bihash_kv_8_8_t kv;
+
+ /* session lookup tables */
+ init_nat_i2o_k (&kv, s);
+ if (clib_bihash_add_del_8_8 (&nm->in2out, &kv, 0))
+ nat_elog_warn (nm, "in2out key del failed");
+ init_nat_o2i_k (&kv, s);
+ if (clib_bihash_add_del_8_8 (&nm->out2in, &kv, 0))
+ nat_elog_warn (nm, "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 (nat44_ei_is_unk_proto_session (s))
+ return;
+
+ if (!is_ha)
+ {
+ /* log NAT event */
+ nat_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);
+ }
+
+ if (nat44_ei_is_session_static (s))
+ return;
+
+ nat44_ei_free_outside_address_and_port (nm->addresses, thread_index,
+ &s->out2in.addr, s->out2in.port,
+ s->nat_proto);
+}
+
+nat44_ei_user_t *
+nat44_ei_user_get_or_create (nat44_ei_main_t *nm, ip4_address_t *addr,
+ u32 fib_index, u32 thread_index)
+{
+ nat44_ei_user_t *u = 0;
+ nat44_ei_user_key_t user_key;
+ clib_bihash_kv_8_8_t kv, value;
+ nat44_ei_main_per_thread_data_t *tnm = &nm->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 (&tnm->user_hash, &kv, &value))
+ {
+ if (pool_elts (tnm->users) >= nm->max_users_per_thread)
+ {
+ vlib_increment_simple_counter (&nm->user_limit_reached, thread_index,
+ 0, 1);
+ nat_elog_warn (nm, "maximum user limit reached");
+ return NULL;
+ }
+ /* no, make a new one */
+ pool_get (tnm->users, u);
+ clib_memset (u, 0, sizeof (*u));
+
+ u->addr.as_u32 = addr->as_u32;
+ u->fib_index = fib_index;
+
+ pool_get (tnm->list_pool, per_user_list_head_elt);
+
+ u->sessions_per_user_list_head_index =
+ per_user_list_head_elt - tnm->list_pool;
+
+ clib_dlist_init (tnm->list_pool, u->sessions_per_user_list_head_index);
+
+ kv.value = u - tnm->users;
+
+ /* add user */
+ if (clib_bihash_add_del_8_8 (&tnm->user_hash, &kv, 1))
+ {
+ nat_elog_warn (nm, "user_hash key add failed");
+ nat44_ei_delete_user_with_no_session (nm, u, thread_index);
+ return NULL;
+ }
+
+ vlib_set_simple_counter (&nm->total_users, thread_index, 0,
+ pool_elts (tnm->users));
+ }
+ else
+ {
+ u = pool_elt_at_index (tnm->users, value.value);
+ }
+
+ return u;
+}
+
+nat44_ei_session_t *
+nat44_ei_session_alloc_or_recycle (nat44_ei_main_t *nm, nat44_ei_user_t *u,
+ u32 thread_index, f64 now)
+{
+ nat44_ei_session_t *s;
+ nat44_ei_main_per_thread_data_t *tnm = &nm->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) >= nm->max_translations_per_user)
+ {
+ oldest_per_user_translation_list_index = clib_dlist_remove_head (
+ tnm->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 (tnm->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 (
+ tnm->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 (tnm->sessions, session_index);
+
+ nat44_ei_free_session_data_v2 (nm, s, thread_index, 0);
+ if (nat44_ei_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 (tnm->sessions, s);
+ clib_memset (s, 0, sizeof (*s));
+
+ /* Create list elts */
+ pool_get (tnm->list_pool, per_user_translation_list_elt);
+ clib_dlist_init (tnm->list_pool,
+ per_user_translation_list_elt - tnm->list_pool);
+
+ per_user_translation_list_elt->value = s - tnm->sessions;
+ s->per_user_index = per_user_translation_list_elt - tnm->list_pool;
+ s->per_user_list_head_index = u->sessions_per_user_list_head_index;
+
+ clib_dlist_addtail (tnm->list_pool, s->per_user_list_head_index,
+ per_user_translation_list_elt - tnm->list_pool);
+
+ s->user_index = u - tnm->users;
+ vlib_set_simple_counter (&nm->total_sessions, thread_index, 0,
+ pool_elts (tnm->sessions));
+ }
+
+ s->ha_last_refreshed = now;
+
+ return s;
+}
+
+void
+nat44_ei_free_session_data (nat44_ei_main_t *nm, nat44_ei_session_t *s,
+ u32 thread_index, u8 is_ha)
+{
+ clib_bihash_kv_8_8_t kv;
+
+ init_nat_i2o_k (&kv, s);
+ if (clib_bihash_add_del_8_8 (&nm->in2out, &kv, 0))
+ nat_elog_warn (nm, "in2out key del failed");
+
+ init_nat_o2i_k (&kv, s);
+ if (clib_bihash_add_del_8_8 (&nm->out2in, &kv, 0))
+ nat_elog_warn (nm, "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);
+
+ nat_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);
+ }
+
+ if (nat44_ei_is_session_static (s))
+ return;
+
+ nat44_ei_free_outside_address_and_port (nm->addresses, thread_index,
+ &s->out2in.addr, s->out2in.port,
+ s->nat_proto);
+}
+
+static_always_inline void
+nat44_ei_user_del_sessions (nat44_ei_user_t *u, u32 thread_index)
+{
+ dlist_elt_t *elt;
+ nat44_ei_session_t *s;
+
+ nat44_ei_main_t *nm = &nat44_ei_main;
+ nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index];
+
+ // get head
+ elt =
+ pool_elt_at_index (tnm->list_pool, u->sessions_per_user_list_head_index);
+ // get first element
+ elt = pool_elt_at_index (tnm->list_pool, elt->next);
+
+ while (elt->value != ~0)
+ {
+ s = pool_elt_at_index (tnm->sessions, elt->value);
+ elt = pool_elt_at_index (tnm->list_pool, elt->next);
+
+ nat44_ei_free_session_data (nm, s, thread_index, 0);
+ nat44_ei_delete_session (nm, s, thread_index);
+ }
+}
+
+int
+nat44_ei_user_del (ip4_address_t *addr, u32 fib_index)
+{
+ int rv = 1;
+
+ nat44_ei_main_t *nm = &nat44_ei_main;
+ nat44_ei_main_per_thread_data_t *tnm;
+
+ nat44_ei_user_key_t user_key;
+ clib_bihash_kv_8_8_t kv, value;
+
+ user_key.addr.as_u32 = addr->as_u32;
+ user_key.fib_index = fib_index;
+ kv.key = user_key.as_u64;
+
+ if (nm->num_workers > 1)
+ {
+ vec_foreach (tnm, nm->per_thread_data)
+ {
+ if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value))
+ {
+ nat44_ei_user_del_sessions (
+ pool_elt_at_index (tnm->users, value.value),
+ tnm->thread_index);
+ rv = 0;
+ break;
+ }
+ }
+ }
+ else
+ {
+ tnm = vec_elt_at_index (nm->per_thread_data, nm->num_workers);
+ if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value))
+ {
+ nat44_ei_user_del_sessions (
+ pool_elt_at_index (tnm->users, value.value), tnm->thread_index);
+ rv = 0;
+ }
+ }
+ return rv;
+}
+
+void
+nat44_ei_static_mapping_del_sessions (nat44_ei_main_t *nm,
+ nat44_ei_main_per_thread_data_t *tnm,
+ nat44_ei_user_key_t u_key, int addr_only,
+ ip4_address_t e_addr, u16 e_port)
+{
+ clib_bihash_kv_8_8_t kv, value;
+ kv.key = u_key.as_u64;
+ u64 user_index;
+ dlist_elt_t *head, *elt;
+ nat44_ei_user_t *u;
+ nat44_ei_session_t *s;
+ u32 elt_index, head_index, ses_index;
+
+ if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value))
+ {
+ user_index = value.value;
+ u = pool_elt_at_index (tnm->users, user_index);
+ if (u->nstaticsessions)
+ {
+ head_index = u->sessions_per_user_list_head_index;
+ head = pool_elt_at_index (tnm->list_pool, head_index);
+ elt_index = head->next;
+ elt = pool_elt_at_index (tnm->list_pool, elt_index);
+ ses_index = elt->value;
+ while (ses_index != ~0)
+ {
+ s = pool_elt_at_index (tnm->sessions, ses_index);
+ elt = pool_elt_at_index (tnm->list_pool, elt->next);
+ ses_index = elt->value;
+
+ if (!addr_only)
+ {
+ if ((s->out2in.addr.as_u32 != e_addr.as_u32) ||
+ (s->out2in.port != e_port))
+ continue;
+ }
+
+ if (!nat44_ei_is_session_static (s))
+ continue;
+
+ nat44_ei_free_session_data_v2 (nm, s, tnm - nm->per_thread_data,
+ 0);
+ nat44_ei_delete_session (nm, s, tnm - nm->per_thread_data);
+
+ if (!addr_only)
+ break;
+ }
+ }
+ }
+}
+
+u32
+nat44_ei_get_in2out_worker_index (ip4_header_t *ip0, u32 rx_fib_index0,
+ u8 is_output)
+{
+ nat44_ei_main_t *nm = &nat44_ei_main;
+ u32 next_worker_index = 0;
+ u32 hash;
+
+ next_worker_index = nm->first_worker_index;
+ hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) +
+ (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >> 24);
+
+ if (PREDICT_TRUE (is_pow2 (_vec_len (nm->workers))))
+ next_worker_index += nm->workers[hash & (_vec_len (nm->workers) - 1)];
+ else
+ next_worker_index += nm->workers[hash % _vec_len (nm->workers)];
+
+ return next_worker_index;
+}
+
+u32
+nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0,
+ u32 rx_fib_index0, u8 is_output)
+{
+ nat44_ei_main_t *nm = &nat44_ei_main;
+ udp_header_t *udp;
+ u16 port;
+ clib_bihash_kv_8_8_t kv, value;
+ nat44_ei_static_mapping_t *m;
+ u32 proto;
+ u32 next_worker_index = 0;
+
+ /* first try static mappings without port */
+ if (PREDICT_FALSE (pool_elts (nm->static_mappings)))
+ {
+ init_nat_k (&kv, ip0->dst_address, 0, rx_fib_index0, 0);
+ if (!clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv,
+ &value))
+ {
+ m = pool_elt_at_index (nm->static_mappings, value.value);
+ return m->workers[0];
+ }
+ }
+
+ proto = ip_proto_to_nat_proto (ip0->protocol);
+ udp = ip4_next_header (ip0);
+ port = udp->dst_port;
+
+ /* unknown protocol */
+ if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER))
+ {
+ /* use current thread */