Add reverse DNS (ip to name) resolution 42/8942/1
authorDave Barach <dave@barachs.net>
Fri, 20 Oct 2017 13:21:35 +0000 (09:21 -0400)
committerDave Barach <dave@barachs.net>
Fri, 20 Oct 2017 13:34:11 +0000 (09:34 -0400)
Change-Id: Ic531d820b1846ff7363e5c396ac0b1176e87b401
Signed-off-by: Dave Barach <dave@barachs.net>
src/vat/api_format.c
src/vnet/dns/dns.api
src/vnet/dns/dns.c
src/vnet/dns/dns.h
src/vnet/dns/dns_packet.h
src/vnet/dns/resolver_process.c
src/vpp/api/custom_dump.c

index bc5e959..08d0c2e 100644 (file)
@@ -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 <nnn>")                                    \
 _(app_namespace_add_del, "[add] id <ns-id> secret <nn> sw_if_index <nn>")\
 _(dns_enable_disable, "[enable][disable]")                             \
 _(dns_name_server_add_del, "<ip-address> [del]")                       \
-_(dns_resolve_name, "<hostname>")
+_(dns_resolve_name, "<hostname>")                                      \
+_(dns_resolve_ip, "<ip4|ip6>")
 
 /* List of command functions, CLI names map directly to functions */
 #define foreach_cli_function                                    \
index 5557445..f188b05 100644 (file)
@@ -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];
+};
+
index 9facb9b..d3f573d 100644 (file)
@@ -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 <vpp/api/vpe_all_api_h.h>
 #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
index c55c6f3..442ef86 100644 (file)
 #include <vnet/dns/dns_packet.h>
 #include <vnet/ip/ip.h>
 
+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;
index aa5daac..da5ddfa 100644 (file)
@@ -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
index 5603371..f473422 100644 (file)
@@ -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);
index c1aa14d..e5392fe 100644 (file)
@@ -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)
 {