+static int
+nat_ed_alloc_addr_and_port (snat_main_t * sm, u32 rx_fib_index,
+ u32 nat_proto, u32 thread_index,
+ ip4_address_t r_addr, u16 r_port, u8 proto,
+ u16 port_per_thread, u32 snat_thread_index,
+ snat_session_t * s,
+ ip4_address_t * outside_addr,
+ u16 * outside_port,
+ clib_bihash_kv_16_8_t * out2in_ed_kv)
+{
+ int i;
+ snat_address_t *a, *ga = 0;
+ snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
+
+ const u16 port_thread_offset = (port_per_thread * snat_thread_index) + 1024;
+
+ for (i = 0; i < vec_len (sm->addresses); i++)
+ {
+ a = sm->addresses + i;
+ switch (nat_proto)
+ {
+#define _(N, j, n, unused) \
+ case NAT_PROTOCOL_##N: \
+ if (a->fib_index == rx_fib_index) \
+ { \
+ /* first try port suggested by caller */ \
+ u16 port = clib_net_to_host_u16 (*outside_port); \
+ u16 port_offset = port - port_thread_offset; \
+ if (port <= port_thread_offset || \
+ port > port_thread_offset + port_per_thread) \
+ { \
+ /* need to pick a different port, suggested port doesn't fit in \
+ * this thread's port range */ \
+ port_offset = snat_random_port (0, port_per_thread - 1); \
+ port = port_thread_offset + port_offset; \
+ } \
+ u16 attempts = ED_PORT_ALLOC_ATTEMPTS; \
+ do \
+ { \
+ init_ed_kv (out2in_ed_kv, a->addr, clib_host_to_net_u16 (port), \
+ r_addr, r_port, s->out2in.fib_index, proto, \
+ thread_index, s - tsm->sessions); \
+ int rv = clib_bihash_add_del_16_8 (&sm->out2in_ed, out2in_ed_kv, \
+ 2 /* is_add */); \
+ if (0 == rv) \
+ { \
+ ++a->busy_##n##_port_refcounts[port]; \
+ a->busy_##n##_ports_per_thread[thread_index]++; \
+ a->busy_##n##_ports++; \
+ *outside_addr = a->addr; \
+ *outside_port = clib_host_to_net_u16 (port); \
+ return 0; \
+ } \
+ port_offset = snat_random_port (0, port_per_thread - 1); \
+ port = port_thread_offset + port_offset; \
+ --attempts; \
+ } \
+ while (attempts > 0); \
+ } \
+ else if (a->fib_index == ~0) \
+ { \
+ ga = a; \
+ } \
+ break;
+
+ foreach_nat_protocol;
+ default:
+ nat_elog_info ("unknown protocol");
+ return 1;
+ }
+ }
+
+ if (ga)
+ {
+ /* fake fib_index to reuse macro */
+ rx_fib_index = ~0;
+ a = ga;
+ switch (nat_proto)
+ {
+ foreach_nat_protocol;
+ default:
+ nat_elog_info ("unknown protocol");
+ return 1;
+ }
+ }
+
+#undef _
+
+ /* Totally out of translations to use... */
+ nat_ipfix_logging_addresses_exhausted (thread_index, 0);
+ return 1;
+}
+
+static_always_inline u32
+nat_outside_fib_index_lookup (snat_main_t * sm, ip4_address_t addr)
+{
+ fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
+ nat_outside_fib_t *outside_fib;
+ fib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_addr = {.ip4.as_u32 = addr.as_u32,}
+ ,
+ };
+ // TODO: multiple vrfs none can resolve addr
+ /* *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)
+ {
+ return outside_fib->fib_index;
+ }
+ }
+ }
+ /* *INDENT-ON* */
+ return ~0;
+}
+