X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fsnat%2Fsnat.c;h=750cc925d7f120b3a7147da8af7081aeb7a98766;hb=3b46cba8f4e909bc363403c6c92215159abb2f11;hp=bc9956841d2e031dce743ed43e4b3f836ebd692f;hpb=cb034b9b374927c7552e36dcbc306d8456b2a0cb;p=vpp.git diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c index bc9956841d2..750cc925d7f 100644 --- a/src/plugins/snat/snat.c +++ b/src/plugins/snat/snat.c @@ -16,13 +16,19 @@ */ #include +#include +#include #include #include #include +#include +#include +#include #include #include #include +#include snat_main_t snat_main; @@ -124,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" */ @@ -208,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) @@ -221,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, @@ -237,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; @@ -313,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. * @@ -324,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 */ @@ -350,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)) @@ -399,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; } } @@ -424,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; @@ -444,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; @@ -482,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; } } @@ -492,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); @@ -548,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; @@ -574,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; } @@ -582,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"; @@ -611,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; } })); @@ -622,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; } @@ -689,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; @@ -843,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) { @@ -860,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); @@ -875,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); @@ -887,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; } @@ -907,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); @@ -1092,6 +1290,115 @@ static void *vl_api_snat_worker_dump_t_print FINISH; } +static int snat_add_interface_address(snat_main_t *sm, + u32 sw_if_index, + int is_del); + +static void +vl_api_snat_add_del_interface_addr_t_handler +(vl_api_snat_add_del_interface_addr_t * mp) +{ + snat_main_t * sm = &snat_main; + vl_api_snat_add_del_interface_addr_reply_t * rmp; + u8 is_del = mp->is_add == 0; + u32 sw_if_index = ntohl(mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX(mp); + + rv = snat_add_interface_address (sm, sw_if_index, is_del); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO(VL_API_SNAT_ADD_DEL_INTERFACE_ADDR_REPLY); +} + +static void *vl_api_snat_add_del_interface_addr_t_print +(vl_api_snat_add_del_interface_addr_t * mp, void *handle) +{ + u8 * s; + + s = format (0, "SCRIPT: snat_add_del_interface_addr "); + s = format (s, "sw_if_index %d %s", + clib_host_to_net_u32(mp->sw_if_index), + mp->is_add ? "" : "del"); + + FINISH; +} + +static void +send_snat_interface_addr_details +(u32 sw_if_index, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_interface_addr_details_t *rmp; + snat_main_t * sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_SNAT_INTERFACE_ADDR_DETAILS+sm->msg_id_base); + rmp->sw_if_index = ntohl (sw_if_index); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_snat_interface_addr_dump_t_handler +(vl_api_snat_interface_addr_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t * sm = &snat_main; + u32 * i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + vec_foreach (i, sm->auto_add_sw_if_indices) + send_snat_interface_addr_details(*i, q, mp->context); +} + +static void *vl_api_snat_interface_addr_dump_t_print +(vl_api_snat_interface_addr_dump_t *mp, void * handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_interface_addr_dump "); + + 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) \ @@ -1103,7 +1410,10 @@ _(SNAT_SHOW_CONFIG, snat_show_config) \ _(SNAT_ADDRESS_DUMP, snat_address_dump) \ _(SNAT_INTERFACE_DUMP, snat_interface_dump) \ _(SNAT_SET_WORKERS, snat_set_workers) \ -_(SNAT_WORKER_DUMP, snat_worker_dump) +_(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_IPFIX_ENABLE_DISABLE, snat_ipfix_enable_disable) /* Set up the API message handling tables */ static clib_error_t * @@ -1146,6 +1456,16 @@ 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, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, + u32 is_delete); + static clib_error_t * snat_init (vlib_main_t * vm) { snat_main_t * sm = &snat_main; @@ -1158,6 +1478,7 @@ static clib_error_t * snat_init (vlib_main_t * vm) vlib_thread_main_t *tm = vlib_get_thread_main (); uword *bitmap = 0; u32 i; + ip4_add_del_interface_address_callback_t cb4; name = format (0, "snat_%08x%c", api_version, 0); @@ -1205,6 +1526,15 @@ static clib_error_t * snat_init (vlib_main_t * vm) plugin_custom_dump_configure (sm); vec_free(name); + /* Set up the interface address add/del callback */ + cb4.function = snat_ip4_add_del_interface_address_cb; + cb4.function_opaque = 0; + + vec_add1 (im->add_del_interface_address_callbacks, cb4); + + /* Init IPFIX logging */ + snat_ipfix_logging_init(vm); + return error; } @@ -1221,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; + } } /** @@ -1246,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) @@ -1254,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; @@ -1262,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; @@ -1299,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; } @@ -1383,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) { @@ -1475,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, @@ -1486,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)) @@ -1505,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 @@ -1516,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) { @@ -1621,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) { @@ -1801,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); @@ -1822,7 +2274,7 @@ show_snat_command_fn (vlib_main_t * vm, snat_address_t * ap; vnet_main_t *vnm = vnet_get_main(); snat_main_per_thread_data_t *tsm; - u32 users_num = 0, sessions_num = 0, *worker; + u32 users_num = 0, sessions_num = 0, *worker, *sw_if_index; uword j = 0; if (unformat (input, "detail")) @@ -1852,15 +2304,23 @@ show_snat_command_fn (vlib_main_t * vm, i->is_inside ? "in" : "out"); })); + if (vec_len (sm->auto_add_sw_if_indices)) + { + vlib_cli_output (vm, "SNAT pool addresses interfaces:"); + vec_foreach (sw_if_index, sm->auto_add_sw_if_indices) + { + vlib_cli_output (vm, "%U", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, *sw_if_index)); + } + } + 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 _ } } @@ -1873,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); } } } @@ -1924,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)); @@ -1955,3 +2415,180 @@ VLIB_CLI_COMMAND (show_snat_command, static) = { .short_help = "show snat", .function = show_snat_command_fn, }; + + +static void +snat_ip4_add_del_interface_address_cb (ip4_main_t * im, + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, + 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++) + { + if (sw_if_index == sm->auto_add_sw_if_indices[i]) + { + if (!is_delete) + { + /* Don't trip over lease renewal, static config */ + for (j = 0; j < vec_len(sm->addresses); j++) + if (sm->addresses[j].addr.as_u32 == address->as_u32) + 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], 1); + return; + } + } + } +} + + +static int snat_add_interface_address (snat_main_t *sm, + u32 sw_if_index, + int is_del) +{ + ip4_main_t * ip4_main = sm->ip4_main; + ip4_address_t * first_int_addr; + 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*/); + + for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++) + { + if (sm->auto_add_sw_if_indices[i] == sw_if_index) + { + if (is_del) + { + /* if have address remove it */ + if (first_int_addr) + (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 + return VNET_API_ERROR_VALUE_EXIST; + + return 0; + } + } + + if (is_del) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + /* add to the auto-address list */ + vec_add1(sm->auto_add_sw_if_indices, sw_if_index); + + /* If the address is already bound - or static - add it now */ + if (first_int_addr) + snat_add_address (sm, first_int_addr); + + return 0; +} + +static clib_error_t * +snat_add_interface_address_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + u32 sw_if_index; + int rv; + int is_del = 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, "%U", unformat_vnet_sw_interface, + sm->vnet_main, &sw_if_index)) + ; + else if (unformat (line_input, "del")) + is_del = 1; + else + return clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + } + + rv = snat_add_interface_address (sm, sw_if_index, is_del); + + switch (rv) + { + case 0: + break; + + default: + return clib_error_return (0, "snat_add_interface_address returned %d", + rv); + } + return 0; +} + +VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = { + .path = "snat add interface address", + .short_help = "snat add interface address [del]", + .function = snat_add_interface_address_command_fn, +};