Typos. A bunch of typos I've been collecting.
[vpp.git] / src / vnet / dns / dns.c
index d3f573d..2951679 100644 (file)
@@ -55,9 +55,7 @@ dns_cache_clear (dns_main_t * dm)
   pool_foreach (ep, dm->entries,
   ({
     vec_free (ep->name);
-    vec_free (ep->pending_api_requests);
-    vec_free (ep->ip4_peers_to_notify);
-    vec_free (ep->ip6_peers_to_notify);
+    vec_free (ep->pending_requests);
   }));
   /* *INDENT-ON* */
 
@@ -74,6 +72,7 @@ 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;
+  vlib_main_t *vm = dm->vlib_main;
 
   if (is_enable)
     {
@@ -81,6 +80,23 @@ dns_enable_disable (dns_main_t * dm, int is_enable)
          && (vec_len (dm->ip6_name_servers) == 0))
        return VNET_API_ERROR_NO_NAME_SERVERS;
 
+      if (dm->udp_ports_registered == 0)
+       {
+         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 */ );
+
+         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 */ );
+
+         dm->udp_ports_registered = 1;
+       }
+
       if (dm->cache_entry_by_name == 0)
        {
          if (n_vlib_mains > 1)
@@ -199,9 +215,9 @@ static void vl_api_dns_name_server_add_del_t_handler
   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)
+void
+vnet_dns_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);
@@ -249,12 +265,12 @@ send_dns4_request (dns_main_t * dm,
     {
       clib_warning
        ("route to %U exists, fei %d, get_resolving_interface returned"
-        " ~0", fei, format_ip4_address, &prefix.fp_addr);
+        " ~0", format_ip4_address, &prefix.fp_addr, fei);
       return;
     }
 
   /* *INDENT-OFF* */
-  foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnummbered */,
+  foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnumbered */,
   ({
     src_address = ip_interface_address_get_address (lm4, ia);
     goto found_src_address;
@@ -280,9 +296,9 @@ found_src_address:
   vnet_buffer (b)->sw_if_index[VLIB_TX] = 0;   /* default VRF for now */
 
   ip = vlib_buffer_get_current (b);
-  memset (ip, 0, sizeof (*ip));
+  clib_memset (ip, 0, sizeof (*ip));
   udp = (udp_header_t *) (ip + 1);
-  memset (udp, 0, sizeof (*udp));
+  clib_memset (udp, 0, sizeof (*udp));
 
   dns_request = (u8 *) (udp + 1);
 
@@ -315,9 +331,9 @@ found_src_address:
   ep->retry_timer = now + 2.0;
 }
 
-static void
-send_dns6_request (dns_main_t * dm,
-                  dns_cache_entry_t * ep, ip6_address_t * server)
+void
+vnet_dns_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);
@@ -362,7 +378,7 @@ send_dns6_request (dns_main_t * dm,
   sw_if_index = fib_entry_get_resolving_interface (fei);
 
   /* *INDENT-OFF* */
-  foreach_ip_interface_address(lm6, ia, sw_if_index, 1 /* honor unnummbered */,
+  foreach_ip_interface_address(lm6, ia, sw_if_index, 1 /* honor unnumbered */,
   ({
     src_address = ip_interface_address_get_address (lm6, ia);
     goto found_src_address;
@@ -386,9 +402,9 @@ found_src_address:
     VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
 
   ip = vlib_buffer_get_current (b);
-  memset (ip, 0, sizeof (*ip));
+  clib_memset (ip, 0, sizeof (*ip));
   udp = (udp_header_t *) (ip + 1);
-  memset (udp, 0, sizeof (*udp));
+  clib_memset (udp, 0, sizeof (*udp));
 
   dns_request = (u8 *) (udp + 1);
 
@@ -472,7 +488,7 @@ name_to_labels (u8 * name)
  * 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)
+vnet_dns_labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here)
 {
   u8 *reply = 0;
   u16 offset;
@@ -519,11 +535,11 @@ 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;
+  u8 *request, *name_copy;
   u32 qp_offset;
 
   /* This can easily happen if sitting in GDB, etc. */
-  if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
+  if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID || ep->server_fails > 1)
     return;
 
   /* Construct the dns request, if we haven't been here already */
@@ -535,14 +551,29 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
        * per label is 63, enforce that.
        */
       request = name_to_labels (ep->name);
+      name_copy = vec_dup (request);
       qp_offset = vec_len (request);
 
+      /*
+       * At least when testing against "known good" DNS servers:
+       * it turns out that sending 2x requests - one for an A-record
+       * and another for a AAAA-record - seems to work better than
+       * sending a DNS_TYPE_ALL request.
+       */
+
       /* Add space for the query header */
-      vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
+      vec_validate (request, 2 * qp_offset + 2 * sizeof (dns_query_t) - 1);
 
       qp = (dns_query_t *) (request + qp_offset);
 
-      qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
+      qp->type = clib_host_to_net_u16 (DNS_TYPE_A);
+      qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+      qp++;
+      clib_memcpy (qp, name_copy, vec_len (name_copy));
+      qp = (dns_query_t *) (((u8 *) qp) + vec_len (name_copy));
+      vec_free (name_copy);
+
+      qp->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
       qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
 
       /* Punch in space for the dns_header_t */
@@ -556,7 +587,7 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
       /* 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->qdcount = clib_host_to_net_u16 (2);
       h->nscount = 0;
       h->arcount = 0;
 
@@ -572,8 +603,8 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
        {
          if (vec_len (dm->ip6_name_servers))
            {
-             send_dns6_request (dm, ep,
-                                dm->ip6_name_servers + ep->server_rotor);
+             vnet_dns_send_dns6_request
+               (dm, ep, dm->ip6_name_servers + ep->server_rotor);
              goto out;
            }
          else
@@ -581,7 +612,8 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
        }
       if (vec_len (dm->ip4_name_servers))
        {
-         send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
+         vnet_dns_send_dns4_request
+           (dm, ep, dm->ip4_name_servers + ep->server_rotor);
          goto out;
        }
     }
@@ -608,9 +640,11 @@ vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
     }
 
   if (ep->server_af == 1 /* ip6 */ )
-    send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor);
+    vnet_dns_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);
+    vnet_dns_send_dns4_request
+      (dm, ep, dm->ip4_name_servers + ep->server_rotor);
 
 out:
 
@@ -646,9 +680,7 @@ vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index)
 found:
   hash_unset_mem (dm->cache_entry_by_name, ep->name);
   vec_free (ep->name);
-  vec_free (ep->pending_api_requests);
-  vec_free (ep->ip4_peers_to_notify);
-  vec_free (ep->ip6_peers_to_notify);
+  vec_free (ep->pending_requests);
   pool_put (dm->entries, ep);
 
   return 0;
@@ -754,7 +786,7 @@ dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data)
     }
 
   pool_get (dm->entries, ep);
-  memset (ep, 0, sizeof (*ep));
+  clib_memset (ep, 0, sizeof (*ep));
 
   /* Note: consumes the name vector */
   ep->name = name;
@@ -766,16 +798,16 @@ dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data)
   return 0;
 }
 
-static int
-dns_resolve_name (dns_main_t * dm,
-                 u8 * name, u32 client_index, u32 client_context,
-                 u32 request_type, dns_cache_entry_t ** retp)
+int
+vnet_dns_resolve_name (dns_main_t * dm, u8 * name, dns_pending_request_t * t,
+                      dns_cache_entry_t ** retp)
 {
   dns_cache_entry_t *ep;
   int rv;
   f64 now;
   uword *p;
-  pending_api_request_t *pr;
+  dns_pending_request_t *pr;
+  int count;
 
   now = vlib_time_now (dm->vlib_main);
 
@@ -850,11 +882,10 @@ search_again:
        {
          /*
           * Resolution pending. Add request to the pending vector
+          * by copying the template request
           */
-         vec_add2 (ep->pending_api_requests, pr, 1);
-         pr->request_type = request_type;
-         pr->client_index = client_index;
-         pr->client_context = client_context;
+         vec_add2 (ep->pending_requests, pr, 1);
+         memcpy (pr, t, sizeof (*pr));
          dns_cache_unlock (dm);
          return (0);
        }
@@ -874,7 +905,7 @@ re_resolve:
 
   /* add new hash table entry */
   pool_get (dm->entries, ep);
-  memset (ep, 0, sizeof (*ep));
+  clib_memset (ep, 0, sizeof (*ep));
 
   ep->name = format (0, "%s%c", name, 0);
   _vec_len (ep->name) = vec_len (ep->name) - 1;
@@ -882,19 +913,38 @@ re_resolve:
   hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
 
   vec_add1 (dm->unresolved_entries, ep - dm->entries);
-  vec_add2 (ep->pending_api_requests, pr, 1);
-  pr->request_type = request_type;
-  pr->client_index = client_index;
-  pr->client_context = client_context;
+  vec_add2 (ep->pending_requests, pr, 1);
+
+  pr->request_type = t->request_type;
+
+  /* Remember details so we can reply later... */
+  if (t->request_type == DNS_API_PENDING_NAME_TO_IP ||
+      t->request_type == DNS_API_PENDING_IP_TO_NAME)
+    {
+      pr->client_index = t->client_index;
+      pr->client_context = t->client_context;
+    }
+  else
+    {
+      pr->client_index = ~0;
+      pr->is_ip6 = t->is_ip6;
+      pr->dst_port = t->dst_port;
+      pr->id = t->id;
+      pr->name = t->name;
+      if (t->is_ip6)
+       count = 16;
+      else
+       count = 4;
+      clib_memcpy (pr->dst_address, t->dst_address, count);
+    }
+
   vnet_send_dns_request (dm, ep);
   dns_cache_unlock (dm);
   return 0;
 }
 
 #define foreach_notification_to_move            \
-_(pending_api_requests)                                \
-_(ip4_peers_to_notify)                          \
-_(ip6_peers_to_notify)
+_(pending_requests)
 
 /**
  * Handle cname indirection. JFC. Called with the cache locked.
@@ -909,9 +959,11 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply)
   dns_rr_t *rr;
   u8 *curpos;
   u8 *pos, *pos2;
+  u8 *cname_pos = 0;
   int len, i;
   u8 *cname = 0;
   u8 *request = 0;
+  u8 *name_copy;
   u32 qp_offset;
   u16 flags;
   u16 rcode;
@@ -933,7 +985,7 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply)
     case DNS_RCODE_SERVER_FAILURE:
     case DNS_RCODE_NOT_IMPLEMENTED:
     case DNS_RCODE_REFUSED:
-      return 0;
+      return -1;
     }
 
   curpos = (u8 *) (h + 1);
@@ -957,13 +1009,46 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply)
   else
     return 0;
 
-  rr = (dns_rr_t *) pos;
+  /* Walk the answer(s) to see what to do next */
+  for (i = 0; i < clib_net_to_host_u16 (h->anscount); i++)
+    {
+      rr = (dns_rr_t *) pos;
+      switch (clib_net_to_host_u16 (rr->type))
+       {
+         /* Real address record? Done.. */
+       case DNS_TYPE_A:
+       case DNS_TYPE_AAAA:
+         return 0;
+         /*
+          * Maybe chase a CNAME pointer?
+          * It's not unheard-of for name-servers to return
+          * both CNAME and A/AAAA records...
+          */
+       case DNS_TYPE_CNAME:
+         cname_pos = pos;
+         break;
 
-  /* This is a real record, not a CNAME record */
-  if (clib_net_to_host_u16 (rr->type) != DNS_TYPE_CNAME)
-    return 0;
+         /* Some other junk, e.g. a nameserver... */
+       default:
+         break;
+       }
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      /* Skip name... */
+      if ((pos2[0] & 0xc0) == 0xc0)
+       pos += 2;
+    }
+
+  /* Neither a CNAME nor a real address. Try another server */
+  if (cname_pos == 0)
+    {
+      flags &= ~DNS_RCODE_MASK;
+      flags |= DNS_RCODE_NAME_ERROR;
+      h->flags = clib_host_to_net_u16 (flags);
+      return -1;
+    }
 
   /* This is a CNAME record, chase the name chain. */
+  pos = cname_pos;
 
   /* The last request is no longer pending.. */
   for (i = 0; i < vec_len (dm->unresolved_entries); i++)
@@ -977,7 +1062,7 @@ vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply)
 found_last_request:
 
   now = vlib_time_now (dm->vlib_main);
-  cname = labels_to_name (rr->rdata, reply, &pos2);
+  cname = vnet_dns_labels_to_name (rr->rdata, reply, &pos2);
   /* Save the cname */
   vec_add1 (cname, 0);
   _vec_len (cname) -= 1;
@@ -985,6 +1070,8 @@ found_last_request:
   ep->cname = cname;
   ep->flags |= (DNS_CACHE_ENTRY_FLAG_CNAME | DNS_CACHE_ENTRY_FLAG_VALID);
   /* Save the response */
+  if (ep->dns_response)
+    vec_free (ep->dns_response);
   ep->dns_response = reply;
   /* Set up expiration time */
   ep->expiration_time = now + clib_net_to_host_u32 (rr->ttl);
@@ -994,7 +1081,7 @@ found_last_request:
   /* Need to recompute ep post pool-get */
   ep = pool_elt_at_index (dm->entries, ep_index);
 
-  memset (next_ep, 0, sizeof (*next_ep));
+  clib_memset (next_ep, 0, sizeof (*next_ep));
   next_ep->name = vec_dup (cname);
   vec_add1 (next_ep->name, 0);
   _vec_len (next_ep->name) -= 1;
@@ -1012,15 +1099,22 @@ found_last_request:
 #undef _
 
   request = name_to_labels (cname);
+  name_copy = vec_dup (request);
 
   qp_offset = vec_len (request);
 
   /* Add space for the query header */
-  vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
+  vec_validate (request, 2 * qp_offset + 2 * sizeof (dns_query_t) - 1);
 
   qp = (dns_query_t *) (request + qp_offset);
 
-  qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
+  qp->type = clib_host_to_net_u16 (DNS_TYPE_A);
+  qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+  clib_memcpy (qp, name_copy, vec_len (name_copy));
+  qp = (dns_query_t *) (((u8 *) qp) + vec_len (name_copy));
+  vec_free (name_copy);
+
+  qp->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
   qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
 
   /* Punch in space for the dns_header_t */
@@ -1033,7 +1127,7 @@ found_last_request:
 
   /* 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->qdcount = clib_host_to_net_u16 (2);
   h->nscount = 0;
   h->arcount = 0;
 
@@ -1061,10 +1155,11 @@ vnet_dns_response_to_reply (u8 * response,
   dns_rr_t *rr;
   int i, limit;
   u8 len;
-  u8 *curpos, *pos;
+  u8 *curpos, *pos, *pos2;
   u16 flags;
   u16 rcode;
   u32 ttl;
+  int pointer_chase;
 
   h = (dns_header_t *) response;
   flags = clib_net_to_host_u16 (h->flags);
@@ -1120,29 +1215,42 @@ vnet_dns_response_to_reply (u8 * response,
 
   for (i = 0; i < limit; i++)
     {
-      pos = curpos;
+      pos = pos2 = curpos;
+      pointer_chase = 0;
 
       /* Expect pointer chases in the answer section... */
-      if ((pos[0] & 0xC0) == 0xC0)
-       curpos += 2;
-      else
+      if ((pos2[0] & 0xC0) == 0xC0)
        {
-         len = *pos++;
-         while (len)
+         pos = pos2 + 2;
+         pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
+         pointer_chase = 1;
+       }
+
+      len = *pos2++;
+
+      while (len)
+       {
+         pos2 += len;
+         if ((pos2[0] & 0xc0) == 0xc0)
            {
-             if ((pos[0] & 0xC0) == 0xC0)
-               {
-                 curpos = pos + 2;
-                 goto curpos_set;
-               }
-             pos += len;
-             len = *pos++;
+             /*
+              * If we've already done one pointer chase,
+              * do not move the pos pointer.
+              */
+             if (pointer_chase == 0)
+               pos = pos2 + 2;
+             pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
+             len = *pos2++;
+             pointer_chase = 1;
            }
-         curpos = pos;
+         else
+           len = *pos2++;
        }
 
-    curpos_set:
-      rr = (dns_rr_t *) curpos;
+      if (pointer_chase == 0)
+       pos = pos2;
+
+      rr = (dns_rr_t *) pos;
 
       switch (clib_net_to_host_u16 (rr->type))
        {
@@ -1162,13 +1270,15 @@ vnet_dns_response_to_reply (u8 * response,
            *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);
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      curpos = pos;
     }
 
   if ((rmp->ip4_set + rmp->ip6_set) == 0)
@@ -1186,13 +1296,14 @@ vnet_dns_response_to_name (u8 * response,
   dns_rr_t *rr;
   int i, limit;
   u8 len;
-  u8 *curpos, *pos;
+  u8 *curpos, *pos, *pos2;
   u16 flags;
   u16 rcode;
   u8 *name;
   u32 ttl;
   u8 *junk __attribute__ ((unused));
   int name_set = 0;
+  int pointer_chase;
 
   h = (dns_header_t *) response;
   flags = clib_net_to_host_u16 (h->flags);
@@ -1248,37 +1359,50 @@ vnet_dns_response_to_name (u8 * response,
 
   for (i = 0; i < limit; i++)
     {
-      pos = curpos;
+      pos = pos2 = curpos;
+      pointer_chase = 0;
 
       /* Expect pointer chases in the answer section... */
-      if ((pos[0] & 0xC0) == 0xC0)
-       curpos += 2;
-      else
+      if ((pos2[0] & 0xC0) == 0xC0)
        {
-         len = *pos++;
-         while (len)
+         pos = pos2 + 2;
+         pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
+         pointer_chase = 1;
+       }
+
+      len = *pos2++;
+
+      while (len)
+       {
+         pos2 += len;
+         if ((pos2[0] & 0xc0) == 0xc0)
            {
-             if ((pos[0] & 0xC0) == 0xC0)
-               {
-                 curpos = pos + 2;
-                 goto curpos_set;
-               }
-             pos += len;
-             len = *pos++;
+             /*
+              * If we've already done one pointer chase,
+              * do not move the pos pointer.
+              */
+             if (pointer_chase == 0)
+               pos = pos2 + 2;
+             pos2 = response + ((pos2[0] & 0x3f) << 8) + pos2[1];
+             len = *pos2++;
+             pointer_chase = 1;
            }
-         curpos = pos;
+         else
+           len = *pos2++;
        }
 
-    curpos_set:
-      rr = (dns_rr_t *) curpos;
+      if (pointer_chase == 0)
+       pos = pos2;
+
+      rr = (dns_rr_t *) pos;
 
       switch (clib_net_to_host_u16 (rr->type))
        {
        case DNS_TYPE_PTR:
-         name = labels_to_name (rr->rdata, response, &junk);
+         name = vnet_dns_labels_to_name (rr->rdata, response, &junk);
          memcpy (rmp->name, name, vec_len (name));
          ttl = clib_net_to_host_u32 (rr->ttl);
-         if (*min_ttlp)
+         if (min_ttlp)
            *min_ttlp = ttl;
          rmp->name[vec_len (name)] = 0;
          name_set = 1;
@@ -1289,7 +1413,8 @@ vnet_dns_response_to_name (u8 * response,
       /* Might as well stop ASAP */
       if (name_set == 1)
        break;
-      curpos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
+      curpos = pos;
     }
 
   if (name_set == 0)
@@ -1303,13 +1428,17 @@ 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;
+  dns_pending_request_t _t0, *t0 = &_t0;
   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,
-                        DNS_API_PENDING_NAME_TO_IP, &ep);
+  t0->request_type = DNS_API_PENDING_NAME_TO_IP;
+  t0->client_index = mp->client_index;
+  t0->client_context = mp->context;
+
+  rv = vnet_dns_resolve_name (dm, mp->name, t0, &ep);
 
   /* Error, e.g. not enabled? Tell the user */
   if (rv < 0)
@@ -1347,6 +1476,7 @@ vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp)
   int i, len;
   u8 *lookup_name = 0;
   u8 digit, nybble;
+  dns_pending_request_t _t0, *t0 = &_t0;
 
   if (mp->is_ip6)
     {
@@ -1382,8 +1512,11 @@ vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp)
 
   vec_add1 (lookup_name, 0);
 
-  rv = dns_resolve_name (dm, lookup_name, mp->client_index, mp->context,
-                        DNS_API_PENDING_IP_TO_NAME, &ep);
+  t0->request_type = DNS_API_PENDING_IP_TO_NAME;
+  t0->client_index = mp->client_index;
+  t0->client_context = mp->context;
+
+  rv = vnet_dns_resolve_name (dm, lookup_name, t0, &ep);
 
   vec_free (lookup_name);
 
@@ -1407,7 +1540,7 @@ vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp)
   /* *INDENT-ON* */
 
   /*
-   * dns_resolve_name leaves the cache locked when it returns
+   * vnet_dns_resolve_name leaves the cache locked when it returns
    * a cached result, so unlock it here.
    */
   dns_cache_unlock (dm);
@@ -1482,19 +1615,6 @@ dns_init (vlib_main_t * vm)
   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;
 }
 
@@ -1680,7 +1800,7 @@ format_dns_reply_data (u8 * s, va_list * args)
   u8 *pos, *pos2;
   dns_rr_t *rr;
   int i;
-  int initial_pointer_chase = 0;
+  int pointer_chase = 0;
   u16 *tp;
   u16 rrtype_host_byte_order;
 
@@ -1690,11 +1810,11 @@ format_dns_reply_data (u8 * s, va_list * args)
     s = format (s, "    ");
 
   /* chase pointer? almost always yes here... */
-  if (pos2[0] == 0xc0)
+  if ((pos2[0] & 0xc0) == 0xc0)
     {
-      pos2 = reply + pos2[1];
-      pos += 2;
-      initial_pointer_chase = 1;
+      pos = pos2 + 2;
+      pos2 = reply + ((pos2[0] & 0x3f) << 8) + pos2[1];
+      pointer_chase = 1;
     }
 
   len = *pos2++;
@@ -1707,7 +1827,20 @@ format_dns_reply_data (u8 * s, va_list * args)
            vec_add1 (s, *pos2);
          pos2++;
        }
-      len = *pos2++;
+      if ((pos2[0] & 0xc0) == 0xc0)
+       {
+         /*
+          * If we've already done one pointer chase,
+          * do not move the pos pointer.
+          */
+         if (pointer_chase == 0)
+           pos = pos2 + 2;
+         pos2 = reply + ((pos2[0] & 0x3f) << 8) + pos2[1];
+         len = *pos2++;
+         pointer_chase = 1;
+       }
+      else
+       len = *pos2++;
       if (len)
        {
          if (verbose > 1)
@@ -1720,7 +1853,7 @@ format_dns_reply_data (u8 * s, va_list * args)
        }
     }
 
-  if (initial_pointer_chase == 0)
+  if (pointer_chase == 0)
     pos = pos2;
 
   rr = (dns_rr_t *) pos;
@@ -1806,8 +1939,11 @@ format_dns_reply_data (u8 * s, va_list * args)
          pos2 = rr->rdata;
 
          /* chase pointer? */
-         if (pos2[0] == 0xc0)
-           pos2 = reply + pos2[1];
+         if ((pos2[0] & 0xc0) == 0xc0)
+           {
+             pos = pos2 + 2;
+             pos2 = reply + ((pos2[0] & 0x3f) << 8) + pos2[1];
+           }
 
          len = *pos2++;
 
@@ -2095,7 +2231,7 @@ format_dns_cache (u8 * s, va_list * args)
 
             if (verbose > 2)
               s = format (s, "    %d client notifications pending\n",
-                          vec_len(ep->pending_api_requests));
+                          vec_len(ep->pending_requests));
           }
       }
     else
@@ -2314,7 +2450,78 @@ static u8 dns_reply_data_initializer[] = {
   0x03, 0x63, 0x6f, 0x6d, 0x06, 0x61, 0x6b, 0x61, 0x64,
   0x6e, 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00,
 };
-#else
+
+/* bind8 (linux widget, w/ nasty double pointer chasees */
+static u8 dns_reply_data_initializer[] = {
+  /* 0 */
+  0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x08,
+  /* 8 */
+  0x00, 0x06, 0x00, 0x06, 0x0a, 0x6f, 0x72, 0x69,
+  /* 16 */
+  0x67, 0x69, 0x6e, 0x2d, 0x77, 0x77, 0x77, 0x05,
+  /* 24 */
+  0x63, 0x69, 0x73, 0x63, 0x6f, 0x03, 0x63, 0x6f,
+  /* 32 */
+  0x6d, 0x00, 0x00, 0xff, 0x00, 0x01, 0x0a, 0x6f,
+  /* 40 */
+  0x72, 0x69, 0x67, 0x69, 0x6e, 0x2d, 0x77, 0x77,
+  /* 48 */
+  0x77, 0x05, 0x43, 0x49, 0x53, 0x43, 0x4f, 0xc0,
+
+  /* 56 */
+  0x1d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05,
+
+  /* 64 */
+  0x9a, 0x00, 0x18, 0x15, 0x72, 0x63, 0x64,
+  0x6e, 0x39, 0x2d, 0x31, 0x34, 0x70, 0x2d, 0x64, 0x63,
+  0x7a, 0x30, 0x35, 0x6e, 0x2d, 0x67, 0x73, 0x73, 0x31,
+  0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02, 0x00, 0x01, 0x00,
+  0x00, 0x05, 0x9a, 0x00, 0x1a, 0x17, 0x61, 0x6c, 0x6c,
+  0x6e, 0x30, 0x31, 0x2d, 0x61, 0x67, 0x30, 0x39, 0x2d,
+  0x64, 0x63, 0x7a, 0x30, 0x33, 0x6e, 0x2d, 0x67, 0x73,
+  0x73, 0x31, 0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02, 0x00,
+  0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x10, 0x0d, 0x72,
+  0x74, 0x70, 0x35, 0x2d, 0x64, 0x6d, 0x7a, 0x2d, 0x67,
+  0x73, 0x73, 0x31, 0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02,
+  0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x18, 0x15,
+  0x6d, 0x74, 0x76, 0x35, 0x2d, 0x61, 0x70, 0x31, 0x30,
+  0x2d, 0x64, 0x63, 0x7a, 0x30, 0x36, 0x6e, 0x2d, 0x67,
+  0x73, 0x73, 0x31, 0xc0, 0x17, 0xc0, 0x26, 0x00, 0x02,
+  0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x1b, 0x18,
+  0x73, 0x6e, 0x67, 0x64, 0x63, 0x30, 0x31, 0x2d, 0x61,
+  0x62, 0x30, 0x37, 0x2d, 0x64, 0x63, 0x7a, 0x30, 0x31,
+  0x6e, 0x2d, 0x67, 0x73, 0x73, 0x31, 0xc0, 0x17, 0xc0,
+  0x26, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05, 0x9a,
+  0x00, 0x1a, 0x17, 0x61, 0x65, 0x72, 0x30, 0x31, 0x2d,
+  0x72, 0x34, 0x63, 0x32, 0x35, 0x2d, 0x64, 0x63, 0x7a,
+  0x30, 0x31, 0x6e, 0x2d, 0x67, 0x73, 0x73, 0x31, 0xc0,
+  0x17, 0xc0, 0x26, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+  0x00, 0x81, 0x00, 0x04, 0x48, 0xa3, 0x04, 0xa1, 0xc0,
+  0x26, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x82,
+  0x00, 0x10, 0x20, 0x01, 0x04, 0x20, 0x12, 0x01, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a,
+  0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05,
+  0x9a, 0x00, 0x02, 0xc0, 0xf4, 0xc0, 0x0c, 0x00, 0x02,
+  0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x02, 0xc0,
+  0xcd, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
+  0x05, 0x9a, 0x00, 0x02, 0xc0, 0x8d, 0xc0, 0x0c, 0x00,
+  0x02, 0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00, 0x02,
+  0xc0, 0x43, 0xc0, 0x0c, 0x00, 0x02, 0x00, 0x01, 0x00,
+  0x00, 0x05, 0x9a, 0x00, 0x02, 0xc0, 0xa9, 0xc0, 0x0c,
+  0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x05, 0x9a, 0x00,
+  0x02, 0xc0, 0x67, 0xc0, 0x8d, 0x00, 0x01, 0x00, 0x01,
+  0x00, 0x00, 0x07, 0x08, 0x00, 0x04, 0x40, 0x66, 0xf6,
+  0x05, 0xc0, 0xa9, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+  0x07, 0x08, 0x00, 0x04, 0xad, 0x24, 0xe0, 0x64, 0xc0,
+  0x43, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x07, 0x08,
+  0x00, 0x04, 0x48, 0xa3, 0x04, 0x1c, 0xc0, 0xf4, 0x00,
+  0x01, 0x00, 0x01, 0x00, 0x00, 0x07, 0x08, 0x00, 0x04,
+  0xad, 0x26, 0xd4, 0x6c, 0xc0, 0x67, 0x00, 0x01, 0x00,
+  0x01, 0x00, 0x00, 0x07, 0x08, 0x00, 0x04, 0xad, 0x25,
+  0x90, 0x64, 0xc0, 0xcd, 0x00, 0x01, 0x00, 0x01, 0x00,
+  0x00, 0x07, 0x08, 0x00, 0x04, 0xad, 0x27, 0x70, 0x44,
+};
+
 /* google.com */
 static u8 dns_reply_data_initializer[] =
   { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x6,
@@ -2348,6 +2555,21 @@ static u8 dns_reply_data_initializer[] =
   0x57,
   0x0, 0x9, 0x0, 0x14, 0x4, 0x61, 0x6c, 0x74, 0x31, 0xc0, 0x9b
 };
+
+#else
+/* www.weatherlink.com */
+static u8 dns_reply_data_initializer[] = {
+  0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
+  0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x0b,
+  0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x6c, 0x69,
+  0x6e, 0x6b, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0xff,
+  0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00,
+  0x00, 0x0c, 0x9e, 0x00, 0x1f, 0x0e, 0x64, 0x33, 0x6b,
+  0x72, 0x30, 0x67, 0x75, 0x62, 0x61, 0x31, 0x64, 0x76,
+  0x77, 0x66, 0x0a, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x66,
+  0x72, 0x6f, 0x6e, 0x74, 0x03, 0x6e, 0x65, 0x74, 0x00,
+};
+
 #endif
 
 static clib_error_t *
@@ -2377,7 +2599,7 @@ test_dns_fmt_command_fn (vlib_main_t * vm,
 
   vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
 
-  memset (rmp, 0, sizeof (*rmp));
+  clib_memset (rmp, 0, sizeof (*rmp));
 
   rv = vnet_dns_response_to_reply (dns_reply_data, rmp, 0 /* ttl-ptr */ );
 
@@ -2462,7 +2684,7 @@ test_dns_expire_command_fn (vlib_main_t * vm,
                            vlib_cli_command_t * cmd)
 {
   dns_main_t *dm = &dns_main;
-  u8 *name;
+  u8 *name = 0;
   uword *p;
   clib_error_t *e;
   dns_cache_entry_t *ep;
@@ -2472,6 +2694,8 @@ test_dns_expire_command_fn (vlib_main_t * vm,
       vec_add1 (name, 0);
       _vec_len (name) -= 1;
     }
+  else
+    return clib_error_return (0, "no name provided");
 
   dns_cache_lock (dm);
 
@@ -2501,6 +2725,257 @@ VLIB_CLI_COMMAND (test_dns_expire_command) =
 /* *INDENT-ON* */
 #endif
 
+void
+vnet_send_dns6_reply (dns_main_t * dm, dns_pending_request_t * pr,
+                     dns_cache_entry_t * ep, vlib_buffer_t * b0)
+{
+  clib_warning ("Unimplemented...");
+}
+
+
+void
+vnet_send_dns4_reply (dns_main_t * dm, dns_pending_request_t * pr,
+                     dns_cache_entry_t * ep, vlib_buffer_t * b0)
+{
+  vlib_main_t *vm = dm->vlib_main;
+  u32 bi = 0;
+  fib_prefix_t prefix;
+  fib_node_index_t fei;
+  u32 sw_if_index, fib_index;
+  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;
+  ip4_header_t *ip;
+  udp_header_t *udp;
+  dns_header_t *dh;
+  vlib_frame_t *f;
+  u32 *to_next;
+  u8 *dns_response;
+  u8 *reply;
+  vl_api_dns_resolve_name_reply_t _rnr, *rnr = &_rnr;
+  vl_api_dns_resolve_ip_reply_t _rir, *rir = &_rir;
+  u32 ttl, tmp;
+  u32 qp_offset;
+  dns_query_t *qp;
+  dns_rr_t *rr;
+  u8 *rrptr;
+  int is_fail = 0;
+
+  ASSERT (ep && ep->dns_response);
+
+  if (pr->request_type == DNS_PEER_PENDING_NAME_TO_IP)
+    {
+      /* Quick and dirty way to dig up the A-record address. $$ FIXME */
+      clib_memset (rnr, 0, sizeof (*rnr));
+      if (vnet_dns_response_to_reply (ep->dns_response, rnr, &ttl))
+       {
+         /* clib_warning ("response_to_reply failed..."); */
+         is_fail = 1;
+       }
+      if (rnr->ip4_set == 0)
+       {
+         /* clib_warning ("No A-record..."); */
+         is_fail = 1;
+       }
+    }
+  else if (pr->request_type == DNS_PEER_PENDING_IP_TO_NAME)
+    {
+      clib_memset (rir, 0, sizeof (*rir));
+      if (vnet_dns_response_to_name (ep->dns_response, rir, &ttl))
+       {
+         /* clib_warning ("response_to_name failed..."); */
+         is_fail = 1;
+       }
+    }
+  else
+    {
+      clib_warning ("Unknown request type %d", pr->request_type);
+      return;
+    }
+
+  /* Initialize a buffer */
+  if (b0 == 0)
+    {
+      if (vlib_buffer_alloc (vm, &bi, 1) != 1)
+       return;
+      b0 = vlib_get_buffer (vm, bi);
+    }
+
+  if (b0->flags & VLIB_BUFFER_NEXT_PRESENT)
+    vlib_buffer_free_one (vm, b0->next_buffer);
+
+  /*
+   * Reset the buffer. We recycle the DNS request packet in the cache
+   * hit case, and reply immediately from the request node.
+   *
+   * In the resolution-required / deferred case, resetting a freshly-allocated
+   * buffer won't hurt. We hope.
+   */
+  b0->flags |= (VNET_BUFFER_F_LOCALLY_ORIGINATED
+               | VLIB_BUFFER_TOTAL_LENGTH_VALID);
+  b0->current_data = 0;
+  b0->current_length = 0;
+  b0->total_length_not_including_first_buffer = 0;
+  vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;  /* "local0" */
+  vnet_buffer (b0)->sw_if_index[VLIB_TX] = 0;  /* default VRF for now */
+
+  /* Find a FIB path to the peer we're trying to answer */
+  clib_memcpy (&prefix.fp_addr.ip4, pr->dst_address, sizeof (ip4_address_t));
+  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 unnumbered */,
+  ({
+    src_address = ip_interface_address_get_address (lm4, ia);
+    goto found_src_address;
+  }));
+  /* *INDENT-ON* */
+
+  clib_warning ("FIB BUG");
+  return;
+
+found_src_address:
+
+  ip = vlib_buffer_get_current (b0);
+  udp = (udp_header_t *) (ip + 1);
+  dns_response = (u8 *) (udp + 1);
+  clib_memset (ip, 0, sizeof (*ip) + sizeof (*udp));
+
+  /*
+   * 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.
+   */
+  reply = name_to_labels (pr->name);
+  vec_free (pr->name);
+
+  qp_offset = vec_len (reply);
+
+  /* Add space for the query header */
+  vec_validate (reply, qp_offset + sizeof (dns_query_t) - 1);
+
+  qp = (dns_query_t *) (reply + qp_offset);
+
+  if (pr->request_type == DNS_PEER_PENDING_NAME_TO_IP)
+    qp->type = clib_host_to_net_u16 (DNS_TYPE_A);
+  else
+    qp->type = clib_host_to_net_u16 (DNS_TYPE_PTR);
+
+  qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
+
+  /* Punch in space for the dns_header_t */
+  vec_insert (reply, sizeof (dns_header_t), 0);
+
+  dh = (dns_header_t *) reply;
+
+  /* Transaction ID = pool index */
+  dh->id = pr->id;
+
+  /* Announce that we did a recursive lookup */
+  tmp = DNS_AA | DNS_RA | DNS_RD | DNS_OPCODE_QUERY | DNS_QR;
+  if (is_fail)
+    tmp |= DNS_RCODE_NAME_ERROR;
+  dh->flags = clib_host_to_net_u16 (tmp);
+  dh->qdcount = clib_host_to_net_u16 (1);
+  dh->anscount = (is_fail == 0) ? clib_host_to_net_u16 (1) : 0;
+  dh->nscount = 0;
+  dh->arcount = 0;
+
+  /* If the name resolution worked, cough up an appropriate RR */
+  if (is_fail == 0)
+    {
+      /* Add the answer. First, a name pointer (0xC00C) */
+      vec_add1 (reply, 0xC0);
+      vec_add1 (reply, 0x0C);
+
+      /* Now, add single A-rec RR */
+      if (pr->request_type == DNS_PEER_PENDING_NAME_TO_IP)
+       {
+         vec_add2 (reply, rrptr, sizeof (dns_rr_t) + sizeof (ip4_address_t));
+         rr = (dns_rr_t *) rrptr;
+
+         rr->type = clib_host_to_net_u16 (DNS_TYPE_A);
+         rr->class = clib_host_to_net_u16 (1 /* internet */ );
+         rr->ttl = clib_host_to_net_u32 (ttl);
+         rr->rdlength = clib_host_to_net_u16 (sizeof (ip4_address_t));
+         clib_memcpy (rr->rdata, rnr->ip4_address, sizeof (ip4_address_t));
+       }
+      else
+       {
+         /* Or a single PTR RR */
+         u8 *vecname = format (0, "%s", rir->name);
+         u8 *label_vec = name_to_labels (vecname);
+         vec_free (vecname);
+
+         vec_add2 (reply, rrptr, sizeof (dns_rr_t) + vec_len (label_vec));
+         rr = (dns_rr_t *) rrptr;
+         rr->type = clib_host_to_net_u16 (DNS_TYPE_PTR);
+         rr->class = clib_host_to_net_u16 (1 /* internet */ );
+         rr->ttl = clib_host_to_net_u32 (ttl);
+         rr->rdlength = clib_host_to_net_u16 (vec_len (label_vec));
+         clib_memcpy (rr->rdata, label_vec, vec_len (label_vec));
+         vec_free (label_vec);
+       }
+    }
+  clib_memcpy (dns_response, reply, vec_len (reply));
+
+  /* Set the packet length */
+  b0->current_length = sizeof (*ip) + sizeof (*udp) + vec_len (reply);
+
+  /* IP header */
+  ip->ip_version_and_header_length = 0x45;
+  ip->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
+  ip->ttl = 255;
+  ip->protocol = IP_PROTOCOL_UDP;
+  ip->src_address.as_u32 = src_address->as_u32;
+  clib_memcpy (ip->dst_address.as_u8, pr->dst_address,
+              sizeof (ip4_address_t));
+  ip->checksum = ip4_header_checksum (ip);
+
+  /* UDP header */
+  udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
+  udp->dst_port = pr->dst_port;
+  udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
+                                     vec_len (reply));
+  udp->checksum = 0;
+  vec_free (reply);
+
+  /* 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);
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *