blob: d3f573d4c6300088ba3de487e26d384c9486a870 [file] [log] [blame]
Dave Barach65457162017-10-10 17:53:14 -04001/*
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
18#include <vnet/vnet.h>
19#include <vnet/fib/fib.h>
20#include <vlibmemory/api.h>
21
22#include <vnet/udp/udp.h>
23
24#include <vnet/vnet_msg_enum.h>
25
26#define vl_typedefs /* define message structures */
27#include <vnet/vnet_all_api_h.h>
28#undef vl_typedefs
29
30#define vl_endianfun /* define message structures */
31#include <vnet/vnet_all_api_h.h>
32#undef vl_endianfun
33
34/* instantiate all the print functions we know about */
35#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
36#define vl_printfun
37#include <vnet/vnet_all_api_h.h>
38#undef vl_printfun
39
40#include <vlibapi/api_helper_macros.h>
41
42dns_main_t dns_main;
43
44static int
45dns_cache_clear (dns_main_t * dm)
46{
47 dns_cache_entry_t *ep;
48
49 if (dm->is_enabled == 0)
50 return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
51
52 dns_cache_lock (dm);
53
54 /* *INDENT-OFF* */
55 pool_foreach (ep, dm->entries,
56 ({
57 vec_free (ep->name);
Dave Barachd2080152017-10-20 09:21:35 -040058 vec_free (ep->pending_api_requests);
Dave Barach65457162017-10-10 17:53:14 -040059 vec_free (ep->ip4_peers_to_notify);
60 vec_free (ep->ip6_peers_to_notify);
61 }));
62 /* *INDENT-ON* */
63
64 pool_free (dm->entries);
65 hash_free (dm->cache_entry_by_name);
66 dm->cache_entry_by_name = hash_create_string (0, sizeof (uword));
67 vec_free (dm->unresolved_entries);
68 dns_cache_unlock (dm);
69 return 0;
70}
71
72static int
73dns_enable_disable (dns_main_t * dm, int is_enable)
74{
75 vlib_thread_main_t *tm = &vlib_thread_main;
76 u32 n_vlib_mains = tm->n_vlib_mains;
77
78 if (is_enable)
79 {
80 if (vec_len (dm->ip4_name_servers) == 0
81 && (vec_len (dm->ip6_name_servers) == 0))
82 return VNET_API_ERROR_NO_NAME_SERVERS;
83
84 if (dm->cache_entry_by_name == 0)
85 {
86 if (n_vlib_mains > 1)
87 dm->cache_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
88 CLIB_CACHE_LINE_BYTES);
89
90 dm->cache_entry_by_name = hash_create_string (0, sizeof (uword));
91 }
92
93 dm->is_enabled = 1;
94 }
95 else
96 {
97 dns_cache_clear (dm);
98 dm->is_enabled = 0;
99 }
100 return 0;
101}
102
103static void vl_api_dns_enable_disable_t_handler
104 (vl_api_dns_enable_disable_t * mp)
105{
106 vl_api_dns_enable_disable_reply_t *rmp;
107 dns_main_t *dm = &dns_main;
108 int rv;
109
110 rv = dns_enable_disable (dm, mp->enable);
111
112 REPLY_MACRO (VL_API_DNS_ENABLE_DISABLE_REPLY);
113}
114
115static int
116dns6_name_server_add_del (dns_main_t * dm,
117 u8 * server_address_as_u8, int is_add)
118{
119 int i;
120 ip6_address_t *ap;
121
122 if (is_add)
123 {
124 /* Already there? done... */
125 for (i = 0; i < vec_len (dm->ip6_name_servers); i++)
126 {
127 if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8,
128 sizeof (ip6_address_t)))
129 return 0;
130 }
131
132 vec_add2 (dm->ip6_name_servers, ap, 1);
133 clib_memcpy (ap, server_address_as_u8, sizeof (*ap));
134 }
135 else
136 {
137 for (i = 0; i < vec_len (dm->ip6_name_servers); i++)
138 {
139 if (!memcmp (dm->ip6_name_servers + i, server_address_as_u8,
140 sizeof (ip6_address_t)))
141 {
142 vec_delete (dm->ip6_name_servers, 1, i);
143 return 0;
144 }
145 }
146 return VNET_API_ERROR_NAME_SERVER_NOT_FOUND;
147 }
148 return 0;
149}
150
151static int
152dns4_name_server_add_del (dns_main_t * dm,
153 u8 * server_address_as_u8, int is_add)
154{
155 int i;
156 ip4_address_t *ap;
157
158 if (is_add)
159 {
160 /* Already there? done... */
161 for (i = 0; i < vec_len (dm->ip4_name_servers); i++)
162 {
163 if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8,
164 sizeof (ip4_address_t)))
165 return 0;
166 }
167
168 vec_add2 (dm->ip4_name_servers, ap, 1);
169 clib_memcpy (ap, server_address_as_u8, sizeof (*ap));
170 }
171 else
172 {
173 for (i = 0; i < vec_len (dm->ip4_name_servers); i++)
174 {
175 if (!memcmp (dm->ip4_name_servers + i, server_address_as_u8,
176 sizeof (ip4_address_t)))
177 {
178 vec_delete (dm->ip4_name_servers, 1, i);
179 return 0;
180 }
181 }
182 return VNET_API_ERROR_NAME_SERVER_NOT_FOUND;
183 }
184 return 0;
185}
186
187static void vl_api_dns_name_server_add_del_t_handler
188 (vl_api_dns_name_server_add_del_t * mp)
189{
190 dns_main_t *dm = &dns_main;
191 vl_api_dns_name_server_add_del_reply_t *rmp;
192 int rv;
193
194 if (mp->is_ip6)
195 rv = dns6_name_server_add_del (dm, mp->server_address, mp->is_add);
196 else
197 rv = dns4_name_server_add_del (dm, mp->server_address, mp->is_add);
198
199 REPLY_MACRO (VL_API_DNS_NAME_SERVER_ADD_DEL_REPLY);
200}
201
202static void
203send_dns4_request (dns_main_t * dm,
204 dns_cache_entry_t * ep, ip4_address_t * server)
205{
206 vlib_main_t *vm = dm->vlib_main;
207 f64 now = vlib_time_now (vm);
208 u32 bi;
209 vlib_buffer_t *b;
210 ip4_header_t *ip;
211 fib_prefix_t prefix;
212 fib_node_index_t fei;
213 u32 sw_if_index, fib_index;
214 udp_header_t *udp;
215 ip4_main_t *im4 = &ip4_main;
216 ip_lookup_main_t *lm4 = &im4->lookup_main;
217 ip_interface_address_t *ia = 0;
218 ip4_address_t *src_address;
219 u8 *dns_request;
220 vlib_frame_t *f;
221 u32 *to_next;
222
223 ASSERT (ep->dns_request);
224
225 /* Find a FIB path to the server */
226 clib_memcpy (&prefix.fp_addr.ip4, server, sizeof (*server));
227 prefix.fp_proto = FIB_PROTOCOL_IP4;
228 prefix.fp_len = 32;
229
230 fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
231 if (fib_index == (u32) ~ 0)
232 {
233 clib_warning ("no fib table");
234 return;
235 }
236
237 fei = fib_table_lookup (fib_index, &prefix);
238
239 /* Couldn't find route to destination. Bail out. */
240 if (fei == FIB_NODE_INDEX_INVALID)
241 {
242 clib_warning ("no route to DNS server");
243 return;
244 }
245
246 sw_if_index = fib_entry_get_resolving_interface (fei);
247
248 if (sw_if_index == ~0)
249 {
250 clib_warning
251 ("route to %U exists, fei %d, get_resolving_interface returned"
252 " ~0", fei, format_ip4_address, &prefix.fp_addr);
253 return;
254 }
255
256 /* *INDENT-OFF* */
257 foreach_ip_interface_address(lm4, ia, sw_if_index, 1 /* honor unnummbered */,
258 ({
259 src_address = ip_interface_address_get_address (lm4, ia);
260 goto found_src_address;
261 }));
262 /* *INDENT-ON* */
263
264 clib_warning ("FIB BUG");
265 return;
266
267found_src_address:
268
269 /* Go get a buffer */
270 if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1)
271 return;
272
273 b = vlib_get_buffer (vm, bi);
274 b->current_length = sizeof (ip4_header_t) + sizeof (udp_header_t) +
275 vec_len (ep->dns_request);
276 b->total_length_not_including_first_buffer = 0;
277 b->flags =
278 VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
279 vnet_buffer (b)->sw_if_index[VLIB_RX] = 0; /* "local0" */
280 vnet_buffer (b)->sw_if_index[VLIB_TX] = 0; /* default VRF for now */
281
282 ip = vlib_buffer_get_current (b);
283 memset (ip, 0, sizeof (*ip));
284 udp = (udp_header_t *) (ip + 1);
285 memset (udp, 0, sizeof (*udp));
286
287 dns_request = (u8 *) (udp + 1);
288
289 /* IP header */
290 ip->ip_version_and_header_length = 0x45;
291 ip->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b));
292 ip->ttl = 255;
293 ip->protocol = IP_PROTOCOL_UDP;
294 ip->src_address.as_u32 = src_address->as_u32;
295 ip->dst_address.as_u32 = server->as_u32;
296 ip->checksum = ip4_header_checksum (ip);
297
298 /* UDP header */
299 udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply);
300 udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
301 udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
302 vec_len (ep->dns_request));
303 udp->checksum = 0;
304
305 /* The actual DNS request */
306 clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request));
307
308 /* Ship it to ip4_lookup */
309 f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
310 to_next = vlib_frame_vector_args (f);
311 to_next[0] = bi;
312 f->n_vectors = 1;
313 vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
314
315 ep->retry_timer = now + 2.0;
316}
317
318static void
319send_dns6_request (dns_main_t * dm,
320 dns_cache_entry_t * ep, ip6_address_t * server)
321{
322 vlib_main_t *vm = dm->vlib_main;
323 f64 now = vlib_time_now (vm);
324 u32 bi;
325 vlib_buffer_t *b;
326 ip6_header_t *ip;
327 fib_prefix_t prefix;
328 fib_node_index_t fei;
329 u32 sw_if_index, fib_index;
330 udp_header_t *udp;
331 ip6_main_t *im6 = &ip6_main;
332 ip_lookup_main_t *lm6 = &im6->lookup_main;
333 ip_interface_address_t *ia = 0;
334 ip6_address_t *src_address;
335 u8 *dns_request;
336 vlib_frame_t *f;
337 u32 *to_next;
338 int junk __attribute__ ((unused));
339
340 ASSERT (ep->dns_request);
341
342 /* Find a FIB path to the server */
343 clib_memcpy (&prefix.fp_addr, server, sizeof (*server));
344 prefix.fp_proto = FIB_PROTOCOL_IP6;
345 prefix.fp_len = 32;
346
347 fib_index = fib_table_find (prefix.fp_proto, 0 /* default VRF for now */ );
348 if (fib_index == (u32) ~ 0)
349 {
350 clib_warning ("no fib table");
351 return;
352 }
353
354 fei = fib_table_lookup (fib_index, &prefix);
355
356 /* Couldn't find route to destination. Bail out. */
357 if (fei == FIB_NODE_INDEX_INVALID)
358 {
359 clib_warning ("no route to DNS server");
360 }
361
362 sw_if_index = fib_entry_get_resolving_interface (fei);
363
364 /* *INDENT-OFF* */
365 foreach_ip_interface_address(lm6, ia, sw_if_index, 1 /* honor unnummbered */,
366 ({
367 src_address = ip_interface_address_get_address (lm6, ia);
368 goto found_src_address;
369 }));
370 /* *INDENT-ON* */
371
372 clib_warning ("FIB BUG");
373 return;
374
375found_src_address:
376
377 /* Go get a buffer */
378 if (vlib_buffer_alloc (dm->vlib_main, &bi, 1) != 1)
379 return;
380
381 b = vlib_get_buffer (vm, bi);
382 b->current_length = sizeof (ip6_header_t) + sizeof (udp_header_t) +
383 vec_len (ep->dns_request);
384 b->total_length_not_including_first_buffer = 0;
385 b->flags =
386 VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_LOCALLY_ORIGINATED;
387
388 ip = vlib_buffer_get_current (b);
389 memset (ip, 0, sizeof (*ip));
390 udp = (udp_header_t *) (ip + 1);
391 memset (udp, 0, sizeof (*udp));
392
393 dns_request = (u8 *) (udp + 1);
394
395 /* IP header */
396 ip->ip_version_traffic_class_and_flow_label =
397 clib_host_to_net_u32 (0x6 << 28);
398
399 ip->payload_length =
400 clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b)
401 - sizeof (ip6_header_t));
402 ip->hop_limit = 255;
403 ip->protocol = IP_PROTOCOL_UDP;
404 clib_memcpy (&ip->src_address, src_address, sizeof (ip6_address_t));
405 clib_memcpy (&ip->dst_address, server, sizeof (ip6_address_t));
406
407 /* UDP header */
408 udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_dns_reply);
409 udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_dns);
410 udp->length = clib_host_to_net_u16 (sizeof (udp_header_t) +
411 vec_len (ep->dns_request));
412 udp->checksum = 0;
413 udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip, &junk);
414
415 /* The actual DNS request */
416 clib_memcpy (dns_request, ep->dns_request, vec_len (ep->dns_request));
417
418 /* Ship it to ip6_lookup */
419 f = vlib_get_frame_to_node (vm, ip6_lookup_node.index);
420 to_next = vlib_frame_vector_args (f);
421 to_next[0] = bi;
422 f->n_vectors = 1;
423
424 ep->retry_timer = now + 2.0;
425}
426
427/**
428 * Translate "foo.com" into "0x3 f o o 0x3 c o m 0x0"
429 * A historical / hysterical micro-TLV scheme. DGMS.
430 */
431u8 *
432name_to_labels (u8 * name)
433{
434 int i;
435 int last_label_index;
436 u8 *rv;
437
438 rv = vec_dup (name);
439
440 /* punch in space for the first length */
441 vec_insert (rv, 1, 0);
442 last_label_index = 0;
443 i = 1;
444
445 while (i < vec_len (rv))
446 {
447 if (rv[i] == '.')
448 {
449 rv[last_label_index] = (i - last_label_index) - 1;
450 if ((i - last_label_index) > 63)
451 clib_warning ("stupid name, label length %d",
452 i - last_label_index);
453 last_label_index = i;
454 rv[i] = 0;
455 }
456 i++;
457 }
458 /* Set the last real label length */
459 rv[last_label_index] = (i - last_label_index) - 1;
460
461 /*
462 * Add a [sic] NULL root label. Otherwise, the name parser can't figure out
463 * where to stop.
464 */
465 vec_add1 (rv, 0);
466 return rv;
467}
468
469/**
470 * arc-function for the above.
471 * Translate "0x3 f o o 0x3 c o m 0x0" into "foo.com"
472 * Produces a non-NULL-terminated u8 *vector. %v format is your friend.
473 */
474u8 *
475labels_to_name (u8 * label, u8 * full_text, u8 ** parse_from_here)
476{
477 u8 *reply = 0;
478 u16 offset;
479 u8 len;
480 int i;
481
482 *parse_from_here = 0;
483
484 /* chase initial pointer? */
485 if ((label[0] & 0xC0) == 0xC0)
486 {
487 *parse_from_here = label + 2;
488 offset = ((label[0] & 0x3f) << 8) + label[1];
489 label = full_text + offset;
490 }
491
492 len = *label++;
493
494 while (len)
495 {
496 for (i = 0; i < len; i++)
497 vec_add1 (reply, *label++);
498
499 /* chase pointer? */
500 if ((label[0] & 0xC0) == 0xC0)
501 {
502 *parse_from_here = label + 2;
503 offset = ((label[0] & 0x3f) << 8) + label[1];
504 label = full_text + offset;
505 }
506
507 len = *label++;
508 if (len)
509 vec_add1 (reply, '.');
510 }
511 if (*parse_from_here == 0)
512 *parse_from_here = label;
513 return reply;
514}
515
516void
517vnet_send_dns_request (dns_main_t * dm, dns_cache_entry_t * ep)
518{
519 dns_header_t *h;
520 dns_query_t *qp;
521 u16 tmp;
522 u8 *request;
523 u32 qp_offset;
524
Dave Barach0cb01bd2017-10-16 14:39:52 -0400525 /* This can easily happen if sitting in GDB, etc. */
526 if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
527 return;
528
Dave Barach65457162017-10-10 17:53:14 -0400529 /* Construct the dns request, if we haven't been here already */
530 if (vec_len (ep->dns_request) == 0)
531 {
532 /*
533 * Start with the variadic portion of the exercise.
534 * Turn the name into a set of DNS "labels". Max length
535 * per label is 63, enforce that.
536 */
537 request = name_to_labels (ep->name);
538 qp_offset = vec_len (request);
539
540 /* Add space for the query header */
541 vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
542
543 qp = (dns_query_t *) (request + qp_offset);
544
545 qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
546 qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
547
548 /* Punch in space for the dns_header_t */
549 vec_insert (request, sizeof (dns_header_t), 0);
550
551 h = (dns_header_t *) request;
552
553 /* Transaction ID = pool index */
554 h->id = clib_host_to_net_u16 (ep - dm->entries);
555
556 /* Ask for a recursive lookup */
557 tmp = DNS_RD | DNS_OPCODE_QUERY;
558 h->flags = clib_host_to_net_u16 (tmp);
559 h->qdcount = clib_host_to_net_u16 (1);
560 h->nscount = 0;
561 h->arcount = 0;
562
563 ep->dns_request = request;
564 }
565
566 /* Work out which server / address family we're going to use */
567
568 /* Retry using current server */
569 if (ep->retry_count++ < DNS_RETRIES_PER_SERVER)
570 {
571 if (ep->server_af == 1 /* ip6 */ )
572 {
573 if (vec_len (dm->ip6_name_servers))
574 {
575 send_dns6_request (dm, ep,
576 dm->ip6_name_servers + ep->server_rotor);
577 goto out;
578 }
579 else
580 ep->server_af = 0;
581 }
582 if (vec_len (dm->ip4_name_servers))
583 {
584 send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
585 goto out;
586 }
587 }
588 else /* switch to a new server */
589 {
590 ep->retry_count = 1;
591 ep->server_rotor++;
592 if (ep->server_af == 1 /* ip6 */ )
593 {
594 if (ep->server_rotor >= vec_len (dm->ip6_name_servers))
595 {
596 ep->server_rotor = 0;
597 ep->server_af = vec_len (dm->ip4_name_servers) > 0 ? 0 : 1;
598 }
599 }
600 else
601 {
602 if (ep->server_rotor >= vec_len (dm->ip4_name_servers))
603 {
604 ep->server_rotor = 0;
605 ep->server_af = vec_len (dm->ip6_name_servers) > 0 ? 1 : 0;
606 }
607 }
608 }
609
610 if (ep->server_af == 1 /* ip6 */ )
611 send_dns6_request (dm, ep, dm->ip6_name_servers + ep->server_rotor);
612 else
613 send_dns4_request (dm, ep, dm->ip4_name_servers + ep->server_rotor);
614
615out:
616
617 vlib_process_signal_event_mt (dm->vlib_main, dns_resolver_node.index,
618 DNS_RESOLVER_EVENT_PENDING, 0);
619}
620
621int
622vnet_dns_delete_entry_by_index_nolock (dns_main_t * dm, u32 index)
623{
624 dns_cache_entry_t *ep;
625 int i;
626
627 if (dm->is_enabled == 0)
628 return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
629
630 if (pool_is_free_index (dm->entries, index))
631 return VNET_API_ERROR_NO_SUCH_ENTRY;
632
633 ep = pool_elt_at_index (dm->entries, index);
Dave Barach65457162017-10-10 17:53:14 -0400634 if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_VALID))
635 {
636 for (i = 0; i < vec_len (dm->unresolved_entries); i++)
637 if (index == dm->unresolved_entries[i])
638 {
639 vec_delete (dm->unresolved_entries, 1, i);
640 goto found;
641 }
642 clib_warning ("pool elt %d supposedly pending, but not found...",
643 index);
644 }
645
646found:
647 hash_unset_mem (dm->cache_entry_by_name, ep->name);
648 vec_free (ep->name);
Dave Barachd2080152017-10-20 09:21:35 -0400649 vec_free (ep->pending_api_requests);
Dave Barach65457162017-10-10 17:53:14 -0400650 vec_free (ep->ip4_peers_to_notify);
651 vec_free (ep->ip6_peers_to_notify);
652 pool_put (dm->entries, ep);
653
654 return 0;
655}
656
657static int
658dns_delete_by_name (dns_main_t * dm, u8 * name)
659{
660 int rv;
661 uword *p;
662
663 if (dm->is_enabled == 0)
664 return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
665
666 dns_cache_lock (dm);
667 p = hash_get_mem (dm->cache_entry_by_name, name);
668 if (!p)
669 {
670 dns_cache_unlock (dm);
671 return VNET_API_ERROR_NO_SUCH_ENTRY;
672 }
673 rv = vnet_dns_delete_entry_by_index_nolock (dm, p[0]);
674
675 dns_cache_unlock (dm);
676
677 return rv;
678}
679
680static int
681delete_random_entry (dns_main_t * dm)
682{
683 int rv;
684 u32 victim_index, start_index, i;
685 u32 limit;
686 dns_cache_entry_t *ep;
687
688 if (dm->is_enabled == 0)
689 return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
690
Dave Barachb9f2cf02017-10-17 13:13:42 -0400691 /*
692 * Silence spurious coverity warning. We know pool_elts >> 0, or
693 * we wouldn't be here...
694 */
695#ifdef __COVERITY__
696 if (pool_elts (dm->entries) == 0)
697 return VNET_API_ERROR_UNSPECIFIED;
698#endif
699
Dave Barach65457162017-10-10 17:53:14 -0400700 dns_cache_lock (dm);
701 limit = pool_elts (dm->entries);
702 start_index = random_u32 (&dm->random_seed) % limit;
703
704 for (i = 0; i < limit; i++)
705 {
706 victim_index = (start_index + i) % limit;
707
708 if (!pool_is_free_index (dm->entries, victim_index))
709 {
710 ep = pool_elt_at_index (dm->entries, victim_index);
711 /* Delete only valid, non-static entries */
712 if ((ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
713 && ((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0))
714 {
715 rv = vnet_dns_delete_entry_by_index_nolock (dm, victim_index);
716 dns_cache_unlock (dm);
717 return rv;
718 }
719 }
720 }
721 dns_cache_unlock (dm);
722
723 clib_warning ("Couldn't find an entry to delete?");
724 return VNET_API_ERROR_UNSPECIFIED;
725}
726
727static int
728dns_add_static_entry (dns_main_t * dm, u8 * name, u8 * dns_reply_data)
729{
730 dns_cache_entry_t *ep;
731 uword *p;
732 int rv;
733
734 if (dm->is_enabled == 0)
735 return VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED;
736
737 dns_cache_lock (dm);
738 p = hash_get_mem (dm->cache_entry_by_name, name);
739 if (p)
740 {
741 dns_cache_unlock (dm);
742 return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
743 }
744
745 if (pool_elts (dm->entries) == dm->name_cache_size)
746 {
747 /* Will only fail if the cache is totally filled w/ static entries... */
748 rv = delete_random_entry (dm);
749 if (rv)
750 {
751 dns_cache_unlock (dm);
752 return rv;
753 }
754 }
755
756 pool_get (dm->entries, ep);
757 memset (ep, 0, sizeof (*ep));
758
759 /* Note: consumes the name vector */
760 ep->name = name;
761 hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
762 ep->flags = DNS_CACHE_ENTRY_FLAG_VALID | DNS_CACHE_ENTRY_FLAG_STATIC;
763 ep->dns_response = dns_reply_data;
764
765 dns_cache_unlock (dm);
766 return 0;
767}
768
769static int
770dns_resolve_name (dns_main_t * dm,
771 u8 * name, u32 client_index, u32 client_context,
Dave Barachd2080152017-10-20 09:21:35 -0400772 u32 request_type, dns_cache_entry_t ** retp)
Dave Barach65457162017-10-10 17:53:14 -0400773{
774 dns_cache_entry_t *ep;
775 int rv;
776 f64 now;
777 uword *p;
Dave Barachd2080152017-10-20 09:21:35 -0400778 pending_api_request_t *pr;
Dave Barach65457162017-10-10 17:53:14 -0400779
780 now = vlib_time_now (dm->vlib_main);
781
782 /* In case we can't actually answer the question right now... */
783 *retp = 0;
784
785 dns_cache_lock (dm);
Dave Barach0cb01bd2017-10-16 14:39:52 -0400786search_again:
Dave Barach65457162017-10-10 17:53:14 -0400787 p = hash_get_mem (dm->cache_entry_by_name, name);
788 if (p)
789 {
790 ep = pool_elt_at_index (dm->entries, p[0]);
791 if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
792 {
793 /* Has the entry expired? */
794 if (((ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC) == 0)
795 && (now > ep->expiration_time))
796 {
Dave Barach0cb01bd2017-10-16 14:39:52 -0400797 int i;
798 u32 *indices_to_delete = 0;
799
800 /*
801 * Take out the rest of the resolution chain
802 * This isn't optimal, but it won't happen very often.
803 */
804 while (ep)
805 {
806 if ((ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME))
807 {
808 vec_add1 (indices_to_delete, ep - dm->entries);
809
810 p = hash_get_mem (dm->cache_entry_by_name, ep->cname);
811 if (!p)
812 break;
813 ep = pool_elt_at_index (dm->entries, p[0]);
814 }
815 else
816 {
817 vec_add1 (indices_to_delete, ep - dm->entries);
818 break;
819 }
820 }
821 for (i = 0; i < vec_len (indices_to_delete); i++)
822 {
823 /* Reenable to watch re-resolutions */
824 if (0)
825 {
826 ep = pool_elt_at_index (dm->entries,
827 indices_to_delete[i]);
828 clib_warning ("Re-resolve %s", ep->name);
829 }
830
831 vnet_dns_delete_entry_by_index_nolock
832 (dm, indices_to_delete[i]);
833 }
834 vec_free (indices_to_delete);
Dave Barach65457162017-10-10 17:53:14 -0400835 /* Yes, kill it... */
Dave Barach65457162017-10-10 17:53:14 -0400836 goto re_resolve;
837 }
838
Dave Barach0cb01bd2017-10-16 14:39:52 -0400839 if (ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
840 {
841 name = ep->cname;
842 goto search_again;
843 }
844
Dave Barach65457162017-10-10 17:53:14 -0400845 /* Note: caller must drop the lock! */
846 *retp = ep;
847 return (0);
848 }
Dave Barach0cb01bd2017-10-16 14:39:52 -0400849 else
850 {
851 /*
Dave Barachd2080152017-10-20 09:21:35 -0400852 * Resolution pending. Add request to the pending vector
853 */
854 vec_add2 (ep->pending_api_requests, pr, 1);
855 pr->request_type = request_type;
856 pr->client_index = client_index;
857 pr->client_context = client_context;
Dave Barach0cb01bd2017-10-16 14:39:52 -0400858 dns_cache_unlock (dm);
859 return (0);
860 }
Dave Barach65457162017-10-10 17:53:14 -0400861 }
862
Dave Barach0cb01bd2017-10-16 14:39:52 -0400863re_resolve:
Dave Barach65457162017-10-10 17:53:14 -0400864 if (pool_elts (dm->entries) == dm->name_cache_size)
865 {
866 /* Will only fail if the cache is totally filled w/ static entries... */
867 rv = delete_random_entry (dm);
868 if (rv)
869 {
870 dns_cache_unlock (dm);
871 return rv;
872 }
873 }
874
Dave Barach65457162017-10-10 17:53:14 -0400875 /* add new hash table entry */
876 pool_get (dm->entries, ep);
877 memset (ep, 0, sizeof (*ep));
878
879 ep->name = format (0, "%s%c", name, 0);
880 _vec_len (ep->name) = vec_len (ep->name) - 1;
881
882 hash_set_mem (dm->cache_entry_by_name, ep->name, ep - dm->entries);
883
884 vec_add1 (dm->unresolved_entries, ep - dm->entries);
Dave Barachd2080152017-10-20 09:21:35 -0400885 vec_add2 (ep->pending_api_requests, pr, 1);
886 pr->request_type = request_type;
887 pr->client_index = client_index;
888 pr->client_context = client_context;
Dave Barach65457162017-10-10 17:53:14 -0400889 vnet_send_dns_request (dm, ep);
890 dns_cache_unlock (dm);
Dave Barach65457162017-10-10 17:53:14 -0400891 return 0;
892}
893
Dave Barach0cb01bd2017-10-16 14:39:52 -0400894#define foreach_notification_to_move \
Dave Barachd2080152017-10-20 09:21:35 -0400895_(pending_api_requests) \
Dave Barach0cb01bd2017-10-16 14:39:52 -0400896_(ip4_peers_to_notify) \
897_(ip6_peers_to_notify)
898
Dave Barach65457162017-10-10 17:53:14 -0400899/**
900 * Handle cname indirection. JFC. Called with the cache locked.
901 * returns 0 if the reply is not a CNAME.
902 */
903
904int
Dave Barach0cb01bd2017-10-16 14:39:52 -0400905vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply)
Dave Barach65457162017-10-10 17:53:14 -0400906{
907 dns_header_t *h;
908 dns_query_t *qp;
909 dns_rr_t *rr;
910 u8 *curpos;
911 u8 *pos, *pos2;
912 int len, i;
913 u8 *cname = 0;
914 u8 *request = 0;
915 u32 qp_offset;
916 u16 flags;
917 u16 rcode;
Dave Barach0cb01bd2017-10-16 14:39:52 -0400918 dns_cache_entry_t *ep, *next_ep;
919 f64 now;
Dave Barach65457162017-10-10 17:53:14 -0400920
921 h = (dns_header_t *) reply;
922 flags = clib_net_to_host_u16 (h->flags);
923 rcode = flags & DNS_RCODE_MASK;
924
925 /* See if the response is OK */
926 switch (rcode)
927 {
928 case DNS_RCODE_NO_ERROR:
929 break;
930
931 case DNS_RCODE_NAME_ERROR:
932 case DNS_RCODE_FORMAT_ERROR:
933 case DNS_RCODE_SERVER_FAILURE:
934 case DNS_RCODE_NOT_IMPLEMENTED:
935 case DNS_RCODE_REFUSED:
936 return 0;
937 }
938
939 curpos = (u8 *) (h + 1);
940 pos = curpos;
941 len = *pos++;
942
943 /* Skip the questions */
944 for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
945 {
946 while (len)
947 {
948 pos += len;
949 len = *pos++;
950 }
Dave Barachb9f2cf02017-10-17 13:13:42 -0400951 pos += sizeof (dns_query_t);
Dave Barach65457162017-10-10 17:53:14 -0400952 }
953 pos2 = pos;
954 /* expect a pointer chase here for a CNAME record */
955 if ((pos2[0] & 0xC0) == 0xC0)
956 pos += 2;
957 else
958 return 0;
959
960 rr = (dns_rr_t *) pos;
961
962 /* This is a real record, not a CNAME record */
963 if (clib_net_to_host_u16 (rr->type) != DNS_TYPE_CNAME)
964 return 0;
965
Dave Barach0cb01bd2017-10-16 14:39:52 -0400966 /* This is a CNAME record, chase the name chain. */
Dave Barach65457162017-10-10 17:53:14 -0400967
Dave Barach0cb01bd2017-10-16 14:39:52 -0400968 /* The last request is no longer pending.. */
969 for (i = 0; i < vec_len (dm->unresolved_entries); i++)
970 if (ep_index == dm->unresolved_entries[i])
971 {
972 vec_delete (dm->unresolved_entries, 1, i);
973 goto found_last_request;
974 }
975 clib_warning ("pool elt %d supposedly pending, but not found...", ep_index);
976
977found_last_request:
978
979 now = vlib_time_now (dm->vlib_main);
Dave Barach65457162017-10-10 17:53:14 -0400980 cname = labels_to_name (rr->rdata, reply, &pos2);
Dave Barach0cb01bd2017-10-16 14:39:52 -0400981 /* Save the cname */
982 vec_add1 (cname, 0);
983 _vec_len (cname) -= 1;
984 ep = pool_elt_at_index (dm->entries, ep_index);
985 ep->cname = cname;
986 ep->flags |= (DNS_CACHE_ENTRY_FLAG_CNAME | DNS_CACHE_ENTRY_FLAG_VALID);
987 /* Save the response */
988 ep->dns_response = reply;
989 /* Set up expiration time */
990 ep->expiration_time = now + clib_net_to_host_u32 (rr->ttl);
991
992 pool_get (dm->entries, next_ep);
993
994 /* Need to recompute ep post pool-get */
995 ep = pool_elt_at_index (dm->entries, ep_index);
996
997 memset (next_ep, 0, sizeof (*next_ep));
998 next_ep->name = vec_dup (cname);
999 vec_add1 (next_ep->name, 0);
1000 _vec_len (next_ep->name) -= 1;
1001
1002 hash_set_mem (dm->cache_entry_by_name, next_ep->name,
1003 next_ep - dm->entries);
1004
1005 /* Use the same server */
1006 next_ep->server_rotor = ep->server_rotor;
1007 next_ep->server_af = ep->server_af;
1008
1009 /* Move notification data to the next name in the chain */
1010#define _(a) next_ep->a = ep->a; ep->a = 0;
1011 foreach_notification_to_move;
1012#undef _
1013
Dave Barach65457162017-10-10 17:53:14 -04001014 request = name_to_labels (cname);
Dave Barach65457162017-10-10 17:53:14 -04001015
1016 qp_offset = vec_len (request);
1017
1018 /* Add space for the query header */
1019 vec_validate (request, qp_offset + sizeof (dns_query_t) - 1);
1020
1021 qp = (dns_query_t *) (request + qp_offset);
1022
1023 qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
1024 qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1025
1026 /* Punch in space for the dns_header_t */
1027 vec_insert (request, sizeof (dns_header_t), 0);
1028
1029 h = (dns_header_t *) request;
1030
1031 /* Transaction ID = pool index */
Dave Barach0cb01bd2017-10-16 14:39:52 -04001032 h->id = clib_host_to_net_u16 (next_ep - dm->entries);
Dave Barach65457162017-10-10 17:53:14 -04001033
1034 /* Ask for a recursive lookup */
1035 h->flags = clib_host_to_net_u16 (DNS_RD | DNS_OPCODE_QUERY);
1036 h->qdcount = clib_host_to_net_u16 (1);
1037 h->nscount = 0;
1038 h->arcount = 0;
1039
Dave Barach0cb01bd2017-10-16 14:39:52 -04001040 next_ep->dns_request = request;
1041 next_ep->retry_timer = now + 2.0;
1042 next_ep->retry_count = 0;
Dave Barach65457162017-10-10 17:53:14 -04001043
1044 /*
1045 * Enable this to watch recursive resolution happen...
1046 * fformat (stdout, "%U", format_dns_reply, request, 2);
1047 */
1048
Dave Barach0cb01bd2017-10-16 14:39:52 -04001049 vec_add1 (dm->unresolved_entries, next_ep - dm->entries);
1050 vnet_send_dns_request (dm, next_ep);
Dave Barach65457162017-10-10 17:53:14 -04001051 return (1);
1052}
1053
1054int
1055vnet_dns_response_to_reply (u8 * response,
1056 vl_api_dns_resolve_name_reply_t * rmp,
1057 u32 * min_ttlp)
1058{
1059 dns_header_t *h;
1060 dns_query_t *qp;
1061 dns_rr_t *rr;
1062 int i, limit;
1063 u8 len;
1064 u8 *curpos, *pos;
1065 u16 flags;
1066 u16 rcode;
1067 u32 ttl;
1068
1069 h = (dns_header_t *) response;
1070 flags = clib_net_to_host_u16 (h->flags);
1071 rcode = flags & DNS_RCODE_MASK;
1072
1073 /* See if the response is OK, etc. */
1074 switch (rcode)
1075 {
1076 default:
1077 case DNS_RCODE_NO_ERROR:
1078 break;
1079
1080 case DNS_RCODE_NAME_ERROR:
1081 case DNS_RCODE_FORMAT_ERROR:
1082 return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
1083
1084 case DNS_RCODE_SERVER_FAILURE:
1085 case DNS_RCODE_NOT_IMPLEMENTED:
1086 case DNS_RCODE_REFUSED:
1087 return VNET_API_ERROR_NAME_SERVER_NEXT_SERVER;
1088 }
1089
1090 /* No answers? Loser... */
1091 if (clib_net_to_host_u16 (h->anscount) < 1)
1092 return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
1093
1094 curpos = (u8 *) (h + 1);
1095
1096 /* Skip the name we asked about */
1097 pos = curpos;
1098 len = *pos++;
1099 /* Should never happen, but stil... */
1100 if ((len & 0xC0) == 0xC0)
1101 curpos += 2;
1102 else
1103 {
1104 /* skip the name / label-set */
1105 while (len)
1106 {
1107 pos += len;
1108 len = *pos++;
1109 }
1110 curpos = pos;
1111 }
1112 /* Skip queries */
1113 limit = clib_net_to_host_u16 (h->qdcount);
1114 qp = (dns_query_t *) curpos;
1115 qp += limit;
1116 curpos = (u8 *) qp;
1117
1118 /* Parse answers */
1119 limit = clib_net_to_host_u16 (h->anscount);
1120
1121 for (i = 0; i < limit; i++)
1122 {
1123 pos = curpos;
1124
1125 /* Expect pointer chases in the answer section... */
1126 if ((pos[0] & 0xC0) == 0xC0)
1127 curpos += 2;
1128 else
1129 {
1130 len = *pos++;
1131 while (len)
1132 {
1133 if ((pos[0] & 0xC0) == 0xC0)
1134 {
1135 curpos = pos + 2;
Dave Barachb9f2cf02017-10-17 13:13:42 -04001136 goto curpos_set;
Dave Barach65457162017-10-10 17:53:14 -04001137 }
1138 pos += len;
1139 len = *pos++;
1140 }
1141 curpos = pos;
1142 }
1143
Dave Barachb9f2cf02017-10-17 13:13:42 -04001144 curpos_set:
Dave Barach65457162017-10-10 17:53:14 -04001145 rr = (dns_rr_t *) curpos;
1146
1147 switch (clib_net_to_host_u16 (rr->type))
1148 {
1149 case DNS_TYPE_A:
1150 /* Collect an ip4 address. Do not pass go. Do not collect $200 */
1151 memcpy (rmp->ip4_address, rr->rdata, sizeof (ip4_address_t));
1152 rmp->ip4_set = 1;
1153 ttl = clib_net_to_host_u32 (rr->ttl);
1154 if (min_ttlp && *min_ttlp > ttl)
1155 *min_ttlp = ttl;
1156 break;
1157 case DNS_TYPE_AAAA:
1158 /* Collect an ip6 address. Do not pass go. Do not collect $200 */
1159 memcpy (rmp->ip6_address, rr->rdata, sizeof (ip6_address_t));
1160 ttl = clib_net_to_host_u32 (rr->ttl);
1161 if (min_ttlp && *min_ttlp > ttl)
1162 *min_ttlp = ttl;
1163 rmp->ip6_set = 1;
1164 break;
1165 default:
1166 break;
1167 }
1168 /* Might as well stop ASAP */
1169 if (rmp->ip4_set && rmp->ip6_set)
1170 break;
1171 curpos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1172 }
1173
1174 if ((rmp->ip4_set + rmp->ip6_set) == 0)
1175 return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
1176 return 0;
1177}
1178
Dave Barachd2080152017-10-20 09:21:35 -04001179int
1180vnet_dns_response_to_name (u8 * response,
1181 vl_api_dns_resolve_ip_reply_t * rmp,
1182 u32 * min_ttlp)
1183{
1184 dns_header_t *h;
1185 dns_query_t *qp;
1186 dns_rr_t *rr;
1187 int i, limit;
1188 u8 len;
1189 u8 *curpos, *pos;
1190 u16 flags;
1191 u16 rcode;
1192 u8 *name;
1193 u32 ttl;
1194 u8 *junk __attribute__ ((unused));
1195 int name_set = 0;
1196
1197 h = (dns_header_t *) response;
1198 flags = clib_net_to_host_u16 (h->flags);
1199 rcode = flags & DNS_RCODE_MASK;
1200
1201 /* See if the response is OK, etc. */
1202 switch (rcode)
1203 {
1204 default:
1205 case DNS_RCODE_NO_ERROR:
1206 break;
1207
1208 case DNS_RCODE_NAME_ERROR:
1209 case DNS_RCODE_FORMAT_ERROR:
1210 return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
1211
1212 case DNS_RCODE_SERVER_FAILURE:
1213 case DNS_RCODE_NOT_IMPLEMENTED:
1214 case DNS_RCODE_REFUSED:
1215 return VNET_API_ERROR_NAME_SERVER_NEXT_SERVER;
1216 }
1217
1218 /* No answers? Loser... */
1219 if (clib_net_to_host_u16 (h->anscount) < 1)
1220 return VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES;
1221
1222 curpos = (u8 *) (h + 1);
1223
1224 /* Skip the name we asked about */
1225 pos = curpos;
1226 len = *pos++;
1227 /* Should never happen, but stil... */
1228 if ((len & 0xC0) == 0xC0)
1229 curpos += 2;
1230 else
1231 {
1232 /* skip the name / label-set */
1233 while (len)
1234 {
1235 pos += len;
1236 len = *pos++;
1237 }
1238 curpos = pos;
1239 }
1240 /* Skip queries */
1241 limit = clib_net_to_host_u16 (h->qdcount);
1242 qp = (dns_query_t *) curpos;
1243 qp += limit;
1244 curpos = (u8 *) qp;
1245
1246 /* Parse answers */
1247 limit = clib_net_to_host_u16 (h->anscount);
1248
1249 for (i = 0; i < limit; i++)
1250 {
1251 pos = curpos;
1252
1253 /* Expect pointer chases in the answer section... */
1254 if ((pos[0] & 0xC0) == 0xC0)
1255 curpos += 2;
1256 else
1257 {
1258 len = *pos++;
1259 while (len)
1260 {
1261 if ((pos[0] & 0xC0) == 0xC0)
1262 {
1263 curpos = pos + 2;
1264 goto curpos_set;
1265 }
1266 pos += len;
1267 len = *pos++;
1268 }
1269 curpos = pos;
1270 }
1271
1272 curpos_set:
1273 rr = (dns_rr_t *) curpos;
1274
1275 switch (clib_net_to_host_u16 (rr->type))
1276 {
1277 case DNS_TYPE_PTR:
1278 name = labels_to_name (rr->rdata, response, &junk);
1279 memcpy (rmp->name, name, vec_len (name));
1280 ttl = clib_net_to_host_u32 (rr->ttl);
1281 if (*min_ttlp)
1282 *min_ttlp = ttl;
1283 rmp->name[vec_len (name)] = 0;
1284 name_set = 1;
1285 break;
1286 default:
1287 break;
1288 }
1289 /* Might as well stop ASAP */
1290 if (name_set == 1)
1291 break;
1292 curpos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1293 }
1294
1295 if (name_set == 0)
1296 return VNET_API_ERROR_NAME_SERVER_NO_SUCH_NAME;
1297 return 0;
1298}
1299
Dave Barach65457162017-10-10 17:53:14 -04001300static void
1301vl_api_dns_resolve_name_t_handler (vl_api_dns_resolve_name_t * mp)
1302{
1303 dns_main_t *dm = &dns_main;
1304 vl_api_dns_resolve_name_reply_t *rmp;
1305 dns_cache_entry_t *ep;
1306 int rv;
1307
1308 /* Sanitize the name slightly */
1309 mp->name[ARRAY_LEN (mp->name) - 1] = 0;
1310
Dave Barachd2080152017-10-20 09:21:35 -04001311 rv = dns_resolve_name (dm, mp->name, mp->client_index, mp->context,
1312 DNS_API_PENDING_NAME_TO_IP, &ep);
Dave Barach65457162017-10-10 17:53:14 -04001313
1314 /* Error, e.g. not enabled? Tell the user */
1315 if (rv < 0)
1316 {
1317 REPLY_MACRO (VL_API_DNS_RESOLVE_NAME_REPLY);
1318 return;
1319 }
1320
1321 /* Resolution pending? Don't reply... */
1322 if (ep == 0)
1323 return;
1324
1325 /* *INDENT-OFF* */
1326 REPLY_MACRO2(VL_API_DNS_RESOLVE_NAME_REPLY,
1327 ({
1328 rv = vnet_dns_response_to_reply (ep->dns_response, rmp, 0 /* ttl-ptr */);
1329 rmp->retval = clib_host_to_net_u32 (rv);
1330 }));
1331 /* *INDENT-ON* */
1332
1333 /*
1334 * dns_resolve_name leaves the cache locked when it returns
1335 * a cached result, so unlock it here.
1336 */
1337 dns_cache_unlock (dm);
1338}
1339
Dave Barachd2080152017-10-20 09:21:35 -04001340static void
1341vl_api_dns_resolve_ip_t_handler (vl_api_dns_resolve_ip_t * mp)
1342{
1343 dns_main_t *dm = &dns_main;
1344 vl_api_dns_resolve_ip_reply_t *rmp;
1345 dns_cache_entry_t *ep;
1346 int rv;
1347 int i, len;
1348 u8 *lookup_name = 0;
1349 u8 digit, nybble;
1350
1351 if (mp->is_ip6)
1352 {
1353 for (i = 15; i >= 0; i--)
1354 {
1355 digit = mp->address[i];
1356 nybble = (digit & 0x0F);
1357 if (nybble > 9)
1358 vec_add1 (lookup_name, (nybble - 10) + 'a');
1359 else
1360 vec_add1 (lookup_name, nybble + '0');
1361 vec_add1 (lookup_name, '.');
1362 nybble = (digit & 0xF0) >> 4;
1363 if (nybble > 9)
1364 vec_add1 (lookup_name, (nybble - 10) + 'a');
1365 else
1366 vec_add1 (lookup_name, nybble + '0');
1367 vec_add1 (lookup_name, '.');
1368 }
1369 len = vec_len (lookup_name);
1370 vec_validate (lookup_name, len + 8);
1371 memcpy (lookup_name + len, "ip6.arpa", 8);
1372 }
1373 else
1374 {
1375 for (i = 3; i >= 0; i--)
1376 {
1377 digit = mp->address[i];
1378 lookup_name = format (lookup_name, "%d.", digit);
1379 }
1380 lookup_name = format (lookup_name, "in-addr.arpa");
1381 }
1382
1383 vec_add1 (lookup_name, 0);
1384
1385 rv = dns_resolve_name (dm, lookup_name, mp->client_index, mp->context,
1386 DNS_API_PENDING_IP_TO_NAME, &ep);
1387
1388 vec_free (lookup_name);
1389
1390 /* Error, e.g. not enabled? Tell the user */
1391 if (rv < 0)
1392 {
1393 REPLY_MACRO (VL_API_DNS_RESOLVE_IP_REPLY);
1394 return;
1395 }
1396
1397 /* Resolution pending? Don't reply... */
1398 if (ep == 0)
1399 return;
1400
1401 /* *INDENT-OFF* */
1402 REPLY_MACRO2(VL_API_DNS_RESOLVE_IP_REPLY,
1403 ({
1404 rv = vnet_dns_response_to_name (ep->dns_response, rmp, 0 /* ttl-ptr */);
1405 rmp->retval = clib_host_to_net_u32 (rv);
1406 }));
1407 /* *INDENT-ON* */
1408
1409 /*
1410 * dns_resolve_name leaves the cache locked when it returns
1411 * a cached result, so unlock it here.
1412 */
1413 dns_cache_unlock (dm);
1414}
1415
Dave Barach65457162017-10-10 17:53:14 -04001416#define vl_msg_name_crc_list
1417#include <vpp/api/vpe_all_api_h.h>
1418#undef vl_msg_name_crc_list
1419
1420static void
1421setup_message_id_table (api_main_t * am)
1422{
1423#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
1424 foreach_vl_msg_name_crc_dns;
1425#undef _
1426}
1427
1428#define foreach_dns_api_msg \
1429_(DNS_ENABLE_DISABLE, dns_enable_disable) \
1430_(DNS_NAME_SERVER_ADD_DEL, dns_name_server_add_del) \
Dave Barachd2080152017-10-20 09:21:35 -04001431_(DNS_RESOLVE_NAME, dns_resolve_name) \
1432_(DNS_RESOLVE_IP, dns_resolve_ip)
Dave Barach65457162017-10-10 17:53:14 -04001433
1434static clib_error_t *
1435dns_api_hookup (vlib_main_t * vm)
1436{
1437#define _(N,n) \
1438 vl_msg_api_set_handlers(VL_API_##N, #n, \
1439 vl_api_##n##_t_handler, \
1440 vl_noop_handler, \
1441 vl_api_##n##_t_endian, \
1442 vl_api_##n##_t_print, \
1443 sizeof(vl_api_##n##_t), 1);
1444 foreach_dns_api_msg;
1445#undef _
1446
1447 setup_message_id_table (&api_main);
1448 return 0;
1449}
1450
1451VLIB_API_INIT_FUNCTION (dns_api_hookup);
1452
1453
1454static clib_error_t *
1455dns_config_fn (vlib_main_t * vm, unformat_input_t * input)
1456{
1457 dns_main_t *dm = &dns_main;
1458
1459 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1460 {
1461 if (unformat (input, "max-cache-size %u", &dm->name_cache_size))
1462 ;
1463 else if (unformat (input, "max-ttl %u", &dm->max_ttl_in_seconds))
1464 ;
1465 else
1466 return clib_error_return (0, "unknown input `%U'",
1467 format_unformat_error, input);
1468 }
1469 return 0;
1470}
1471
1472VLIB_CONFIG_FUNCTION (dns_config_fn, "dns");
1473
1474static clib_error_t *
1475dns_init (vlib_main_t * vm)
1476{
1477 dns_main_t *dm = &dns_main;
1478
1479 dm->vlib_main = vm;
1480 dm->vnet_main = vnet_get_main ();
1481 dm->name_cache_size = 65535;
1482 dm->max_ttl_in_seconds = 86400;
1483 dm->random_seed = 0xDEADDABE;
1484
1485 udp_register_dst_port (vm, UDP_DST_PORT_dns_reply, dns46_reply_node.index,
1486 1 /* is_ip4 */ );
1487
1488 udp_register_dst_port (vm, UDP_DST_PORT_dns_reply6, dns46_reply_node.index,
1489 0 /* is_ip4 */ );
1490
1491#if 0
1492 udp_register_dst_port (vm, UDP_DST_PORT_dns, dns4_request_node.index,
1493 1 /* is_ip4 */ );
1494 udp_register_dst_port (vm, UDP_DST_PORT_dns6, dns6_request_node.index,
1495 0 /* is_ip4 */ );
1496#endif
1497
1498 return 0;
1499}
1500
1501VLIB_INIT_FUNCTION (dns_init);
1502
1503uword
1504unformat_dns_reply (unformat_input_t * input, va_list * args)
1505{
1506 u8 **result = va_arg (*args, u8 **);
1507 u8 **namep = va_arg (*args, u8 **);
1508 ip4_address_t a4;
1509 ip6_address_t a6;
1510 int a4_set = 0;
1511 int a6_set = 0;
1512 u8 *name;
1513 int name_set = 0;
1514 u8 *ce;
1515 u32 qp_offset;
1516 dns_header_t *h;
1517 dns_query_t *qp;
1518 dns_rr_t *rr;
1519 u8 *rru8;
1520
1521 if (unformat (input, "%v", &name))
1522 name_set = 1;
1523
1524 if (unformat (input, "%U", unformat_ip4_address, &a4))
1525 {
1526 a4_set = 1;
1527 if (unformat (input, "%U", unformat_ip6_address, &a6))
1528 a6_set = 1;
1529 }
1530
1531 if (unformat (input, "%U", unformat_ip6_address, &a6))
1532 {
1533 a6_set = 1;
1534 if (unformat (input, "%U", unformat_ip4_address, &a6))
1535 a4_set = 1;
1536 }
1537
1538 /* Must have a name */
1539 if (!name_set)
1540 return 0;
1541
1542 /* Must have at least one address */
1543 if (!(a4_set + a6_set))
1544 return 0;
1545
1546 /* Build a fake DNS cache entry string, one hemorrhoid at a time */
1547 ce = name_to_labels (name);
1548 qp_offset = vec_len (ce);
1549
1550 /* Add space for the query header */
1551 vec_validate (ce, qp_offset + sizeof (dns_query_t) - 1);
1552 qp = (dns_query_t *) (ce + qp_offset);
1553
1554 qp->type = clib_host_to_net_u16 (DNS_TYPE_ALL);
1555 qp->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1556
1557 /* Punch in space for the dns_header_t */
1558 vec_insert (ce, sizeof (dns_header_t), 0);
1559
1560 h = (dns_header_t *) ce;
1561
1562 /* Fake Transaction ID */
1563 h->id = 0xFFFF;
1564
1565 h->flags = clib_host_to_net_u16 (DNS_RD | DNS_RA);
1566 h->qdcount = clib_host_to_net_u16 (1);
1567 h->anscount = clib_host_to_net_u16 (a4_set + a6_set);
1568 h->nscount = 0;
1569 h->arcount = 0;
1570
1571 /* Now append one or two A/AAAA RR's... */
1572 if (a4_set)
1573 {
1574 /* Pointer to the name (DGMS) */
1575 vec_add1 (ce, 0xC0);
1576 vec_add1 (ce, 0x0C);
1577 vec_add2 (ce, rru8, sizeof (*rr) + 4);
1578 rr = (void *) rru8;
1579 rr->type = clib_host_to_net_u16 (DNS_TYPE_A);
1580 rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1581 rr->ttl = clib_host_to_net_u32 (86400);
1582 rr->rdlength = clib_host_to_net_u16 (4);
1583 memcpy (rr->rdata, &a4, sizeof (a4));
1584 }
1585 if (a6_set)
1586 {
1587 /* Pointer to the name (DGMS) */
1588 vec_add1 (ce, 0xC0);
1589 vec_add1 (ce, 0x0C);
1590 vec_add2 (ce, rru8, sizeof (*rr) + 16);
1591 rr = (void *) rru8;
1592 rr->type = clib_host_to_net_u16 (DNS_TYPE_AAAA);
1593 rr->class = clib_host_to_net_u16 (DNS_CLASS_IN);
1594 rr->ttl = clib_host_to_net_u32 (86400);
1595 rr->rdlength = clib_host_to_net_u16 (16);
1596 memcpy (rr->rdata, &a6, sizeof (a6));
1597 }
1598 *result = ce;
1599 if (namep)
1600 *namep = name;
1601 else
1602 vec_free (name);
1603
1604 return 1;
1605}
1606
1607u8 *
1608format_dns_query (u8 * s, va_list * args)
1609{
1610 u8 **curpos = va_arg (*args, u8 **);
1611 int verbose = va_arg (*args, int);
1612 u8 *pos;
1613 dns_query_t *qp;
1614 int len, i;
1615 if (verbose > 1)
1616 s = format (s, " Name: ");
1617
1618 /* Unwind execrated counted-label sheit */
1619 pos = *curpos;
1620 len = *pos++;
1621
1622 while (len)
1623 {
1624 for (i = 0; i < len; i++)
1625 vec_add1 (s, *pos++);
1626
1627 len = *pos++;
1628 if (len)
1629 vec_add1 (s, '.');
1630 else
1631 {
1632 vec_add1 (s, ':');
1633 vec_add1 (s, ' ');
1634 }
1635 }
1636
1637 qp = (dns_query_t *) pos;
1638 if (verbose > 1)
1639 {
1640 switch (clib_net_to_host_u16 (qp->type))
1641 {
1642 case DNS_TYPE_A:
1643 s = format (s, "type A\n");
1644 break;
1645 case DNS_TYPE_AAAA:
1646 s = format (s, "type AAAA\n");
1647 break;
1648 case DNS_TYPE_ALL:
1649 s = format (s, "type ALL\n");
1650 break;
1651
1652 default:
1653 s = format (s, "type %d\n", clib_net_to_host_u16 (qp->type));
1654 break;
1655 }
1656 }
1657
1658 pos += sizeof (*qp);
1659
1660 *curpos = pos;
1661 return s;
1662}
1663
1664/**
1665 * format dns reply data
1666 * verbose > 1, dump everything
1667 * verbose == 1, dump all A and AAAA records
1668 * verbose == 0, dump one A record, and one AAAA record
1669 */
1670
1671u8 *
1672format_dns_reply_data (u8 * s, va_list * args)
1673{
1674 u8 *reply = va_arg (*args, u8 *);
1675 u8 **curpos = va_arg (*args, u8 **);
1676 int verbose = va_arg (*args, int);
1677 int *print_ip4 = va_arg (*args, int *);
1678 int *print_ip6 = va_arg (*args, int *);
1679 int len;
1680 u8 *pos, *pos2;
1681 dns_rr_t *rr;
1682 int i;
1683 int initial_pointer_chase = 0;
1684 u16 *tp;
Dave Barachd2080152017-10-20 09:21:35 -04001685 u16 rrtype_host_byte_order;
Dave Barach65457162017-10-10 17:53:14 -04001686
1687 pos = pos2 = *curpos;
1688
1689 if (verbose > 1)
1690 s = format (s, " ");
1691
1692 /* chase pointer? almost always yes here... */
1693 if (pos2[0] == 0xc0)
1694 {
1695 pos2 = reply + pos2[1];
1696 pos += 2;
1697 initial_pointer_chase = 1;
1698 }
1699
1700 len = *pos2++;
1701
1702 while (len)
1703 {
1704 for (i = 0; i < len; i++)
1705 {
1706 if (verbose > 1)
1707 vec_add1 (s, *pos2);
1708 pos2++;
1709 }
1710 len = *pos2++;
1711 if (len)
1712 {
1713 if (verbose > 1)
1714 vec_add1 (s, '.');
1715 }
1716 else
1717 {
1718 if (verbose > 1)
1719 vec_add1 (s, ' ');
1720 }
1721 }
1722
1723 if (initial_pointer_chase == 0)
1724 pos = pos2;
1725
1726 rr = (dns_rr_t *) pos;
Dave Barachd2080152017-10-20 09:21:35 -04001727 rrtype_host_byte_order = clib_net_to_host_u16 (rr->type);
Dave Barach65457162017-10-10 17:53:14 -04001728
Dave Barachd2080152017-10-20 09:21:35 -04001729 switch (rrtype_host_byte_order)
Dave Barach65457162017-10-10 17:53:14 -04001730 {
1731 case DNS_TYPE_A:
1732 if (verbose > 1)
1733 {
1734 s = format (s, "A: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
1735 format_ip4_address, rr->rdata);
1736 }
1737 else
1738 {
1739 if (*print_ip4)
1740 s = format (s, "%U [%u] ", format_ip4_address, rr->rdata,
1741 clib_net_to_host_u32 (rr->ttl));
1742 if (verbose == 0)
1743 *print_ip4 = 0;
1744
1745 }
1746 pos += sizeof (*rr) + 4;
1747 break;
1748
1749 case DNS_TYPE_AAAA:
1750 if (verbose > 1)
1751 {
1752 s = format (s, "AAAA: ttl %d %U\n", clib_net_to_host_u32 (rr->ttl),
1753 format_ip6_address, rr->rdata);
1754 }
1755 else
1756 {
1757 if (*print_ip6)
1758 s = format (s, "%U [%u] ", format_ip6_address, rr->rdata,
1759 clib_net_to_host_u32 (rr->ttl));
1760 if (verbose == 0)
1761 *print_ip6 = 0;
1762 }
1763 pos += sizeof (*rr) + 16;
1764 break;
1765
1766 case DNS_TYPE_TEXT:
1767 if (verbose > 1)
1768 {
1769 s = format (s, "TEXT: ");
1770 for (i = 0; i < clib_net_to_host_u16 (rr->rdlength); i++)
1771 vec_add1 (s, rr->rdata[i]);
1772 vec_add1 (s, '\n');
1773 }
1774 pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1775 break;
1776
Dave Barach0cb01bd2017-10-16 14:39:52 -04001777 case DNS_TYPE_HINFO:
1778 {
1779 /* Two counted strings. DGMS */
1780 u8 *len;
1781 u8 *curpos;
1782 int i;
1783 if (verbose > 1)
1784 {
1785 s = format (s, "HINFO: ");
1786 len = rr->rdata;
1787 curpos = len + 1;
1788 for (i = 0; i < *len; i++)
1789 vec_add1 (s, *curpos++);
1790
1791 vec_add1 (s, ' ');
1792 len = curpos++;
1793 for (i = 0; i < *len; i++)
1794 vec_add1 (s, *curpos++);
1795
1796 vec_add1 (s, '\n');
1797 }
1798 }
1799 pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1800 break;
1801
Dave Barach65457162017-10-10 17:53:14 -04001802 case DNS_TYPE_NAMESERVER:
1803 if (verbose > 1)
1804 {
1805 s = format (s, "Nameserver: ");
1806 pos2 = rr->rdata;
1807
1808 /* chase pointer? */
1809 if (pos2[0] == 0xc0)
1810 pos2 = reply + pos2[1];
1811
1812 len = *pos2++;
1813
1814 while (len)
1815 {
1816 for (i = 0; i < len; i++)
1817 vec_add1 (s, *pos2++);
1818
1819 /* chase pointer, typically to offset 12... */
1820 if (pos2[0] == 0xC0)
1821 pos2 = reply + pos2[1];
1822
1823 len = *pos2++;
1824 if (len)
1825 vec_add1 (s, '.');
1826 else
1827 vec_add1 (s, '\n');
1828 }
1829 }
1830 pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1831 break;
1832
1833 case DNS_TYPE_MAIL_EXCHANGE:
1834 if (verbose > 1)
1835 {
1836 tp = (u16 *) rr->rdata;
1837
1838 s = format (s, "Mail Exchange: Preference %d ", (u32)
1839 clib_net_to_host_u16 (*tp));
1840
1841 pos2 = rr->rdata + 2;
1842
1843 /* chase pointer? */
1844 if (pos2[0] == 0xc0)
1845 pos2 = reply + pos2[1];
1846
1847 len = *pos2++;
1848
1849 while (len)
1850 {
1851 for (i = 0; i < len; i++)
1852 vec_add1 (s, *pos2++);
1853
1854 /* chase pointer */
1855 if (pos2[0] == 0xC0)
1856 pos2 = reply + pos2[1];
1857
1858 len = *pos2++;
1859 if (len)
1860 vec_add1 (s, '.');
1861 else
1862 vec_add1 (s, '\n');
1863 }
1864 }
1865
1866 pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1867 break;
1868
Dave Barachd2080152017-10-20 09:21:35 -04001869 case DNS_TYPE_PTR:
Dave Barach65457162017-10-10 17:53:14 -04001870 case DNS_TYPE_CNAME:
1871 if (verbose > 1)
1872 {
1873 tp = (u16 *) rr->rdata;
1874
Dave Barachd2080152017-10-20 09:21:35 -04001875 if (rrtype_host_byte_order == DNS_TYPE_CNAME)
1876 s = format (s, "CNAME: ");
1877 else
1878 s = format (s, "PTR: ");
Dave Barach65457162017-10-10 17:53:14 -04001879
1880 pos2 = rr->rdata;
1881
1882 /* chase pointer? */
1883 if (pos2[0] == 0xc0)
1884 pos2 = reply + pos2[1];
1885
1886 len = *pos2++;
1887
1888 while (len)
1889 {
1890 for (i = 0; i < len; i++)
1891 vec_add1 (s, *pos2++);
1892
1893 /* chase pointer */
1894 if (pos2[0] == 0xC0)
1895 pos2 = reply + pos2[1];
1896
1897 len = *pos2++;
1898 if (len)
1899 vec_add1 (s, '.');
1900 else
1901 vec_add1 (s, '\n');
1902 }
1903 }
1904 pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1905 break;
1906
1907 default:
1908 if (verbose > 1)
1909 s = format (s, "type %d: len %d\n",
1910 (int) clib_net_to_host_u16 (rr->type),
1911 sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength));
1912 pos += sizeof (*rr) + clib_net_to_host_u16 (rr->rdlength);
1913 break;
1914 }
1915
1916 *curpos = pos;
1917
1918 return s;
1919}
1920
1921u8 *
1922format_dns_reply (u8 * s, va_list * args)
1923{
1924 u8 *reply_as_u8 = va_arg (*args, u8 *);
1925 int verbose = va_arg (*args, int);
1926 dns_header_t *h;
1927 u16 id, flags;
1928 u8 *curpos;
1929 int i;
1930 int print_ip4 = 1;
1931 int print_ip6 = 1;
1932
1933 h = (dns_header_t *) reply_as_u8;
1934 id = clib_net_to_host_u16 (h->id);
1935 flags = clib_net_to_host_u16 (h->flags);
1936
1937 if (verbose > 1)
1938 {
1939 s = format (s, "DNS %s: id %d\n", (flags & DNS_QR) ? "reply" : "query",
1940 id);
1941 s = format (s, " %s %s %s %s\n",
1942 (flags & DNS_RA) ? "recur" : "no-recur",
1943 (flags & DNS_RD) ? "recur-des" : "no-recur-des",
1944 (flags & DNS_TC) ? "trunc" : "no-trunc",
1945 (flags & DNS_AA) ? "auth" : "non-auth");
1946 s = format (s, " %d queries, %d answers, %d name-servers,"
1947 " %d add'l recs\n",
1948 clib_net_to_host_u16 (h->qdcount),
1949 clib_net_to_host_u16 (h->anscount),
1950 clib_net_to_host_u16 (h->nscount),
1951 clib_net_to_host_u16 (h->arcount));
1952 }
1953
1954 curpos = (u8 *) (h + 1);
1955
1956 if (h->qdcount)
1957 {
1958 if (verbose > 1)
1959 s = format (s, " Queries:\n");
1960 for (i = 0; i < clib_net_to_host_u16 (h->qdcount); i++)
1961 {
1962 /* The query is variable-length, so curpos is a value-result parm */
1963 s = format (s, "%U", format_dns_query, &curpos, verbose);
1964 }
1965 }
1966 if (h->anscount)
1967 {
1968 if (verbose > 1)
1969 s = format (s, " Replies:\n");
1970
1971 for (i = 0; i < clib_net_to_host_u16 (h->anscount); i++)
1972 {
1973 /* curpos is a value-result parm */
1974 s = format (s, "%U", format_dns_reply_data, reply_as_u8, &curpos,
1975 verbose, &print_ip4, &print_ip6);
1976 }
1977 }
1978 return s;
1979}
1980
1981u8 *
1982format_dns_cache (u8 * s, va_list * args)
1983{
1984 dns_main_t *dm = va_arg (*args, dns_main_t *);
1985 f64 now = va_arg (*args, f64);
1986 int verbose = va_arg (*args, int);
1987 u8 *name = va_arg (*args, u8 *);
1988 dns_cache_entry_t *ep;
1989 char *ss;
1990 uword *p;
1991
1992 if (dm->is_enabled == 0)
1993 {
1994 s = format (s, "The DNS cache is disabled...");
1995 return s;
1996 }
1997
1998 if (pool_elts (dm->entries) == 0)
1999 {
2000 s = format (s, "The DNS cache is empty...");
2001 return s;
2002 }
2003
2004 dns_cache_lock (dm);
2005
2006 if (name)
2007 {
2008 p = hash_get_mem (dm->cache_entry_by_name, name);
2009 if (!p)
2010 {
2011 s = format (s, "%s is not in the cache...", name);
2012 dns_cache_unlock (dm);
2013 return (s);
2014 }
2015
2016 ep = pool_elt_at_index (dm->entries, p[0]);
2017 /* Magic to spit out a C-initializer to research hemorrhoids... */
2018 if (verbose == 3)
2019 {
2020 int i, j;
2021 s = format (s, "static u8 dns_reply_data_initializer[] =\n");
2022 s = format (s, "{\n");
2023 j = 0;
2024 for (i = 0; i < vec_len (ep->dns_response); i++)
2025 {
2026 if (j++ == 8)
2027 {
2028 j = 0;
2029 vec_add1 (s, '\n');
2030 }
2031 s = format (s, "0x%02x, ", ep->dns_response[i]);
2032 }
2033 s = format (s, "};\n");
2034 }
2035 else
2036 {
2037 if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
2038 {
2039 ASSERT (ep->dns_response);
2040 if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
2041 ss = "[S] ";
2042 else
2043 ss = " ";
2044
Dave Barach0cb01bd2017-10-16 14:39:52 -04002045 if (verbose < 2 && ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
2046 s = format (s, "%s%s -> %s", ss, ep->name, ep->cname);
2047 else
2048 s = format (s, "%s%s -> %U", ss, ep->name,
2049 format_dns_reply, ep->dns_response, verbose);
Dave Barach65457162017-10-10 17:53:14 -04002050 if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
2051 {
2052 f64 time_left = ep->expiration_time - now;
2053 if (time_left > 0.0)
2054 s = format (s, " TTL left %.1f", time_left);
2055 else
2056 s = format (s, " EXPIRED");
2057 }
2058 }
2059 else
2060 {
2061 ASSERT (ep->dns_request);
2062 s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
2063 verbose);
2064 }
2065 vec_add1 (s, '\n');
2066 }
2067 return s;
2068 }
2069
2070 /* *INDENT-OFF* */
2071 pool_foreach (ep, dm->entries,
2072 ({
2073 if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
2074 {
2075 ASSERT (ep->dns_response);
2076 if (ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC)
2077 ss = "[S] ";
2078 else
2079 ss = " ";
2080
Dave Barach0cb01bd2017-10-16 14:39:52 -04002081 if (verbose < 2 && ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
2082 s = format (s, "%s%s -> %s", ss, ep->name, ep->cname);
2083 else
2084 s = format (s, "%s%s -> %U", ss, ep->name,
2085 format_dns_reply,
2086 ep->dns_response,
2087 verbose);
Dave Barach65457162017-10-10 17:53:14 -04002088 if (!(ep->flags & DNS_CACHE_ENTRY_FLAG_STATIC))
2089 {
2090 f64 time_left = ep->expiration_time - now;
2091 if (time_left > 0.0)
2092 s = format (s, " TTL left %.1f", time_left);
2093 else
2094 s = format (s, " EXPIRED");
Dave Barach0cb01bd2017-10-16 14:39:52 -04002095
2096 if (verbose > 2)
2097 s = format (s, " %d client notifications pending\n",
Dave Barachd2080152017-10-20 09:21:35 -04002098 vec_len(ep->pending_api_requests));
Dave Barach65457162017-10-10 17:53:14 -04002099 }
2100 }
2101 else
2102 {
2103 ASSERT (ep->dns_request);
2104 s = format (s, "[P] %U", format_dns_reply, ep->dns_request,
2105 verbose);
2106 }
2107 vec_add1 (s, '\n');
2108 }));
2109 /* *INDENT-ON* */
2110
2111 dns_cache_unlock (dm);
2112
2113 return s;
2114}
2115
2116static clib_error_t *
2117show_dns_cache_command_fn (vlib_main_t * vm,
2118 unformat_input_t * input, vlib_cli_command_t * cmd)
2119{
2120 dns_main_t *dm = &dns_main;
2121 int verbose = 0;
2122 u8 *name = 0;
2123 f64 now = vlib_time_now (vm);
2124
2125 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2126 {
2127 if (unformat (input, "verbose %d", &verbose))
2128 ;
2129 else if (unformat (input, "verbose"))
2130 verbose = 1;
2131 else if (unformat (input, "name %s", &name))
2132 ;
2133 else
2134 return clib_error_return (0, "unknown input `%U'",
2135 format_unformat_error, input);
2136 }
2137
2138 vlib_cli_output (vm, "%U", format_dns_cache, dm, now, verbose, name);
2139
2140 return 0;
2141}
2142
2143/* *INDENT-OFF* */
2144VLIB_CLI_COMMAND (show_dns_cache_command) =
2145{
2146 .path = "show dns cache",
2147 .short_help = "show dns cache [verbose [nn]]",
2148 .function = show_dns_cache_command_fn,
2149};
2150/* *INDENT-ON* */
2151
2152static clib_error_t *
2153dns_cache_add_del_command_fn (vlib_main_t * vm,
2154 unformat_input_t * input,
2155 vlib_cli_command_t * cmd)
2156{
2157 dns_main_t *dm = &dns_main;
2158 u8 *dns_reply_data;
2159 u8 *name;
2160 int is_add = -1;
2161 int is_clear = -1;
2162 int rv;
2163 clib_error_t *error;
2164
2165 if (unformat (input, "add"))
2166 is_add = 1;
2167 if (unformat (input, "del"))
2168 is_add = 0;
2169 if (unformat (input, "clear"))
2170 is_clear = 1;
2171
2172 if (is_add == -1 && is_clear == -1)
2173 return clib_error_return (0, "add / del / clear required...");
2174
2175 if (is_clear == 1)
2176 {
2177 rv = dns_cache_clear (dm);
2178 switch (rv)
2179 {
2180 case 0:
2181 return 0;
2182
2183 case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
2184 error = clib_error_return (0, "Name resolution not enabled");
2185 return error;
2186 }
2187 }
2188
2189 /* Delete (by name)? */
2190 if (is_add == 0)
2191 {
2192 if (unformat (input, "%v", &name))
2193 {
2194 rv = dns_delete_by_name (dm, name);
2195 switch (rv)
2196 {
2197 case VNET_API_ERROR_NO_SUCH_ENTRY:
2198 error = clib_error_return (0, "%v not in the cache...", name);
2199 vec_free (name);
2200 return error;
2201
2202 case VNET_API_ERROR_NAME_RESOLUTION_NOT_ENABLED:
2203 error = clib_error_return (0, "Name resolution not enabled");
2204 vec_free (name);
2205 return error;
2206
2207 case 0:
2208 vec_free (name);
2209 return 0;
2210
2211 default:
2212 error = clib_error_return (0, "dns_delete_by_name returned %d",
2213 rv);
2214 vec_free (name);
2215 return error;
2216 }
2217 }
2218 return clib_error_return (0, "unknown input `%U'",
2219 format_unformat_error, input);
2220 }
2221
2222 /* Note: dns_add_static_entry consumes the name vector if OK... */
2223 if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data, &name))
2224 {
2225 rv = dns_add_static_entry (dm, name, dns_reply_data);
2226 switch (rv)
2227 {
2228 case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
2229 vec_free (name);
2230 vec_free (dns_reply_data);
2231 return clib_error_return (0, "%v already in the cache...", name);
2232 case 0:
2233 return 0;
2234
2235 default:
2236 return clib_error_return (0, "dns_add_static_entry returned %d",
2237 rv);
2238 }
2239 }
2240
2241 return 0;
2242}
2243
2244/* *INDENT-OFF* */
2245VLIB_CLI_COMMAND (dns_cache_add_del_command) =
2246{
2247 .path = "dns cache",
2248 .short_help = "dns cache [add|del|clear] <name> [ip4][ip6]",
2249 .function = dns_cache_add_del_command_fn,
2250};
2251/* *INDENT-ON* */
2252
2253#define DNS_FORMAT_TEST 1
2254
2255#if DNS_FORMAT_TEST > 0
2256#if 0
2257/* yahoo.com */
2258static u8 dns_reply_data_initializer[] =
2259 { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x5,
2260 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x3, 0x63, 0x6f, 0x6d,
2261 0x0, /* null lbl */
2262 0x0, 0xff, /* type ALL */
2263 0x0, 0x1, /* class IN */
2264 0xc0, 0xc, /* pointer to yahoo.com name */
2265 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x24, 0x23,
2266 0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x72, 0x65, 0x64, 0x69, 0x72,
2267 0x65, 0x63, 0x74, 0x3d, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x6d, 0x61, 0x69,
2268 0x6c, 0x2e, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0xc0,
2269 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73,
2270 0x35, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0,
2271 0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
2272 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc,
2273 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x32,
2274 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x50, 0xd4, 0x0, 0x6,
2275 0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0,
2276 0x6, 0x5c, 0x0, 0x19, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x36, 0x3, 0x61,
2277 0x6d, 0x30, 0x8, 0x79, 0x61, 0x68, 0x6f, 0x6f, 0x64, 0x6e, 0x73, 0x3,
2278 0x6e,
2279 0x65, 0x74, 0x0, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0,
2280 0x9, 0x0, 0x1, 0x4, 0x6d, 0x74, 0x61, 0x37, 0xc0, 0xb8, 0xc0, 0xc, 0x0,
2281 0xf, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x9, 0x0, 0x1, 0x4, 0x6d, 0x74,
2282 0x61, 0x35, 0xc0, 0xb8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
2283 0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x44, 0x2, 0x4, 0x0, 0x0,
2284 0x0,
2285 0x0, 0x0, 0x0, 0x0, 0xa7, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
2286 0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0xc, 0xa, 0x6, 0x0, 0x0, 0x0,
2287 0x0, 0x0, 0x2, 0x40, 0x8, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x6,
2288 0x5c, 0x0, 0x10, 0x20, 0x1, 0x49, 0x98, 0x0, 0x58, 0xc, 0x2, 0x0, 0x0,
2289 0x0,
2290 0x0, 0x0, 0x0, 0x0, 0xa9, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x6,
2291 0x5c, 0x0, 0x4, 0x62, 0x8a, 0xfd, 0x6d, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1,
2292 0x0,
2293 0x0, 0x6, 0x5c, 0x0, 0x4, 0xce, 0xbe, 0x24, 0x2d, 0xc0, 0xc, 0x0, 0x1,
2294 0x0,
2295 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x4, 0x62, 0x8b, 0xb4, 0x95, 0xc0, 0xc,
2296 0x0,
2297 0x6, 0x0, 0x1, 0x0, 0x0, 0x6, 0x5c, 0x0, 0x2d, 0xc0, 0x7b, 0xa, 0x68,
2298 0x6f,
2299 0x73, 0x74, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x9, 0x79, 0x61, 0x68,
2300 0x6f, 0x6f, 0x2d, 0x69, 0x6e, 0x63, 0xc0, 0x12, 0x78, 0x3a, 0x85, 0x44,
2301 0x0, 0x0, 0xe, 0x10, 0x0, 0x0, 0x1, 0x2c, 0x0, 0x1b, 0xaf, 0x80, 0x0, 0x0,
2302 0x2, 0x58
2303};
2304
2305/* www.cisco.com, has no addresses in reply */
2306static u8 dns_reply_data_initializer[] = {
2307 0x00, 0x01, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
2308 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x05,
2309 0x63, 0x69, 0x73, 0x63, 0x6f, 0x03, 0x63, 0x6f, 0x6d,
2310
2311 0x00, 0x00, 0xff, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05,
2312 0x00, 0x01, 0x00, 0x00, 0x0b, 0xd3, 0x00, 0x1a, 0x03,
2313 0x77, 0x77, 0x77, 0x05, 0x63, 0x69, 0x73, 0x63, 0x6f,
2314 0x03, 0x63, 0x6f, 0x6d, 0x06, 0x61, 0x6b, 0x61, 0x64,
2315 0x6e, 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00,
2316};
2317#else
2318/* google.com */
2319static u8 dns_reply_data_initializer[] =
2320 { 0x0, 0x0, 0x81, 0x80, 0x0, 0x1, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x6,
2321 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x3, 0x63, 0x6f, 0x6d, 0x0, 0x0, 0xff,
2322 0x0, 0x1, 0xc0, 0xc, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x1, 0x2b, 0x0, 0x4,
2323 0xac, 0xd9, 0x3, 0x2e, 0xc0, 0xc, 0x0, 0x1c, 0x0, 0x1, 0x0, 0x0, 0x1,
2324 0x2b,
2325 0x0, 0x10, 0x26, 0x7, 0xf8, 0xb0, 0x40, 0x4, 0x8, 0xf, 0x0, 0x0, 0x0, 0x0,
2326 0x0, 0x0, 0x20, 0xe, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f,
2327 0x0, 0x6, 0x3, 0x6e, 0x73, 0x31, 0xc0, 0xc, 0xc0, 0xc, 0x0, 0x6, 0x0, 0x1,
2328 0x0, 0x0, 0x0, 0x3b, 0x0, 0x22, 0xc0, 0x54, 0x9, 0x64, 0x6e, 0x73, 0x2d,
2329 0x61, 0x64, 0x6d, 0x69, 0x6e, 0xc0, 0xc, 0xa, 0x3d, 0xc7, 0x30, 0x0, 0x0,
2330 0x3, 0x84, 0x0, 0x0, 0x3, 0x84, 0x0, 0x0, 0x7, 0x8, 0x0, 0x0, 0x0, 0x3c,
2331 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x11, 0x0, 0x1e,
2332 0x4, 0x61, 0x6c, 0x74, 0x32, 0x5, 0x61, 0x73, 0x70, 0x6d, 0x78, 0x1, 0x6c,
2333 0xc0, 0xc, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x4,
2334 0x0, 0xa, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0xe, 0xf,
2335 0x0, 0x24, 0x23, 0x76, 0x3d, 0x73, 0x70, 0x66, 0x31, 0x20, 0x69, 0x6e,
2336 0x63, 0x6c, 0x75, 0x64, 0x65, 0x3a, 0x5f, 0x73, 0x70, 0x66, 0x2e, 0x67,
2337 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x7e, 0x61,
2338 0x6c, 0x6c, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x51, 0x7f, 0x0, 0x6,
2339 0x3, 0x6e, 0x73, 0x32, 0xc0, 0xc, 0xc0, 0xc, 0x1, 0x1, 0x0, 0x1, 0x0, 0x1,
2340 0x51, 0x7f, 0x0, 0xf, 0x0, 0x5, 0x69, 0x73, 0x73, 0x75, 0x65, 0x70, 0x6b,
2341 0x69, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0,
2342 0x1, 0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x34, 0xc0, 0xc, 0xc0, 0xc,
2343 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x28, 0x4, 0x61,
2344 0x6c, 0x74, 0x33, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1,
2345 0x51, 0x7f, 0x0, 0x6, 0x3, 0x6e, 0x73, 0x33, 0xc0, 0xc, 0xc0, 0xc, 0x0,
2346 0xf, 0x0, 0x1, 0x0, 0x0, 0x2, 0x57, 0x0, 0x9, 0x0, 0x32, 0x4, 0x61, 0x6c,
2347 0x74, 0x34, 0xc0, 0x9b, 0xc0, 0xc, 0x0, 0xf, 0x0, 0x1, 0x0, 0x0, 0x2,
2348 0x57,
2349 0x0, 0x9, 0x0, 0x14, 0x4, 0x61, 0x6c, 0x74, 0x31, 0xc0, 0x9b
2350};
2351#endif
2352
2353static clib_error_t *
2354test_dns_fmt_command_fn (vlib_main_t * vm,
2355 unformat_input_t * input, vlib_cli_command_t * cmd)
2356{
2357 u8 *dns_reply_data = 0;
2358 int verbose = 0;
2359 int rv;
2360 vl_api_dns_resolve_name_reply_t _rm, *rmp = &_rm;
2361
2362 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2363 {
2364 if (unformat (input, "verbose %d", &verbose))
2365 ;
2366 else if (unformat (input, "verbose"))
2367 verbose = 1;
2368 else
2369 return clib_error_return (0, "unknown input `%U'",
2370 format_unformat_error, input);
2371 }
2372
2373 vec_validate (dns_reply_data, ARRAY_LEN (dns_reply_data_initializer) - 1);
2374
2375 memcpy (dns_reply_data, dns_reply_data_initializer,
2376 ARRAY_LEN (dns_reply_data_initializer));
2377
2378 vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
2379
2380 memset (rmp, 0, sizeof (*rmp));
2381
2382 rv = vnet_dns_response_to_reply (dns_reply_data, rmp, 0 /* ttl-ptr */ );
2383
2384 switch (rv)
2385 {
2386 case VNET_API_ERROR_NAME_SERVER_NO_ADDRESSES:
2387 vlib_cli_output (vm, "no addresses found...");
2388 break;
2389
2390 default:
2391 vlib_cli_output (vm, "response to reply returned %d", rv);
2392 break;
2393
2394 case 0:
2395 if (rmp->ip4_set)
2396 vlib_cli_output (vm, "ip4 address: %U", format_ip4_address,
2397 (ip4_address_t *) rmp->ip4_address);
2398 if (rmp->ip6_set)
2399 vlib_cli_output (vm, "ip6 address: %U", format_ip6_address,
2400 (ip6_address_t *) rmp->ip6_address);
2401 break;
2402 }
2403
2404 vec_free (dns_reply_data);
2405
2406 return 0;
2407}
2408
2409
2410/* *INDENT-OFF* */
2411VLIB_CLI_COMMAND (test_dns_fmt_command) =
2412{
2413 .path = "test dns format",
2414 .short_help = "test dns format",
2415 .function = test_dns_fmt_command_fn,
2416};
2417/* *INDENT-ON* */
2418
2419static clib_error_t *
2420test_dns_unfmt_command_fn (vlib_main_t * vm,
2421 unformat_input_t * input, vlib_cli_command_t * cmd)
2422{
2423 u8 *dns_reply_data = 0;
2424 int verbose = 0;
2425 int reply_set = 0;
2426
2427 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2428 {
2429 if (unformat (input, "verbose %d", &verbose))
2430 ;
2431 else if (unformat (input, "verbose"))
2432 verbose = 1;
2433 else if (unformat (input, "%U", unformat_dns_reply, &dns_reply_data))
2434 reply_set = 1;
2435 else
2436 return clib_error_return (0, "unknown input `%U'",
2437 format_unformat_error, input);
2438 }
2439
2440 if (reply_set == 0)
2441 return clib_error_return (0, "dns data not set...");
2442
2443 vlib_cli_output (vm, "%U", format_dns_reply, dns_reply_data, verbose);
2444
2445 vec_free (dns_reply_data);
2446
2447 return 0;
2448}
2449
2450/* *INDENT-OFF* */
2451VLIB_CLI_COMMAND (test_dns_unfmt_command) =
2452{
2453 .path = "test dns unformat",
2454 .short_help = "test dns unformat <name> [ip4][ip6]",
2455 .function = test_dns_unfmt_command_fn,
2456};
2457/* *INDENT-ON* */
Dave Barach0cb01bd2017-10-16 14:39:52 -04002458
2459static clib_error_t *
2460test_dns_expire_command_fn (vlib_main_t * vm,
2461 unformat_input_t * input,
2462 vlib_cli_command_t * cmd)
2463{
2464 dns_main_t *dm = &dns_main;
2465 u8 *name;
2466 uword *p;
2467 clib_error_t *e;
2468 dns_cache_entry_t *ep;
2469
2470 if (unformat (input, "%v", &name))
2471 {
2472 vec_add1 (name, 0);
2473 _vec_len (name) -= 1;
2474 }
2475
2476 dns_cache_lock (dm);
2477
2478 p = hash_get_mem (dm->cache_entry_by_name, name);
2479 if (!p)
2480 {
2481 dns_cache_unlock (dm);
2482 e = clib_error_return (0, "%s is not in the cache...", name);
2483 vec_free (name);
2484 return e;
2485 }
2486
2487 ep = pool_elt_at_index (dm->entries, p[0]);
2488
2489 ep->expiration_time = 0;
2490
2491 return 0;
2492}
2493
2494/* *INDENT-OFF* */
2495VLIB_CLI_COMMAND (test_dns_expire_command) =
2496{
2497 .path = "test dns expire",
2498 .short_help = "test dns expire <name>",
2499 .function = test_dns_expire_command_fn,
2500};
2501/* *INDENT-ON* */
Dave Barach65457162017-10-10 17:53:14 -04002502#endif
2503
2504/*
2505 * fd.io coding-style-patch-verification: ON
2506 *
2507 * Local Variables:
2508 * eval: (c-set-style "gnu")
2509 * End:
2510 */