ip4 network request processing
[vpp.git] / src / vnet / dns / dns.c
index d3f573d..d361aa1 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* */
 
@@ -472,7 +470,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;
@@ -646,9 +644,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;
@@ -766,16 +762,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 +846,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);
        }
@@ -882,19 +877,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.
@@ -977,7 +991,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;
@@ -1275,7 +1289,7 @@ vnet_dns_response_to_name (u8 * response,
       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)
@@ -1303,13 +1317,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 +1365,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 +1401,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 +1429,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);
@@ -1488,13 +1510,11 @@ dns_init (vlib_main_t * vm)
   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;
 }
 
@@ -2095,7 +2115,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
@@ -2501,6 +2521,258 @@ 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;
+  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 */
+      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)
+    {
+      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 &= VLIB_BUFFER_FREE_LIST_INDEX_MASK;
+  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 unnummbered */,
+  ({
+    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);
+  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
  *