VPP-1027: DNS name resolver 43/8743/9
authorDave Barach <dave@barachs.net>
Tue, 10 Oct 2017 21:53:14 +0000 (17:53 -0400)
committerFlorin Coras <florin.coras@gmail.com>
Fri, 13 Oct 2017 20:20:09 +0000 (20:20 +0000)
This patch is a plausible first-cut, suitable for initial testing by
vcl (host stack client library).

Main features;
- recursive name resolution
- multiple ip4/ip6 name servers
- cache size limit enforcement
  - currently limited to 65K
- ttl / aging
- static mapping support
- show / clear / debug CLI commands

Binary APIs provided for the following:
- add/delete name servers
- enable/disable the name cache
- resolve a name

To Do list:
- Respond to ip4/ip6 client DNS requests (vs. binary API requests)
- Perf / scale tuning
  - map pending transaction ids to pool indices, so the cache
    can (greatly) exceed 65K entries
- Security improvements
  - Use unpredictable dns transaction IDs, related to previous item
  - Make sure that response-packet src ip addresses match the server
- Add binary APIs
  - deliver raw response data to clients
  - control recursive name resolution
- Documentation

Change-Id: I48c373d5c05d7108ccd814d4055caf8c75ca10b7
Signed-off-by: Dave Barach <dave@barachs.net>
14 files changed:
src/vat/api_format.c
src/vlibapi/api_shared.c
src/vnet.am
src/vnet/api_errno.h
src/vnet/dns/dns.api [new file with mode: 0644]
src/vnet/dns/dns.c [new file with mode: 0644]
src/vnet/dns/dns.h [new file with mode: 0644]
src/vnet/dns/dns_packet.h [new file with mode: 0644]
src/vnet/dns/reply_node.c [new file with mode: 0644]
src/vnet/dns/resolver_process.c [new file with mode: 0644]
src/vnet/ip/ip_init.c
src/vnet/udp/udp.h
src/vnet/vnet_all_api_h.h
src/vpp/api/custom_dump.c

index ae1db41..556bcf1 100644 (file)
@@ -2179,6 +2179,39 @@ static void vl_api_memfd_segment_create_reply_t_handler_json
   clib_warning ("no");
 }
 
+static void vl_api_dns_resolve_name_reply_t_handler
+  (vl_api_dns_resolve_name_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)
+       {
+         if (mp->ip4_set)
+           clib_warning ("ip4 address %U", format_ip4_address,
+                         (ip4_address_t *) mp->ip4_address);
+         if (mp->ip6_set)
+           clib_warning ("ip6 address %U", format_ip6_address,
+                         (ip6_address_t *) mp->ip6_address);
+       }
+      else
+       clib_warning ("retval %d", retval);
+    }
+}
+
+static void vl_api_dns_resolve_name_reply_t_handler_json
+  (vl_api_dns_resolve_name_reply_t * mp)
+{
+  clib_warning ("no");
+}
 
 static void vl_api_ip_address_details_t_handler
   (vl_api_ip_address_details_t * mp)
@@ -5066,8 +5099,8 @@ _(want_stats_reply)                                       \
 _(cop_interface_enable_disable_reply)                  \
 _(cop_whitelist_enable_disable_reply)                   \
 _(sw_interface_clear_stats_reply)                       \
-_(ioam_enable_reply)                              \
-_(ioam_disable_reply)                              \
+_(ioam_enable_reply)                                    \
+_(ioam_disable_reply)                                   \
 _(one_add_del_locator_reply)                            \
 _(one_add_del_local_eid_reply)                          \
 _(one_add_del_remote_mapping_reply)                     \
@@ -5117,7 +5150,9 @@ _(p2p_ethernet_del_reply)                               \
 _(lldp_config_reply)                                    \
 _(sw_interface_set_lldp_reply)                         \
 _(tcp_configure_src_addresses_reply)                   \
-_(app_namespace_add_del_reply)
+_(app_namespace_add_del_reply)                          \
+_(dns_enable_disable_reply)                             \
+_(dns_name_server_add_del_reply)
 
 #define _(n)                                    \
     static void vl_api_##n##_t_handler          \
@@ -5422,7 +5457,10 @@ _(P2P_ETHERNET_DEL_REPLY, p2p_ethernet_del_reply)                       \
 _(LLDP_CONFIG_REPLY, lldp_config_reply)                                 \
 _(SW_INTERFACE_SET_LLDP_REPLY, sw_interface_set_lldp_reply)            \
 _(TCP_CONFIGURE_SRC_ADDRESSES_REPLY, tcp_configure_src_addresses_reply)        \
-_(APP_NAMESPACE_ADD_DEL_REPLY, app_namespace_add_del_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)
 
 #define foreach_standalone_reply_msg                                   \
 _(SW_INTERFACE_EVENT, sw_interface_event)                               \
@@ -5432,7 +5470,7 @@ _(VNET_IP4_FIB_COUNTERS, vnet_ip4_fib_counters)                         \
 _(VNET_IP6_FIB_COUNTERS, vnet_ip6_fib_counters)                         \
 _(VNET_IP4_NBR_COUNTERS, vnet_ip4_nbr_counters)                         \
 _(VNET_IP6_NBR_COUNTERS, vnet_ip6_nbr_counters)                                \
-_(MEMFD_SEGMENT_CREATE_REPLY, memfd_segment_create_reply)
+_(MEMFD_SEGMENT_CREATE_REPLY, memfd_segment_create_reply)               \
 
 typedef struct
 {
@@ -20830,6 +20868,132 @@ api_memfd_segment_create (vat_main_t * vam)
   return ret;
 }
 
+static int
+api_dns_enable_disable (vat_main_t * vam)
+{
+  unformat_input_t *line_input = vam->input;
+  vl_api_dns_enable_disable_t *mp;
+  u8 enable_disable = 1;
+  int ret;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "disable"))
+       enable_disable = 0;
+      if (unformat (line_input, "enable"))
+       enable_disable = 1;
+      else
+       break;
+    }
+
+  /* Construct the API message */
+  M (DNS_ENABLE_DISABLE, mp);
+  mp->enable = enable_disable;
+
+  /* send it... */
+  S (mp);
+  /* Wait for the reply */
+  W (ret);
+  return ret;
+}
+
+static int
+api_dns_resolve_name (vat_main_t * vam)
+{
+  unformat_input_t *line_input = vam->input;
+  vl_api_dns_resolve_name_t *mp;
+  u8 *name = 0;
+  int ret;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "%s", &name))
+       ;
+      else
+       break;
+    }
+
+  if (vec_len (name) > 127)
+    {
+      errmsg ("name too long");
+      return -99;
+    }
+
+  /* Construct the API message */
+  M (DNS_RESOLVE_NAME, mp);
+  memcpy (mp->name, name, vec_len (name));
+  vec_free (name);
+
+  /* send it... */
+  S (mp);
+  /* Wait for the reply */
+  W (ret);
+  return ret;
+}
+
+static int
+api_dns_name_server_add_del (vat_main_t * vam)
+{
+  unformat_input_t *i = vam->input;
+  vl_api_dns_name_server_add_del_t *mp;
+  u8 is_add = 1;
+  ip6_address_t ip6_server;
+  ip4_address_t ip4_server;
+  int ip6_set = 0;
+  int ip4_set = 0;
+  int ret = 0;
+
+  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (i, "%U", unformat_ip6_address, &ip6_server))
+       ip6_set = 1;
+      else if (unformat (i, "%U", unformat_ip4_address, &ip4_server))
+       ip4_set = 1;
+      else if (unformat (i, "del"))
+       is_add = 0;
+      else
+       {
+         clib_warning ("parse error '%U'", format_unformat_error, i);
+         return -99;
+       }
+    }
+
+  if (ip4_set && ip6_set)
+    {
+      errmsg ("Only one server address allowed per message");
+      return -99;
+    }
+  if ((ip4_set + ip6_set) == 0)
+    {
+      errmsg ("Server address required");
+      return -99;
+    }
+
+  /* Construct the API message */
+  M (DNS_NAME_SERVER_ADD_DEL, mp);
+
+  if (ip6_set)
+    {
+      memcpy (mp->server_address, &ip6_server, sizeof (ip6_address_t));
+      mp->is_ip6 = 1;
+    }
+  else
+    {
+      memcpy (mp->server_address, &ip4_server, sizeof (ip4_address_t));
+      mp->is_ip6 = 0;
+    }
+
+  mp->is_add = is_add;
+
+  /* send it... */
+  S (mp);
+
+  /* Wait for a reply, return good/bad news  */
+  W (ret);
+  return ret;
+}
+
+
 static int
 q_or_quit (vat_main_t * vam)
 {
@@ -21621,6 +21785,9 @@ _(sw_interface_set_lldp, "<intfc> | sw_if_index <nn> [port-desc <description>]\n
 _(tcp_configure_src_addresses, "<ip4|6>first-<ip4|6>last [vrf <id>]")  \
 _(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>")
 
 /* List of command functions, CLI names map directly to functions */
 #define foreach_cli_function                                    \
index 1bb50d4..4d11b51 100644 (file)
@@ -665,7 +665,22 @@ vl_msg_api_config (vl_msg_api_msg_config_t * c)
 {
   api_main_t *am = &api_main;
 
-  ASSERT (c->id > 0);
+  /*
+   * This happens during the java core tests if the message
+   * dictionary is missing newly added xxx_reply_t messages.
+   * Should never happen, but since I shot myself in the foot once
+   * this way, I thought I'd make it easy to debug if I ever do
+   * it again... (;-)...
+   */
+  if (c->id == 0)
+    {
+      if (c->name)
+       clib_warning ("Trying to register %s with a NULL msg id!", c->name);
+      else
+       clib_warning ("Trying to register a NULL msg with a NULL msg id!");
+      clib_warning ("Did you forget to call setup_message_id_table?");
+      return;
+    }
 
 #define _(a) vec_validate (am->a, c->id);
   foreach_msg_api_vector;
index 19b215f..520bee4 100644 (file)
@@ -812,6 +812,22 @@ nobase_include_HEADERS +=                  \
 
 API_FILES += vnet/span/span.api
 
+########################################
+# DNS proxy, API
+########################################
+libvnet_la_SOURCES +=                          \
+ vnet/dns/dns.c                                        \
+ vnet/dns/dns.h                                        \
+ vnet/dns/dns_packet.h                         \
+ vnet/dns/reply_node.c                         \
+ vnet/dns/resolver_process.c
+
+nobase_include_HEADERS +=                      \
+ vnet/dns/dns.api.h                            \
+ vnet/dns/dns.h
+
+API_FILES += vnet/dns/dns.api
+
 ########################################
 # Packet generator
 ########################################
index eeb7276..d77af97 100644 (file)
@@ -122,6 +122,14 @@ _(APP_WRONG_NS_SECRET, -129, "Wrong app namespace secret")         \
 _(APP_CONNECT_SCOPE, -130, "Connect scope")                            \
 _(APP_ALREADY_ATTACHED, -131, "App already attached")                  \
 _(SESSION_REDIRECT, -132, "Redirect failed")                           \
+_(ILLEGAL_NAME, -133, "Illegal name")                                  \
+_(NO_NAME_SERVERS, -134, "No name servers configured")                 \
+_(NAME_SERVER_NOT_FOUND, -135, "Name server not found")                        \
+_(NAME_RESOLUTION_NOT_ENABLED, -136, "Name resolution not enabled")    \
+_(NAME_SERVER_FORMAT_ERROR, -137, "Server format error (bug!)")                \
+_(NAME_SERVER_NO_SUCH_NAME, -138, "No such name")                       \
+_(NAME_SERVER_NO_ADDRESSES, -139, "No addresses available")            \
+_(NAME_SERVER_NEXT_SERVER, -140, "Retry with new server")
 
 typedef enum
 {
diff --git a/src/vnet/dns/dns.api b/src/vnet/dns/dns.api
new file mode 100644 (file)
index 0000000..5557445
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+vl_api_version 1.0.0
+
+/** \brief enable/disable name resolution
+
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_enable - 1 = enable, 0 = disable
+*/
+autoreply define dns_enable_disable {
+    u32 client_index;
+    u32 context;
+    u8 enable;
+};
+
+/** \brief add or delete an upstream name server
+
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param is_ip6 - an ip6 name server
+    @param is_add - add = 1, delete = 0
+    @param server_address - server ip address
+*/
+autoreply define dns_name_server_add_del {
+    u32 client_index;
+    u32 context;
+    u8 is_ip6;
+    u8 is_add;
+    u8 server_address[16];
+};
+
+/** \brief DNS name resolution request
+
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param name - the name to resolve
+*/
+define dns_resolve_name {
+    u32 client_index;
+    u32 context;
+    u8 name[128];
+ };
+
+define dns_resolve_name_reply {
+    u32 context;
+    i32 retval;
+    u8 ip4_set;
+    u8 ip6_set;
+    u8 ip4_address[4];
+    u8 ip6_address[16];
+};
+
diff --git a/src/vnet/dns/dns.c b/src/vnet/dns/dns.c
new file mode 100644 (file)
index 0000000..90079e1
--- /dev/null
@@ -0,0 +1,2114 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/dns/dns.h>
+
+#include <vnet/vnet.h>
+#include <vnet/fib/fib.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/udp/udp.h>
+
+#include <vnet/vnet_msg_enum.h>
+
+#define vl_typedefs            /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun           /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <vnet/vnet_all_api_h.h>
+#undef vl_printfun
+
+#include <vlibapi/api_helper_macros.h>
+
+dns_main_t dns_main;
+
+static int
+dns_cache_clear (dns_main_t * dm)
+{
+  dns_cache_entry_t *ep;
+
+  if (dm->is_enabled == 0)
+    return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
+
+  dns_cache_lock (dm);
+
+  /* *INDENT-OFF* */
+  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->ip4_peers_to_notify);
+    vec_free (ep->ip6_peers_to_notify);
+  }));
+  /* *INDENT-ON* */
+
+  pool_free (dm->entries);
+  hash_free (dm->cache_entry_by_name);
+  dm->cache_entry_by_name = hash_create_string (0, sizeof (uword));
+  vec_free (dm->unresolved_entries);
+  dns_cache_unlock (dm);
+  return 0;
+}
+
+static int
+dns_enable_disable (dns_main_t * dm, int is_enable)
+{
+  vlib_thread_main_t *tm = &vlib_thread_main;
+  u32 n_vlib_mains = tm->n_vlib_mains;
+
+  if (is_enable)
+    {
+      if (vec_len (dm->ip4_name_servers) == 0
+         && (vec_len (dm->ip6_name_servers) == 0))
+       return VNET_API_ERROR_NO_NAME_SERVERS;
+
+      if (dm->cache_entry_by_name == 0)
+       {
+         if (n_vlib_mains > 1)
+           dm->cache_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
+                                                    CLIB_CACHE_LINE_BYTES);
+
+         dm->cache_entry_by_name = hash_create_string (0, sizeof (uword));
+       }
+
+      dm->is_enabled = 1;
+    }
+  else
+    {
+      dns_cache_clear (dm);
+      dm->is_enabled = 0;
+    }
+  return 0;
+}
+
+static void vl_api_dns_enable_disable_t_handler
+  (vl_api_dns_enable_disable_t * mp)
+{
+  vl_api_dns_enable_disable_reply_t *rmp;
+  dns_main_t *dm = &dns_main;
+  int rv;
+
+  rv = dns_enable_disable (dm, mp->enable);
+
+  REPLY_MACRO (VL_API_DNS_ENABLE_DISABLE_REPLY);
+}
+
+static int
+dns6_name_server_add_del (dns_main_t * dm,
+                         u8 * server_address_as_u8, int is_add)
+{
+  int i;
+  ip6_address_t *ap;
+
+  if (is_add)
+    {
+      /* Already there? done... */
+      for (i = 0; i < vec_len (dm->ip6_name_servers); i++)
+       {
+         if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8,
+                      sizeof (ip6_address_t)))
+           return 0;
+       }
+
+      vec_add2 (dm->ip6_name_servers, ap, 1);
+      clib_memcpy (ap, server_address_as_u8, sizeof (*ap));
+    }
+  else
+    {
+      for (i = 0; i < vec_len (dm->ip6_name_servers); i++)
+       {
+         if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8,
+                      sizeof (ip6_address_t)))
+           {
+             vec_delete (dm->ip6_name_servers, 1, i);
+             return 0;
+           }
+       }
+      return VNET_API_ERROR_NAME_SERVER_NOT_FOUND;
+    }
+  return 0;
+}
+
+static int
+dns4_name_server_add_del (dns_main_t * dm,
+                         u8 * server_address_as_u8, int is_add)
+{
+  int i;
+  ip4_address_t *ap;
+
+  if (is_add)
+    {
+      /* Already there? done... */
+      for (i = 0; i < vec_len (dm->ip4_name_servers); i++)
+       {
+         if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8,
+                      sizeof (ip4_address_t)))
+           return 0;
+       }
+
+      vec_add2 (dm->ip4_name_servers, ap, 1);
+      clib_memcpy (ap, server_address_as_u8, sizeof (*ap));
+    }
+  else
+    {
+      for (i = 0; i < vec_len (dm->ip4_name_servers); i++)
+       {
+         if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8,
+                      sizeof (ip4_address_t)))
+           {
+             vec_delete (dm->ip4_name_servers, 1, i);
+             return 0;
+           }
+       }
+      return VNET_API_ERROR_NAME_SERVER_NOT_FOUND;
+    }
+  return 0;
+}
+
+static void vl_api_dns_name_server_add_del_t_handler
+  (vl_api_dns_name_server_add_del_t * mp)
+{
+  dns_main_t *dm = &dns_main;
+  vl_api_dns_name_server_add_del_reply_t *rmp;
+  int rv;
+
+  if (mp->is_ip6)
+    rv = dns6_name_server_add_del (dm, mp->server_address, mp->is_add);
+  else
+    rv = dns4_name_server_add_del (dm, mp->server_address, mp->is_add);
+
+  REPLY_MACRO (VL_API_DNS_NAME_SERVER_ADD_DEL_REPLY);
+}
+
+static void
+send_dns4_request (dns_main_t * dm,
+                  dns_cache_entry_t * ep, ip4_address_t * server)
+{
+  vlib_main_t *vm = dm->vlib_main;
+  f64 now = vlib_time_now (vm);
+  u32 bi;
+  vlib_buffer_t *b;
+  ip4_header_t *ip;
+  fib_prefix_t prefix;
+  fib_node_index_t fei;
+  u32 sw_if_index, fib_index;
+  udp_header_t *udp;
+  ip4_main_t *im4 = &ip4_main;
+  ip_lookup_main_t *lm4 = &im4->lookup_main;
+  ip_interface_address_t *ia = 0;
+  ip4_address_t *src_address;
+  u8 *dns_request;
+  vlib_frame_t *f;
+  u32 *to_next;
+
+  ASSERT (ep->dns_request);
+
+  /* Find a FIB path to the server */
+  clib_memcpy (&prefix.fp_addr.ip4, server, sizeof (*server));
+  prefix.fp_proto = FIB_PROTOCOL_IP4;
+  prefix.fp_len = 32;
+
+  fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
+  if (fib_index == (u32) ~ 0)
+    {
+      clib_warning ("no fib table");
+      return;
+    }
+
+  fei = fib_table_lookup (fib_index, &prefix);
+
+  /* Couldn't find route to destination. Bail out. */
+  if (fei == FIB_NODE_INDEX_INVALID)
+    {
+      clib_warning ("no route to DNS server");
+      return;
+    }
+
+  sw_if_index = fib_entry_get_resolving_interface (fei);
+
+  if (sw_if_index == ~0)
+    {
+      clib_warning
+       ("route to %U exists, fei %d, get_resolving_interface returned"
+        " ~0", fei, format_ip4_address, &prefix.fp_addr);
+      return;
+    }
+
+  /* *INDENT-OFF* */
+  foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnummbered */,
+  ({
+    src_address = ip_interface_address_get_address (lm4, ia);
+    goto found_src_address;
+  }));
+  /* *INDENT-ON* */
+
+  clib_warning ("FIB BUG");
+  return;
+
+found_src_address:
+
+  /* Go get a buffer */
+  if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1)
+    return;
+
+  b = vlib_get_buffer (vm, bi);
+  b->current_length = sizeof (ip4_header_t) + sizeof (udp_header_t) +
+    vec_len (ep->dns_request);
+  b->total_length_not_including_first_buffer = 0;
+  b->flags =
+    VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
+  vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;   /* "local0" */
+  vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;   /* default VRF for now */
+
+  ip = vlib_buffer_get_current (b);
+  memset (ip, 0, sizeof (*ip));
+  udp = (udp_header_t *) (ip + 1);
+  memset (udp, 0, sizeof (*udp));
+
+  dns_request = (u8 *) (udp + 1);
+
+  /* IP header */
+  ip->ip_version_and_header_length = 0x45;
+  ip->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b));
+  ip->ttl = 255;
+  ip->protocol = IP_PROTOCOL_UDP;
+  ip->src_address.as_u32 = src_address->as_u32;
+  ip->dst_address.as_u32 = server->as_u32;
+  ip->checksum = ip4_header_checksum (ip);
+
+  /* UDP header */
+  udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply);
+  udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
+  udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
+                                     vec_len (ep->dns_request));
+  udp->checksum = 0;
+
+  /* The actual DNS request */
+  clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request));
+
+  /* Ship it to ip4_lookup */
+  f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
+  to_next = vlib_frame_vector_args (f);
+  to_next[0] = bi;
+  f->n_vectors = 1;
+  vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
+
+  ep->retry_timer = now + 2.0;
+}
+
+static void
+send_dns6_request (dns_main_t * dm,
+                  dns_cache_entry_t * ep, ip6_address_t * server)
+{
+  vlib_main_t *vm = dm->vlib_main;
+  f64 now = vlib_time_now (vm);
+  u32 bi;
+  vlib_buffer_t *b;
+  ip6_header_t *ip;
+  fib_prefix_t prefix;
+  fib_node_index_t fei;
+  u32 sw_if_index, fib_index;
+  udp_header_t *udp;
+  ip6_main_t *im6 = &ip6_main;
+  ip_lookup_main_t *lm6 = &im6->lookup_main;
+  ip_interface_address_t *ia = 0;
+  ip6_address_t *src_address;
+  u8 *dns_request;
+  vlib_frame_t *f;
+  u32 *to_next;
+  int junk __attribute__ ((unused));
+
+  ASSERT (ep->dns_request);
+
+  /* Find a FIB path to the server */
+  clib_memcpy (&prefix.fp_addr, server, sizeof (*server));
+  prefix.fp_proto = FIB_PROTOCOL_IP6;
+  prefix.fp_len = 32;
+
+  fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
+  if (fib_index == (u32) ~ 0)
+    {
+      clib_warning ("no fib table");
+      return;
+    }
+
+  fei = fib_table_lookup (fib_index, &prefix);
+
+  /* Couldn't find route to destination. Bail out. */
+  if (fei == FIB_NODE_INDEX_INVALID)
+    {
+      clib_warning ("no route to DNS server");
+    }
+
+  sw_if_index = fib_entry_get_resolving_interface (fei);
+
+  /* *INDENT-OFF* */
+  foreach_ip_interface_address(lm6, ia, sw_if_index, 1 /* honor unnummbered */,
+  ({
+    src_address = ip_interface_address_get_address (lm6, ia);
+    goto found_src_address;
+  }));
+  /* *INDENT-ON* */
+
+  clib_warning ("FIB BUG");
+  return;
+
+found_src_address:
+
+  /* Go get a buffer */
+  if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1)
+    return;
+
+  b = vlib_get_buffer (vm, bi);
+  b->current_length = sizeof (ip6_header_t) + sizeof (udp_header_t) +
+    vec_len (ep->dns_request);
+  b->total_length_not_including_first_buffer = 0;
+  b->flags =
+    VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
+
+  ip = vlib_buffer_get_current (b);
+  memset (ip, 0, sizeof (*ip));
+  udp = (udp_header_t *) (ip + 1);
+  memset (udp, 0, sizeof (*udp));
+
+  dns_request = (u8 *) (udp + 1);
+
+  /* IP header */
+  ip->ip_version_traffic_class_and_flow_label =
+    clib_host_to_net_u32 (0x6 << 28);
+
+  ip->payload_length =
+    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b)
+                         - sizeof (ip6_header_t));
+  ip->hop_limit = 255;
+  ip->protocol = IP_PROTOCOL_UDP;
+  clib_memcpy (&ip->src_address, src_address, sizeof (ip6_address_t));
+  clib_memcpy (&ip->dst_address, server, sizeof (ip6_address_t));
+
+  /* UDP header */
+  udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply);
+  udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
+  udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
+                                     vec_len (ep->dns_request));
+  udp->checksum = 0;
+  udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip, &junk);
+
+  /* The actual DNS request */
+  clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request));
+
+  /* Ship it to ip6_lookup */
+  f = vlib_get_frame_to_node (vm, ip6_lookup_node.index);
+  to_next = vlib_frame_vector_args (f);
+  to_next[0] = bi;
+  f->n_vectors = 1;
+
+  ep->retry_timer = now + 2.0;
+}
+
+/**
+ * Translate "foo.com" into "0x3 f o o 0x3 c o m 0x0"
+ * A historical / hysterical micro-TLV scheme. DGMS.
+ */
+u8 *
+name_to_labels (u8 * name)
+{
+  int i;
+  int last_label_index;
+  u8 *rv;
+
+  rv = vec_dup (name);
+
+  /* punch in space for the first length */
+  vec_insert (rv, 1, 0);
+  last_label_index = 0;
+  i = 1;
+
+  while (i < vec_len (rv))
+    {
+      if (rv[i] == '.')
+       {
+         rv[last_label_index] = (i - last_label_index) - 1;
+         if ((i - last_label_index) > 63)
+           clib_warning ("stupid name, label length %d",
+                         i - last_label_index);
+         last_label_index = i;
+         rv[i] = 0;
+       }
+      i++;
+    }
+  /* Set the last real label length */
+  rv[last_label_index] = (i - last_label_index) - 1;
+
+  /*
+   * Add a [sic] NULL root label. Otherwise, the name parser can't figure out
+   * where to stop.
+   */
+  vec_add1 (rv, 0);
+  return rv;
+}
+
+/**
+ * arc-function for the above.
+ * Translate "0x3 f o o 0x3 c o m 0x0" into "foo.com"
+ * Produces a non-NULL-terminated u8 *vector. %v format is your friend.
+ */
+u8 *
+labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here)
+{
+  u8 *reply = 0;
+  u16 offset;
+  u8 len;
+  int i;
+
+  *parse_from_here = 0;
+
+  /* chase initial pointer? */
+  if ((label[0] & 0xC0) == 0xC0)
+    {
+      *parse_from_here = label + 2;
+      offset = ((label[0] & 0x3f) << 8) + label[1];
+      label = full_text + offset;
+    }
+
+  len = *label++;
+
+  while (len)
+    {
+      for (i = 0; i < len; i++)
+       vec_add1 (reply, *label++);
+
+      /* chase pointer? */
+      if ((label[0] & 0xC0) == 0xC0)
+       {
+         *parse_from_here = label + 2;
+         offset = ((label[0] & 0x3f) << 8) + label[1];
+         label = full_text + offset;
+       }
+
+      len = *label++;
+      if (len)
+       vec_add1 (reply, '.');
+    }
+  if (*parse_from_here == 0)
+    *parse_from_here = label;
+  return reply;
+}
+
+void
+vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
+{
+  dns_header_t *h;
+  dns_query_t *qp;
+  u16 tmp;
+  u8 *request;
+  u32 qp_offset;
+
+  /* Construct the dns request, if we haven't been here already */
+  if (vec_len (ep->dns_request) == 0)
+    {
+      /*
+       * Start with the variadic portion of the exercise.
+       * Turn the name into a set of DNS "labels". Max length
+       * per label is 63, enforce that.
+       */
+      request = name_to_labels (ep->name);
+      qp_offset = vec_len (request);
+
+      /* Add space for the query header */
+      vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
+
+      qp = (dns_query_t *) (request + qp_offset);
+
+      qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
+      qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+
+      /* Punch in space for the dns_header_t */
+      vec_insert (request, sizeof (dns_header_t), 0);
+
+      h = (dns_header_t *) request;
+
+      /* Transaction ID = pool index */
+      h->id = clib_host_to_net_u16 (ep - dm->entries);
+
+      /* Ask for a recursive lookup */
+      tmp = DNS_RD | DNS_OPCODE_QUERY;
+      h->flags = clib_host_to_net_u16 (tmp);
+      h->qdcount = clib_host_to_net_u16 (1);
+      h->nscount = 0;
+      h->arcount = 0;
+
+      ep->dns_request = request;
+    }
+
+  /* Work out which server / address family we're going to use */
+
+  /* Retry using current server */
+  if (ep->retry_count++ < DNS_RETRIES_PER_SERVER)
+    {
+      if (ep->server_af == 1 /* ip6 */ )
+       {
+         if (vec_len (dm->ip6_name_servers))
+           {
+             send_dns6_request (dm, ep,
+                                dm->ip6_name_servers + ep->server_rotor);
+             goto out;
+           }
+         else
+           ep->server_af = 0;
+       }
+      if (vec_len (dm->ip4_name_servers))
+       {
+         send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
+         goto out;
+       }
+    }
+  else                         /* switch to a new server */
+    {
+      ep->retry_count = 1;
+      ep->server_rotor++;
+      if (ep->server_af == 1 /* ip6 */ )
+       {
+         if (ep->server_rotor >= vec_len (dm->ip6_name_servers))
+           {
+             ep->server_rotor = 0;
+             ep->server_af = vec_len (dm->ip4_name_servers) > 0 ? 0 : 1;
+           }
+       }
+      else
+       {
+         if (ep->server_rotor >= vec_len (dm->ip4_name_servers))
+           {
+             ep->server_rotor = 0;
+             ep->server_af = vec_len (dm->ip6_name_servers) > 0 ? 1 : 0;
+           }
+       }
+    }
+
+  if (ep->server_af == 1 /* ip6 */ )
+    send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor);
+  else
+    send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
+
+out:
+
+  vlib_process_signal_event_mt (dm->vlib_main, dns_resolver_node.index,
+                               DNS_RESOLVER_EVENT_PENDING, 0);
+}
+
+int
+vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index)
+{
+  dns_cache_entry_t *ep;
+  int i;
+
+  if (dm->is_enabled == 0)
+    return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
+
+  if (pool_is_free_index (dm->entries, index))
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+  ep = pool_elt_at_index (dm->entries, index);
+
+  if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_VALID))
+    {
+      for (i = 0; i < vec_len (dm->unresolved_entries); i++)
+       if (index == dm->unresolved_entries[i])
+         {
+           vec_delete (dm->unresolved_entries, 1, i);
+           goto found;
+         }
+      clib_warning ("pool elt %d supposedly pending, but not found...",
+                   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->ip4_peers_to_notify);
+  vec_free (ep->ip6_peers_to_notify);
+  pool_put (dm->entries, ep);
+
+  return 0;
+}
+
+static int
+dns_delete_by_name (dns_main_t * dm, u8 * name)
+{
+  int rv;
+  uword *p;
+
+  if (dm->is_enabled == 0)
+    return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
+
+  dns_cache_lock (dm);
+  p = hash_get_mem (dm->cache_entry_by_name, name);
+  if (!p)
+    {
+      dns_cache_unlock (dm);
+      return VNET_API_ERROR_NO_SUCH_ENTRY;
+    }
+  rv = vnet_dns_delete_entry_by_index_nolock (dm, p[0]);
+
+  dns_cache_unlock (dm);
+
+  return rv;
+}
+
+static int
+delete_random_entry (dns_main_t * dm)
+{
+  int rv;
+  u32 victim_index, start_index, i;
+  u32 limit;
+  dns_cache_entry_t *ep;
+
+  if (dm->is_enabled == 0)
+    return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
+
+  dns_cache_lock (dm);
+  limit = pool_elts (dm->entries);
+  start_index = random_u32 (&dm->random_seed) % limit;
+
+  for (i = 0; i < limit; i++)
+    {
+      victim_index = (start_index + i) % limit;
+
+      if (!pool_is_free_index (dm->entries, victim_index))
+       {
+         ep = pool_elt_at_index (dm->entries, victim_index);
+         /* Delete only valid, non-static entries */
+         if ((ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
+             && ((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0))
+           {
+             rv = vnet_dns_delete_entry_by_index_nolock (dm, victim_index);
+             dns_cache_unlock (dm);
+             return rv;
+           }
+       }
+    }
+  dns_cache_unlock (dm);
+
+  clib_warning ("Couldn't find an entry to delete?");
+  return VNET_API_ERROR_UNSPECIFIED;
+}
+
+static int
+dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data)
+{
+  dns_cache_entry_t *ep;
+  uword *p;
+  int rv;
+
+  if (dm->is_enabled == 0)
+    return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
+
+  dns_cache_lock (dm);
+  p = hash_get_mem (dm->cache_entry_by_name, name);
+  if (p)
+    {
+      dns_cache_unlock (dm);
+      return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
+    }
+
+  if (pool_elts (dm->entries) == dm->name_cache_size)
+    {
+      /* Will only fail if the cache is totally filled w/ static entries... */
+      rv = delete_random_entry (dm);
+      if (rv)
+       {
+         dns_cache_unlock (dm);
+         return rv;
+       }
+    }
+
+  pool_get (dm->entries, ep);
+  memset (ep, 0, sizeof (*ep));
+
+  /* Note: consumes the name vector */
+  ep->name = name;
+  hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
+  ep->flags = DNS_CACHE_ENTRY_FLAG_VALID | DNS_CACHE_ENTRY_FLAG_STATIC;
+  ep->dns_response = dns_reply_data;
+
+  dns_cache_unlock (dm);
+  return 0;
+}
+
+static int
+dns_resolve_name (dns_main_t * dm,
+                 u8 * name, u32 client_index, u32 client_context,
+                 dns_cache_entry_t ** retp)
+{
+  dns_cache_entry_t *ep;
+  int rv;
+  f64 now;
+  uword *p;
+
+  now = vlib_time_now (dm->vlib_main);
+
+  /* In case we can't actually answer the question right now... */
+  *retp = 0;
+
+  dns_cache_lock (dm);
+  p = hash_get_mem (dm->cache_entry_by_name, name);
+  if (p)
+    {
+      ep = pool_elt_at_index (dm->entries, p[0]);
+      if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
+       {
+         /* Has the entry expired? */
+         if (((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0)
+             && (now > ep->expiration_time))
+           {
+             clib_warning ("Re-resolve %s", name);
+             /* Yes, kill it... */
+             vnet_dns_delete_entry_by_index_nolock (dm, p[0]);
+             goto re_resolve;
+           }
+
+         /* Note: caller must drop the lock! */
+         *retp = ep;
+         return (0);
+       }
+    }
+
+  if (pool_elts (dm->entries) == dm->name_cache_size)
+    {
+      /* Will only fail if the cache is totally filled w/ static entries... */
+      rv = delete_random_entry (dm);
+      if (rv)
+       {
+         dns_cache_unlock (dm);
+         return rv;
+       }
+    }
+
+re_resolve:
+  /* add new hash table entry */
+  pool_get (dm->entries, ep);
+  memset (ep, 0, sizeof (*ep));
+
+  ep->name = format (0, "%s%c", name, 0);
+  _vec_len (ep->name) = vec_len (ep->name) - 1;
+
+  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);
+  vnet_send_dns_request (dm, ep);
+  dns_cache_unlock (dm);
+
+  return 0;
+}
+
+/**
+ * Handle cname indirection. JFC. Called with the cache locked.
+ * returns 0 if the reply is not a CNAME.
+ */
+
+int
+vnet_dns_cname_indirection_nolock (dns_main_t * dm, dns_cache_entry_t * ep,
+                                  u8 * reply)
+{
+  dns_header_t *h;
+  dns_query_t *qp;
+  dns_rr_t *rr;
+  u8 *curpos;
+  u8 *pos, *pos2;
+  int len, i;
+  u8 *cname = 0;
+  u8 *request = 0;
+  u32 qp_offset;
+  u16 flags;
+  u16 rcode;
+
+  h = (dns_header_t *) reply;
+  flags = clib_net_to_host_u16 (h->flags);
+  rcode = flags & DNS_RCODE_MASK;
+
+  /* See if the response is OK */
+  switch (rcode)
+    {
+    case DNS_RCODE_NO_ERROR:
+      break;
+
+    case DNS_RCODE_NAME_ERROR:
+    case DNS_RCODE_FORMAT_ERROR:
+    case DNS_RCODE_SERVER_FAILURE:
+    case DNS_RCODE_NOT_IMPLEMENTED:
+    case DNS_RCODE_REFUSED:
+      return 0;
+    }
+
+  curpos = (u8 *) (h + 1);
+  pos = curpos;
+  len = *pos++;
+
+  /* Skip the questions */
+  for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
+    {
+      while (len)
+       {
+         pos += len;
+         len = *pos++;
+       }
+      qp = (dns_query_t *) pos;
+      pos += sizeof (*qp);
+    }
+  pos2 = pos;
+  /* expect a pointer chase here for a CNAME record */
+  if ((pos2[0] & 0xC0) == 0xC0)
+    pos += 2;
+  else
+    return 0;
+
+  rr = (dns_rr_t *) pos;
+
+  /* This is a real record, not a CNAME record */
+  if (clib_net_to_host_u16 (rr->type) != DNS_TYPE_CNAME)
+    return 0;
+
+  /* Crap. Chase the CNAME name chain. */
+
+  cname = labels_to_name (rr->rdata, reply, &pos2);
+  request = name_to_labels (cname);
+  vec_free (cname);
+
+  qp_offset = vec_len (request);
+
+  /* Add space for the query header */
+  vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
+
+  qp = (dns_query_t *) (request + qp_offset);
+
+  qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
+  qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+
+  /* Punch in space for the dns_header_t */
+  vec_insert (request, sizeof (dns_header_t), 0);
+
+  h = (dns_header_t *) request;
+
+  /* Transaction ID = pool index */
+  h->id = clib_host_to_net_u16 (ep - dm->entries);
+
+  /* Ask for a recursive lookup */
+  h->flags = clib_host_to_net_u16 (DNS_RD | DNS_OPCODE_QUERY);
+  h->qdcount = clib_host_to_net_u16 (1);
+  h->nscount = 0;
+  h->arcount = 0;
+
+  vec_free (ep->dns_request);
+  ep->dns_request = request;
+  ep->retry_timer = vlib_time_now (dm->vlib_main) + 2.0;
+  ep->retry_count = 0;
+
+  /*
+   * Enable this to watch recursive resolution happen...
+   * fformat (stdout, "%U", format_dns_reply, request, 2);
+   */
+
+  if (ep->server_af == 1 /* ip6 */ )
+    send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor);
+  else
+    send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
+
+  vec_free (reply);
+  return (1);
+}
+
+int
+vnet_dns_response_to_reply (u8 * response,
+                           vl_api_dns_resolve_name_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;
+  u32 ttl;
+
+  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;
+                 break;
+               }
+             pos += len;
+             len = *pos++;
+           }
+         curpos = pos;
+       }
+
+      rr = (dns_rr_t *) curpos;
+
+      switch (clib_net_to_host_u16 (rr->type))
+       {
+       case DNS_TYPE_A:
+         /* Collect an ip4 address. Do not pass go. Do not collect $200 */
+         memcpy (rmp->ip4_address, rr->rdata, sizeof (ip4_address_t));
+         rmp->ip4_set = 1;
+         ttl = clib_net_to_host_u32 (rr->ttl);
+         if (min_ttlp && *min_ttlp > ttl)
+           *min_ttlp = ttl;
+         break;
+       case DNS_TYPE_AAAA:
+         /* Collect an ip6 address. Do not pass go. Do not collect $200 */
+         memcpy (rmp->ip6_address, rr->rdata, sizeof (ip6_address_t));
+         ttl = clib_net_to_host_u32 (rr->ttl);
+         if (min_ttlp && *min_ttlp > ttl)
+           *min_ttlp = ttl;
+         rmp->ip6_set = 1;
+         break;
+       default:
+         break;
+       }
+      /* Might as well stop ASAP */
+      if (rmp->ip4_set && rmp->ip6_set)
+       break;
+      curpos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+    }
+
+  if ((rmp->ip4_set + rmp->ip6_set) == 0)
+    return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
+  return 0;
+}
+
+static void
+vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp)
+{
+  dns_main_t *dm = &dns_main;
+  vl_api_dns_resolve_name_reply_t *rmp;
+  dns_cache_entry_t *ep;
+  int rv;
+
+  /* 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);
+
+  /* Error, e.g. not enabled? Tell the user */
+  if (rv < 0)
+    {
+      REPLY_MACRO (VL_API_DNS_RESOLVE_NAME_REPLY);
+      return;
+    }
+
+  /* Resolution pending? Don't reply... */
+  if (ep == 0)
+    return;
+
+  /* *INDENT-OFF* */
+  REPLY_MACRO2(VL_API_DNS_RESOLVE_NAME_REPLY,
+  ({
+    rv = vnet_dns_response_to_reply (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
+
+static void
+setup_message_id_table (api_main_t * am)
+{
+#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
+  foreach_vl_msg_name_crc_dns;
+#undef _
+}
+
+#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)
+
+static clib_error_t *
+dns_api_hookup (vlib_main_t * vm)
+{
+#define _(N,n)                                                  \
+    vl_msg_api_set_handlers(VL_API_##N, #n,                     \
+                           vl_api_##n##_t_handler,              \
+                           vl_noop_handler,                     \
+                           vl_api_##n##_t_endian,               \
+                           vl_api_##n##_t_print,                \
+                           sizeof(vl_api_##n##_t), 1);
+  foreach_dns_api_msg;
+#undef _
+
+  setup_message_id_table (&api_main);
+  return 0;
+}
+
+VLIB_API_INIT_FUNCTION (dns_api_hookup);
+
+
+static clib_error_t *
+dns_config_fn (vlib_main_t * vm, unformat_input_t * input)
+{
+  dns_main_t *dm = &dns_main;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "max-cache-size %u", &dm->name_cache_size))
+       ;
+      else if (unformat (input, "max-ttl %u", &dm->max_ttl_in_seconds))
+       ;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+  return 0;
+}
+
+VLIB_CONFIG_FUNCTION (dns_config_fn, "dns");
+
+static clib_error_t *
+dns_init (vlib_main_t * vm)
+{
+  dns_main_t *dm = &dns_main;
+
+  dm->vlib_main = vm;
+  dm->vnet_main = vnet_get_main ();
+  dm->name_cache_size = 65535;
+  dm->max_ttl_in_seconds = 86400;
+  dm->random_seed = 0xDEADDABE;
+
+  udp_register_dst_port (vm, UDP_DST_PORT_dns_reply, dns46_reply_node.index,
+                        1 /* is_ip4 */ );
+
+  udp_register_dst_port (vm, UDP_DST_PORT_dns_reply6, dns46_reply_node.index,
+                        0 /* is_ip4 */ );
+
+#if 0
+  udp_register_dst_port (vm, UDP_DST_PORT_dns, dns4_request_node.index,
+                        1 /* is_ip4 */ );
+  udp_register_dst_port (vm, UDP_DST_PORT_dns6, dns6_request_node.index,
+                        0 /* is_ip4 */ );
+#endif
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (dns_init);
+
+uword
+unformat_dns_reply (unformat_input_t * input, va_list * args)
+{
+  u8 **result = va_arg (*args, u8 **);
+  u8 **namep = va_arg (*args, u8 **);
+  ip4_address_t a4;
+  ip6_address_t a6;
+  int a4_set = 0;
+  int a6_set = 0;
+  u8 *name;
+  int name_set = 0;
+  u8 *ce;
+  u32 qp_offset;
+  dns_header_t *h;
+  dns_query_t *qp;
+  dns_rr_t *rr;
+  u8 *rru8;
+
+  if (unformat (input, "%v", &name))
+    name_set = 1;
+
+  if (unformat (input, "%U", unformat_ip4_address, &a4))
+    {
+      a4_set = 1;
+      if (unformat (input, "%U", unformat_ip6_address, &a6))
+       a6_set = 1;
+    }
+
+  if (unformat (input, "%U", unformat_ip6_address, &a6))
+    {
+      a6_set = 1;
+      if (unformat (input, "%U", unformat_ip4_address, &a6))
+       a4_set = 1;
+    }
+
+  /* Must have a name */
+  if (!name_set)
+    return 0;
+
+  /* Must have at least one address */
+  if (!(a4_set + a6_set))
+    return 0;
+
+  /* Build a fake DNS cache entry string, one hemorrhoid at a time */
+  ce = name_to_labels (name);
+  qp_offset = vec_len (ce);
+
+  /* Add space for the query header */
+  vec_validate (ce, qp_offset + sizeof (dns_query_t) - 1);
+  qp = (dns_query_t *) (ce + qp_offset);
+
+  qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
+  qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+
+  /* Punch in space for the dns_header_t */
+  vec_insert (ce, sizeof (dns_header_t), 0);
+
+  h = (dns_header_t *) ce;
+
+  /* Fake Transaction ID */
+  h->id = 0xFFFF;
+
+  h->flags = clib_host_to_net_u16 (DNS_RD | DNS_RA);
+  h->qdcount = clib_host_to_net_u16 (1);
+  h->anscount = clib_host_to_net_u16 (a4_set + a6_set);
+  h->nscount = 0;
+  h->arcount = 0;
+
+  /* Now append one or two A/AAAA RR's... */
+  if (a4_set)
+    {
+      /* Pointer to the name (DGMS) */
+      vec_add1 (ce, 0xC0);
+      vec_add1 (ce, 0x0C);
+      vec_add2 (ce, rru8, sizeof (*rr) + 4);
+      rr = (void *) rru8;
+      rr->type = clib_host_to_net_u16 (DNS_TYPE_A);
+      rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+      rr->ttl = clib_host_to_net_u32 (86400);
+      rr->rdlength = clib_host_to_net_u16 (4);
+      memcpy (rr->rdata, &a4, sizeof (a4));
+    }
+  if (a6_set)
+    {
+      /* Pointer to the name (DGMS) */
+      vec_add1 (ce, 0xC0);
+      vec_add1 (ce, 0x0C);
+      vec_add2 (ce, rru8, sizeof (*rr) + 16);
+      rr = (void *) rru8;
+      rr->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
+      rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+      rr->ttl = clib_host_to_net_u32 (86400);
+      rr->rdlength = clib_host_to_net_u16 (16);
+      memcpy (rr->rdata, &a6, sizeof (a6));
+    }
+  *result = ce;
+  if (namep)
+    *namep = name;
+  else
+    vec_free (name);
+
+  return 1;
+}
+
+u8 *
+format_dns_query (u8 * s, va_list * args)
+{
+  u8 **curpos = va_arg (*args, u8 **);
+  int verbose = va_arg (*args, int);
+  u8 *pos;
+  dns_query_t *qp;
+  int len, i;
+  if (verbose > 1)
+    s = format (s, "    Name: ");
+
+  /* Unwind execrated counted-label sheit */
+  pos = *curpos;
+  len = *pos++;
+
+  while (len)
+    {
+      for (i = 0; i < len; i++)
+       vec_add1 (s, *pos++);
+
+      len = *pos++;
+      if (len)
+       vec_add1 (s, '.');
+      else
+       {
+         vec_add1 (s, ':');
+         vec_add1 (s, ' ');
+       }
+    }
+
+  qp = (dns_query_t *) pos;
+  if (verbose > 1)
+    {
+      switch (clib_net_to_host_u16 (qp->type))
+       {
+       case DNS_TYPE_A:
+         s = format (s, "type A\n");
+         break;
+       case DNS_TYPE_AAAA:
+         s = format (s, "type AAAA\n");
+         break;
+       case DNS_TYPE_ALL:
+         s = format (s, "type ALL\n");
+         break;
+
+       default:
+         s = format (s, "type %d\n", clib_net_to_host_u16 (qp->type));
+         break;
+       }
+    }
+
+  pos += sizeof (*qp);
+
+  *curpos = pos;
+  return s;
+}
+
+/**
+ * format dns reply data
+ * verbose > 1, dump everything
+ * verbose == 1, dump all A and AAAA records
+ * verbose == 0, dump one A record, and one AAAA record
+ */
+
+u8 *
+format_dns_reply_data (u8 * s, va_list * args)
+{
+  u8 *reply = va_arg (*args, u8 *);
+  u8 **curpos = va_arg (*args, u8 **);
+  int verbose = va_arg (*args, int);
+  int *print_ip4 = va_arg (*args, int *);
+  int *print_ip6 = va_arg (*args, int *);
+  int len;
+  u8 *pos, *pos2;
+  dns_rr_t *rr;
+  int i;
+  int initial_pointer_chase = 0;
+  u16 *tp;
+
+  pos = pos2 = *curpos;
+
+  if (verbose > 1)
+    s = format (s, "    ");
+
+  /* chase pointer? almost always yes here... */
+  if (pos2[0] == 0xc0)
+    {
+      pos2 = reply + pos2[1];
+      pos += 2;
+      initial_pointer_chase = 1;
+    }
+
+  len = *pos2++;
+
+  while (len)
+    {
+      for (i = 0; i < len; i++)
+       {
+         if (verbose > 1)
+           vec_add1 (s, *pos2);
+         pos2++;
+       }
+      len = *pos2++;
+      if (len)
+       {
+         if (verbose > 1)
+           vec_add1 (s, '.');
+       }
+      else
+       {
+         if (verbose > 1)
+           vec_add1 (s, ' ');
+       }
+    }
+
+  if (initial_pointer_chase == 0)
+    pos = pos2;
+
+  rr = (dns_rr_t *) pos;
+
+  switch (clib_net_to_host_u16 (rr->type))
+    {
+    case DNS_TYPE_A:
+      if (verbose > 1)
+       {
+         s = format (s, "A: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
+                     format_ip4_address, rr->rdata);
+       }
+      else
+       {
+         if (*print_ip4)
+           s = format (s, "%U [%u] ", format_ip4_address, rr->rdata,
+                       clib_net_to_host_u32 (rr->ttl));
+         if (verbose == 0)
+           *print_ip4 = 0;
+
+       }
+      pos += sizeof (*rr) + 4;
+      break;
+
+    case DNS_TYPE_AAAA:
+      if (verbose > 1)
+       {
+         s = format (s, "AAAA: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
+                     format_ip6_address, rr->rdata);
+       }
+      else
+       {
+         if (*print_ip6)
+           s = format (s, "%U [%u] ", format_ip6_address, rr->rdata,
+                       clib_net_to_host_u32 (rr->ttl));
+         if (verbose == 0)
+           *print_ip6 = 0;
+       }
+      pos += sizeof (*rr) + 16;
+      break;
+
+    case DNS_TYPE_TEXT:
+      if (verbose > 1)
+       {
+         s = format (s, "TEXT: ");
+         for (i = 0; i < clib_net_to_host_u16 (rr->rdlength); i++)
+           vec_add1 (s, rr->rdata[i]);
+         vec_add1 (s, '\n');
+       }
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      break;
+
+    case DNS_TYPE_NAMESERVER:
+      if (verbose > 1)
+       {
+         s = format (s, "Nameserver: ");
+         pos2 = rr->rdata;
+
+         /* chase pointer? */
+         if (pos2[0] == 0xc0)
+           pos2 = reply + pos2[1];
+
+         len = *pos2++;
+
+         while (len)
+           {
+             for (i = 0; i < len; i++)
+               vec_add1 (s, *pos2++);
+
+             /* chase pointer, typically to offset 12... */
+             if (pos2[0] == 0xC0)
+               pos2 = reply + pos2[1];
+
+             len = *pos2++;
+             if (len)
+               vec_add1 (s, '.');
+             else
+               vec_add1 (s, '\n');
+           }
+       }
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      break;
+
+    case DNS_TYPE_MAIL_EXCHANGE:
+      if (verbose > 1)
+       {
+         tp = (u16 *) rr->rdata;
+
+         s = format (s, "Mail Exchange: Preference %d ", (u32)
+                     clib_net_to_host_u16 (*tp));
+
+         pos2 = rr->rdata + 2;
+
+         /* chase pointer? */
+         if (pos2[0] == 0xc0)
+           pos2 = reply + pos2[1];
+
+         len = *pos2++;
+
+         while (len)
+           {
+             for (i = 0; i < len; i++)
+               vec_add1 (s, *pos2++);
+
+             /* chase pointer */
+             if (pos2[0] == 0xC0)
+               pos2 = reply + pos2[1];
+
+             len = *pos2++;
+             if (len)
+               vec_add1 (s, '.');
+             else
+               vec_add1 (s, '\n');
+           }
+       }
+
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      break;
+
+    case DNS_TYPE_CNAME:
+      if (verbose > 1)
+       {
+         tp = (u16 *) rr->rdata;
+
+         s = format (s, "CNAME: ");
+
+         pos2 = rr->rdata;
+
+         /* chase pointer? */
+         if (pos2[0] == 0xc0)
+           pos2 = reply + pos2[1];
+
+         len = *pos2++;
+
+         while (len)
+           {
+             for (i = 0; i < len; i++)
+               vec_add1 (s, *pos2++);
+
+             /* chase pointer */
+             if (pos2[0] == 0xC0)
+               pos2 = reply + pos2[1];
+
+             len = *pos2++;
+             if (len)
+               vec_add1 (s, '.');
+             else
+               vec_add1 (s, '\n');
+           }
+       }
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      break;
+
+    default:
+      if (verbose > 1)
+       s = format (s, "type %d: len %d\n",
+                   (int) clib_net_to_host_u16 (rr->type),
+                   sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength));
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      break;
+    }
+
+  *curpos = pos;
+
+  return s;
+}
+
+u8 *
+format_dns_reply (u8 * s, va_list * args)
+{
+  u8 *reply_as_u8 = va_arg (*args, u8 *);
+  int verbose = va_arg (*args, int);
+  dns_header_t *h;
+  u16 id, flags;
+  u8 *curpos;
+  int i;
+  int print_ip4 = 1;
+  int print_ip6 = 1;
+
+  h = (dns_header_t *) reply_as_u8;
+  id = clib_net_to_host_u16 (h->id);
+  flags = clib_net_to_host_u16 (h->flags);
+
+  if (verbose > 1)
+    {
+      s = format (s, "DNS %s: id %d\n", (flags & DNS_QR) ? "reply" : "query",
+                 id);
+      s = format (s, "  %s %s %s %s\n",
+                 (flags & DNS_RA) ? "recur" : "no-recur",
+                 (flags & DNS_RD) ? "recur-des" : "no-recur-des",
+                 (flags & DNS_TC) ? "trunc" : "no-trunc",
+                 (flags & DNS_AA) ? "auth" : "non-auth");
+      s = format (s, "  %d queries, %d answers, %d name-servers,"
+                 " %d add'l recs\n",
+                 clib_net_to_host_u16 (h->qdcount),
+                 clib_net_to_host_u16 (h->anscount),
+                 clib_net_to_host_u16 (h->nscount),
+                 clib_net_to_host_u16 (h->arcount));
+    }
+
+  curpos = (u8 *) (h + 1);
+
+  if (h->qdcount)
+    {
+      if (verbose > 1)
+       s = format (s, "  Queries:\n");
+      for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
+       {
+         /* The query is variable-length, so curpos is a value-result parm */
+         s = format (s, "%U", format_dns_query, &curpos, verbose);
+       }
+    }
+  if (h->anscount)
+    {
+      if (verbose > 1)
+       s = format (s, "  Replies:\n");
+
+      for (i = 0; i < clib_net_to_host_u16 (h->anscount); i++)
+       {
+         /* curpos is a value-result parm */
+         s = format (s, "%U", format_dns_reply_data, reply_as_u8, &curpos,
+                     verbose, &print_ip4, &print_ip6);
+       }
+    }
+  return s;
+}
+
+u8 *
+format_dns_cache (u8 * s, va_list * args)
+{
+  dns_main_t *dm = va_arg (*args, dns_main_t *);
+  f64 now = va_arg (*args, f64);
+  int verbose = va_arg (*args, int);
+  u8 *name = va_arg (*args, u8 *);
+  dns_cache_entry_t *ep;
+  char *ss;
+  uword *p;
+
+  if (dm->is_enabled == 0)
+    {
+      s = format (s, "The DNS cache is disabled...");
+      return s;
+    }
+
+  if (pool_elts (dm->entries) == 0)
+    {
+      s = format (s, "The DNS cache is empty...");
+      return s;
+    }
+
+  dns_cache_lock (dm);
+
+  if (name)
+    {
+      p = hash_get_mem (dm->cache_entry_by_name, name);
+      if (!p)
+       {
+         s = format (s, "%s is not in the cache...", name);
+         dns_cache_unlock (dm);
+         return (s);
+       }
+
+      ep = pool_elt_at_index (dm->entries, p[0]);
+      /* Magic to spit out a C-initializer to research hemorrhoids... */
+      if (verbose == 3)
+       {
+         int i, j;
+         s = format (s, "static u8 dns_reply_data_initializer[] =\n");
+         s = format (s, "{\n");
+         j = 0;
+         for (i = 0; i < vec_len (ep->dns_response); i++)
+           {
+             if (j++ == 8)
+               {
+                 j = 0;
+                 vec_add1 (s, '\n');
+               }
+             s = format (s, "0x%02x, ", ep->dns_response[i]);
+           }
+         s = format (s, "};\n");
+       }
+      else
+       {
+         if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
+           {
+             ASSERT (ep->dns_response);
+             if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
+               ss = "[S] ";
+             else
+               ss = "    ";
+
+             s = format (s, "%s%s -> %U", ss, ep->name,
+                         format_dns_reply, ep->dns_response, verbose);
+             if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
+               {
+                 f64 time_left = ep->expiration_time - now;
+                 if (time_left > 0.0)
+                   s = format (s, "  TTL left %.1f", time_left);
+                 else
+                   s = format (s, "  EXPIRED");
+               }
+           }
+         else
+           {
+             ASSERT (ep->dns_request);
+             s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
+                         verbose);
+           }
+         vec_add1 (s, '\n');
+       }
+      return s;
+    }
+
+  /* *INDENT-OFF* */
+  pool_foreach (ep, dm->entries,
+  ({
+    if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
+      {
+        ASSERT (ep->dns_response);
+        if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
+          ss = "[S] ";
+        else
+          ss = "    ";
+
+        s = format (s, "%s%s -> %U", ss, ep->name,
+                    format_dns_reply,
+                    ep->dns_response,
+                    verbose);
+        if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
+          {
+            f64 time_left = ep->expiration_time - now;
+            if (time_left > 0.0)
+              s = format (s, "  TTL left %.1f", time_left);
+            else
+              s = format (s, "  EXPIRED");
+          }
+      }
+    else
+      {
+        ASSERT (ep->dns_request);
+        s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
+                    verbose);
+      }
+    vec_add1 (s, '\n');
+  }));
+  /* *INDENT-ON* */
+
+  dns_cache_unlock (dm);
+
+  return s;
+}
+
+static clib_error_t *
+show_dns_cache_command_fn (vlib_main_t * vm,
+                          unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  dns_main_t *dm = &dns_main;
+  int verbose = 0;
+  u8 *name = 0;
+  f64 now = vlib_time_now (vm);
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "verbose %d", &verbose))
+       ;
+      else if (unformat (input, "verbose"))
+       verbose = 1;
+      else if (unformat (input, "name %s", &name))
+       ;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+
+  vlib_cli_output (vm, "%U", format_dns_cache, dm, now, verbose, name);
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_dns_cache_command) =
+{
+  .path = "show dns cache",
+  .short_help = "show dns cache [verbose [nn]]",
+  .function = show_dns_cache_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+dns_cache_add_del_command_fn (vlib_main_t * vm,
+                             unformat_input_t * input,
+                             vlib_cli_command_t * cmd)
+{
+  dns_main_t *dm = &dns_main;
+  u8 *dns_reply_data;
+  u8 *name;
+  int is_add = -1;
+  int is_clear = -1;
+  int rv;
+  clib_error_t *error;
+
+  if (unformat (input, "add"))
+    is_add = 1;
+  if (unformat (input, "del"))
+    is_add = 0;
+  if (unformat (input, "clear"))
+    is_clear = 1;
+
+  if (is_add == -1 && is_clear == -1)
+    return clib_error_return (0, "add / del / clear required...");
+
+  if (is_clear == 1)
+    {
+      rv = dns_cache_clear (dm);
+      switch (rv)
+       {
+       case 0:
+         return 0;
+
+       case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
+         error = clib_error_return (0, "Name resolution not enabled");
+         return error;
+       }
+    }
+
+  /* Delete (by name)? */
+  if (is_add == 0)
+    {
+      if (unformat (input, "%v", &name))
+       {
+         rv = dns_delete_by_name (dm, name);
+         switch (rv)
+           {
+           case VNET_API_ERROR_NO_SUCH_ENTRY:
+             error = clib_error_return (0, "%v not in the cache...", name);
+             vec_free (name);
+             return error;
+
+           case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
+             error = clib_error_return (0, "Name resolution not enabled");
+             vec_free (name);
+             return error;
+
+           case 0:
+             vec_free (name);
+             return 0;
+
+           default:
+             error = clib_error_return (0, "dns_delete_by_name returned %d",
+                                        rv);
+             vec_free (name);
+             return error;
+           }
+       }
+      return clib_error_return (0, "unknown input `%U'",
+                               format_unformat_error, input);
+    }
+
+  /* Note: dns_add_static_entry consumes the name vector if OK... */
+  if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data, &name))
+    {
+      rv = dns_add_static_entry (dm, name, dns_reply_data);
+      switch (rv)
+       {
+       case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
+         vec_free (name);
+         vec_free (dns_reply_data);
+         return clib_error_return (0, "%v already in the cache...", name);
+       case 0:
+         return 0;
+
+       default:
+         return clib_error_return (0, "dns_add_static_entry returned %d",
+                                   rv);
+       }
+    }
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (dns_cache_add_del_command) =
+{
+  .path = "dns cache",
+  .short_help = "dns cache [add|del|clear] <name> [ip4][ip6]",
+  .function = dns_cache_add_del_command_fn,
+};
+/* *INDENT-ON* */
+
+#define DNS_FORMAT_TEST 1
+
+#if DNS_FORMAT_TEST > 0
+#if 0
+/* yahoo.com */
+static u8 dns_reply_data_initializer[] =
+  { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x5,
+  0x79, 0x61, 0x68, 0x6f, 0x6f, 0x3, 0x63, 0x6f, 0x6d,
+  0x0,                         /* null lbl */
+  0x0, 0xff,                   /* type ALL */
+  0x0, 0x1,                    /* class IN */
+  0xc0, 0xc,                   /* pointer to yahoo.com name */
+  0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x24, 0x23,
+  0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x72, 0x65, 0x64, 0x69, 0x72,
+  0x65, 0x63, 0x74, 0x3d, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x6d, 0x61, 0x69,
+  0x6c, 0x2e, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0xc0,
+  0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73,
+  0x35, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0,
+  0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
+  0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc,
+  0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x32,
+  0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6,
+  0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0,
+  0x6, 0x5c, 0x0, 0x19, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x36, 0x3, 0x61,
+  0x6d, 0x30, 0x8, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x64, 0x6e, 0x73, 0x3,
+  0x6e,
+  0x65, 0x74, 0x0, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0,
+  0x9, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x37, 0xc0, 0xb8, 0xc0, 0xc, 0x0,
+  0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x9, 0x0, 0x1, 0x4, 0x6d, 0x74,
+  0x61, 0x35, 0xc0, 0xb8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
+  0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x44, 0x2, 0x4, 0x0, 0x0,
+  0x0,
+  0x0, 0x0, 0x0, 0x0, 0xa7, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
+  0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0xc, 0xa, 0x6, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x2, 0x40, 0x8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
+  0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x58, 0xc, 0x2, 0x0, 0x0,
+  0x0,
+  0x0, 0x0, 0x0, 0x0, 0xa9, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x6,
+  0x5c, 0x0, 0x4, 0x62, 0x8a, 0xfd, 0x6d, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1,
+  0x0,
+  0x0, 0x6, 0x5c, 0x0, 0x4, 0xce, 0xbe, 0x24, 0x2d, 0xc0, 0xc, 0x0, 0x1,
+  0x0,
+  0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x4, 0x62, 0x8b, 0xb4, 0x95, 0xc0, 0xc,
+  0x0,
+  0x6, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x2d, 0xc0, 0x7b, 0xa, 0x68,
+  0x6f,
+  0x73, 0x74, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x9, 0x79, 0x61, 0x68,
+  0x6f, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0xc0, 0x12, 0x78, 0x3a, 0x85, 0x44,
+  0x0, 0x0, 0xe, 0x10, 0x0, 0x0, 0x1, 0x2c, 0x0, 0x1b, 0xaf, 0x80, 0x0, 0x0,
+  0x2, 0x58
+};
+
+/* www.cisco.com, has no addresses in reply */
+static u8 dns_reply_data_initializer[] = {
+  0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
+  0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x05,
+  0x63, 0x69, 0x73, 0x63, 0x6f, 0x03, 0x63, 0x6f, 0x6d,
+
+  0x00, 0x00, 0xff, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05,
+  0x00, 0x01, 0x00, 0x00, 0x0b, 0xd3, 0x00, 0x1a, 0x03,
+  0x77, 0x77, 0x77, 0x05, 0x63, 0x69, 0x73, 0x63, 0x6f,
+  0x03, 0x63, 0x6f, 0x6d, 0x06, 0x61, 0x6b, 0x61, 0x64,
+  0x6e, 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00,
+};
+#else
+/* google.com */
+static u8 dns_reply_data_initializer[] =
+  { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x6,
+  0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x3, 0x63, 0x6f, 0x6d, 0x0, 0x0, 0xff,
+  0x0, 0x1, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x2b, 0x0, 0x4,
+  0xac, 0xd9, 0x3, 0x2e, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x1,
+  0x2b,
+  0x0, 0x10, 0x26, 0x7, 0xf8, 0xb0, 0x40, 0x4, 0x8, 0xf, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x20, 0xe, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f,
+  0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x6, 0x0, 0x1,
+  0x0, 0x0, 0x0, 0x3b, 0x0, 0x22, 0xc0, 0x54, 0x9, 0x64, 0x6e, 0x73, 0x2d,
+  0x61, 0x64, 0x6d, 0x69, 0x6e, 0xc0, 0xc, 0xa, 0x3d, 0xc7, 0x30, 0x0, 0x0,
+  0x3, 0x84, 0x0, 0x0, 0x3, 0x84, 0x0, 0x0, 0x7, 0x8, 0x0, 0x0, 0x0, 0x3c,
+  0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x11, 0x0, 0x1e,
+  0x4, 0x61, 0x6c, 0x74, 0x32, 0x5, 0x61, 0x73, 0x70, 0x6d, 0x78, 0x1, 0x6c,
+  0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x4,
+  0x0, 0xa, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0xe, 0xf,
+  0x0, 0x24, 0x23, 0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x69, 0x6e,
+  0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x67,
+  0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x7e, 0x61,
+  0x6c, 0x6c, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f, 0x0, 0x6,
+  0x3, 0x6e, 0x73, 0x32, 0xc0, 0xc, 0xc0, 0xc, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1,
+  0x51, 0x7f, 0x0, 0xf, 0x0, 0x5, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, 0x6b,
+  0x69, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
+  0x1, 0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc,
+  0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x28, 0x4, 0x61,
+  0x6c, 0x74, 0x33, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1,
+  0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0,
+  0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x32, 0x4, 0x61, 0x6c,
+  0x74, 0x34, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2,
+  0x57,
+  0x0, 0x9, 0x0, 0x14, 0x4, 0x61, 0x6c, 0x74, 0x31, 0xc0, 0x9b
+};
+#endif
+
+static clib_error_t *
+test_dns_fmt_command_fn (vlib_main_t * vm,
+                        unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  u8 *dns_reply_data = 0;
+  int verbose = 0;
+  int rv;
+  vl_api_dns_resolve_name_reply_t _rm, *rmp = &_rm;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "verbose %d", &verbose))
+       ;
+      else if (unformat (input, "verbose"))
+       verbose = 1;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+
+  vec_validate (dns_reply_data, ARRAY_LEN (dns_reply_data_initializer) - 1);
+
+  memcpy (dns_reply_data, dns_reply_data_initializer,
+         ARRAY_LEN (dns_reply_data_initializer));
+
+  vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
+
+  memset (rmp, 0, sizeof (*rmp));
+
+  rv = vnet_dns_response_to_reply (dns_reply_data, rmp, 0 /* ttl-ptr */ );
+
+  switch (rv)
+    {
+    case VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES:
+      vlib_cli_output (vm, "no addresses found...");
+      break;
+
+    default:
+      vlib_cli_output (vm, "response to reply returned %d", rv);
+      break;
+
+    case 0:
+      if (rmp->ip4_set)
+       vlib_cli_output (vm, "ip4 address: %U", format_ip4_address,
+                        (ip4_address_t *) rmp->ip4_address);
+      if (rmp->ip6_set)
+       vlib_cli_output (vm, "ip6 address: %U", format_ip6_address,
+                        (ip6_address_t *) rmp->ip6_address);
+      break;
+    }
+
+  vec_free (dns_reply_data);
+
+  return 0;
+}
+
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (test_dns_fmt_command) =
+{
+  .path = "test dns format",
+  .short_help = "test dns format",
+  .function = test_dns_fmt_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+test_dns_unfmt_command_fn (vlib_main_t * vm,
+                          unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  u8 *dns_reply_data = 0;
+  int verbose = 0;
+  int reply_set = 0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "verbose %d", &verbose))
+       ;
+      else if (unformat (input, "verbose"))
+       verbose = 1;
+      else if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data))
+       reply_set = 1;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+
+  if (reply_set == 0)
+    return clib_error_return (0, "dns data not set...");
+
+  vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
+
+  vec_free (dns_reply_data);
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (test_dns_unfmt_command) =
+{
+  .path = "test dns unformat",
+  .short_help = "test dns unformat <name> [ip4][ip6]",
+  .function = test_dns_unfmt_command_fn,
+};
+/* *INDENT-ON* */
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/dns/dns.h b/src/vnet/dns/dns.h
new file mode 100644 (file)
index 0000000..5da2615
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_dns_h
+#define included_dns_h
+
+#include <vppinfra/time.h>
+#include <vppinfra/cache.h>
+#include <vppinfra/error.h>
+
+#include <vppinfra/hash.h>
+#include <vnet/dns/dns_packet.h>
+#include <vnet/ip/ip.h>
+
+typedef struct
+{
+  /** flags */
+  volatile u8 flags;
+
+  /** The name in "normal human being" notation, e.g. www.foobar.com */
+  u8 *name;
+
+  /** Expiration time */
+  f64 expiration_time;
+
+  /** Cached dns request, for sending retries */
+  u8 *dns_request;
+
+  /** Retry parameters */
+  int retry_count;
+  int server_rotor;
+  int server_af;
+  f64 retry_timer;
+
+  /** Cached dns response */
+  u8 *dns_response;
+
+  /** Clients awaiting responses */
+  u32 *api_clients_to_notify;
+  u32 *api_client_contexts;
+  ip4_address_t *ip4_peers_to_notify;
+  ip6_address_t *ip6_peers_to_notify;
+} dns_cache_entry_t;
+
+#define DNS_CACHE_ENTRY_FLAG_VALID     (1<<0) /**< we have Actual Data */
+#define DNS_CACHE_ENTRY_FLAG_STATIC    (1<<1) /**< static entry */
+
+#define DNS_RETRIES_PER_SERVER 3
+
+#define DNS_RESOLVER_EVENT_RESOLVED    1
+#define DNS_RESOLVER_EVENT_PENDING     2
+
+
+typedef struct
+{
+  /** Pool of cache entries */
+  dns_cache_entry_t *entries;
+
+  /** Pool indices of unresolved entries */
+  u32 *unresolved_entries;
+
+  /** Find cached record by name */
+  uword *cache_entry_by_name;
+  uword *cache_lock;
+
+  /** enable / disable flag */
+  int is_enabled;
+
+  /** upstream name servers, e.g. 8.8.8.8 */
+  ip4_address_t *ip4_name_servers;
+  ip6_address_t *ip6_name_servers;
+
+  /** config parameters */
+  u32 name_cache_size;
+  u32 max_ttl_in_seconds;
+  u32 random_seed;
+
+  /* convenience */
+  vlib_main_t *vlib_main;
+  vnet_main_t *vnet_main;
+} dns_main_t;
+
+extern dns_main_t dns_main;
+
+extern vlib_node_registration_t dns46_reply_node;
+extern vlib_node_registration_t dns_resolver_node;
+
+#define foreach_dns46_reply_error                       \
+_(PROCESSED, "DNS reply pkts processed")                \
+_(NO_ELT, "No DNS pool element")                        \
+_(FORMAT_ERROR, "DNS format errors")                    \
+_(TEST_DROP, "DNS reply pkt dropped for test purposes")
+
+typedef enum
+{
+#define _(sym,str) DNS46_REPLY_ERROR_##sym,
+  foreach_dns46_reply_error
+#undef _
+    DNS46_REPLY_N_ERROR,
+} dns46_reply_error_t;
+
+void vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep);
+int vnet_dns_cname_indirection_nolock (dns_main_t * dm,
+                                      dns_cache_entry_t * ep, u8 * reply);
+int vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index);
+
+format_function_t format_dns_reply;
+
+static inline void
+dns_cache_lock (dns_main_t * dm)
+{
+  if (dm->cache_lock)
+    {
+      while (__sync_lock_test_and_set (dm->cache_lock, 1))
+       ;
+    }
+}
+
+static inline void
+dns_cache_unlock (dns_main_t * dm)
+{
+  if (dm->cache_lock)
+    {
+      CLIB_MEMORY_BARRIER ();
+      *dm->cache_lock = 0;
+    }
+}
+
+#endif /* included_dns_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/dns/dns_packet.h b/src/vnet/dns/dns_packet.h
new file mode 100644 (file)
index 0000000..e0ea8fe
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_dns_packet_h
+#define included_dns_packet_h
+
+/**
+ * DNS packet header format
+ */
+
+/* *INDENT-OFF* */
+typedef CLIB_PACKED (struct {
+  u16 id;                       /**< transaction ID */
+  u16 flags;                    /**< flags  */
+  u16 qdcount;                  /**< number of questions */
+  u16 anscount;                 /**< number of answers */
+  u16 nscount;                  /**< number of name servers */
+  u16 arcount;                  /**< number of additional records */
+}) dns_header_t;
+/* *INDENT-ON* */
+
+#define DNS_RCODE_MASK (0xf)
+#define DNS_RCODE_NO_ERROR 0
+#define DNS_RCODE_FORMAT_ERROR 1
+#define DNS_RCODE_SERVER_FAILURE 2
+#define DNS_RCODE_NAME_ERROR 3
+#define DNS_RCODE_NOT_IMPLEMENTED 4
+#define DNS_RCODE_REFUSED 5
+
+#define DNS_RA (1<<7)          /**< recursion available */
+#define DNS_RD (1<<8)          /**< recursion desired */
+#define DNS_TC (1<<9)         /**< truncation  */
+#define DNS_AA (1<<10)         /**< authoritative answer  */
+#define DNS_OPCODE_MASK (0xf<<11) /**< opcode mask */
+#define DNS_OPCODE_QUERY (0<<11)  /**< standard query */
+#define DNS_OPCODE_IQUERY (1<<11) /**< inverse query (deprecated) */
+#define DNS_OPCODE_STATUS (2<<11) /**< server status  */
+#define DNS_QR (1<<15)         /**< query=0, response=1  */
+
+
+/*
+ * Note: in DNS-land, www.foobar.com is encoded as three "labels,"
+ * each of which amount to a 1 octet length followed by up to 63
+ * octets of name. Don't forget to add a "null root label" after the last
+ * real one, or the poor slob trying to parse the name will have
+ * no chance whatsoever.
+ *
+ * All RRs have the same top level format shown below:
+ *
+ *                                    1  1  1  1  1  1
+ *      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                                               |
+ *    /                                               /
+ *    /                      NAME                     /
+ *    |                                               |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                      TYPE                     |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                     CLASS                     |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                      TTL                      |
+ *    |                                               |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *    |                   RDLENGTH                    |
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+ *    /                     RDATA                     /
+ *    /                                               /
+ *    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *
+ *
+ *  DNS "questions" have the following format:
+ *
+ *                                     1  1  1  1  1  1
+ *       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                                               |
+ *     /                     QNAME                     /
+ *     /                                               /
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                     QTYPE                     |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ *     |                     QCLASS                    |
+ *     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ */
+
+/**
+ * DNS "question" fixed header.
+ */
+/* *INDENT-OFF* */
+typedef CLIB_PACKED (struct {
+  u16 type;    /**< record type requested */
+  u16 class;   /**< class, 1 = internet */
+}) dns_query_t;
+/* *INDENT-ON* */
+
+/**
+ * DNS RR fixed header.
+ */
+/* *INDENT-OFF* */
+typedef CLIB_PACKED (struct {
+  u16 type;    /**< record type */
+  u16 class;   /**< class, 1 = internet */
+  u32 ttl;     /**< time to live, in seconds */
+  u16 rdlength;
+  /**< length of r */
+  u8 rdata[0];
+}) dns_rr_t;
+/* *INDENT-ON* */
+
+/*
+ * There are quite a number of DNS record types
+ * Feel free to add as needed
+ */
+#define foreach_dns_type                        \
+_(A, 1)        /**< ip4 host address */        \
+_(AAAA, 28)     /**< ip6 host address */        \
+_(ALL, 255)     /**< all available data */      \
+_(TEXT, 16)     /**< a text string */           \
+_(NAMESERVER, 2) /**< a nameserver */           \
+_(CNAME, 5)      /**< a CNAME (alias) */       \
+_(MAIL_EXCHANGE, 15) /**< a mail exchange  */
+
+typedef enum
+{
+#define _(name,value) DNS_TYPE_##name = value,
+  foreach_dns_type
+#undef _
+} dns_type_t;
+
+#define DNS_CLASS_IN   1       /**< The Internet */
+
+
+#endif /* included_dns_packet_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/dns/reply_node.c b/src/vnet/dns/reply_node.c
new file mode 100644 (file)
index 0000000..e045ba8
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/dns/dns.h>
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+vlib_node_registration_t dns46_reply_node;
+
+typedef struct
+{
+  u32 pool_index;
+  u32 disposition;
+} dns46_reply_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_dns46_reply_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  dns46_reply_trace_t *t = va_arg (*args, dns46_reply_trace_t *);
+
+  s = format (s, "DNS46_REPLY: pool index %d, disposition  %d",
+             t->pool_index, t->disposition);
+  return s;
+}
+
+vlib_node_registration_t dns46_reply_node;
+
+static char *dns46_reply_error_strings[] = {
+#define _(sym,string) string,
+  foreach_dns46_reply_error
+#undef _
+};
+
+typedef enum
+{
+  DNS46_REPLY_NEXT_DROP,
+  DNS46_REPLY_N_NEXT,
+} dns46_reply_next_t;
+
+static uword
+dns46_reply_node_fn (vlib_main_t * vm,
+                    vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  u32 n_left_from, *from, *to_next;
+  dns46_reply_next_t next_index;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+#if 0
+      while (n_left_from >= 4 && n_left_to_next >= 2)
+       {
+         u32 next0 = DNS46_REPLY_NEXT_INTERFACE_OUTPUT;
+         u32 next1 = DNS46_REPLY_NEXT_INTERFACE_OUTPUT;
+         u32 sw_if_index0, sw_if_index1;
+         u8 tmp0[6], tmp1[6];
+         ethernet_header_t *en0, *en1;
+         u32 bi0, bi1;
+         vlib_buffer_t *b0, *b1;
+
+         /* Prefetch next iteration. */
+         {
+           vlib_buffer_t *p2, *p3;
+
+           p2 = vlib_get_buffer (vm, from[2]);
+           p3 = vlib_get_buffer (vm, from[3]);
+
+           vlib_prefetch_buffer_header (p2, LOAD);
+           vlib_prefetch_buffer_header (p3, LOAD);
+
+           CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
+           CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
+         }
+
+         /* speculatively enqueue b0 and b1 to the current next frame */
+         to_next[0] = bi0 = from[0];
+         to_next[1] = bi1 = from[1];
+         from += 2;
+         to_next += 2;
+         n_left_from -= 2;
+         n_left_to_next -= 2;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         b1 = vlib_get_buffer (vm, bi1);
+
+         /* $$$$$ End of processing 2 x packets $$$$$ */
+
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
+           {
+             if (b0->flags & VLIB_BUFFER_IS_TRACED)
+               {
+                 dns46_reply_trace_t *t =
+                   vlib_add_trace (vm, node, b0, sizeof (*t));
+                 t->sw_if_index = sw_if_index0;
+                 t->next_index = next0;
+               }
+             if (b1->flags & VLIB_BUFFER_IS_TRACED)
+               {
+                 dns46_reply_trace_t *t =
+                   vlib_add_trace (vm, node, b1, sizeof (*t));
+                 t->sw_if_index = sw_if_index1;
+                 t->next_index = next1;
+               }
+           }
+
+         /* verify speculative enqueues, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, bi1, next0, next1);
+       }
+#endif
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         u32 next0 = DNS46_REPLY_NEXT_DROP;
+         dns_header_t *d0;
+         u32 pool_index0;
+         u32 error0;
+         u8 *resp0 = 0;
+
+         /* speculatively enqueue b0 to the current next frame */
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         d0 = vlib_buffer_get_current (b0);
+
+         pool_index0 = clib_host_to_net_u16 (d0->id);
+
+         /* Save the reply */
+         vec_validate (resp0, vlib_buffer_length_in_chain (vm, b0) - 1);
+         clib_memcpy (resp0, d0, vlib_buffer_length_in_chain (vm, b0));
+
+         /*
+          * Deal with everything in process ctx on the main thread
+          */
+         vlib_process_signal_event_mt (vm, dns_resolver_node.index,
+                                       DNS_RESOLVER_EVENT_RESOLVED,
+                                       (uword) resp0);
+         error0 = DNS46_REPLY_ERROR_PROCESSED;
+
+         b0->error = node->errors[error0];
+
+         if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+                            && (b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             dns46_reply_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->disposition = error0;
+             t->pool_index = pool_index0;
+           }
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (dns46_reply_node) =
+{
+  .function = dns46_reply_node_fn,.name = "dns46_reply",.vector_size =
+    sizeof (u32),.format_trace = format_dns46_reply_trace,.type =
+    VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (dns46_reply_error_strings),.error_strings =
+    dns46_reply_error_strings,.n_next_nodes = DNS46_REPLY_N_NEXT,
+    /* edit / add dispositions here */
+    .next_nodes =
+  {
+  [DNS46_REPLY_NEXT_DROP] = "error-drop",}
+,};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/dns/resolver_process.c b/src/vnet/dns/resolver_process.c
new file mode 100644 (file)
index 0000000..91e5cef
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/dns/dns.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+
+#include <vnet/vnet_msg_enum.h>
+
+#define vl_typedefs            /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun           /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <vnet/vnet_all_api_h.h>
+#undef vl_printfun
+
+#include <vlibapi/api_helper_macros.h>
+
+vlib_node_registration_t dns_resolver_node;
+
+extern int
+vnet_dns_response_to_reply (u8 * response,
+                           vl_api_dns_resolve_name_reply_t * rmp,
+                           u32 * min_ttlp);
+
+static void
+resolve_event (dns_main_t * dm, f64 now, u8 * reply)
+{
+  vlib_main_t *vm = dm->vlib_main;
+  dns_header_t *d;
+  u32 pool_index;
+  dns_cache_entry_t *ep;
+  u32 min_ttl;
+  u16 flags;
+  u16 rcode;
+  int i;
+  int rv = 0;
+
+  d = (dns_header_t *) reply;
+  flags = clib_net_to_host_u16 (d->flags);
+  rcode = flags & DNS_RCODE_MASK;
+
+  /* $$$ u16 limits cache to 65K entries, fix later multiple dst ports */
+  pool_index = clib_net_to_host_u16 (d->id);
+  dns_cache_lock (dm);
+
+  if (pool_is_free_index (dm->entries, pool_index))
+    {
+      vec_free (reply);
+      vlib_node_increment_counter (vm, dns46_reply_node.index,
+                                  DNS46_REPLY_ERROR_NO_ELT, 1);
+      dns_cache_unlock (dm);
+      return;
+    }
+
+  ep = pool_elt_at_index (dm->entries, pool_index);
+
+  if (ep->dns_response)
+    vec_free (ep->dns_response);
+
+  /* Handle [sic] recursion AKA CNAME indirection */
+  if (vnet_dns_cname_indirection_nolock (dm, ep, reply))
+    {
+      dns_cache_unlock (dm);
+      return;
+    }
+
+  /* Save the response */
+  ep->dns_response = reply;
+  /* Pick some sensible default. */
+  ep->expiration_time = now + 600.0;
+  if (vec_len (ep->dns_response))
+    ep->flags |= DNS_CACHE_ENTRY_FLAG_VALID;
+
+  /* Most likely, send 1 message */
+  for (i = 0; i < vec_len (ep->api_clients_to_notify); 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]);
+
+      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);
+    }
+  vec_free (ep->api_clients_to_notify);
+  vec_free (ep->api_client_contexts);
+
+  /* $$$ Add ip4/ip6 reply code */
+
+  for (i = 0; i < vec_len (dm->unresolved_entries); i++)
+    {
+      if (dm->unresolved_entries[i] == pool_index)
+       {
+         vec_delete (dm->unresolved_entries, 1, i);
+         goto found;
+       }
+    }
+  clib_warning ("pool index %d AWOL from unresolved vector", pool_index);
+
+found:
+  /* Deal with bogus names, server issues, etc. */
+  switch (rcode)
+    {
+    default:
+    case DNS_RCODE_NO_ERROR:
+      break;
+
+    case DNS_RCODE_SERVER_FAILURE:
+    case DNS_RCODE_NOT_IMPLEMENTED:
+    case DNS_RCODE_REFUSED:
+      if (ep->server_af == 0)
+       clib_warning ("name server %U backfire",
+                     format_ip4_address,
+                     dm->ip4_name_servers + ep->server_rotor);
+      else
+       clib_warning ("name server %U backfire",
+                     format_ip6_address,
+                     dm->ip6_name_servers + ep->server_rotor);
+      /* FALLTHROUGH */
+    case DNS_RCODE_NAME_ERROR:
+    case DNS_RCODE_FORMAT_ERROR:
+      /* remove trash from the cache... */
+      vnet_dns_delete_entry_by_index_nolock (dm, ep - dm->entries);
+      break;
+    }
+
+  dns_cache_unlock (dm);
+  return;
+}
+
+static void
+retry_scan (dns_main_t * dm, f64 now)
+{
+  int i;
+  dns_cache_entry_t *ep;
+
+  for (i = 0; i < vec_len (dm->unresolved_entries); i++)
+    {
+      dns_cache_lock (dm);
+      ep = pool_elt_at_index (dm->entries, dm->unresolved_entries[i]);
+
+      ASSERT ((ep->flags & DNS_CACHE_ENTRY_FLAG_VALID) == 0);
+
+      vnet_send_dns_request (dm, ep);
+      dns_cache_unlock (dm);
+    }
+}
+
+static uword
+dns_resolver_process (vlib_main_t * vm,
+                     vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+  dns_main_t *dm = &dns_main;
+  f64 now;
+  f64 timeout = 1000.0;
+  uword *event_data = 0;
+  uword event_type;
+  int i;
+
+  while (1)
+    {
+      vlib_process_wait_for_event_or_clock (vm, timeout);
+
+      now = vlib_time_now (vm);
+
+      event_type = vlib_process_get_events (vm, (uword **) & event_data);
+
+      switch (event_type)
+       {
+         /* Send one of these when a resolution is pending */
+       case DNS_RESOLVER_EVENT_PENDING:
+         timeout = 2.0;
+         break;
+
+       case DNS_RESOLVER_EVENT_RESOLVED:
+         for (i = 0; i < vec_len (event_data); i++)
+           resolve_event (dm, now, (u8 *) event_data[i]);
+         break;
+
+       case ~0:                /* timeout */
+         retry_scan (dm, now);
+         break;
+       }
+      vec_reset_length (event_data);
+
+      /* No work? Back to slow timeout mode... */
+      if (vec_len (dm->unresolved_entries) == 0)
+       timeout = 1000.0;
+    }
+  return 0;                    /* or not */
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dns_resolver_node) =
+{
+  .function = dns_resolver_process,
+  .type = VLIB_NODE_TYPE_PROCESS,
+  .name = "dns-resolver-process",
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index f7635b3..e181a4f 100644 (file)
@@ -137,6 +137,9 @@ do {                                                \
   if ((error = vlib_call_init_function (vm, flow_classify_init)))
     return error;
 
+  if ((error = vlib_call_init_function (vm, dns_init)))
+    return error;
+
   return error;
 }
 
index dae611c..aa37701 100644 (file)
@@ -78,6 +78,7 @@ typedef enum
 } udp_error_t;
 
 #define foreach_udp4_dst_port                  \
+_ (53, dns)                                    \
 _ (67, dhcp_to_server)                          \
 _ (68, dhcp_to_client)                          \
 _ (500, ikev2)                                  \
@@ -91,10 +92,12 @@ _ (4789, vxlan)                                     \
 _ (4789, vxlan6)                               \
 _ (4790, VXLAN_GPE)                            \
 _ (6633, vpath_3)                              \
-_ (6081, geneve)
+_ (6081, geneve)                               \
+_ (53053, dns_reply)
 
 
 #define foreach_udp6_dst_port                   \
+_ (53, dns6)                                   \
 _ (547, dhcpv6_to_server)                       \
 _ (546, dhcpv6_to_client)                      \
 _ (2152, GTPU6)                                        \
@@ -104,7 +107,8 @@ _ (4341, lisp_gpe6)                             \
 _ (4342, lisp_cp6)                             \
 _ (4790, VXLAN6_GPE)                            \
 _ (6633, vpath6_3)                             \
-_ (6081, geneve6)
+_ (6081, geneve6)                              \
+_ (53053, dns_reply6)
 
 typedef enum
 {
index c926d0e..936f0a5 100644 (file)
@@ -60,6 +60,7 @@
 #include <vnet/policer/policer.api.h>
 #include <vnet/ethernet/p2p_ethernet.api.h>
 #include <vnet/tcp/tcp.api.h>
+#include <vnet/dns/dns.api.h>
 
 /*
  * fd.io coding-style-patch-verification: ON
index 3abf4cd..46ce0b2 100644 (file)
@@ -3127,6 +3127,16 @@ static void *vl_api_lldp_config_t_print
   s = format (s, "system_name %s ", mp->system_name);
   s = format (s, "tx_hold %d ", ntohl (mp->tx_hold));
   s = format (s, "tx_interval %d ", ntohl (mp->tx_interval));
+  FINISH;
+}
+
+static void *vl_api_dns_enable_disable_t_print
+  (vl_api_dns_enable_disable_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: dns_enable_disable ");
+  s = format (s, "%s ", mp->enable ? "enable" : "disable");
 
   FINISH;
 }
@@ -3160,6 +3170,35 @@ static void *vl_api_sw_interface_set_lldp_t_print
   FINISH;
 }
 
+static void *vl_api_dns_name_server_add_del_t_print
+  (vl_api_dns_name_server_add_del_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: dns_name_server_add_del ");
+  if (mp->is_ip6)
+    s = format (s, "%U ", format_ip6_address,
+               (ip6_address_t *) mp->server_address);
+  else
+    s = format (s, "%U ", format_ip4_address,
+               (ip4_address_t *) mp->server_address);
+
+  if (mp->is_add == 0)
+    s = format (s, "del ");
+
+  FINISH;
+}
+
+static void *vl_api_dns_resolve_name_t_print
+  (vl_api_dns_resolve_name_t * mp, void *handle)
+{
+  u8 *s;
+
+  s = format (0, "SCRIPT: dns_resolve_name ");
+  s = format (s, "%s ", mp->name);
+  FINISH;
+}
+
 #define foreach_custom_print_no_arg_function                            \
 _(lisp_eid_table_vni_dump)                                              \
 _(lisp_map_resolver_dump)                                               \
@@ -3352,7 +3391,10 @@ _(P2P_ETHERNET_DEL, p2p_ethernet_del)                                    \
 _(TCP_CONFIGURE_SRC_ADDRESSES, tcp_configure_src_addresses)            \
 _(APP_NAMESPACE_ADD_DEL, app_namespace_add_del)                         \
 _(LLDP_CONFIG, lldp_config)                                             \
-_(SW_INTERFACE_SET_LLDP, sw_interface_set_lldp)
+_(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)
   void
 vl_msg_api_custom_dump_configure (api_main_t * am)
 {