+u32
+nat64_get_worker_in2out (ip6_address_t * addr)
+{
+ nat64_main_t *nm = &nat64_main;
+ snat_main_t *sm = nm->sm;
+ u32 next_worker_index = nm->sm->first_worker_index;
+ u32 hash;
+
+#ifdef clib_crc32c_uses_intrinsics
+ hash = clib_crc32c ((u8 *) addr->as_u32, 16);
+#else
+ u64 tmp = addr->as_u64[0] ^ addr->as_u64[1];
+ hash = clib_xxhash (tmp);
+#endif
+
+ 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)];
+
+ return next_worker_index;
+}
+
+u32
+nat64_get_worker_out2in (ip4_header_t * ip)
+{
+ nat64_main_t *nm = &nat64_main;
+ snat_main_t *sm = nm->sm;
+ udp_header_t *udp;
+ u16 port;
+ u32 proto;
+
+ proto = ip_proto_to_snat_proto (ip->protocol);
+ udp = ip4_next_header (ip);
+ port = udp->dst_port;
+
+ /* fragments */
+ if (PREDICT_FALSE (ip4_is_fragment (ip)))
+ {
+ if (PREDICT_FALSE (nat_reass_is_drop_frag (0)))
+ return vlib_get_thread_index ();
+
+ if (PREDICT_TRUE (!ip4_is_first_fragment (ip)))
+ {
+ nat_reass_ip4_t *reass;
+
+ reass = nat_ip4_reass_find (ip->src_address, ip->dst_address,
+ ip->fragment_id, ip->protocol);
+
+ if (reass && (reass->thread_index != (u32) ~ 0))
+ return reass->thread_index;
+ else
+ return vlib_get_thread_index ();
+ }
+ }
+
+ /* unknown protocol */
+ if (PREDICT_FALSE (proto == ~0))
+ {
+ nat64_db_t *db;
+ ip46_address_t daddr;
+ nat64_db_bib_entry_t *bibe;
+
+ memset (&daddr, 0, sizeof (daddr));
+ daddr.ip4.as_u32 = ip->dst_address.as_u32;
+
+ /* *INDENT-OFF* */
+ vec_foreach (db, nm->db)
+ {
+ bibe = nat64_db_bib_entry_find (db, &daddr, 0, ip->protocol, 0, 0);
+ if (bibe)
+ return (u32) (db - nm->db);
+ }
+ /* *INDENT-ON* */
+ return vlib_get_thread_index ();
+ }
+
+ /* ICMP */
+ 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 ();
+ }
+ }
+ }
+
+ /* worker by outside port (TCP/UDP) */
+ port = clib_net_to_host_u16 (port);
+ if (port > 1024)
+ return nm->sm->first_worker_index + ((port - 1024) / sm->port_per_thread);
+
+ return vlib_get_thread_index ();
+}
+