X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fsnat%2Fsnat.c;h=750cc925d7f120b3a7147da8af7081aeb7a98766;hb=3b46cba8f4e909bc363403c6c92215159abb2f11;hp=a1236cf74f4cad1c1516a570bc96d7c5339d1eb7;hpb=8bf68e858a30a9c04329668d2b5dd67e9ad6f5af;p=vpp.git diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c index a1236cf74f4..750cc925d7f 100644 --- a/src/plugins/snat/snat.c +++ b/src/plugins/snat/snat.c @@ -21,10 +21,14 @@ #include #include #include +#include +#include +#include #include #include #include +#include snat_main_t snat_main; @@ -126,27 +130,11 @@ VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = { .runs_before = VNET_FEATURES ("ip4-lookup"), }; - -/* - * This routine exists to convince the vlib plugin framework that - * we haven't accidentally copied a random .dll into the plugin directory. - * - * Also collects global variable pointers passed from the vpp engine - */ - -clib_error_t * -vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h, - int from_early_init) -{ - snat_main_t * sm = &snat_main; - clib_error_t * error = 0; - - sm->vlib_main = vm; - sm->vnet_main = h->vnet_main; - sm->ethernet_main = h->ethernet_main; - - return error; -} +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, +}; +/* *INDENT-ON* */ /*$$$$$ move to an installed header file */ #if (1 || CLIB_DEBUG > 0) /* "trust, but verify" */ @@ -210,9 +198,53 @@ bad_tx_sw_if_index: \ #endif /* CLIB_DEBUG > 0 */ +/** + * @brief Add/del NAT address to FIB. + * + * Add the external NAT address to the FIB as receive entries. This ensures + * that VPP will reply to ARP for this address and we don't need to enable + * proxy ARP on the outside interface. + * + * @param addr IPv4 address. + * @param sw_if_index Interface. + * @param is_add If 0 delete, otherwise add. + */ +static void +snat_add_del_addr_to_fib (ip4_address_t * addr, u32 sw_if_index, int is_add) +{ + fib_prefix_t prefix = { + .fp_len = 32, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_addr = { + .ip4.as_u32 = addr->as_u32, + }, + }; + u32 fib_index = ip4_fib_table_get_index_for_sw_if_index(sw_if_index); + + if (is_add) + fib_table_entry_update_one_path(fib_index, + &prefix, + FIB_SOURCE_PLUGIN_HI, + (FIB_ENTRY_FLAG_CONNECTED | + FIB_ENTRY_FLAG_LOCAL | + FIB_ENTRY_FLAG_EXCLUSIVE), + FIB_PROTOCOL_IP4, + NULL, + sw_if_index, + ~0, + 1, + NULL, + FIB_ROUTE_PATH_FLAG_NONE); + else + fib_table_entry_delete(fib_index, + &prefix, + FIB_SOURCE_PLUGIN_HI); +} + void snat_add_address (snat_main_t *sm, ip4_address_t *addr) { snat_address_t * ap; + snat_interface_t *i; /* Check if address already exists */ vec_foreach (ap, sm->addresses) @@ -223,7 +255,20 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr) vec_add2 (sm->addresses, ap, 1); ap->addr = *addr; - clib_bitmap_alloc (ap->busy_port_bitmap, 65535); +#define _(N, i, n, s) \ + clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535); + foreach_snat_protocol +#undef _ + + /* Add external address to FIB */ + pool_foreach (i, sm->interfaces, + ({ + if (i->is_inside) + continue; + + snat_add_del_addr_to_fib(addr, i->sw_if_index, 1); + break; + })); } static int is_snat_address_used_in_static_mapping (snat_main_t *sm, @@ -239,74 +284,6 @@ static int is_snat_address_used_in_static_mapping (snat_main_t *sm, return 0; } -int snat_del_address (snat_main_t *sm, ip4_address_t addr) -{ - snat_address_t *a = 0; - snat_session_t *ses; - u32 *ses_to_be_removed = 0, *ses_index; - clib_bihash_kv_8_8_t kv, value; - snat_user_key_t user_key; - snat_user_t *u; - snat_main_per_thread_data_t *tsm; - - int i; - - /* Find SNAT address */ - for (i=0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == addr.as_u32) - { - a = sm->addresses + i; - break; - } - } - if (!a) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - /* Check if address is used in some static mapping */ - if (is_snat_address_used_in_static_mapping(sm, addr)) - { - clib_warning ("address used in static mapping"); - return VNET_API_ERROR_UNSPECIFIED; - } - - /* Delete sessions using address */ - if (a->busy_ports) - { - vec_foreach (tsm, sm->per_thread_data) - { - pool_foreach (ses, tsm->sessions, ({ - if (ses->out2in.addr.as_u32 == addr.as_u32) - { - vec_add1 (ses_to_be_removed, ses - tsm->sessions); - kv.key = ses->in2out.as_u64; - clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0); - kv.key = ses->out2in.as_u64; - clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0); - clib_dlist_remove (tsm->list_pool, ses->per_user_index); - user_key.addr = ses->in2out.addr; - user_key.fib_index = ses->in2out.fib_index; - kv.key = user_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) - { - u = pool_elt_at_index (tsm->users, value.value); - u->nsessions--; - } - } - })); - - vec_foreach (ses_index, ses_to_be_removed) - pool_put_index (tsm->sessions, ses_index[0]); - - vec_free (ses_to_be_removed); - } - } - - vec_del1 (sm->addresses, i); - - return 0; -} - static void increment_v4_address (ip4_address_t * a) { u32 v; @@ -315,6 +292,30 @@ static void increment_v4_address (ip4_address_t * a) a->as_u32 = clib_host_to_net_u32(v); } +static void +snat_add_static_mapping_when_resolved (snat_main_t * sm, + ip4_address_t l_addr, + u16 l_port, + u32 sw_if_index, + u16 e_port, + u32 vrf_id, + snat_protocol_t proto, + int addr_only, + int is_add) +{ + snat_static_map_resolve_t *rp; + + vec_add2 (sm->to_resolve, rp, 1); + rp->l_addr.as_u32 = l_addr.as_u32; + rp->l_port = l_port; + rp->sw_if_index = sw_if_index; + rp->e_port = e_port; + rp->vrf_id = vrf_id; + rp->proto = proto; + rp->addr_only = addr_only; + rp->is_add = is_add; +} + /** * @brief Add static mapping. * @@ -326,21 +327,23 @@ static void increment_v4_address (ip4_address_t * a) * @param e_port External port number. * @param vrf_id VRF ID. * @param addr_only If 0 address port and pair mapping, otherwise address only. + * @param sw_if_index External port instead of specific IP address. * @param is_add If 0 delete static mapping, otherwise add. * * @returns */ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, u16 l_port, u16 e_port, u32 vrf_id, int addr_only, - int is_add) + u32 sw_if_index, snat_protocol_t proto, int is_add) { snat_main_t * sm = &snat_main; snat_static_mapping_t *m; - snat_static_mapping_key_t m_key; + snat_session_key_t m_key; clib_bihash_kv_8_8_t kv, value; snat_address_t *a = 0; u32 fib_index = ~0; uword * p; + snat_interface_t *interface; int i; /* If outside FIB index is not resolved yet */ @@ -352,8 +355,30 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, sm->outside_fib_index = p[0]; } + /* If the external address is a specific interface address */ + if (sw_if_index != ~0) + { + ip4_address_t * first_int_addr; + + /* Might be already set... */ + first_int_addr = ip4_interface_first_address + (sm->ip4_main, sw_if_index, 0 /* just want the address*/); + + /* DHCP resolution required? */ + if (first_int_addr == 0) + { + snat_add_static_mapping_when_resolved + (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto, + addr_only, is_add); + return 0; + } + else + e_addr.as_u32 = first_int_addr->as_u32; + } + m_key.addr = e_addr; m_key.port = addr_only ? 0 : e_port; + m_key.protocol = addr_only ? 0 : proto; m_key.fib_index = sm->outside_fib_index; kv.key = m_key.as_u64; if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) @@ -401,12 +426,22 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, { a = sm->addresses + i; /* External port must be unused */ - if (clib_bitmap_get_no_check (a->busy_port_bitmap, e_port)) - return VNET_API_ERROR_INVALID_VALUE; - clib_bitmap_set_no_check (a->busy_port_bitmap, e_port, 1); - if (e_port > 1024) - a->busy_ports++; - + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \ + return VNET_API_ERROR_INVALID_VALUE; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \ + if (e_port > 1024) \ + a->busy_##n##_ports++; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown_protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } break; } } @@ -426,10 +461,12 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, { m->local_port = l_port; m->external_port = e_port; + m->proto = proto; } m_key.addr = m->local_addr; m_key.port = m->local_port; + m_key.protocol = m->proto; m_key.fib_index = m->fib_index; kv.key = m_key.as_u64; kv.value = m - sm->static_mappings; @@ -446,7 +483,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, if (sm->workers) { snat_user_key_t w_key0; - snat_static_mapping_key_t w_key1; + snat_worker_key_t w_key1; w_key0.addr = m->local_addr; w_key0.fib_index = m->fib_index; @@ -484,9 +521,20 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) { a = sm->addresses + i; - clib_bitmap_set_no_check (a->busy_port_bitmap, e_port, 0); - a->busy_ports--; - + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \ + if (e_port > 1024) \ + a->busy_##n##_ports--; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown_protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } break; } } @@ -494,6 +542,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, m_key.addr = m->local_addr; m_key.port = m->local_port; + m_key.protocol = m->proto; m_key.fib_index = m->fib_index; kv.key = m_key.as_u64; clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0); @@ -550,6 +599,14 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, continue; } + /* log NAT event */ + snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + value.key = s->in2out.as_u64; clib_bihash_add_del_8_8 (&sm->in2out, &value, 0); value.key = s->out2in.as_u64; @@ -576,6 +633,119 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, pool_put (sm->static_mappings, m); } + if (!addr_only) + return 0; + + /* Add/delete external address to FIB */ + pool_foreach (interface, sm->interfaces, + ({ + if (interface->is_inside) + continue; + + snat_add_del_addr_to_fib(&e_addr, interface->sw_if_index, is_add); + break; + })); + + return 0; +} + +int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) +{ + snat_address_t *a = 0; + snat_session_t *ses; + u32 *ses_to_be_removed = 0, *ses_index; + clib_bihash_kv_8_8_t kv, value; + snat_user_key_t user_key; + snat_user_t *u; + snat_main_per_thread_data_t *tsm; + snat_static_mapping_t *m; + snat_interface_t *interface; + int i; + + /* Find SNAT address */ + for (i=0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == addr.as_u32) + { + a = sm->addresses + i; + break; + } + } + if (!a) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (delete_sm) + { + pool_foreach (m, sm->static_mappings, + ({ + if (m->external_addr.as_u32 == addr.as_u32) + (void) snat_add_static_mapping (m->local_addr, m->external_addr, + m->local_port, m->external_port, + m->vrf_id, m->addr_only, ~0, + m->proto, 0); + })); + } + else + { + /* Check if address is used in some static mapping */ + if (is_snat_address_used_in_static_mapping(sm, addr)) + { + clib_warning ("address used in static mapping"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + + /* Delete sessions using address */ + if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports) + { + vec_foreach (tsm, sm->per_thread_data) + { + pool_foreach (ses, tsm->sessions, ({ + if (ses->out2in.addr.as_u32 == addr.as_u32) + { + /* log NAT event */ + snat_ipfix_logging_nat44_ses_delete(ses->in2out.addr.as_u32, + ses->out2in.addr.as_u32, + ses->in2out.protocol, + ses->in2out.port, + ses->out2in.port, + ses->in2out.fib_index); + vec_add1 (ses_to_be_removed, ses - tsm->sessions); + kv.key = ses->in2out.as_u64; + clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0); + kv.key = ses->out2in.as_u64; + clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0); + clib_dlist_remove (tsm->list_pool, ses->per_user_index); + user_key.addr = ses->in2out.addr; + user_key.fib_index = ses->in2out.fib_index; + kv.key = user_key.as_u64; + if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + { + u = pool_elt_at_index (tsm->users, value.value); + u->nsessions--; + } + } + })); + + vec_foreach (ses_index, ses_to_be_removed) + pool_put_index (tsm->sessions, ses_index[0]); + + vec_free (ses_to_be_removed); + } + } + + vec_del1 (sm->addresses, i); + + /* Delete external address from FIB */ + pool_foreach (interface, sm->interfaces, + ({ + if (interface->is_inside) + continue; + + snat_add_del_addr_to_fib(&addr, interface->sw_if_index, 0); + break; + })); + return 0; } @@ -584,6 +754,8 @@ static int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) snat_main_t *sm = &snat_main; snat_interface_t *i; const char * feature_name; + snat_address_t * ap; + snat_static_mapping_t * m; if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) feature_name = is_inside ? "snat-in2out-fast" : "snat-out2in-fast"; @@ -613,7 +785,7 @@ static int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) else return VNET_API_ERROR_VALUE_EXIST; - return 0; + goto fib; } })); @@ -624,6 +796,22 @@ static int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) i->sw_if_index = sw_if_index; i->is_inside = is_inside; + /* Add/delete external addresses to FIB */ +fib: + if (is_inside) + return 0; + + vec_foreach (ap, sm->addresses) + snat_add_del_addr_to_fib(&ap->addr, sw_if_index, !is_del); + + pool_foreach (m, sm->static_mappings, + ({ + if (!(m->addr_only)) + continue; + + snat_add_del_addr_to_fib(&m->external_addr, sw_if_index, !is_del); + })); + return 0; } @@ -691,7 +879,7 @@ vl_api_snat_add_address_range_t_handler if (mp->is_add) snat_add_address (sm, &this_addr); else - rv = snat_del_address (sm, this_addr); + rv = snat_del_address (sm, this_addr, 0); if (rv) goto send_reply; @@ -845,8 +1033,9 @@ vl_api_snat_add_static_mapping_t_handler vl_api_snat_add_static_mapping_reply_t * rmp; ip4_address_t local_addr, external_addr; u16 local_port = 0, external_port = 0; - u32 vrf_id; + u32 vrf_id, external_sw_if_index; int rv = 0; + snat_protocol_t proto; if (mp->is_ip4 != 1) { @@ -862,10 +1051,12 @@ vl_api_snat_add_static_mapping_t_handler external_port = clib_net_to_host_u16 (mp->external_port); } vrf_id = clib_net_to_host_u32 (mp->vrf_id); + external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); + proto = ip_proto_to_snat_proto (mp->protocol); rv = snat_add_static_mapping(local_addr, external_addr, local_port, external_port, vrf_id, mp->addr_only, - mp->is_add); + external_sw_if_index, proto, mp->is_add); send_reply: REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY); @@ -877,7 +1068,8 @@ static void *vl_api_snat_add_static_mapping_t_print u8 * s; s = format (0, "SCRIPT: snat_add_static_mapping "); - s = format (s, "local_addr %U external_addr %U ", + s = format (s, "protocol %d local_addr %U external_addr %U ", + mp->protocol, format_ip4_address, mp->local_ip_address, format_ip4_address, mp->external_ip_address); @@ -889,6 +1081,9 @@ static void *vl_api_snat_add_static_mapping_t_print if (mp->vrf_id != ~0) s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id)); + if (mp->external_sw_if_index != ~0) + s = format (s, "external_sw_if_index %d", + clib_net_to_host_u32 (mp->external_sw_if_index)); FINISH; } @@ -909,6 +1104,7 @@ send_snat_static_mapping_details rmp->local_port = htons (m->local_port); rmp->external_port = htons (m->external_port); rmp->vrf_id = htonl (m->vrf_id); + rmp->protocol = snat_proto_to_ip_proto (m->proto); rmp->context = context; vl_msg_api_send_shmem (q, (u8 *) & rmp); @@ -1172,6 +1368,37 @@ static void *vl_api_snat_interface_addr_dump_t_print FINISH; } +static void +vl_api_snat_ipfix_enable_disable_t_handler +(vl_api_snat_ipfix_enable_disable_t * mp) +{ + snat_main_t * sm = &snat_main; + vl_api_snat_ipfix_enable_disable_reply_t * rmp; + int rv = 0; + + rv = snat_ipfix_logging_enable_disable(mp->enable, + clib_host_to_net_u32 (mp->domain_id), + clib_host_to_net_u16 (mp->src_port)); + + REPLY_MACRO (VL_API_SNAT_IPFIX_ENABLE_DISABLE_REPLY); +} + +static void *vl_api_snat_ipfix_enable_disable_t_print +(vl_api_snat_ipfix_enable_disable_t *mp, void * handle) +{ + u8 * s; + + s = format (0, "SCRIPT: snat_ipfix_enable_disable "); + if (mp->domain_id) + s = format (s, "domain %d ", clib_net_to_host_u32 (mp->domain_id)); + if (mp->src_port) + s = format (s, "src_port %d ", clib_net_to_host_u16 (mp->src_port)); + if (!mp->enable) + s = format (s, "disable "); + + FINISH; +} + /* List of message types that this plugin understands */ #define foreach_snat_plugin_api_msg \ _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range) \ @@ -1185,7 +1412,8 @@ _(SNAT_INTERFACE_DUMP, snat_interface_dump) \ _(SNAT_SET_WORKERS, snat_set_workers) \ _(SNAT_WORKER_DUMP, snat_worker_dump) \ _(SNAT_ADD_DEL_INTERFACE_ADDR, snat_add_del_interface_addr) \ -_(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_dump) +_(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_dump) \ +_(SNAT_IPFIX_ENABLE_DISABLE, snat_ipfix_enable_disable) /* Set up the API message handling tables */ static clib_error_t * @@ -1228,6 +1456,7 @@ static void plugin_custom_dump_configure (snat_main_t * sm) #undef _ } + static void snat_ip4_add_del_interface_address_cb (ip4_main_t * im, uword opaque, @@ -1303,6 +1532,9 @@ static clib_error_t * snat_init (vlib_main_t * vm) vec_add1 (im->add_del_interface_address_callbacks, cb4); + /* Init IPFIX logging */ + snat_ipfix_logging_init(vm); + return error; } @@ -1319,11 +1551,22 @@ void snat_free_outside_address_and_port (snat_main_t * sm, a = sm->addresses + address_index; - ASSERT (clib_bitmap_get_no_check (a->busy_port_bitmap, - port_host_byte_order) == 1); - - clib_bitmap_set_no_check (a->busy_port_bitmap, port_host_byte_order, 0); - a->busy_ports--; + switch (k->protocol) + { +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ + port_host_byte_order) == 1); \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ + port_host_byte_order, 0); \ + a->busy_##n##_ports--; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown_protocol"); + return; + } } /** @@ -1344,7 +1587,7 @@ int snat_static_mapping_match (snat_main_t * sm, { clib_bihash_kv_8_8_t kv, value; snat_static_mapping_t *m; - snat_static_mapping_key_t m_key; + snat_session_key_t m_key; clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local; if (by_external) @@ -1352,6 +1595,7 @@ int snat_static_mapping_match (snat_main_t * sm, m_key.addr = match.addr; m_key.port = clib_net_to_host_u16 (match.port); + m_key.protocol = match.protocol; m_key.fib_index = match.fib_index; kv.key = m_key.as_u64; @@ -1360,6 +1604,7 @@ int snat_static_mapping_match (snat_main_t * sm, { /* Try address only mapping */ m_key.port = 0; + m_key.protocol = 0; kv.key = m_key.as_u64; if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) return 1; @@ -1397,29 +1642,40 @@ int snat_alloc_outside_address_and_port (snat_main_t * sm, for (i = 0; i < vec_len (sm->addresses); i++) { - if (sm->addresses[i].busy_ports < (65535-1024)) + a = sm->addresses + i; + switch (k->protocol) { - a = sm->addresses + i; - - while (1) - { - portnum = random_u32 (&sm->random_seed); - portnum &= 0xFFFF; - if (portnum < 1024) - continue; - if (clib_bitmap_get_no_check (a->busy_port_bitmap, portnum)) - continue; - clib_bitmap_set_no_check (a->busy_port_bitmap, portnum, 1); - a->busy_ports++; - /* Caller sets protocol and fib index */ - k->addr = a->addr; - k->port = clib_host_to_net_u16(portnum); - *address_indexp = i; - return 0; - } +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (a->busy_##n##_ports < (65535-1024)) \ + { \ + while (1) \ + { \ + portnum = random_u32 (&sm->random_seed); \ + portnum &= 0xFFFF; \ + if (portnum < 1024) \ + continue; \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \ + continue; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \ + a->busy_##n##_ports++; \ + k->addr = a->addr; \ + k->port = clib_host_to_net_u16(portnum); \ + *address_indexp = i; \ + return 0; \ + } \ + } \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown protocol"); + return 1; } + } /* Totally out of translations to use... */ + snat_ipfix_logging_addresses_exhausted(0); return 1; } @@ -1481,7 +1737,7 @@ add_address_command_fn (vlib_main_t * vm, if (is_add) snat_add_address (sm, &this_addr); else - rv = snat_del_address (sm, this_addr); + rv = snat_del_address (sm, this_addr, 0); switch (rv) { @@ -1573,6 +1829,38 @@ VLIB_CLI_COMMAND (set_interface_snat_command, static) = { .short_help = "set interface snat in out [del]", }; +uword +unformat_snat_protocol (unformat_input_t * input, va_list * args) +{ + u32 *r = va_arg (*args, u32 *); + + if (0); +#define _(N, i, n, s) else if (unformat (input, s)) *r = SNAT_PROTOCOL_##N; + foreach_snat_protocol +#undef _ + else + return 0; + return 1; +} + +u8 * +format_snat_protocol (u8 * s, va_list * args) +{ + u32 i = va_arg (*args, u32); + u8 *t = 0; + + switch (i) + { +#define _(N, j, n, str) case SNAT_PROTOCOL_##N: t = (u8 *) str; break; + foreach_snat_protocol +#undef _ + default: + s = format (s, "unknown"); + } + s = format (s, "%s", t); + return s; +} + static clib_error_t * add_static_mapping_command_fn (vlib_main_t * vm, unformat_input_t * input, @@ -1584,7 +1872,10 @@ add_static_mapping_command_fn (vlib_main_t * vm, u32 l_port = 0, e_port = 0, vrf_id = ~0; int is_add = 1; int addr_only = 1; + u32 sw_if_index = ~0; + vnet_main_t * vnm = vnet_get_main(); int rv; + snat_protocol_t proto; /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) @@ -1603,8 +1894,18 @@ add_static_mapping_command_fn (vlib_main_t * vm, else if (unformat (line_input, "external %U", unformat_ip4_address, &e_addr)) ; + else if (unformat (line_input, "external %U %u", + unformat_vnet_sw_interface, vnm, &sw_if_index, + &e_port)) + addr_only = 0; + + else if (unformat (line_input, "external %U", + unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; else if (unformat (line_input, "vrf %u", &vrf_id)) ; + else if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) + ; else if (unformat (line_input, "del")) is_add = 0; else @@ -1614,7 +1915,7 @@ add_static_mapping_command_fn (vlib_main_t * vm, unformat_free (line_input); rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port, - vrf_id, addr_only, is_add); + vrf_id, addr_only, sw_if_index, proto, is_add); switch (rv) { @@ -1719,6 +2020,58 @@ VLIB_CLI_COMMAND (set_workers_command, static) = { "set snat workers ", }; +static clib_error_t * +snat_ipfix_logging_enable_disable_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u32 domain_id = 0; + u32 src_port = 0; + u8 enable = 1; + int rv = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "domain %d", &domain_id)) + ; + else if (unformat (line_input, "src-port %d", &src_port)) + ; + else if (unformat (line_input, "disable")) + enable = 0; + else + return clib_error_return (0, "unknown input '%U'", + format_unformat_error, input); + } + unformat_free (line_input); + + rv = snat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port); + + if (rv) + return clib_error_return (0, "ipfix logging enable failed"); + + return 0; +} + +/*? + * @cliexpar + * @cliexstart{snat ipfix logging} + * To enable SNAT IPFIX logging use: + * vpp# snat ipfix logging + * To set IPFIX exporter use: + * vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1 + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_ipfix_logging_enable_disable_command, static) = { + .path = "snat ipfix logging", + .function = snat_ipfix_logging_enable_disable_command_fn, + .short_help = "snat ipfix logging [domain ] [src-port ] [disable]", +}; + static clib_error_t * snat_config (vlib_main_t * vm, unformat_input_t * input) { @@ -1899,7 +2252,8 @@ u8 * format_snat_static_mapping (u8 * s, va_list * args) format_ip4_address, &m->external_addr, m->vrf_id); else - s = format (s, "local %U:%d external %U:%d vrf %d", + s = format (s, "%U local %U:%d external %U:%d vrf %d", + format_snat_protocol, m->proto, format_ip4_address, &m->local_addr, m->local_port, format_ip4_address, &m->external_addr, m->external_port, m->vrf_id); @@ -1962,13 +2316,11 @@ show_snat_command_fn (vlib_main_t * vm, vec_foreach (ap, sm->addresses) { - u8 * s = format (0, ""); vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr); - clib_bitmap_foreach (j, ap->busy_port_bitmap, - ({ - s = format (s, " %d", j); - })); - vlib_cli_output (vm, " %d busy ports:%v", ap->busy_ports, s); +#define _(N, i, n, s) \ + vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s); + foreach_snat_protocol +#undef _ } } @@ -1981,7 +2333,7 @@ show_snat_command_fn (vlib_main_t * vm, { vlib_worker_thread_t *w = vlib_worker_threads + *worker + sm->first_worker_index; - vlib_cli_output (vm, " %v", w->name); + vlib_cli_output (vm, " %s", w->name); } } } @@ -2032,7 +2384,7 @@ show_snat_command_fn (vlib_main_t * vm, continue; vlib_worker_thread_t *w = vlib_worker_threads + j; - vlib_cli_output (vm, "Thread %d (%v at lcore %u):", j, w->name, + vlib_cli_output (vm, "Thread %d (%s at lcore %u):", j, w->name, w->lcore_id); vlib_cli_output (vm, " %d list pool elements", pool_elts (tsm->list_pool)); @@ -2075,7 +2427,10 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im, u32 is_delete) { snat_main_t *sm = &snat_main; + snat_static_map_resolve_t *rp; + u32 *indices_to_delete = 0; int i, j; + int rv; for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++) { @@ -2089,11 +2444,42 @@ snat_ip4_add_del_interface_address_cb (ip4_main_t * im, return; snat_add_address (sm, address); + /* Scan static map resolution vector */ + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + /* On this interface? */ + if (rp->sw_if_index == sw_if_index) + { + /* Add the static mapping */ + rv = snat_add_static_mapping (rp->l_addr, + address[0], + rp->l_port, + rp->e_port, + rp->vrf_id, + rp->addr_only, + ~0 /* sw_if_index */, + rp->proto, + rp->is_add); + if (rv) + clib_warning ("snat_add_static_mapping returned %d", + rv); + vec_add1 (indices_to_delete, j); + } + } + /* If we resolved any of the outstanding static mappings */ + if (vec_len(indices_to_delete)) + { + /* Delete them */ + for (j = vec_len(indices_to_delete)-1; j >= 0; j--) + vec_delete(sm->to_resolve, 1, j); + vec_free(indices_to_delete); + } return; } else { - (void) snat_del_address(sm, address[0]); + (void) snat_del_address(sm, address[0], 1); return; } } @@ -2107,7 +2493,9 @@ static int snat_add_interface_address (snat_main_t *sm, { ip4_main_t * ip4_main = sm->ip4_main; ip4_address_t * first_int_addr; - int i; + snat_static_map_resolve_t *rp; + u32 *indices_to_delete = 0; + int i, j; first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0 /* just want the address*/); @@ -2120,7 +2508,22 @@ static int snat_add_interface_address (snat_main_t *sm, { /* if have address remove it */ if (first_int_addr) - (void) snat_del_address (sm, first_int_addr[0]); + (void) snat_del_address (sm, first_int_addr[0], 1); + else + { + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + if (rp->sw_if_index == sw_if_index) + vec_add1 (indices_to_delete, j); + } + if (vec_len(indices_to_delete)) + { + for (j = vec_len(indices_to_delete)-1; j >= 0; j--) + vec_del1(sm->to_resolve, j); + vec_free(indices_to_delete); + } + } vec_del1(sm->auto_add_sw_if_indices, i); } else