VPP-1027: DNS name resolver
[vpp.git] / src / vnet / dns / resolver_process.c
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:
+ */