+ nat_reass_ip4_t *reass;
+ 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 (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))
+ {
+ /* use current thread */
+ return vlib_get_thread_index ();
+ }
+
+ if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_ICMP))
+ {
+ icmp46_header_t *icmp = (icmp46_header_t *) udp;
+ icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
+ if (!icmp_is_error_message (icmp))
+ port = echo->identifier;
+ else
+ {
+ ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
+ proto = ip_proto_to_snat_proto (inner_ip->protocol);
+ void *l4_header = ip4_next_header (inner_ip);
+ switch (proto)
+ {
+ case SNAT_PROTOCOL_ICMP:
+ icmp = (icmp46_header_t *) l4_header;
+ echo = (icmp_echo_header_t *) (icmp + 1);
+ port = echo->identifier;
+ break;
+ case SNAT_PROTOCOL_UDP:
+ case SNAT_PROTOCOL_TCP:
+ port = ((tcp_udp_header_t *) l4_header)->src_port;
+ break;
+ default:
+ return vlib_get_thread_index ();
+ }
+ }
+ }
+
+ /* try static mappings with port */
+ 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);
+ return m->workers[0];
+ }
+ }
+
+ /* worker by outside port */
+ next_worker_index = sm->first_worker_index;
+ next_worker_index +=
+ sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
+ return next_worker_index;
+}
+
+static u32
+nat44_ed_get_worker_in2out_cb (ip4_header_t * ip, u32 rx_fib_index,
+ u8 is_output)
+{
+ snat_main_t *sm = &snat_main;
+ u32 next_worker_index = sm->first_worker_index;
+ u32 hash;
+
+ clib_bihash_kv_16_8_t kv16, value16;
+ snat_main_per_thread_data_t *tsm;
+ udp_header_t *udp;
+
+ if (PREDICT_FALSE (is_output))
+ {
+ u32 fib_index = sm->outside_fib_index;
+ nat_outside_fib_t *outside_fib;
+ fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_addr = {
+ .ip4.as_u32 = ip->dst_address.as_u32,
+ }
+ ,
+ };
+
+ udp = ip4_next_header (ip);
+
+ switch (vec_len (sm->outside_fibs))
+ {
+ case 0:
+ fib_index = sm->outside_fib_index;
+ break;
+ case 1:
+ 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)
+ {
+ fib_index = outside_fib->fib_index;
+ break;
+ }
+ }
+ }
+ /* *INDENT-ON* */
+ break;
+ }
+
+ make_ed_kv (&kv16, &ip->src_address, &ip->dst_address,
+ ip->protocol, fib_index, udp->src_port, udp->dst_port);
+
+ /* *INDENT-OFF* */
+ vec_foreach (tsm, sm->per_thread_data)
+ {
+ if (PREDICT_TRUE (!clib_bihash_search_16_8 (&tsm->out2in_ed,
+ &kv16, &value16)))
+ {
+ next_worker_index += tsm->thread_index;
+
+ nat_elog_debug_handoff (
+ "HANDOFF IN2OUT-OUTPUT-FEATURE (session)",
+ next_worker_index, fib_index,
+ clib_net_to_host_u32 (ip->src_address.as_u32),
+ clib_net_to_host_u32 (ip->dst_address.as_u32));
+
+ return next_worker_index;
+ }
+ }
+ /* *INDENT-ON* */
+ }
+
+ hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) +
+ (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24);
+
+ if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
+ next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
+ else
+ next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
+
+ if (PREDICT_TRUE (!is_output))
+ {
+ nat_elog_debug_handoff ("HANDOFF IN2OUT",
+ next_worker_index, rx_fib_index,
+ clib_net_to_host_u32 (ip->src_address.as_u32),
+ clib_net_to_host_u32 (ip->dst_address.as_u32));
+ }
+ else
+ {
+ nat_elog_debug_handoff ("HANDOFF IN2OUT-OUTPUT-FEATURE",
+ next_worker_index, rx_fib_index,
+ clib_net_to_host_u32 (ip->src_address.as_u32),
+ clib_net_to_host_u32 (ip->dst_address.as_u32));
+ }
+
+ return next_worker_index;
+}
+
+static u32
+nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index,
+ u8 is_output)
+{
+ snat_main_t *sm = &snat_main;
+ clib_bihash_kv_8_8_t kv, value;
+ clib_bihash_kv_16_8_t kv16, value16;
+ snat_main_per_thread_data_t *tsm;
+
+ u32 proto, next_worker_index = 0;
+ udp_header_t *udp;
+ u16 port;
+ snat_static_mapping_t *m;
+ u32 hash;
+
+ proto = ip_proto_to_snat_proto (ip->protocol);
+
+ if (PREDICT_TRUE (proto == SNAT_PROTOCOL_UDP || proto == SNAT_PROTOCOL_TCP))
+ {
+ udp = ip4_next_header (ip);
+
+ make_ed_kv (&kv16, &ip->dst_address, &ip->src_address,
+ ip->protocol, rx_fib_index, udp->dst_port, udp->src_port);
+
+ /* *INDENT-OFF* */
+ vec_foreach (tsm, sm->per_thread_data)
+ {
+ if (PREDICT_TRUE (!clib_bihash_search_16_8 (&tsm->out2in_ed,
+ &kv16, &value16)))
+ {
+ next_worker_index = sm->first_worker_index + tsm->thread_index;
+ nat_elog_debug_handoff ("HANDOFF OUT2IN (session)",
+ next_worker_index, rx_fib_index,
+ clib_net_to_host_u32 (ip->src_address.as_u32),
+ clib_net_to_host_u32 (ip->dst_address.as_u32));
+ return next_worker_index;
+ }
+ }
+ /* *INDENT-ON* */
+ }
+ else if (proto == SNAT_PROTOCOL_ICMP)
+ {
+ nat_ed_ses_key_t key;
+
+ if (!get_icmp_o2i_ed_key (ip, &key))
+ {
+
+ key.fib_index = rx_fib_index;
+ kv16.key[0] = key.as_u64[0];
+ kv16.key[1] = key.as_u64[1];
+
+ /* *INDENT-OFF* */
+ vec_foreach (tsm, sm->per_thread_data)
+ {
+ if (PREDICT_TRUE (!clib_bihash_search_16_8 (&tsm->out2in_ed,
+ &kv16, &value16)))
+ {
+ next_worker_index = sm->first_worker_index +
+ tsm->thread_index;
+ nat_elog_debug_handoff ("HANDOFF OUT2IN (session)",
+ next_worker_index, rx_fib_index,
+ clib_net_to_host_u32 (ip->src_address.as_u32),
+ clib_net_to_host_u32 (ip->dst_address.as_u32));
+ return next_worker_index;
+ }
+ }
+ /* *INDENT-ON* */
+ }
+ }
+
+ /* first try static mappings without port */
+ if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
+ {
+ make_sm_kv (&kv, &ip->dst_address, 0, 0, 0);
+ if (!clib_bihash_search_8_8
+ (&sm->static_mapping_by_external, &kv, &value))