]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ndp/ndp.c
bhyve: Define an accessor for net backend private data
[FreeBSD/FreeBSD.git] / usr.sbin / ndp / ndp.c
1 /*      $FreeBSD$       */
2 /*      $KAME: ndp.c,v 1.104 2003/06/27 07:48:39 itojun Exp $   */
3
4 /*-
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the project nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * Copyright (c) 1984, 1993
36  *      The Regents of the University of California.  All rights reserved.
37  *
38  * This code is derived from software contributed to Berkeley by
39  * Sun Microsystems, Inc.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  */
65
66 /*
67  * Based on:
68  * "@(#) Copyright (c) 1984, 1993\n\
69  *      The Regents of the University of California.  All rights reserved.\n";
70  *
71  * "@(#)arp.c   8.2 (Berkeley) 1/2/94";
72  */
73
74 /*
75  * ndp - display, set, delete and flush neighbor cache
76  */
77
78
79 #include <sys/param.h>
80 #include <sys/file.h>
81 #include <sys/ioctl.h>
82 #include <sys/socket.h>
83 #include <sys/sysctl.h>
84 #include <sys/time.h>
85 #include <sys/queue.h>
86
87 #include <net/if.h>
88 #include <net/if_dl.h>
89 #include <net/if_types.h>
90 #include <net/route.h>
91
92 #include <netinet/in.h>
93 #include <netinet/if_ether.h>
94
95 #include <netinet/icmp6.h>
96 #include <netinet6/in6_var.h>
97 #include <netinet6/nd6.h>
98
99 #include <arpa/inet.h>
100
101 #include <ctype.h>
102 #include <netdb.h>
103 #include <errno.h>
104 #include <nlist.h>
105 #include <stdio.h>
106 #include <string.h>
107 #include <paths.h>
108 #include <err.h>
109 #include <stdlib.h>
110 #include <fcntl.h>
111 #include <unistd.h>
112 #include <libxo/xo.h>
113 #include "gmt2local.h"
114
115 #define NEXTADDR(w, s)                                  \
116         if (rtm->rtm_addrs & (w)) {                     \
117                 bcopy((char *)&s, cp, sizeof(s));       \
118                 cp += SA_SIZE(&s);                      \
119         }
120
121 static pid_t pid;
122 static int nflag;
123 static int tflag;
124 static int32_t thiszone;        /* time difference with gmt */
125 static int s = -1;
126 static int repeat = 0;
127
128 static char host_buf[NI_MAXHOST];       /* getnameinfo() */
129 static char ifix_buf[IFNAMSIZ];         /* if_indextoname() */
130
131 static int file(char *);
132 static void getsocket(void);
133 static int set(int, char **);
134 static void get(char *);
135 static int delete(char *);
136 static void dump(struct sockaddr_in6 *, int);
137 static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int);
138 static char *ether_str(struct sockaddr_dl *);
139 static int ndp_ether_aton(char *, u_char *);
140 static void usage(void);
141 static int rtmsg(int);
142 static void ifinfo(char *, int, char **);
143 static void rtrlist(void);
144 static void plist(void);
145 static void pfx_flush(void);
146 static void rtr_flush(void);
147 static void harmonize_rtr(void);
148 #ifdef SIOCSDEFIFACE_IN6        /* XXX: check SIOCGDEFIFACE_IN6 as well? */
149 static void getdefif(void);
150 static void setdefif(char *);
151 #endif
152 static char *sec2str(time_t);
153 static void ts_print(const struct timeval *);
154
155 static const char *rtpref_str[] = {
156         "medium",               /* 00 */
157         "high",                 /* 01 */
158         "rsv",                  /* 10 */
159         "low"                   /* 11 */
160 };
161
162 #define NDP_XO_VERSION  "1"
163
164 int
165 main(int argc, char **argv)
166 {
167         int ch, mode = 0;
168         char *arg = NULL;
169
170         pid = getpid();
171         thiszone = gmt2local(0);
172
173         argc = xo_parse_args(argc, argv);
174         if (argc < 0)
175                 exit(1);
176         xo_set_version(NDP_XO_VERSION);
177         xo_open_container("ndp");
178
179         while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1)
180                 switch (ch) {
181                 case 'a':
182                 case 'c':
183                 case 'p':
184                 case 'r':
185                 case 'H':
186                 case 'P':
187                 case 'R':
188                 case 's':
189                 case 'I':
190                         if (mode) {
191                                 usage();
192                                 /*NOTREACHED*/
193                         }
194                         mode = ch;
195                         arg = NULL;
196                         break;
197                 case 'f':
198                         exit(file(optarg) ? 1 : 0);
199                 case 'd':
200                 case 'i':
201                         if (mode) {
202                                 usage();
203                                 /*NOTREACHED*/
204                         }
205                         mode = ch;
206                         arg = optarg;
207                         break;
208                 case 'n':
209                         nflag = 1;
210                         break;
211                 case 't':
212                         tflag = 1;
213                         break;
214                 case 'A':
215                         if (mode) {
216                                 usage();
217                                 /*NOTREACHED*/
218                         }
219                         mode = 'a';
220                         repeat = atoi(optarg);
221                         if (repeat < 0) {
222                                 usage();
223                                 /*NOTREACHED*/
224                         }
225                         break;
226                 default:
227                         usage();
228                 }
229
230         argc -= optind;
231         argv += optind;
232
233         switch (mode) {
234         case 'a':
235         case 'c':
236                 if (argc != 0) {
237                         usage();
238                         /*NOTREACHED*/
239                 }
240                 dump(0, mode == 'c');
241                 break;
242         case 'd':
243                 if (argc != 0) {
244                         usage();
245                         /*NOTREACHED*/
246                 }
247                 xo_open_list("neighbor-cache");
248                 delete(arg);
249                 xo_close_list("neighbor-cache");
250                 break;
251         case 'I':
252 #ifdef SIOCSDEFIFACE_IN6        /* XXX: check SIOCGDEFIFACE_IN6 as well? */
253                 if (argc > 1) {
254                         usage();
255                         /*NOTREACHED*/
256                 } else if (argc == 1) {
257                         if (strcmp(*argv, "delete") == 0 ||
258                             if_nametoindex(*argv))
259                                 setdefif(*argv);
260                         else
261                                 xo_errx(1, "invalid interface %s", *argv);
262                 }
263                 getdefif(); /* always call it to print the result */
264                 break;
265 #else
266                 xo_errx(1, "not supported yet");
267                 /*NOTREACHED*/
268 #endif
269         case 'p':
270                 if (argc != 0) {
271                         usage();
272                         /*NOTREACHED*/
273                 }
274                 plist();
275                 break;
276         case 'i':
277                 ifinfo(arg, argc, argv);
278                 break;
279         case 'r':
280                 if (argc != 0) {
281                         usage();
282                         /*NOTREACHED*/
283                 }
284                 rtrlist();
285                 break;
286         case 's':
287                 if (argc < 2 || argc > 4)
288                         usage();
289                 exit(set(argc, argv) ? 1 : 0);
290         case 'H':
291                 if (argc != 0) {
292                         usage();
293                         /*NOTREACHED*/
294                 }
295                 harmonize_rtr();
296                 break;
297         case 'P':
298                 if (argc != 0) {
299                         usage();
300                         /*NOTREACHED*/
301                 }
302                 pfx_flush();
303                 break;
304         case 'R':
305                 if (argc != 0) {
306                         usage();
307                         /*NOTREACHED*/
308                 }
309                 rtr_flush();
310                 break;
311         case 0:
312                 if (argc != 1) {
313                         usage();
314                         /*NOTREACHED*/
315                 }
316                 get(argv[0]);
317                 break;
318         }
319         xo_close_container("ndp");
320         xo_finish();
321         exit(0);
322 }
323
324 /*
325  * Process a file to set standard ndp entries
326  */
327 static int
328 file(char *name)
329 {
330         FILE *fp;
331         int i, retval;
332         char line[100], arg[5][50], *args[5], *p;
333
334         if ((fp = fopen(name, "r")) == NULL)
335                 xo_err(1, "cannot open %s", name);
336         args[0] = &arg[0][0];
337         args[1] = &arg[1][0];
338         args[2] = &arg[2][0];
339         args[3] = &arg[3][0];
340         args[4] = &arg[4][0];
341         retval = 0;
342         while (fgets(line, sizeof(line), fp) != NULL) {
343                 if ((p = strchr(line, '#')) != NULL)
344                         *p = '\0';
345                 for (p = line; isblank(*p); p++);
346                 if (*p == '\n' || *p == '\0')
347                         continue;
348                 i = sscanf(line, "%49s %49s %49s %49s %49s",
349                     arg[0], arg[1], arg[2], arg[3], arg[4]);
350                 if (i < 2) {
351                         xo_warnx("bad line: %s", line);
352                         retval = 1;
353                         continue;
354                 }
355                 if (set(i, args))
356                         retval = 1;
357         }
358         fclose(fp);
359         return (retval);
360 }
361
362 static void
363 getsocket()
364 {
365         if (s < 0) {
366                 s = socket(PF_ROUTE, SOCK_RAW, 0);
367                 if (s < 0) {
368                         xo_err(1, "socket");
369                         /* NOTREACHED */
370                 }
371         }
372 }
373
374 static struct sockaddr_in6 so_mask = {
375         .sin6_len = sizeof(so_mask),
376         .sin6_family = AF_INET6
377 };
378 static struct sockaddr_in6 blank_sin = {
379         .sin6_len = sizeof(blank_sin),
380         .sin6_family = AF_INET6
381 };
382 static struct sockaddr_in6 sin_m;
383 static struct sockaddr_dl blank_sdl = {
384         .sdl_len = sizeof(blank_sdl),
385         .sdl_family = AF_LINK
386 };
387 static struct sockaddr_dl sdl_m;
388 static time_t expire_time;
389 static int flags, found_entry;
390 static struct {
391         struct  rt_msghdr m_rtm;
392         char    m_space[512];
393 } m_rtmsg;
394
395 /*
396  * Set an individual neighbor cache entry
397  */
398 static int
399 set(int argc, char **argv)
400 {
401         register struct sockaddr_in6 *sin = &sin_m;
402         register struct sockaddr_dl *sdl;
403         register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
404         struct addrinfo hints, *res;
405         int gai_error;
406         u_char *ea;
407         char *host = argv[0], *eaddr = argv[1];
408
409         getsocket();
410         argc -= 2;
411         argv += 2;
412         sdl_m = blank_sdl;
413         sin_m = blank_sin;
414
415         bzero(&hints, sizeof(hints));
416         hints.ai_family = AF_INET6;
417         gai_error = getaddrinfo(host, NULL, &hints, &res);
418         if (gai_error) {
419                 xo_warnx("%s: %s", host, gai_strerror(gai_error));
420                 return 1;
421         }
422         sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
423         sin->sin6_scope_id =
424             ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
425         ea = (u_char *)LLADDR(&sdl_m);
426         if (ndp_ether_aton(eaddr, ea) == 0)
427                 sdl_m.sdl_alen = 6;
428         flags = expire_time = 0;
429         while (argc-- > 0) {
430                 if (strncmp(argv[0], "temp", 4) == 0) {
431                         struct timeval now;
432
433                         gettimeofday(&now, 0);
434                         expire_time = now.tv_sec + 20 * 60;
435                 } else if (strncmp(argv[0], "proxy", 5) == 0)
436                         flags |= RTF_ANNOUNCE;
437                 argv++;
438         }
439         if (rtmsg(RTM_GET) < 0) {
440                 xo_errx(1, "RTM_GET(%s) failed", host);
441                 /* NOTREACHED */
442         }
443         sin = (struct sockaddr_in6 *)(rtm + 1);
444         sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin);
445         if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
446                 if (sdl->sdl_family == AF_LINK &&
447                     !(rtm->rtm_flags & RTF_GATEWAY)) {
448                         switch (sdl->sdl_type) {
449                         case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
450                         case IFT_ISO88024: case IFT_ISO88025:
451                         case IFT_L2VLAN: case IFT_BRIDGE:
452                                 goto overwrite;
453                         }
454                 }
455                 xo_warnx("cannot configure a new entry");
456                 return 1;
457         }
458
459 overwrite:
460         if (sdl->sdl_family != AF_LINK) {
461                 xo_warnx("cannot intuit interface index and type for %s", host);
462                 return (1);
463         }
464         sdl_m.sdl_type = sdl->sdl_type;
465         sdl_m.sdl_index = sdl->sdl_index;
466         return (rtmsg(RTM_ADD));
467 }
468
469 /*
470  * Display an individual neighbor cache entry
471  */
472 static void
473 get(char *host)
474 {
475         struct sockaddr_in6 *sin = &sin_m;
476         struct addrinfo hints, *res;
477         int gai_error;
478
479         sin_m = blank_sin;
480         bzero(&hints, sizeof(hints));
481         hints.ai_family = AF_INET6;
482         gai_error = getaddrinfo(host, NULL, &hints, &res);
483         if (gai_error) {
484                 xo_warnx("%s: %s", host, gai_strerror(gai_error));
485                 return;
486         }
487         sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
488         sin->sin6_scope_id =
489             ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
490         dump(sin, 0);
491         if (found_entry == 0) {
492                 getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
493                     sizeof(host_buf), NULL ,0,
494                     (nflag ? NI_NUMERICHOST : 0));
495                 xo_errx(1, "%s (%s) -- no entry", host, host_buf);
496         }
497 }
498
499 /*
500  * Delete a neighbor cache entry
501  */
502 static int
503 delete(char *host)
504 {
505         struct sockaddr_in6 *sin = &sin_m;
506         register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
507         register char *cp = m_rtmsg.m_space;
508         struct sockaddr_dl *sdl;
509         struct addrinfo hints, *res;
510         int gai_error;
511
512         getsocket();
513         sin_m = blank_sin;
514
515         bzero(&hints, sizeof(hints));
516         hints.ai_family = AF_INET6;
517         gai_error = getaddrinfo(host, NULL, &hints, &res);
518         if (gai_error) {
519                 xo_warnx("%s: %s", host, gai_strerror(gai_error));
520                 return 1;
521         }
522         sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
523         sin->sin6_scope_id =
524             ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
525         if (rtmsg(RTM_GET) < 0) {
526                 xo_errx(1, "RTM_GET(%s) failed", host);
527                 /* NOTREACHED */
528         }
529         sin = (struct sockaddr_in6 *)(rtm + 1);
530         sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin);
531         if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
532                 if (sdl->sdl_family == AF_LINK &&
533                     !(rtm->rtm_flags & RTF_GATEWAY)) {
534                         goto delete;
535                 }
536                 xo_warnx("delete: cannot delete non-NDP entry");
537                 return 1;
538         }
539
540 delete:
541         if (sdl->sdl_family != AF_LINK) {
542                 xo_warnx("cannot locate %s", host);
543                 return (1);
544         }
545         /*
546          * need to reinit the field because it has rt_key
547          * but we want the actual address
548          */
549         NEXTADDR(RTA_DST, sin_m);
550         rtm->rtm_flags |= RTF_LLDATA;
551         if (rtmsg(RTM_DELETE) == 0) {
552                 getnameinfo((struct sockaddr *)sin,
553                     sin->sin6_len, host_buf,
554                     sizeof(host_buf), NULL, 0,
555                     (nflag ? NI_NUMERICHOST : 0));
556                 xo_open_instance("neighbor-cache");
557
558                 char *ifname = if_indextoname(sdl->sdl_index, ifix_buf);
559                 if (ifname == NULL) {
560                         strlcpy(ifix_buf, "?", sizeof(ifix_buf));
561                         ifname = ifix_buf;
562                 }
563                 char abuf[INET6_ADDRSTRLEN];
564                 inet_ntop(AF_INET6, &sin->sin6_addr, abuf, sizeof(abuf));
565
566                 xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf);
567                 xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname);
568                 xo_close_instance("neighbor-cache");
569         }
570
571         return 0;
572 }
573
574 #define W_ADDR  36
575 #define W_LL    17
576 #define W_IF    6
577
578 /*
579  * Dump the entire neighbor cache
580  */
581 static void
582 dump(struct sockaddr_in6 *addr, int cflag)
583 {
584         int mib[6];
585         size_t needed;
586         char *lim, *buf, *next;
587         struct rt_msghdr *rtm;
588         struct sockaddr_in6 *sin;
589         struct sockaddr_dl *sdl;
590         struct timeval now;
591         time_t expire;
592         int addrwidth;
593         int llwidth;
594         int ifwidth;
595         char flgbuf[8];
596         char *ifname;
597
598         /* Print header */
599         if (!tflag && !cflag) {
600                 char xobuf[200];
601                 snprintf(xobuf, sizeof(xobuf),
602                     "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n",
603                     W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF);
604                 xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags");
605         }
606         xo_open_list("neighbor-cache");
607 again:;
608         mib[0] = CTL_NET;
609         mib[1] = PF_ROUTE;
610         mib[2] = 0;
611         mib[3] = AF_INET6;
612         mib[4] = NET_RT_FLAGS;
613 #ifdef RTF_LLINFO
614         mib[5] = RTF_LLINFO;
615 #else
616         mib[5] = 0;
617 #endif
618         if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
619                 xo_err(1, "sysctl(PF_ROUTE estimate)");
620         if (needed > 0) {
621                 if ((buf = malloc(needed)) == NULL)
622                         xo_err(1, "malloc");
623                 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
624                         xo_err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
625                 lim = buf + needed;
626         } else
627                 buf = lim = NULL;
628
629         for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
630                 int isrouter = 0, prbs = 0;
631
632                 rtm = (struct rt_msghdr *)next;
633                 sin = (struct sockaddr_in6 *)(rtm + 1);
634                 sdl = (struct sockaddr_dl *)((char *)sin +
635                     ALIGN(sin->sin6_len));
636
637                 /*
638                  * Some OSes can produce a route that has the LINK flag but
639                  * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
640                  * and BSD/OS, where xx is not the interface identifier on
641                  * lo0).  Such routes entry would annoy getnbrinfo() below,
642                  * so we skip them.
643                  * XXX: such routes should have the GATEWAY flag, not the
644                  * LINK flag.  However, there is rotten routing software
645                  * that advertises all routes that have the GATEWAY flag.
646                  * Thus, KAME kernel intentionally does not set the LINK flag.
647                  * What is to be fixed is not ndp, but such routing software
648                  * (and the kernel workaround)...
649                  */
650                 if (sdl->sdl_family != AF_LINK)
651                         continue;
652
653                 if (!(rtm->rtm_flags & RTF_HOST))
654                         continue;
655
656                 if (addr) {
657                         if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
658                             &sin->sin6_addr) == 0 ||
659                             addr->sin6_scope_id != sin->sin6_scope_id)
660                                 continue;
661                         found_entry = 1;
662                 } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
663                         continue;
664                 if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
665                     IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
666                         /* XXX: should scope id be filled in the kernel? */
667                         if (sin->sin6_scope_id == 0)
668                                 sin->sin6_scope_id = sdl->sdl_index;
669                 }
670                 getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
671                     sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0));
672                 if (cflag) {
673 #ifdef RTF_WASCLONED
674                         if (rtm->rtm_flags & RTF_WASCLONED)
675                                 delete(host_buf);
676 #elif defined(RTF_CLONED)
677                         if (rtm->rtm_flags & RTF_CLONED)
678                                 delete(host_buf);
679 #else
680                         if (rtm->rtm_flags & RTF_PINNED)
681                                 continue;
682                         delete(host_buf);
683 #endif
684                         continue;
685                 }
686                 gettimeofday(&now, 0);
687                 if (tflag)
688                         ts_print(&now);
689
690                 addrwidth = strlen(host_buf);
691                 if (addrwidth < W_ADDR)
692                         addrwidth = W_ADDR;
693                 llwidth = strlen(ether_str(sdl));
694                 if (W_ADDR + W_LL - addrwidth > llwidth)
695                         llwidth = W_ADDR + W_LL - addrwidth;
696                 ifname = if_indextoname(sdl->sdl_index, ifix_buf);
697                 if (ifname == NULL) {
698                         strlcpy(ifix_buf, "?", sizeof(ifix_buf));
699                         ifname = ifix_buf;
700                 }
701                 ifwidth = strlen(ifname);
702                 if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
703                         ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
704
705                 xo_open_instance("neighbor-cache");
706                 /* Compose format string for libxo, as it doesn't support *.* */
707                 char xobuf[200];
708                 snprintf(xobuf, sizeof(xobuf),
709                     "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}",
710                     addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth);
711                 xo_emit(xobuf, host_buf, ether_str(sdl), ifname);
712
713                 /* Print neighbor discovery specific information */
714                 expire = rtm->rtm_rmx.rmx_expire;
715                 int expire_in = expire - now.tv_sec;
716                 if (expire > now.tv_sec)
717                         xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in);
718                 else if (expire == 0)
719                         xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent");
720                 else
721                         xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in);
722
723                 char *lle_state = "";
724                 switch (rtm->rtm_rmx.rmx_state) {
725                 case ND6_LLINFO_NOSTATE:
726                         lle_state = "N";
727                         break;
728 #ifdef ND6_LLINFO_WAITDELETE
729                 case ND6_LLINFO_WAITDELETE:
730                         lle_state = "W";
731                         break;
732 #endif
733                 case ND6_LLINFO_INCOMPLETE:
734                         lle_state = "I";
735                         break;
736                 case ND6_LLINFO_REACHABLE:
737                         lle_state = "R";
738                         break;
739                 case ND6_LLINFO_STALE:
740                         lle_state = "S";
741                         break;
742                 case ND6_LLINFO_DELAY:
743                         lle_state = "D";
744                         break;
745                 case ND6_LLINFO_PROBE:
746                         lle_state = "P";
747                         break;
748                 default:
749                         lle_state = "?";
750                         break;
751                 }
752                 xo_emit(" {:neighbor-state/%s}", lle_state);
753
754                 isrouter = rtm->rtm_flags & RTF_GATEWAY;
755                 prbs = rtm->rtm_rmx.rmx_pksent;
756
757                 /*
758                  * other flags. R: router, P: proxy, W: ??
759                  */
760                 if ((rtm->rtm_addrs & RTA_NETMASK) == 0) {
761                         snprintf(flgbuf, sizeof(flgbuf), "%s%s",
762                             isrouter ? "R" : "",
763                             (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
764                 } else {
765 #if 0                   /* W and P are mystery even for us */
766                         sin = (struct sockaddr_in6 *)
767                             (sdl->sdl_len + (char *)sdl);
768                         snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s",
769                             isrouter ? "R" : "",
770                             !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "P" : "",
771                             (sin->sin6_len != sizeof(struct sockaddr_in6)) ? "W" : "",
772                             (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
773 #else
774                         snprintf(flgbuf, sizeof(flgbuf), "%s%s",
775                             isrouter ? "R" : "",
776                             (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
777 #endif
778                 }
779                 xo_emit(" {:nd-flags/%s}", flgbuf);
780
781                 if (prbs)
782                         xo_emit("{d:/ %d}", prbs);
783
784                 xo_emit("\n");
785                 xo_close_instance("neighbor-cache");
786         }
787         if (buf != NULL)
788                 free(buf);
789
790         if (repeat) {
791                 xo_emit("\n");
792                 xo_flush();
793                 sleep(repeat);
794                 goto again;
795         }
796
797         xo_close_list("neighbor-cache");
798 }
799
800 static struct in6_nbrinfo *
801 getnbrinfo(struct in6_addr *addr, int ifindex, int warning)
802 {
803         static struct in6_nbrinfo nbi;
804         int sock;
805
806         if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
807                 xo_err(1, "socket");
808
809         bzero(&nbi, sizeof(nbi));
810         if_indextoname(ifindex, nbi.ifname);
811         nbi.addr = *addr;
812         if (ioctl(sock, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
813                 if (warning)
814                         xo_warn("ioctl(SIOCGNBRINFO_IN6)");
815                 close(sock);
816                 return(NULL);
817         }
818
819         close(sock);
820         return(&nbi);
821 }
822
823 static char *
824 ether_str(struct sockaddr_dl *sdl)
825 {
826         static char hbuf[NI_MAXHOST];
827
828         if (sdl->sdl_alen == ETHER_ADDR_LEN) {
829                 strlcpy(hbuf, ether_ntoa((struct ether_addr *)LLADDR(sdl)),
830                     sizeof(hbuf));
831         } else if (sdl->sdl_alen) {
832                 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
833                 snprintf(hbuf, sizeof(hbuf), "%s", link_ntoa(sdl) + n);
834         } else
835                 snprintf(hbuf, sizeof(hbuf), "(incomplete)");
836
837         return(hbuf);
838 }
839
840 static int
841 ndp_ether_aton(char *a, u_char *n)
842 {
843         int i, o[6];
844
845         i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
846             &o[3], &o[4], &o[5]);
847         if (i != 6) {
848                 xo_warnx("invalid Ethernet address '%s'", a);
849                 return (1);
850         }
851         for (i = 0; i < 6; i++)
852                 n[i] = o[i];
853         return (0);
854 }
855
856 static void
857 usage()
858 {
859         printf("usage: ndp [-nt] hostname\n");
860         printf("       ndp [-nt] -a | -c | -p | -r | -H | -P | -R\n");
861         printf("       ndp [-nt] -A wait\n");
862         printf("       ndp [-nt] -d hostname\n");
863         printf("       ndp [-nt] -f filename\n");
864         printf("       ndp [-nt] -i interface [flags...]\n");
865 #ifdef SIOCSDEFIFACE_IN6
866         printf("       ndp [-nt] -I [interface|delete]\n");
867 #endif
868         printf("       ndp [-nt] -s nodename etheraddr [temp] [proxy]\n");
869         exit(1);
870 }
871
872 static int
873 rtmsg(int cmd)
874 {
875         static int seq;
876         int rlen;
877         register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
878         register char *cp = m_rtmsg.m_space;
879         register int l;
880
881         errno = 0;
882         if (cmd == RTM_DELETE)
883                 goto doit;
884         bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
885         rtm->rtm_flags = flags;
886         rtm->rtm_version = RTM_VERSION;
887
888         switch (cmd) {
889         default:
890                 xo_errx(1, "internal wrong cmd");
891         case RTM_ADD:
892                 rtm->rtm_addrs |= RTA_GATEWAY;
893                 if (expire_time) {
894                         rtm->rtm_rmx.rmx_expire = expire_time;
895                         rtm->rtm_inits = RTV_EXPIRE;
896                 }
897                 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
898                 /* FALLTHROUGH */
899         case RTM_GET:
900                 rtm->rtm_addrs |= RTA_DST;
901         }
902
903         NEXTADDR(RTA_DST, sin_m);
904         NEXTADDR(RTA_GATEWAY, sdl_m);
905
906         rtm->rtm_msglen = cp - (char *)&m_rtmsg;
907 doit:
908         l = rtm->rtm_msglen;
909         rtm->rtm_seq = ++seq;
910         rtm->rtm_type = cmd;
911         if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
912                 if (errno != ESRCH || cmd != RTM_DELETE) {
913                         xo_err(1, "writing to routing socket");
914                         /* NOTREACHED */
915                 }
916         }
917         do {
918                 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
919         } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq ||
920             rtm->rtm_pid != pid));
921         if (l < 0)
922                 xo_warn("read from routing socket");
923         return (0);
924 }
925
926 static void
927 ifinfo(char *ifname, int argc, char **argv)
928 {
929         struct in6_ndireq nd;
930         int i, sock;
931         u_int32_t newflags;
932 #ifdef IPV6CTL_USETEMPADDR
933         u_int8_t nullbuf[8];
934 #endif
935
936         if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
937                 xo_err(1, "socket");
938                 /* NOTREACHED */
939         }
940         bzero(&nd, sizeof(nd));
941         strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
942         if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
943                 xo_err(1, "ioctl(SIOCGIFINFO_IN6)");
944                 /* NOTREACHED */
945         }
946 #define ND nd.ndi
947         newflags = ND.flags;
948         for (i = 0; i < argc; i++) {
949                 int clear = 0;
950                 char *cp = argv[i];
951
952                 if (*cp == '-') {
953                         clear = 1;
954                         cp++;
955                 }
956
957 #define SETFLAG(s, f) do {                      \
958         if (strcmp(cp, (s)) == 0) {             \
959                 if (clear)                      \
960                         newflags &= ~(f);       \
961                 else                            \
962                         newflags |= (f);        \
963         }                                       \
964 } while (0)
965 /*
966  * XXX: this macro is not 100% correct, in that it matches "nud" against
967  *      "nudbogus".  But we just let it go since this is minor.
968  */
969 #define SETVALUE(f, v) do {                                             \
970         char *valptr;                                                   \
971         unsigned long newval;                                           \
972         v = 0; /* unspecified */                                        \
973         if (strncmp(cp, f, strlen(f)) == 0) {                           \
974                 valptr = strchr(cp, '=');                               \
975                 if (valptr == NULL)                                     \
976                         xo_err(1, "syntax error in %s field", (f));     \
977                 errno = 0;                                              \
978                 newval = strtoul(++valptr, NULL, 0);                    \
979                 if (errno)                                              \
980                         xo_err(1, "syntax error in %s's value", (f));   \
981                 v = newval;                                             \
982         }                                                               \
983 } while (0)
984
985                 SETFLAG("disabled", ND6_IFF_IFDISABLED);
986                 SETFLAG("nud", ND6_IFF_PERFORMNUD);
987 #ifdef ND6_IFF_ACCEPT_RTADV
988                 SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV);
989 #endif
990 #ifdef ND6_IFF_AUTO_LINKLOCAL
991                 SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL);
992 #endif
993 #ifdef ND6_IFF_NO_PREFER_IFACE
994                 SETFLAG("no_prefer_iface", ND6_IFF_NO_PREFER_IFACE);
995 #endif
996                 SETVALUE("basereachable", ND.basereachable);
997                 SETVALUE("retrans", ND.retrans);
998                 SETVALUE("curhlim", ND.chlim);
999
1000                 ND.flags = newflags;
1001                 if (ioctl(sock, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) {
1002                         xo_err(1, "ioctl(SIOCSIFINFO_IN6)");
1003                         /* NOTREACHED */
1004                 }
1005 #undef SETFLAG
1006 #undef SETVALUE
1007         }
1008
1009         if (!ND.initialized) {
1010                 xo_errx(1, "%s: not initialized yet", ifname);
1011                 /* NOTREACHED */
1012         }
1013
1014         if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
1015                 xo_err(1, "ioctl(SIOCGIFINFO_IN6)");
1016                 /* NOTREACHED */
1017         }
1018         xo_open_container("ifinfo");
1019
1020         xo_emit("{e:interface/%s}", ifname);
1021         xo_emit("linkmtu={:linkmtu/%d}", ND.linkmtu);
1022         xo_emit(", maxmtu={:maxmtu/%d}", ND.maxmtu);
1023         xo_emit(", curhlim={:curhlim/%d}", ND.chlim);
1024         xo_emit("{d:/, basereachable=%ds%dms}{e:basereachable_ms/%u}",
1025             ND.basereachable / 1000, ND.basereachable % 1000, ND.basereachable);
1026         xo_emit("{d:/, reachable=%ds}{e:reachable_ms/%u}", ND.reachable, ND.reachable * 1000);
1027         xo_emit("{d:/, retrans=%ds%dms}{e:retrans_ms/%u}", ND.retrans / 1000, ND.retrans % 1000,
1028             ND.retrans);
1029 #ifdef IPV6CTL_USETEMPADDR
1030         memset(nullbuf, 0, sizeof(nullbuf));
1031         if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) {
1032                 int j;
1033                 u_int8_t *rbuf;
1034
1035                 for (i = 0; i < 3; i++) {
1036                         const char *txt, *field;
1037                         switch (i) {
1038                         case 0:
1039                                 txt = "\nRandom seed(0): ";
1040                                 field = "seed_0";
1041                                 rbuf = ND.randomseed0;
1042                                 break;
1043                         case 1:
1044                                 txt = "\nRandom seed(1): ";
1045                                 field = "seed_1";
1046                                 rbuf = ND.randomseed1;
1047                                 break;
1048                         case 2:
1049                                 txt = "\nRandom ID:      ";
1050                                 field = "random_id";
1051                                 rbuf = ND.randomid;
1052                                 break;
1053                         default:
1054                                 xo_errx(1, "impossible case for tempaddr display");
1055                         }
1056                         char abuf[20], xobuf[200];
1057                         for (j = 0; j < 8; j++)
1058                                 snprintf(&abuf[j * 2], sizeof(abuf), "%02X", rbuf[j]);
1059                         snprintf(xobuf, sizeof(xobuf), "%s{:%s/%%s}", txt, field);
1060                         xo_emit(xobuf, abuf);
1061                 }
1062         }
1063 #endif /* IPV6CTL_USETEMPADDR */
1064         if (ND.flags) {
1065                 xo_emit("\nFlags: {e:flags/%u}", ND.flags);
1066                 xo_open_list("flags_pretty");
1067 #ifdef ND6_IFF_IFDISABLED
1068                 if ((ND.flags & ND6_IFF_IFDISABLED))
1069                         xo_emit("{l:%s} ", "disabled");
1070 #endif
1071                 if ((ND.flags & ND6_IFF_PERFORMNUD))
1072                         xo_emit("{l:%s} ", "nud");
1073 #ifdef ND6_IFF_ACCEPT_RTADV
1074                 if ((ND.flags & ND6_IFF_ACCEPT_RTADV))
1075                         xo_emit("{l:%s} ", "accept_rtadv");
1076 #endif
1077 #ifdef ND6_IFF_AUTO_LINKLOCAL
1078                 if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL))
1079                         xo_emit("{l:%s} ", "auto_linklocal");
1080 #endif
1081 #ifdef ND6_IFF_NO_PREFER_IFACE
1082                 if ((ND.flags & ND6_IFF_NO_PREFER_IFACE))
1083                         xo_emit("{l:%s} ", "no_prefer_iface");
1084 #endif
1085                 xo_close_list("flags");
1086         }
1087         xo_emit("\n");
1088 #undef ND
1089         xo_close_container("ifinfo");
1090
1091         close(sock);
1092 }
1093
1094 #ifndef ND_RA_FLAG_RTPREF_MASK  /* XXX: just for compilation on *BSD release */
1095 #define ND_RA_FLAG_RTPREF_MASK  0x18 /* 00011000 */
1096 #endif
1097
1098 static void
1099 rtrlist()
1100 {
1101         int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST };
1102         char *buf;
1103         struct in6_defrouter *p, *ep;
1104         size_t l;
1105         struct timeval now;
1106
1107         if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
1108                 xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
1109                 /*NOTREACHED*/
1110         }
1111         if (l == 0)
1112                 return;
1113         buf = malloc(l);
1114         if (!buf) {
1115                 xo_err(1, "malloc");
1116                 /*NOTREACHED*/
1117         }
1118         if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
1119                 xo_err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
1120                 /*NOTREACHED*/
1121         }
1122
1123         xo_open_list("router-list");
1124
1125         ep = (struct in6_defrouter *)(buf + l);
1126         for (p = (struct in6_defrouter *)buf; p < ep; p++) {
1127                 int rtpref;
1128                 char abuf[INET6_ADDRSTRLEN], *paddr;
1129
1130                 if (getnameinfo((struct sockaddr *)&p->rtaddr,
1131                     p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0,
1132                     (nflag ? NI_NUMERICHOST : 0)) != 0)
1133                         strlcpy(host_buf, "?", sizeof(host_buf));
1134                 if (nflag)
1135                         paddr = host_buf;
1136                 else {
1137                         inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, abuf, sizeof(abuf));
1138                         paddr = abuf;
1139                 }
1140
1141                 xo_open_instance("router-list");
1142                 xo_emit("{:hostname/%s}{e:address/%s} if={:interface/%s}",
1143                     host_buf, paddr,
1144                     if_indextoname(p->if_index, ifix_buf));
1145                 xo_open_list("flags_pretty");
1146                 char rflags[6] = {}, *pflags = rflags;
1147                 if (p->flags & ND_RA_FLAG_MANAGED) {
1148                         *pflags++ = 'M';
1149                         xo_emit("{el:%s}", "managed");
1150                 }
1151                 if (p->flags & ND_RA_FLAG_OTHER) {
1152                         *pflags++ = 'O';
1153                         xo_emit("{el:%s}", "other");
1154                 }
1155 #ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG
1156                 if (p->flags & ND_RA_FLAG_IPV6_ONLY) {
1157                         *pflags++ = 'S';
1158                         xo_emit("{el:%s}", "ipv6only");
1159                 }
1160 #endif
1161                 xo_close_list("flags_pretty");
1162                 xo_emit(", flags={:flags/%s}", rflags);
1163
1164                 rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff;
1165                 xo_emit(", pref={:preference/%s}", rtpref_str[rtpref]);
1166
1167                 gettimeofday(&now, 0);
1168                 if (p->expire == 0)
1169                         xo_emit(", expire=Never\n{en:permanent/true}");
1170                 else
1171                         xo_emit("{d:/, expire=%s\n}{e:expires_sec/%ld}",
1172                             sec2str(p->expire - now.tv_sec),
1173                             (long)p->expire - now.tv_sec);
1174                 xo_close_instance("router-list");
1175         }
1176         free(buf);
1177         xo_close_list("router-list");
1178 }
1179
1180 static void
1181 plist()
1182 {
1183         int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST };
1184         char *buf;
1185         struct in6_prefix *p, *ep, *n;
1186         struct sockaddr_in6 *advrtr;
1187         size_t l;
1188         struct timeval now;
1189         const int niflags = NI_NUMERICHOST;
1190         int ninflags = nflag ? NI_NUMERICHOST : 0;
1191         char namebuf[NI_MAXHOST];
1192
1193         if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
1194                 xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
1195                 /*NOTREACHED*/
1196         }
1197         buf = malloc(l);
1198         if (!buf) {
1199                 xo_err(1, "malloc");
1200                 /*NOTREACHED*/
1201         }
1202         if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
1203                 xo_err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
1204                 /*NOTREACHED*/
1205         }
1206
1207         xo_open_list("prefix-list");
1208
1209         ep = (struct in6_prefix *)(buf + l);
1210         for (p = (struct in6_prefix *)buf; p < ep; p = n) {
1211                 advrtr = (struct sockaddr_in6 *)(p + 1);
1212                 n = (struct in6_prefix *)&advrtr[p->advrtrs];
1213
1214                 xo_open_instance("prefix-list");
1215                 if (getnameinfo((struct sockaddr *)&p->prefix,
1216                     p->prefix.sin6_len, namebuf, sizeof(namebuf),
1217                     NULL, 0, niflags) != 0)
1218                         strlcpy(namebuf, "?", sizeof(namebuf));
1219                 xo_emit("{:prefix/%s%s%d} if={:interface/%s}\n", namebuf, "/",
1220                     p->prefixlen, if_indextoname(p->if_index, ifix_buf));
1221
1222                 gettimeofday(&now, 0);
1223                 /*
1224                  * meaning of fields, especially flags, is very different
1225                  * by origin.  notify the difference to the users.
1226                  */
1227                 char flags[10] = {}, *pflags = flags;
1228                 xo_open_list("flags_pretty");
1229                 if (p->raflags.onlink) {
1230                         *pflags++ = 'L';
1231                         xo_emit("{el:%s}", "ra_onlink");
1232                 }
1233                 if (p->raflags.autonomous) {
1234                         *pflags++ = 'A';
1235                         xo_emit("{el:%s}", "ra_autonomous");
1236                 }
1237                 if (p->flags & NDPRF_ONLINK) {
1238                         *pflags++ = 'O';
1239                         xo_emit("{el:%s}", "is_onlink");
1240                 }
1241                 if (p->flags & NDPRF_DETACHED) {
1242                         *pflags++ = 'D';
1243                         xo_emit("{el:%s}", "is_detached");
1244                 }
1245 #ifdef NDPRF_HOME
1246                 if (p->flags & NDPRF_HOME) {
1247                         *pflags++ = 'H';
1248                         xo_emit("{el:%s}", "is_home");
1249                 }
1250 #endif
1251                 xo_close_list("flags_pretty");
1252                 xo_emit("flags={:flags/%s}", flags);
1253                 int expire_in = p->expire - now.tv_sec;
1254
1255                 if (p->vltime == ND6_INFINITE_LIFETIME)
1256                         xo_emit(" vltime=infinity{e:valid-lifetime/%lu}",
1257                             (unsigned long)p->vltime);
1258                 else
1259                         xo_emit(" vltime={:valid-lifetime/%lu}",
1260                             (unsigned long)p->vltime);
1261                 if (p->pltime == ND6_INFINITE_LIFETIME)
1262                         xo_emit(", pltime=infinity{e:preferred-lifetime/%lu}",
1263                             (unsigned long)p->pltime);
1264                 else
1265                         xo_emit(", pltime={:preferred-lifetime/%lu}",
1266                             (unsigned long)p->pltime);
1267                 if (p->expire == 0)
1268                         xo_emit(", expire=Never{en:permanent/true}");
1269                 else if (p->expire >= now.tv_sec)
1270                         xo_emit(", expire=%s{e:expires_sec/%d}",
1271                             sec2str(expire_in), expire_in);
1272                 else
1273                         xo_emit(", expired{e:expires_sec/%d}", expire_in);
1274                 xo_emit(", ref={:refcount/%d}", p->refcnt);
1275                 xo_emit("\n");
1276                 /*
1277                  * "advertising router" list is meaningful only if the prefix
1278                  * information is from RA.
1279                  */
1280                 if (p->advrtrs) {
1281                         int j;
1282                         struct sockaddr_in6 *sin6;
1283
1284                         sin6 = advrtr;
1285                         xo_emit("  advertised by\n");
1286                         xo_open_list("advertising-routers");
1287                         for (j = 0; j < p->advrtrs; j++) {
1288                                 struct in6_nbrinfo *nbi;
1289
1290                                 xo_open_instance("advertising-routers");
1291                                 if (getnameinfo((struct sockaddr *)sin6,
1292                                     sin6->sin6_len, namebuf, sizeof(namebuf),
1293                                     NULL, 0, ninflags) != 0)
1294                                         strlcpy(namebuf, "?", sizeof(namebuf));
1295                                 char abuf[INET6_ADDRSTRLEN];
1296                                 inet_ntop(AF_INET6, &sin6->sin6_addr, abuf,
1297                                     sizeof(abuf));
1298
1299                                 xo_emit("    {:hostname/%s}{e:address/%s}",
1300                                     namebuf, abuf);
1301
1302                                 nbi = getnbrinfo(&sin6->sin6_addr,
1303                                     p->if_index, 0);
1304                                 const char *state = "";
1305                                 if (nbi) {
1306                                         switch (nbi->state) {
1307                                         case ND6_LLINFO_REACHABLE:
1308                                         case ND6_LLINFO_STALE:
1309                                         case ND6_LLINFO_DELAY:
1310                                         case ND6_LLINFO_PROBE:
1311                                                 state = "reachable";
1312                                                 break;
1313                                         default:
1314                                                 state = "unreachable";
1315                                         }
1316                                 } else
1317                                         state = "no neighbor state";
1318                                 xo_emit(" ({:state/%s})\n", state);
1319                                 sin6++;
1320                                 xo_close_instance("advertising-routers");
1321                         }
1322                         xo_close_list("advertising-routers");
1323                 } else
1324                         xo_emit("  No advertising router\n");
1325                 xo_close_instance("prefix-list");
1326         }
1327         free(buf);
1328
1329         xo_close_list("prefix-list");
1330 }
1331
1332 static void
1333 pfx_flush()
1334 {
1335         char dummyif[IFNAMSIZ+8];
1336         int sock;
1337
1338         if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1339                 xo_err(1, "socket");
1340         strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
1341         if (ioctl(sock, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
1342                 xo_err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
1343
1344         close(sock);
1345 }
1346
1347 static void
1348 rtr_flush()
1349 {
1350         char dummyif[IFNAMSIZ+8];
1351         int sock;
1352
1353         if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1354                 xo_err(1, "socket");
1355         strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
1356         if (ioctl(sock, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
1357                 xo_err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
1358
1359         close(sock);
1360 }
1361
1362 static void
1363 harmonize_rtr()
1364 {
1365         char dummyif[IFNAMSIZ+8];
1366         int sock;
1367
1368         if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1369                 xo_err(1, "socket");
1370         strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
1371         if (ioctl(sock, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0)
1372                 xo_err(1, "ioctl(SIOCSNDFLUSH_IN6)");
1373
1374         close(sock);
1375 }
1376
1377 #ifdef SIOCSDEFIFACE_IN6        /* XXX: check SIOCGDEFIFACE_IN6 as well? */
1378 static void
1379 setdefif(char *ifname)
1380 {
1381         struct in6_ndifreq ndifreq;
1382         unsigned int ifindex;
1383         int sock;
1384
1385         if (strcasecmp(ifname, "delete") == 0)
1386                 ifindex = 0;
1387         else {
1388                 if ((ifindex = if_nametoindex(ifname)) == 0)
1389                         xo_err(1, "failed to resolve i/f index for %s", ifname);
1390         }
1391
1392         if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1393                 xo_err(1, "socket");
1394
1395         strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
1396         ndifreq.ifindex = ifindex;
1397
1398         if (ioctl(sock, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1399                 xo_err(1, "ioctl(SIOCSDEFIFACE_IN6)");
1400
1401         close(sock);
1402 }
1403
1404 static void
1405 getdefif()
1406 {
1407         struct in6_ndifreq ndifreq;
1408         char ifname[IFNAMSIZ+8];
1409         int sock;
1410
1411         if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1412                 xo_err(1, "socket");
1413
1414         memset(&ndifreq, 0, sizeof(ndifreq));
1415         strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
1416
1417         if (ioctl(sock, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1418                 xo_err(1, "ioctl(SIOCGDEFIFACE_IN6)");
1419
1420         if (ndifreq.ifindex == 0)
1421                 xo_emit("No default interface.\n");
1422         else {
1423                 if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL)
1424                         xo_err(1, "failed to resolve ifname for index %lu",
1425                             ndifreq.ifindex);
1426                 xo_emit("ND default interface = {:default-interface/%s}\n", ifname);
1427         }
1428
1429         close(sock);
1430 }
1431 #endif /* SIOCSDEFIFACE_IN6 */
1432
1433 static char *
1434 sec2str(time_t total)
1435 {
1436         static char result[256];
1437         int days, hours, mins, secs;
1438         int first = 1;
1439         char *p = result;
1440         char *ep = &result[sizeof(result)];
1441         int n;
1442
1443         days = total / 3600 / 24;
1444         hours = (total / 3600) % 24;
1445         mins = (total / 60) % 60;
1446         secs = total % 60;
1447
1448         if (days) {
1449                 first = 0;
1450                 n = snprintf(p, ep - p, "%dd", days);
1451                 if (n < 0 || n >= ep - p)
1452                         return "?";
1453                 p += n;
1454         }
1455         if (!first || hours) {
1456                 first = 0;
1457                 n = snprintf(p, ep - p, "%dh", hours);
1458                 if (n < 0 || n >= ep - p)
1459                         return "?";
1460                 p += n;
1461         }
1462         if (!first || mins) {
1463                 first = 0;
1464                 n = snprintf(p, ep - p, "%dm", mins);
1465                 if (n < 0 || n >= ep - p)
1466                         return "?";
1467                 p += n;
1468         }
1469         snprintf(p, ep - p, "%ds", secs);
1470
1471         return(result);
1472 }
1473
1474 /*
1475  * Print the timestamp
1476  * from tcpdump/util.c
1477  */
1478 static void
1479 ts_print(const struct timeval *tvp)
1480 {
1481         int sec;
1482
1483         /* Default */
1484         sec = (tvp->tv_sec + thiszone) % 86400;
1485         xo_emit("{:tv_sec/%lld}{:tv_usec/%lld}%02d:%02d:%02d.%06u ",
1486             tvp->tv_sec, tvp->tv_usec,
1487             sec / 3600, (sec % 3600) / 60, sec % 60, (u_int32_t)tvp->tv_usec);
1488 }
1489
1490 #undef NEXTADDR