VPP-1027: DNS name resolver
[vpp.git] / src / vnet / dns / resolver_process.c
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 <vnet/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 #include <vnet/vnet_msg_enum.h>
24
25 #define vl_typedefs             /* define message structures */
26 #include <vnet/vnet_all_api_h.h>
27 #undef vl_typedefs
28
29 #define vl_endianfun            /* define message structures */
30 #include <vnet/vnet_all_api_h.h>
31 #undef vl_endianfun
32
33 /* instantiate all the print functions we know about */
34 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
35 #define vl_printfun
36 #include <vnet/vnet_all_api_h.h>
37 #undef vl_printfun
38
39 #include <vlibapi/api_helper_macros.h>
40
41 vlib_node_registration_t dns_resolver_node;
42
43 extern int
44 vnet_dns_response_to_reply (u8 * response,
45                             vl_api_dns_resolve_name_reply_t * rmp,
46                             u32 * min_ttlp);
47
48 static void
49 resolve_event (dns_main_t * dm, f64 now, u8 * reply)
50 {
51   vlib_main_t *vm = dm->vlib_main;
52   dns_header_t *d;
53   u32 pool_index;
54   dns_cache_entry_t *ep;
55   u32 min_ttl;
56   u16 flags;
57   u16 rcode;
58   int i;
59   int rv = 0;
60
61   d = (dns_header_t *) reply;
62   flags = clib_net_to_host_u16 (d->flags);
63   rcode = flags & DNS_RCODE_MASK;
64
65   /* $$$ u16 limits cache to 65K entries, fix later multiple dst ports */
66   pool_index = clib_net_to_host_u16 (d->id);
67   dns_cache_lock (dm);
68
69   if (pool_is_free_index (dm->entries, pool_index))
70     {
71       vec_free (reply);
72       vlib_node_increment_counter (vm, dns46_reply_node.index,
73                                    DNS46_REPLY_ERROR_NO_ELT, 1);
74       dns_cache_unlock (dm);
75       return;
76     }
77
78   ep = pool_elt_at_index (dm->entries, pool_index);
79
80   if (ep->dns_response)
81     vec_free (ep->dns_response);
82
83   /* Handle [sic] recursion AKA CNAME indirection */
84   if (vnet_dns_cname_indirection_nolock (dm, ep, reply))
85     {
86       dns_cache_unlock (dm);
87       return;
88     }
89
90   /* Save the response */
91   ep->dns_response = reply;
92   /* Pick some sensible default. */
93   ep->expiration_time = now + 600.0;
94   if (vec_len (ep->dns_response))
95     ep->flags |= DNS_CACHE_ENTRY_FLAG_VALID;
96
97   /* Most likely, send 1 message */
98   for (i = 0; i < vec_len (ep->api_clients_to_notify); i++)
99     {
100       vl_api_registration_t *regp;
101       vl_api_dns_resolve_name_reply_t *rmp;
102
103       regp = vl_api_client_index_to_registration
104         (ep->api_clients_to_notify[i]);
105
106       if (regp == 0)
107         continue;
108
109       rmp = vl_msg_api_alloc (sizeof (*rmp) + vec_len (ep->dns_response));
110       rmp->_vl_msg_id = clib_host_to_net_u16 (VL_API_DNS_RESOLVE_NAME_REPLY);
111       rmp->context = ep->api_client_contexts[i];
112       min_ttl = ~0;
113       rv = vnet_dns_response_to_reply (ep->dns_response, rmp, &min_ttl);
114       if (min_ttl != ~0)
115         ep->expiration_time = now + min_ttl;
116       rmp->retval = clib_host_to_net_u32 (rv);
117       vl_msg_api_send (regp, (u8 *) rmp);
118     }
119   vec_free (ep->api_clients_to_notify);
120   vec_free (ep->api_client_contexts);
121
122   /* $$$ Add ip4/ip6 reply code */
123
124   for (i = 0; i < vec_len (dm->unresolved_entries); i++)
125     {
126       if (dm->unresolved_entries[i] == pool_index)
127         {
128           vec_delete (dm->unresolved_entries, 1, i);
129           goto found;
130         }
131     }
132   clib_warning ("pool index %d AWOL from unresolved vector", pool_index);
133
134 found:
135   /* Deal with bogus names, server issues, etc. */
136   switch (rcode)
137     {
138     default:
139     case DNS_RCODE_NO_ERROR:
140       break;
141
142     case DNS_RCODE_SERVER_FAILURE:
143     case DNS_RCODE_NOT_IMPLEMENTED:
144     case DNS_RCODE_REFUSED:
145       if (ep->server_af == 0)
146         clib_warning ("name server %U backfire",
147                       format_ip4_address,
148                       dm->ip4_name_servers + ep->server_rotor);
149       else
150         clib_warning ("name server %U backfire",
151                       format_ip6_address,
152                       dm->ip6_name_servers + ep->server_rotor);
153       /* FALLTHROUGH */
154     case DNS_RCODE_NAME_ERROR:
155     case DNS_RCODE_FORMAT_ERROR:
156       /* remove trash from the cache... */
157       vnet_dns_delete_entry_by_index_nolock (dm, ep - dm->entries);
158       break;
159     }
160
161   dns_cache_unlock (dm);
162   return;
163 }
164
165 static void
166 retry_scan (dns_main_t * dm, f64 now)
167 {
168   int i;
169   dns_cache_entry_t *ep;
170
171   for (i = 0; i < vec_len (dm->unresolved_entries); i++)
172     {
173       dns_cache_lock (dm);
174       ep = pool_elt_at_index (dm->entries, dm->unresolved_entries[i]);
175
176       ASSERT ((ep->flags & DNS_CACHE_ENTRY_FLAG_VALID) == 0);
177
178       vnet_send_dns_request (dm, ep);
179       dns_cache_unlock (dm);
180     }
181 }
182
183 static uword
184 dns_resolver_process (vlib_main_t * vm,
185                       vlib_node_runtime_t * rt, vlib_frame_t * f)
186 {
187   dns_main_t *dm = &dns_main;
188   f64 now;
189   f64 timeout = 1000.0;
190   uword *event_data = 0;
191   uword event_type;
192   int i;
193
194   while (1)
195     {
196       vlib_process_wait_for_event_or_clock (vm, timeout);
197
198       now = vlib_time_now (vm);
199
200       event_type = vlib_process_get_events (vm, (uword **) & event_data);
201
202       switch (event_type)
203         {
204           /* Send one of these when a resolution is pending */
205         case DNS_RESOLVER_EVENT_PENDING:
206           timeout = 2.0;
207           break;
208
209         case DNS_RESOLVER_EVENT_RESOLVED:
210           for (i = 0; i < vec_len (event_data); i++)
211             resolve_event (dm, now, (u8 *) event_data[i]);
212           break;
213
214         case ~0:                /* timeout */
215           retry_scan (dm, now);
216           break;
217         }
218       vec_reset_length (event_data);
219
220       /* No work? Back to slow timeout mode... */
221       if (vec_len (dm->unresolved_entries) == 0)
222         timeout = 1000.0;
223     }
224   return 0;                     /* or not */
225 }
226
227 /* *INDENT-OFF* */
228 VLIB_REGISTER_NODE (dns_resolver_node) =
229 {
230   .function = dns_resolver_process,
231   .type = VLIB_NODE_TYPE_PROCESS,
232   .name = "dns-resolver-process",
233 };
234 /* *INDENT-ON* */
235
236
237 /*
238  * fd.io coding-style-patch-verification: ON
239  *
240  * Local Variables:
241  * eval: (c-set-style "gnu")
242  * End:
243  */