Add reverse DNS (ip to name) resolution
[vpp.git] / src / vnet / dns / dns.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
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
42 dns_main_t dns_main;
43
44 static int
45 dns_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);
58     vec_free (ep->pending_api_requests);
59     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
72 static int
73 dns_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
103 static 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
115 static int
116 dns6_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
151 static int
152 dns4_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
187 static 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
202 static void
203 send_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
267 found_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
318 static void
319 send_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
375 found_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  */
431 u8 *
432 name_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  */
474 u8 *
475 labels_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
516 void
517 vnet_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
525   /* This can easily happen if sitting in GDB, etc. */
526   if (ep->flags & DNS_CACHE_ENTRY_FLAG_VALID)
527     return;
528
529   /* 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
615 out:
616
617   vlib_process_signal_event_mt (dm->vlib_main, dns_resolver_node.index,
618                                 DNS_RESOLVER_EVENT_PENDING, 0);
619 }
620
621 int
622 vnet_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);
634   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
646 found:
647   hash_unset_mem (dm->cache_entry_by_name, ep->name);
648   vec_free (ep->name);
649   vec_free (ep->pending_api_requests);
650   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
657 static int
658 dns_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
680 static int
681 delete_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
691   /*
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
700   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
727 static int
728 dns_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
769 static int
770 dns_resolve_name (dns_main_t * dm,
771                   u8 * name, u32 client_index, u32 client_context,
772                   u32 request_type, dns_cache_entry_t ** retp)
773 {
774   dns_cache_entry_t *ep;
775   int rv;
776   f64 now;
777   uword *p;
778   pending_api_request_t *pr;
779
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);
786 search_again:
787   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             {
797               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);
835               /* Yes, kill it... */
836               goto re_resolve;
837             }
838
839           if (ep->flags & DNS_CACHE_ENTRY_FLAG_CNAME)
840             {
841               name = ep->cname;
842               goto search_again;
843             }
844
845           /* Note: caller must drop the lock! */
846           *retp = ep;
847           return (0);
848         }
849       else
850         {
851           /*
852            * 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;
858           dns_cache_unlock (dm);
859           return (0);
860         }
861     }
862
863 re_resolve:
864   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
875   /* 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);
885   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;
889   vnet_send_dns_request (dm, ep);
890   dns_cache_unlock (dm);
891   return 0;
892 }
893
894 #define foreach_notification_to_move            \
895 _(pending_api_requests)                         \
896 _(ip4_peers_to_notify)                          \
897 _(ip6_peers_to_notify)
898
899 /**
900  * Handle cname indirection. JFC. Called with the cache locked.
901  * returns 0 if the reply is not a CNAME.
902  */
903
904 int
905 vnet_dns_cname_indirection_nolock (dns_main_t * dm, u32 ep_index, u8 * reply)
906 {
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;
918   dns_cache_entry_t *ep, *next_ep;
919   f64 now;
920
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         }
951       pos += sizeof (dns_query_t);
952     }
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
966   /* This is a CNAME record, chase the name chain. */
967
968   /* 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
977 found_last_request:
978
979   now = vlib_time_now (dm->vlib_main);
980   cname = labels_to_name (rr->rdata, reply, &pos2);
981   /* 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
1014   request = name_to_labels (cname);
1015
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 */
1032   h->id = clib_host_to_net_u16 (next_ep - dm->entries);
1033
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
1040   next_ep->dns_request = request;
1041   next_ep->retry_timer = now + 2.0;
1042   next_ep->retry_count = 0;
1043
1044   /*
1045    * Enable this to watch recursive resolution happen...
1046    * fformat (stdout, "%U", format_dns_reply, request, 2);
1047    */
1048
1049   vec_add1 (dm->unresolved_entries, next_ep - dm->entries);
1050   vnet_send_dns_request (dm, next_ep);
1051   return (1);
1052 }
1053
1054 int
1055 vnet_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;
1136                   goto curpos_set;
1137                 }
1138               pos += len;
1139               len = *pos++;
1140             }
1141           curpos = pos;
1142         }
1143
1144     curpos_set:
1145       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
1179 int
1180 vnet_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
1300 static void
1301 vl_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
1311   rv = dns_resolve_name (dm, mp->name, mp->client_index, mp->context,
1312                          DNS_API_PENDING_NAME_TO_IP, &ep);
1313
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
1340 static void
1341 vl_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
1416 #define vl_msg_name_crc_list
1417 #include <vpp/api/vpe_all_api_h.h>
1418 #undef vl_msg_name_crc_list
1419
1420 static void
1421 setup_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)     \
1431 _(DNS_RESOLVE_NAME, dns_resolve_name)                   \
1432 _(DNS_RESOLVE_IP, dns_resolve_ip)
1433
1434 static clib_error_t *
1435 dns_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
1451 VLIB_API_INIT_FUNCTION (dns_api_hookup);
1452
1453
1454 static clib_error_t *
1455 dns_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
1472 VLIB_CONFIG_FUNCTION (dns_config_fn, "dns");
1473
1474 static clib_error_t *
1475 dns_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
1501 VLIB_INIT_FUNCTION (dns_init);
1502
1503 uword
1504 unformat_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
1607 u8 *
1608 format_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
1671 u8 *
1672 format_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;
1685   u16 rrtype_host_byte_order;
1686
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;
1727   rrtype_host_byte_order = clib_net_to_host_u16 (rr->type);
1728
1729   switch (rrtype_host_byte_order)
1730     {
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
1777     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
1802     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
1869     case DNS_TYPE_PTR:
1870     case DNS_TYPE_CNAME:
1871       if (verbose > 1)
1872         {
1873           tp = (u16 *) rr->rdata;
1874
1875           if (rrtype_host_byte_order == DNS_TYPE_CNAME)
1876             s = format (s, "CNAME: ");
1877           else
1878             s = format (s, "PTR: ");
1879
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
1921 u8 *
1922 format_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
1981 u8 *
1982 format_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
2045               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);
2050               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
2081         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);
2088         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");
2095
2096             if (verbose > 2)
2097               s = format (s, "    %d client notifications pending\n",
2098                           vec_len(ep->pending_api_requests));
2099           }
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
2116 static clib_error_t *
2117 show_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* */
2144 VLIB_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
2152 static clib_error_t *
2153 dns_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* */
2245 VLIB_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 */
2258 static 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 */
2306 static 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 */
2319 static 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
2353 static clib_error_t *
2354 test_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* */
2411 VLIB_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
2419 static clib_error_t *
2420 test_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* */
2451 VLIB_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* */
2458
2459 static clib_error_t *
2460 test_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* */
2495 VLIB_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* */
2502 #endif
2503
2504 /*
2505  * fd.io coding-style-patch-verification: ON
2506  *
2507  * Local Variables:
2508  * eval: (c-set-style "gnu")
2509  * End:
2510  */