LCOV - code coverage report
Current view: top level - plugins/dns - resolver_process.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 16 146 11.0 %
Date: 2023-07-05 22:20:52 Functions: 2 4 50.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : 
      16             : #include <dns/dns.h>
      17             : #include <vlibapi/api.h>
      18             : #include <vlibmemory/api.h>
      19             : 
      20             : #include <vlib/vlib.h>
      21             : #include <vnet/vnet.h>
      22             : 
      23             : /* define message IDs */
      24             : #include <dns/dns.api_enum.h>
      25             : #include <dns/dns.api_types.h>
      26             : 
      27             : #include <vlibapi/api_helper_macros.h>
      28             : 
      29             : int
      30             : vnet_dns_response_to_reply (u8 * response,
      31             :                             vl_api_dns_resolve_name_reply_t * rmp,
      32             :                             u32 * min_ttlp);
      33             : int
      34             : vnet_dns_response_to_name (u8 * response,
      35             :                            vl_api_dns_resolve_ip_reply_t * rmp,
      36             :                            u32 * min_ttlp);
      37             : 
      38             : static void
      39           0 : resolve_event (vlib_main_t * vm, dns_main_t * dm, f64 now, u8 * reply)
      40             : {
      41             :   dns_pending_request_t *pr;
      42             :   dns_header_t *d;
      43             :   u32 pool_index;
      44             :   dns_cache_entry_t *ep;
      45             :   u32 min_ttl;
      46             :   u16 flags;
      47             :   u16 rcode;
      48             :   int i;
      49             :   int entry_was_valid;
      50             :   int remove_count;
      51           0 :   int rv = 0;
      52             : 
      53           0 :   d = (dns_header_t *) reply;
      54           0 :   flags = clib_net_to_host_u16 (d->flags);
      55           0 :   rcode = flags & DNS_RCODE_MASK;
      56             : 
      57             :   /* $$$ u16 limits cache to 65K entries, fix later multiple dst ports */
      58           0 :   pool_index = clib_net_to_host_u16 (d->id);
      59           0 :   dns_cache_lock (dm, 10);
      60             : 
      61           0 :   if (pool_is_free_index (dm->entries, pool_index))
      62             :     {
      63           0 :       vec_free (reply);
      64             :       if (0)
      65             :         clib_warning ("pool index %d is free", pool_index);
      66           0 :       vlib_node_increment_counter (vm, dns46_reply_node.index,
      67             :                                    DNS46_REPLY_ERROR_NO_ELT, 1);
      68           0 :       dns_cache_unlock (dm);
      69           0 :       return;
      70             :     }
      71             : 
      72           0 :   ep = pool_elt_at_index (dm->entries, pool_index);
      73             : 
      74           0 :   if (ep->dns_response)
      75           0 :     vec_free (ep->dns_response);
      76             : 
      77             :   /* Handle [sic] recursion AKA CNAME indirection */
      78           0 :   rv = vnet_dns_cname_indirection_nolock (vm, dm, pool_index, reply);
      79             : 
      80             :   /* CNAME found, further resolution pending, we're done here */
      81           0 :   if (rv > 0)
      82             :     {
      83           0 :       dns_cache_unlock (dm);
      84           0 :       return;
      85             :     }
      86             :   /* Server backfire: refused to answer, or sent zero replies */
      87           0 :   if (rv < 0)
      88             :     {
      89             :       /* Try a different server */
      90           0 :       if (ep->server_af /* ip6 */ )
      91             :         {
      92             :           if (0)
      93             :             clib_warning ("Server %U failed to resolve '%s'",
      94             :                           format_ip6_address,
      95             :                           dm->ip6_name_servers + ep->server_rotor, ep->name);
      96             :           /* Any more servers to try? */
      97           0 :           if (ep->server_fails > 1 || vec_len (dm->ip6_name_servers) <= 1)
      98             :             {
      99             :               /* No, tell the client to go away */
     100           0 :               goto reply;
     101             :             }
     102           0 :           ep->retry_count = 0;
     103           0 :           ep->server_rotor++;
     104           0 :           ep->server_fails++;
     105           0 :           if (ep->server_rotor >= vec_len (dm->ip6_name_servers))
     106           0 :             ep->server_rotor = 0;
     107             :           if (0)
     108             :             clib_warning ("Try server %U", format_ip6_address,
     109             :                           dm->ip6_name_servers + ep->server_rotor);
     110           0 :           vnet_dns_send_dns6_request
     111           0 :             (vm, dm, ep, dm->ip6_name_servers + ep->server_rotor);
     112             :         }
     113             :       else
     114             :         {
     115             :           if (0)
     116             :             clib_warning ("Server %U failed to resolve '%s'",
     117             :                           format_ip4_address,
     118             :                           dm->ip4_name_servers + ep->server_rotor, ep->name);
     119             : 
     120           0 :           if (ep->server_fails > 1 || vec_len (dm->ip4_name_servers) <= 1)
     121             :             {
     122             :               /* No, tell the client to go away */
     123           0 :               goto reply;
     124             :             }
     125           0 :           ep->retry_count = 0;
     126           0 :           ep->server_rotor++;
     127           0 :           ep->server_fails++;
     128           0 :           if (ep->server_rotor >= vec_len (dm->ip4_name_servers))
     129           0 :             ep->server_rotor = 0;
     130             :           if (0)
     131             :             clib_warning ("Try server %U", format_ip4_address,
     132             :                           dm->ip4_name_servers + ep->server_rotor);
     133           0 :           vnet_dns_send_dns4_request
     134           0 :             (vm, dm, ep, dm->ip4_name_servers + ep->server_rotor);
     135             :         }
     136           0 :       dns_cache_unlock (dm);
     137           0 :       return;
     138             :     }
     139             : 
     140           0 : reply:
     141             :   /* Save the response */
     142           0 :   ep->dns_response = reply;
     143             : 
     144             :   /*
     145             :    * Pick a sensible default cache entry expiration time.
     146             :    * We don't play the 10-second timeout game.
     147             :    */
     148           0 :   ep->expiration_time = now + 600.0;
     149             : 
     150             :   if (0)
     151             :     clib_warning ("resolving '%s', was %s valid",
     152             :                   ep->name, (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID) ?
     153             :                   "already" : "not");
     154             :   /*
     155             :    * The world is a mess. A single DNS request sent to e.g. 8.8.8.8
     156             :    * may yield multiple, subtly different responses - all with the same
     157             :    * DNS protocol-level ID.
     158             :    *
     159             :    * Last response wins in terms of what ends up in the cache.
     160             :    * First response wins in terms of the response sent to the client.
     161             :    */
     162             : 
     163             :   /* Strong hint that we may not find a pending resolution entry */
     164           0 :   entry_was_valid = (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID) ? 1 : 0;
     165             : 
     166           0 :   if (vec_len (ep->dns_response))
     167           0 :     ep->flags |= DNS_CACHE_ENTRY_FLAG_VALID;
     168             : 
     169             :   /* Most likely, send 1 message */
     170           0 :   for (i = 0; i < vec_len (ep->pending_requests); i++)
     171             :     {
     172             :       vl_api_registration_t *regp;
     173             : 
     174           0 :       pr = vec_elt_at_index (ep->pending_requests, i);
     175             : 
     176           0 :       switch (pr->request_type)
     177             :         {
     178           0 :         case DNS_API_PENDING_NAME_TO_IP:
     179             :           {
     180             :             vl_api_dns_resolve_name_reply_t *rmp;
     181           0 :             regp = vl_api_client_index_to_registration (pr->client_index);
     182           0 :             if (regp == 0)
     183           0 :               continue;
     184             : 
     185           0 :             rmp = vl_msg_api_alloc (sizeof (*rmp));
     186           0 :             rmp->_vl_msg_id =
     187           0 :               clib_host_to_net_u16 (VL_API_DNS_RESOLVE_NAME_REPLY
     188           0 :                                     + dm->msg_id_base);
     189           0 :             rmp->context = pr->client_context;
     190           0 :             min_ttl = ~0;
     191           0 :             rv = vnet_dns_response_to_reply (ep->dns_response, rmp, &min_ttl);
     192           0 :             if (min_ttl != ~0)
     193           0 :               ep->expiration_time = now + min_ttl;
     194           0 :             rmp->retval = clib_host_to_net_u32 (rv);
     195           0 :             vl_api_send_msg (regp, (u8 *) rmp);
     196             :           }
     197           0 :           break;
     198             : 
     199           0 :         case DNS_API_PENDING_IP_TO_NAME:
     200             :           {
     201             :             vl_api_dns_resolve_ip_reply_t *rmp;
     202             : 
     203           0 :             regp = vl_api_client_index_to_registration (pr->client_index);
     204           0 :             if (regp == 0)
     205           0 :               continue;
     206             : 
     207           0 :             rmp = vl_msg_api_alloc (sizeof (*rmp));
     208           0 :             rmp->_vl_msg_id =
     209           0 :               clib_host_to_net_u16 (VL_API_DNS_RESOLVE_IP_REPLY
     210           0 :                                     + dm->msg_id_base);
     211           0 :             rmp->context = pr->client_context;
     212           0 :             min_ttl = ~0;
     213           0 :             rv = vnet_dns_response_to_name (ep->dns_response, rmp, &min_ttl);
     214           0 :             if (min_ttl != ~0)
     215           0 :               ep->expiration_time = now + min_ttl;
     216           0 :             rmp->retval = clib_host_to_net_u32 (rv);
     217           0 :             vl_api_send_msg (regp, (u8 *) rmp);
     218             :           }
     219           0 :           break;
     220             : 
     221           0 :         case DNS_PEER_PENDING_IP_TO_NAME:
     222             :         case DNS_PEER_PENDING_NAME_TO_IP:
     223           0 :           if (pr->is_ip6)
     224           0 :             vnet_send_dns6_reply (vm, dm, pr, ep, 0 /* allocate a buffer */ );
     225             :           else
     226           0 :             vnet_send_dns4_reply (vm, dm, pr, ep, 0 /* allocate a buffer */ );
     227           0 :           break;
     228           0 :         default:
     229           0 :           clib_warning ("request type %d unknown", pr->request_type);
     230           0 :           break;
     231             :         }
     232             :     }
     233           0 :   vec_free (ep->pending_requests);
     234             : 
     235           0 :   remove_count = 0;
     236           0 :   for (i = 0; i < vec_len (dm->unresolved_entries); i++)
     237             :     {
     238           0 :       if (dm->unresolved_entries[i] == pool_index)
     239             :         {
     240           0 :           vec_delete (dm->unresolved_entries, 1, i);
     241           0 :           remove_count++;
     242           0 :           i--;
     243             :         }
     244             :     }
     245             :   /* See multiple response comment above... */
     246           0 :   if (remove_count == 0)
     247             :     {
     248           0 :       u32 error_code = entry_was_valid ? DNS46_REPLY_ERROR_MULTIPLE_REPLY :
     249             :         DNS46_REPLY_ERROR_NO_UNRESOLVED_ENTRY;
     250             : 
     251           0 :       vlib_node_increment_counter (vm, dns46_reply_node.index, error_code, 1);
     252           0 :       dns_cache_unlock (dm);
     253           0 :       return;
     254             :     }
     255             : 
     256             :   /* Deal with bogus names, server issues, etc. */
     257           0 :   switch (rcode)
     258             :     {
     259           0 :     default:
     260             :     case DNS_RCODE_NO_ERROR:
     261           0 :       break;
     262             : 
     263           0 :     case DNS_RCODE_SERVER_FAILURE:
     264             :     case DNS_RCODE_NOT_IMPLEMENTED:
     265             :     case DNS_RCODE_REFUSED:
     266           0 :       if (ep->server_af == 0)
     267           0 :         clib_warning ("name server %U can't resolve '%s'",
     268             :                       format_ip4_address,
     269             :                       dm->ip4_name_servers + ep->server_rotor, ep->name);
     270             :       else
     271           0 :         clib_warning ("name server %U can't resolve '%s'",
     272             :                       format_ip6_address,
     273             :                       dm->ip6_name_servers + ep->server_rotor, ep->name);
     274             :       /* FALLTHROUGH */
     275             :     case DNS_RCODE_NAME_ERROR:
     276             :     case DNS_RCODE_FORMAT_ERROR:
     277             :       /* remove trash from the cache... */
     278           0 :       vnet_dns_delete_entry_by_index_nolock (dm, ep - dm->entries);
     279           0 :       break;
     280             :     }
     281             : 
     282             : 
     283           0 :   dns_cache_unlock (dm);
     284           0 :   return;
     285             : }
     286             : 
     287             : static void
     288           0 : retry_scan (vlib_main_t * vm, dns_main_t * dm, f64 now)
     289             : {
     290             :   int i;
     291             :   dns_cache_entry_t *ep;
     292             : 
     293           0 :   for (i = 0; i < vec_len (dm->unresolved_entries); i++)
     294             :     {
     295           0 :       dns_cache_lock (dm, 11);
     296           0 :       ep = pool_elt_at_index (dm->entries, dm->unresolved_entries[i]);
     297             : 
     298           0 :       ASSERT ((ep->flags & DNS_CACHE_ENTRY_FLAG_VALID) == 0);
     299           0 :       vnet_send_dns_request (vm, dm, ep);
     300           0 :       dns_cache_unlock (dm);
     301             :     }
     302           0 : }
     303             : 
     304             : static uword
     305           5 : dns_resolver_process (vlib_main_t * vm,
     306             :                       vlib_node_runtime_t * rt, vlib_frame_t * f)
     307             : {
     308           5 :   dns_main_t *dm = &dns_main;
     309             :   f64 now;
     310           5 :   f64 timeout = 1000.0;
     311           5 :   uword *event_data = 0;
     312             :   uword event_type;
     313             :   int i;
     314             : 
     315             :   while (1)
     316             :     {
     317           6 :       vlib_process_wait_for_event_or_clock (vm, timeout);
     318             : 
     319           1 :       now = vlib_time_now (vm);
     320             : 
     321           1 :       event_type = vlib_process_get_events (vm, (uword **) & event_data);
     322             : 
     323           1 :       switch (event_type)
     324             :         {
     325             :           /* Send one of these when a resolution is pending */
     326           1 :         case DNS_RESOLVER_EVENT_PENDING:
     327           1 :           timeout = 2.0;
     328           1 :           break;
     329             : 
     330           0 :         case DNS_RESOLVER_EVENT_RESOLVED:
     331           0 :           for (i = 0; i < vec_len (event_data); i++)
     332           0 :             resolve_event (vm, dm, now, (u8 *) event_data[i]);
     333           0 :           break;
     334             : 
     335           0 :         case ~0:                /* timeout */
     336           0 :           retry_scan (vm, dm, now);
     337           0 :           break;
     338             :         }
     339           1 :       vec_reset_length (event_data);
     340             : 
     341             :       /* No work? Back to slow timeout mode... */
     342           1 :       if (vec_len (dm->unresolved_entries) == 0)
     343           0 :         timeout = 1000.0;
     344             :     }
     345             :   return 0;                     /* or not */
     346             : }
     347             : 
     348             : void
     349           5 : vnet_dns_create_resolver_process (vlib_main_t * vm, dns_main_t * dm)
     350             : {
     351             :   /* Already created the resolver process? */
     352           5 :   if (dm->resolver_process_node_index > 0)
     353           0 :     return;
     354             : 
     355             :   /* No, create it now and make a note of the node index */
     356           5 :   dm->resolver_process_node_index = vlib_process_create
     357             :     (vm, "dns-resolver-process",
     358             :      dns_resolver_process, 16 /* log2_n_stack_bytes */ );
     359             : }
     360             : 
     361             : /*
     362             :  * fd.io coding-style-patch-verification: ON
     363             :  *
     364             :  * Local Variables:
     365             :  * eval: (c-set-style "gnu")
     366             :  * End:
     367             :  */

Generated by: LCOV version 1.14