+static u32
+nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index)
+{
+ snat_main_t *sm = &snat_main;
+ clib_bihash_kv_8_8_t kv, value;
+ u32 proto, next_worker_index = 0;
+ udp_header_t *udp;
+ u16 port;
+ snat_static_mapping_t *m;
+ u32 hash;
+
+ /* first try static mappings without port */
+ if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
+ {
+ make_sm_kv (&kv, &ip->dst_address, 0, rx_fib_index, 0);
+ 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];
+ }
+ }
+
+ proto = ip_proto_to_snat_proto (ip->protocol);
+
+ /* unknown protocol */
+ if (PREDICT_FALSE (proto == ~0))
+ {
+ /* use current thread */
+ return vlib_get_thread_index ();
+ }
+
+ udp = ip4_next_header (ip);
+ port = udp->dst_port;
+
+ if (PREDICT_FALSE (ip->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)))
+ {
+ make_sm_kv (&kv, &ip->dst_address, proto, rx_fib_index,
+ clib_net_to_host_u16 (port));
+ if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
+ {
+ m = pool_elt_at_index (sm->static_mappings, value.value);
+ if (!vec_len(m->locals))
+ return m->workers[0];
+
+ 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 (m->workers))))
+ return m->workers[hash & (_vec_len (m->workers) - 1)];
+ else
+ return m->workers[hash % _vec_len (m->workers)];
+ }
+ }
+
+ /* 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;
+}
+