From d2080159c4287f3c1d491fa60da2cb6e9ab47b55 Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Fri, 20 Oct 2017 09:21:35 -0400 Subject: [PATCH] Add reverse DNS (ip to name) resolution Change-Id: Ic531d820b1846ff7363e5c396ac0b1176e87b401 Signed-off-by: Dave Barach --- src/vat/api_format.c | 79 ++++++++++++- src/vnet/dns/dns.api | 39 ++++++- src/vnet/dns/dns.c | 242 +++++++++++++++++++++++++++++++++++++--- src/vnet/dns/dns.h | 13 ++- src/vnet/dns/dns_packet.h | 1 + src/vnet/dns/resolver_process.c | 50 ++++++--- src/vpp/api/custom_dump.c | 16 ++- 7 files changed, 402 insertions(+), 38 deletions(-) diff --git a/src/vat/api_format.c b/src/vat/api_format.c index bc5e9596bba..08d0c2e033e 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -2209,9 +2209,39 @@ static void vl_api_dns_resolve_name_reply_t_handler static void vl_api_dns_resolve_name_reply_t_handler_json (vl_api_dns_resolve_name_reply_t * mp) { - clib_warning ("no"); + clib_warning ("not implemented"); } +static void vl_api_dns_resolve_ip_reply_t_handler + (vl_api_dns_resolve_ip_reply_t * mp) +{ + vat_main_t *vam = &vat_main; + i32 retval = ntohl (mp->retval); + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + } + else + { + vam->retval = retval; + vam->result_ready = 1; + + if (retval == 0) + { + clib_warning ("canonical name %s", mp->name); + } + else + clib_warning ("retval %d", retval); + } +} + +static void vl_api_dns_resolve_ip_reply_t_handler_json + (vl_api_dns_resolve_ip_reply_t * mp) +{ + clib_warning ("not implemented"); +} + + static void vl_api_ip_address_details_t_handler (vl_api_ip_address_details_t * mp) { @@ -5461,7 +5491,8 @@ _(TCP_CONFIGURE_SRC_ADDRESSES_REPLY, tcp_configure_src_addresses_reply) \ _(APP_NAMESPACE_ADD_DEL_REPLY, app_namespace_add_del_reply) \ _(DNS_ENABLE_DISABLE_REPLY, dns_enable_disable_reply) \ _(DNS_NAME_SERVER_ADD_DEL_REPLY, dns_name_server_add_del_reply) \ -_(DNS_RESOLVE_NAME_REPLY, dns_resolve_name_reply) +_(DNS_RESOLVE_NAME_REPLY, dns_resolve_name_reply) \ +_(DNS_RESOLVE_IP_REPLY, dns_resolve_ip_reply) #define foreach_standalone_reply_msg \ _(SW_INTERFACE_EVENT, sw_interface_event) \ @@ -21002,6 +21033,47 @@ api_dns_resolve_name (vat_main_t * vam) return ret; } +static int +api_dns_resolve_ip (vat_main_t * vam) +{ + unformat_input_t *line_input = vam->input; + vl_api_dns_resolve_ip_t *mp; + int is_ip6 = -1; + ip4_address_t addr4; + ip6_address_t addr6; + int ret; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U", unformat_ip6_address, &addr6)) + is_ip6 = 1; + else if (unformat (line_input, "%U", unformat_ip4_address, &addr4)) + is_ip6 = 0; + else + break; + } + + if (is_ip6 == -1) + { + errmsg ("missing address"); + return -99; + } + + /* Construct the API message */ + M (DNS_RESOLVE_IP, mp); + mp->is_ip6 = is_ip6; + if (is_ip6) + memcpy (mp->address, &addr6, sizeof (addr6)); + else + memcpy (mp->address, &addr4, sizeof (addr4)); + + /* send it... */ + S (mp); + /* Wait for the reply */ + W (ret); + return ret; +} + static int api_dns_name_server_add_del (vat_main_t * vam) { @@ -21860,7 +21932,8 @@ _(memfd_segment_create,"size ") \ _(app_namespace_add_del, "[add] id secret sw_if_index ")\ _(dns_enable_disable, "[enable][disable]") \ _(dns_name_server_add_del, " [del]") \ -_(dns_resolve_name, "") +_(dns_resolve_name, "") \ +_(dns_resolve_ip, "") /* List of command functions, CLI names map directly to functions */ #define foreach_cli_function \ diff --git a/src/vnet/dns/dns.api b/src/vnet/dns/dns.api index 55574458ddd..f188b059c2f 100644 --- a/src/vnet/dns/dns.api +++ b/src/vnet/dns/dns.api @@ -52,9 +52,19 @@ autoreply define dns_name_server_add_del { define dns_resolve_name { u32 client_index; u32 context; - u8 name[128]; + u8 name[256]; }; +/** \brief DNS name resolution reply + + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param retval - return value, 0 => success + @param ip4_set - indicates that the ip4 address is valid + @param ip6_set - indicates that the ip6 address is valid + @param ip4_address - the ip4 name resolution reply + @param ip6_address - the ip6 name resolution reply +*/ define dns_resolve_name_reply { u32 context; i32 retval; @@ -64,3 +74,30 @@ define dns_resolve_name_reply { u8 ip6_address[16]; }; +/** \brief DNS IP -> name resolution request + + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip6 - set if the reverse-DNS request is an ip6 address + @param address - the address to map to a name +*/ +define dns_resolve_ip { + u32 client_index; + u32 context; + u8 is_ip6; + u8 address[16]; + }; + +/** \brief DNS ip->name resolution reply + + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param retval - return value, 0 => success + @param name - canonical name for the indicated IP address +*/ +define dns_resolve_ip_reply { + u32 context; + i32 retval; + u8 name[256]; +}; + diff --git a/src/vnet/dns/dns.c b/src/vnet/dns/dns.c index 9facb9b5640..d3f573d4c63 100644 --- a/src/vnet/dns/dns.c +++ b/src/vnet/dns/dns.c @@ -55,8 +55,7 @@ dns_cache_clear (dns_main_t * dm) pool_foreach (ep, dm->entries, ({ vec_free (ep->name); - vec_free (ep->api_clients_to_notify); - vec_free (ep->api_client_contexts); + vec_free (ep->pending_api_requests); vec_free (ep->ip4_peers_to_notify); vec_free (ep->ip6_peers_to_notify); })); @@ -647,8 +646,7 @@ vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index) found: hash_unset_mem (dm->cache_entry_by_name, ep->name); vec_free (ep->name); - vec_free (ep->api_clients_to_notify); - vec_free (ep->api_client_contexts); + vec_free (ep->pending_api_requests); vec_free (ep->ip4_peers_to_notify); vec_free (ep->ip6_peers_to_notify); pool_put (dm->entries, ep); @@ -771,12 +769,13 @@ dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data) static int dns_resolve_name (dns_main_t * dm, u8 * name, u32 client_index, u32 client_context, - dns_cache_entry_t ** retp) + u32 request_type, dns_cache_entry_t ** retp) { dns_cache_entry_t *ep; int rv; f64 now; uword *p; + pending_api_request_t *pr; now = vlib_time_now (dm->vlib_main); @@ -850,9 +849,12 @@ search_again: else { /* - * Resolution pending. Add request to the pending vector(s) */ - vec_add1 (ep->api_clients_to_notify, client_index); - vec_add1 (ep->api_client_contexts, client_context); + * Resolution pending. Add request to the pending vector + */ + vec_add2 (ep->pending_api_requests, pr, 1); + pr->request_type = request_type; + pr->client_index = client_index; + pr->client_context = client_context; dns_cache_unlock (dm); return (0); } @@ -880,16 +882,17 @@ re_resolve: hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries); vec_add1 (dm->unresolved_entries, ep - dm->entries); - vec_add1 (ep->api_clients_to_notify, client_index); - vec_add1 (ep->api_client_contexts, client_context); + vec_add2 (ep->pending_api_requests, pr, 1); + pr->request_type = request_type; + pr->client_index = client_index; + pr->client_context = client_context; vnet_send_dns_request (dm, ep); dns_cache_unlock (dm); return 0; } #define foreach_notification_to_move \ -_(api_clients_to_notify) \ -_(api_client_contexts) \ +_(pending_api_requests) \ _(ip4_peers_to_notify) \ _(ip6_peers_to_notify) @@ -1173,6 +1176,127 @@ vnet_dns_response_to_reply (u8 * response, return 0; } +int +vnet_dns_response_to_name (u8 * response, + vl_api_dns_resolve_ip_reply_t * rmp, + u32 * min_ttlp) +{ + dns_header_t *h; + dns_query_t *qp; + dns_rr_t *rr; + int i, limit; + u8 len; + u8 *curpos, *pos; + u16 flags; + u16 rcode; + u8 *name; + u32 ttl; + u8 *junk __attribute__ ((unused)); + int name_set = 0; + + h = (dns_header_t *) response; + flags = clib_net_to_host_u16 (h->flags); + rcode = flags & DNS_RCODE_MASK; + + /* See if the response is OK, etc. */ + switch (rcode) + { + default: + case DNS_RCODE_NO_ERROR: + break; + + case DNS_RCODE_NAME_ERROR: + case DNS_RCODE_FORMAT_ERROR: + return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME; + + case DNS_RCODE_SERVER_FAILURE: + case DNS_RCODE_NOT_IMPLEMENTED: + case DNS_RCODE_REFUSED: + return VNET_API_ERROR_NAME_SERVER_NEXT_SERVER; + } + + /* No answers? Loser... */ + if (clib_net_to_host_u16 (h->anscount) < 1) + return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES; + + curpos = (u8 *) (h + 1); + + /* Skip the name we asked about */ + pos = curpos; + len = *pos++; + /* Should never happen, but stil... */ + if ((len & 0xC0) == 0xC0) + curpos += 2; + else + { + /* skip the name / label-set */ + while (len) + { + pos += len; + len = *pos++; + } + curpos = pos; + } + /* Skip queries */ + limit = clib_net_to_host_u16 (h->qdcount); + qp = (dns_query_t *) curpos; + qp += limit; + curpos = (u8 *) qp; + + /* Parse answers */ + limit = clib_net_to_host_u16 (h->anscount); + + for (i = 0; i < limit; i++) + { + pos = curpos; + + /* Expect pointer chases in the answer section... */ + if ((pos[0] & 0xC0) == 0xC0) + curpos += 2; + else + { + len = *pos++; + while (len) + { + if ((pos[0] & 0xC0) == 0xC0) + { + curpos = pos + 2; + goto curpos_set; + } + pos += len; + len = *pos++; + } + curpos = pos; + } + + curpos_set: + rr = (dns_rr_t *) curpos; + + switch (clib_net_to_host_u16 (rr->type)) + { + case DNS_TYPE_PTR: + name = labels_to_name (rr->rdata, response, &junk); + memcpy (rmp->name, name, vec_len (name)); + ttl = clib_net_to_host_u32 (rr->ttl); + if (*min_ttlp) + *min_ttlp = ttl; + rmp->name[vec_len (name)] = 0; + name_set = 1; + break; + default: + break; + } + /* Might as well stop ASAP */ + if (name_set == 1) + break; + curpos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength); + } + + if (name_set == 0) + return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME; + return 0; +} + static void vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp) { @@ -1184,7 +1308,8 @@ vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp) /* Sanitize the name slightly */ mp->name[ARRAY_LEN (mp->name) - 1] = 0; - rv = dns_resolve_name (dm, mp->name, mp->client_index, mp->context, &ep); + rv = dns_resolve_name (dm, mp->name, mp->client_index, mp->context, + DNS_API_PENDING_NAME_TO_IP, &ep); /* Error, e.g. not enabled? Tell the user */ if (rv < 0) @@ -1212,6 +1337,82 @@ vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp) dns_cache_unlock (dm); } +static void +vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp) +{ + dns_main_t *dm = &dns_main; + vl_api_dns_resolve_ip_reply_t *rmp; + dns_cache_entry_t *ep; + int rv; + int i, len; + u8 *lookup_name = 0; + u8 digit, nybble; + + if (mp->is_ip6) + { + for (i = 15; i >= 0; i--) + { + digit = mp->address[i]; + nybble = (digit & 0x0F); + if (nybble > 9) + vec_add1 (lookup_name, (nybble - 10) + 'a'); + else + vec_add1 (lookup_name, nybble + '0'); + vec_add1 (lookup_name, '.'); + nybble = (digit & 0xF0) >> 4; + if (nybble > 9) + vec_add1 (lookup_name, (nybble - 10) + 'a'); + else + vec_add1 (lookup_name, nybble + '0'); + vec_add1 (lookup_name, '.'); + } + len = vec_len (lookup_name); + vec_validate (lookup_name, len + 8); + memcpy (lookup_name + len, "ip6.arpa", 8); + } + else + { + for (i = 3; i >= 0; i--) + { + digit = mp->address[i]; + lookup_name = format (lookup_name, "%d.", digit); + } + lookup_name = format (lookup_name, "in-addr.arpa"); + } + + vec_add1 (lookup_name, 0); + + rv = dns_resolve_name (dm, lookup_name, mp->client_index, mp->context, + DNS_API_PENDING_IP_TO_NAME, &ep); + + vec_free (lookup_name); + + /* Error, e.g. not enabled? Tell the user */ + if (rv < 0) + { + REPLY_MACRO (VL_API_DNS_RESOLVE_IP_REPLY); + return; + } + + /* Resolution pending? Don't reply... */ + if (ep == 0) + return; + + /* *INDENT-OFF* */ + REPLY_MACRO2(VL_API_DNS_RESOLVE_IP_REPLY, + ({ + rv = vnet_dns_response_to_name (ep->dns_response, rmp, 0 /* ttl-ptr */); + rmp->retval = clib_host_to_net_u32 (rv); + })); + /* *INDENT-ON* */ + + /* + * dns_resolve_name leaves the cache locked when it returns + * a cached result, so unlock it here. + */ + dns_cache_unlock (dm); +} + #define vl_msg_name_crc_list #include #undef vl_msg_name_crc_list @@ -1227,7 +1428,8 @@ setup_message_id_table (api_main_t * am) #define foreach_dns_api_msg \ _(DNS_ENABLE_DISABLE, dns_enable_disable) \ _(DNS_NAME_SERVER_ADD_DEL, dns_name_server_add_del) \ -_(DNS_RESOLVE_NAME, dns_resolve_name) +_(DNS_RESOLVE_NAME, dns_resolve_name) \ +_(DNS_RESOLVE_IP, dns_resolve_ip) static clib_error_t * dns_api_hookup (vlib_main_t * vm) @@ -1480,6 +1682,7 @@ format_dns_reply_data (u8 * s, va_list * args) int i; int initial_pointer_chase = 0; u16 *tp; + u16 rrtype_host_byte_order; pos = pos2 = *curpos; @@ -1521,8 +1724,9 @@ format_dns_reply_data (u8 * s, va_list * args) pos = pos2; rr = (dns_rr_t *) pos; + rrtype_host_byte_order = clib_net_to_host_u16 (rr->type); - switch (clib_net_to_host_u16 (rr->type)) + switch (rrtype_host_byte_order) { case DNS_TYPE_A: if (verbose > 1) @@ -1662,12 +1866,16 @@ format_dns_reply_data (u8 * s, va_list * args) pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength); break; + case DNS_TYPE_PTR: case DNS_TYPE_CNAME: if (verbose > 1) { tp = (u16 *) rr->rdata; - s = format (s, "CNAME: "); + if (rrtype_host_byte_order == DNS_TYPE_CNAME) + s = format (s, "CNAME: "); + else + s = format (s, "PTR: "); pos2 = rr->rdata; @@ -1887,7 +2095,7 @@ format_dns_cache (u8 * s, va_list * args) if (verbose > 2) s = format (s, " %d client notifications pending\n", - vec_len(ep->api_clients_to_notify)); + vec_len(ep->pending_api_requests)); } } else diff --git a/src/vnet/dns/dns.h b/src/vnet/dns/dns.h index c55c6f31934..442ef860cfb 100644 --- a/src/vnet/dns/dns.h +++ b/src/vnet/dns/dns.h @@ -24,6 +24,16 @@ #include #include +typedef struct +{ + u32 request_type; + u32 client_index; + u32 client_context; +} pending_api_request_t; + +#define DNS_API_PENDING_NAME_TO_IP 1 +#define DNS_API_PENDING_IP_TO_NAME 2 + typedef struct { /** flags */ @@ -51,8 +61,7 @@ typedef struct u8 *dns_response; /** Clients awaiting responses */ - u32 *api_clients_to_notify; - u32 *api_client_contexts; + pending_api_request_t *pending_api_requests; ip4_address_t *ip4_peers_to_notify; ip6_address_t *ip6_peers_to_notify; } dns_cache_entry_t; diff --git a/src/vnet/dns/dns_packet.h b/src/vnet/dns/dns_packet.h index aa5daacdc57..da5ddfa64fe 100644 --- a/src/vnet/dns/dns_packet.h +++ b/src/vnet/dns/dns_packet.h @@ -132,6 +132,7 @@ _(TEXT, 16) /**< a text string */ \ _(NAMESERVER, 2) /**< a nameserver */ \ _(CNAME, 5) /**< a CNAME (alias) */ \ _(MAIL_EXCHANGE, 15) /**< a mail exchange */ \ +_(PTR, 12) /**< a PTR (pointer) record */ \ _(HINFO, 13) /**< Host info */ typedef enum diff --git a/src/vnet/dns/resolver_process.c b/src/vnet/dns/resolver_process.c index 5603371db52..f473422112f 100644 --- a/src/vnet/dns/resolver_process.c +++ b/src/vnet/dns/resolver_process.c @@ -44,6 +44,10 @@ extern int vnet_dns_response_to_reply (u8 * response, vl_api_dns_resolve_name_reply_t * rmp, u32 * min_ttlp); +extern int +vnet_dns_response_to_name (u8 * response, + vl_api_dns_resolve_ip_reply_t * rmp, + u32 * min_ttlp); static void resolve_event (dns_main_t * dm, f64 now, u8 * reply) @@ -95,29 +99,47 @@ resolve_event (dns_main_t * dm, f64 now, u8 * reply) ep->flags |= DNS_CACHE_ENTRY_FLAG_VALID; /* Most likely, send 1 message */ - for (i = 0; i < vec_len (ep->api_clients_to_notify); i++) + for (i = 0; i < vec_len (ep->pending_api_requests); i++) { vl_api_registration_t *regp; - vl_api_dns_resolve_name_reply_t *rmp; regp = vl_api_client_index_to_registration - (ep->api_clients_to_notify[i]); + (ep->pending_api_requests[i].client_index); if (regp == 0) continue; - rmp = vl_msg_api_alloc (sizeof (*rmp) + vec_len (ep->dns_response)); - rmp->_vl_msg_id = clib_host_to_net_u16 (VL_API_DNS_RESOLVE_NAME_REPLY); - rmp->context = ep->api_client_contexts[i]; - min_ttl = ~0; - rv = vnet_dns_response_to_reply (ep->dns_response, rmp, &min_ttl); - if (min_ttl != ~0) - ep->expiration_time = now + min_ttl; - rmp->retval = clib_host_to_net_u32 (rv); - vl_msg_api_send (regp, (u8 *) rmp); + if (ep->pending_api_requests[i].request_type + == DNS_API_PENDING_NAME_TO_IP) + { + vl_api_dns_resolve_name_reply_t *rmp; + rmp = vl_msg_api_alloc (sizeof (*rmp)); + rmp->_vl_msg_id = + clib_host_to_net_u16 (VL_API_DNS_RESOLVE_NAME_REPLY); + rmp->context = ep->pending_api_requests[i].client_context; + min_ttl = ~0; + rv = vnet_dns_response_to_reply (ep->dns_response, rmp, &min_ttl); + if (min_ttl != ~0) + ep->expiration_time = now + min_ttl; + rmp->retval = clib_host_to_net_u32 (rv); + vl_msg_api_send (regp, (u8 *) rmp); + } + else + { + vl_api_dns_resolve_ip_reply_t *rmp; + rmp = vl_msg_api_alloc (sizeof (*rmp)); + rmp->_vl_msg_id = + clib_host_to_net_u16 (VL_API_DNS_RESOLVE_IP_REPLY); + rmp->context = ep->pending_api_requests[i].client_context; + min_ttl = ~0; + rv = vnet_dns_response_to_name (ep->dns_response, rmp, &min_ttl); + if (min_ttl != ~0) + ep->expiration_time = now + min_ttl; + rmp->retval = clib_host_to_net_u32 (rv); + vl_msg_api_send (regp, (u8 *) rmp); + } } - vec_free (ep->api_clients_to_notify); - vec_free (ep->api_client_contexts); + vec_free (ep->pending_api_requests); /* $$$ Add ip4/ip6 reply code */ vec_free (ep->ip4_peers_to_notify); diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index c1aa14d5497..e5392fe4c5d 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -3196,6 +3196,19 @@ static void *vl_api_dns_resolve_name_t_print FINISH; } +static void *vl_api_dns_resolve_ip_t_print + (vl_api_dns_resolve_ip_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: dns_resolve_ip "); + if (mp->is_ip6) + s = format (s, "%U ", format_ip6_address, mp->address); + else + s = format (s, "%U ", format_ip4_address, mp->address); + FINISH; +} + #define foreach_custom_print_no_arg_function \ _(lisp_eid_table_vni_dump) \ _(lisp_map_resolver_dump) \ @@ -3391,7 +3404,8 @@ _(LLDP_CONFIG, lldp_config) \ _(SW_INTERFACE_SET_LLDP, sw_interface_set_lldp) \ _(DNS_ENABLE_DISABLE, dns_enable_disable) \ _(DNS_NAME_SERVER_ADD_DEL, dns_name_server_add_del) \ -_(DNS_RESOLVE_NAME, dns_resolve_name) +_(DNS_RESOLVE_NAME, dns_resolve_name) \ +_(DNS_RESOLVE_IP, dns_resolve_ip) void vl_msg_api_custom_dump_configure (api_main_t * am) { -- 2.16.6