Home | History | Annotate | Line # | Download | only in ndp
ndp.c revision 1.1
      1 /*
      2  * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. Neither the name of the project nor the names of its contributors
     14  *    may be used to endorse or promote products derived from this software
     15  *    without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
     18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
     21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  * SUCH DAMAGE.
     28  */
     29 /*
     30  * Copyright (c) 1984, 1993
     31  *	The Regents of the University of California.  All rights reserved.
     32  *
     33  * This code is derived from software contributed to Berkeley by
     34  * Sun Microsystems, Inc.
     35  *
     36  * Redistribution and use in source and binary forms, with or without
     37  * modification, are permitted provided that the following conditions
     38  * are met:
     39  * 1. Redistributions of source code must retain the above copyright
     40  *    notice, this list of conditions and the following disclaimer.
     41  * 2. Redistributions in binary form must reproduce the above copyright
     42  *    notice, this list of conditions and the following disclaimer in the
     43  *    documentation and/or other materials provided with the distribution.
     44  * 3. All advertising materials mentioning features or use of this software
     45  *    must display the following acknowledgement:
     46  *	This product includes software developed by the University of
     47  *	California, Berkeley and its contributors.
     48  * 4. Neither the name of the University nor the names of its contributors
     49  *    may be used to endorse or promote products derived from this software
     50  *    without specific prior written permission.
     51  *
     52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     62  * SUCH DAMAGE.
     63  */
     64 
     65 /*
     66  * Based on:
     67  * "@(#) Copyright (c) 1984, 1993\n\
     68  *	The Regents of the University of California.  All rights reserved.\n";
     69  *
     70  * "@(#)arp.c	8.2 (Berkeley) 1/2/94";
     71  */
     72 
     73 /*
     74  * ndp - display, set, delete and flush neighbor cache
     75  */
     76 
     77 
     78 #include <sys/param.h>
     79 #include <sys/file.h>
     80 #include <sys/ioctl.h>
     81 #include <sys/socket.h>
     82 #include <sys/sysctl.h>
     83 #include <sys/time.h>
     84 
     85 #include <net/if.h>
     86 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
     87 #include <net/if_var.h>
     88 #endif /* __FreeBSD__ >= 3 */
     89 #include <net/if_dl.h>
     90 #include <net/if_types.h>
     91 #include <net/route.h>
     92 
     93 #include <netinet/in.h>
     94 #ifndef __NetBSD__
     95 #include <netinet/if_ether.h>
     96 #endif
     97 
     98 #include <netinet/icmp6.h>
     99 #include <netinet6/in6_var.h>
    100 #include <netinet6/nd6.h>
    101 
    102 #include <arpa/inet.h>
    103 
    104 #include <netdb.h>
    105 #include <errno.h>
    106 #include <nlist.h>
    107 #include <stdio.h>
    108 #include <string.h>
    109 #include <paths.h>
    110 #include <err.h>
    111 #include <stdlib.h>
    112 #include <fcntl.h>
    113 #include <unistd.h>
    114 #include "gmt2local.h"
    115 
    116 extern int errno;
    117 static int pid;
    118 static int fflag;
    119 static int nflag;
    120 static int tflag;
    121 static int32_t thiszone;	/* time difference with gmt */
    122 static int s = -1;
    123 static int repeat = 0;
    124 
    125 char ntop_buf[INET6_ADDRSTRLEN];	/* inet_ntop() */
    126 char ifix_buf[IFNAMSIZ];		/* if_indextoname() */
    127 
    128 int main __P((int, char **));
    129 int file __P((char *));
    130 void getsocket __P((void));
    131 int set __P((int, char **));
    132 void get __P((char *));
    133 int delete __P((char *));
    134 void dump __P((struct in6_addr *));
    135 static struct in6_nbrinfo *getnbrinfo __P((struct in6_addr *addr, int ifindex));
    136 static char *ether_str __P((struct sockaddr_dl *));
    137 int ndp_ether_aton __P((char *, u_char *));
    138 void usage __P((void));
    139 int rtmsg __P((int));
    140 void ifinfo __P((char *));
    141 void rtrlist __P((void));
    142 void plist __P((void));
    143 void pfx_flush __P((void));
    144 void rtr_flush __P((void));
    145 void harmonize_rtr __P((void));
    146 void quit __P((char *));
    147 static char *sec2str __P((time_t t));
    148 static char *ether_str __P((struct sockaddr_dl *sdl));
    149 static void ts_print __P((const struct timeval *));
    150 
    151 int
    152 main(argc, argv)
    153 	int argc;
    154 	char **argv;
    155 {
    156 	int ch;
    157 	int aflag = 0, cflag = 0, dflag = 0, sflag = 0, Hflag = 0,
    158 		pflag = 0, rflag = 0, Pflag = 0, Rflag = 0;
    159 	extern char *optarg;
    160 	extern int optind;
    161 
    162 	pid = getpid();
    163 	thiszone = gmt2local(0);
    164 	while ((ch = getopt(argc, argv, "acndfiprstA:HPR")) != EOF)
    165 		switch ((char)ch) {
    166 		case 'a':
    167 			aflag = 1;
    168 			break;
    169 		case 'c':
    170 			fflag = 1;
    171 			cflag = 1;
    172 			break;
    173 		case 'd':
    174 			dflag = 1;
    175 			break;
    176 		case 'i' :
    177 			if (argc != 3)
    178 				usage();
    179 			ifinfo(argv[2]);
    180 			exit(0);
    181 		case 'n':
    182 			nflag = 1;
    183 			continue;
    184 		case 'p':
    185 			pflag = 1;
    186 			break;
    187 		case 'f' :
    188 			if (argc != 3)
    189 				usage();
    190 			file(argv[2]);
    191 			exit(0);
    192 		case 'r' :
    193 			rflag = 1;
    194 			break;
    195 		case 's':
    196 			sflag = 1;
    197 			break;
    198 		case 't':
    199 			tflag = 1;
    200 			break;
    201 		case 'A':
    202 			aflag = 1;
    203 			repeat = atoi(optarg);
    204 			if (repeat < 0)
    205 				usage();
    206 			break;
    207 		case 'H' :
    208 			Hflag = 1;
    209 			break;
    210 		case 'P':
    211 			Pflag = 1;
    212 			break;
    213 		case 'R':
    214 			Rflag = 1;
    215 			break;
    216 		default:
    217 			usage();
    218 		}
    219 
    220 	argc -= optind;
    221 	argv += optind;
    222 
    223 	if (aflag || cflag) {
    224 		dump(0);
    225 		exit(0);
    226 	}
    227 	if (dflag) {
    228 		if (argc != 1)
    229 			usage();
    230 		delete(argv[0]);
    231 	}
    232 	if (pflag) {
    233 		plist();
    234 		exit(0);
    235 	}
    236 	if (rflag) {
    237 		rtrlist();
    238 		exit(0);
    239 	}
    240 	if (sflag) {
    241 		if (argc < 2 || argc > 4)
    242 			usage();
    243 		exit(set(argc, argv) ? 1 : 0);
    244 	}
    245 	if (Hflag) {
    246 		harmonize_rtr();
    247 		exit(0);
    248 	}
    249 	if (Pflag) {
    250 		pfx_flush();
    251 		exit(0);
    252 	}
    253 	if (Rflag) {
    254 		rtr_flush();
    255 		exit(0);
    256 	}
    257 
    258 	if (argc != 1)
    259 		usage();
    260 	get(argv[0]);
    261 	exit(0);
    262 }
    263 
    264 /*
    265  * Process a file to set standard ndp entries
    266  */
    267 int
    268 file(name)
    269 	char *name;
    270 {
    271 	FILE *fp;
    272 	int i, retval;
    273 	char line[100], arg[5][50], *args[5];
    274 
    275 	if ((fp = fopen(name, "r")) == NULL) {
    276 		fprintf(stderr, "ndp: cannot open %s\n", name);
    277 		exit(1);
    278 	}
    279 	args[0] = &arg[0][0];
    280 	args[1] = &arg[1][0];
    281 	args[2] = &arg[2][0];
    282 	args[3] = &arg[3][0];
    283 	args[4] = &arg[4][0];
    284 	retval = 0;
    285 	while(fgets(line, 100, fp) != NULL) {
    286 		i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2],
    287 		    arg[3], arg[4]);
    288 		if (i < 2) {
    289 			fprintf(stderr, "ndp: bad line: %s\n", line);
    290 			retval = 1;
    291 			continue;
    292 		}
    293 		if (set(i, args))
    294 			retval = 1;
    295 	}
    296 	fclose(fp);
    297 	return (retval);
    298 }
    299 
    300 void
    301 getsocket()
    302 {
    303 	if (s < 0) {
    304 		s = socket(PF_ROUTE, SOCK_RAW, 0);
    305 		if (s < 0) {
    306 			perror("ndp: socket");
    307 			exit(1);
    308 		}
    309 	}
    310 }
    311 
    312 struct	sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
    313 struct	sockaddr_in6 blank_sin = {sizeof(blank_sin), AF_INET6 }, sin_m;
    314 struct	sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
    315 int	expire_time, flags, found_entry;
    316 struct	{
    317 	struct	rt_msghdr m_rtm;
    318 	char	m_space[512];
    319 }	m_rtmsg;
    320 
    321 /*
    322  * Set an individual neighbor cache entry
    323  */
    324 int
    325 set(argc, argv)
    326 	int argc;
    327 	char **argv;
    328 {
    329 	register struct sockaddr_in6 *sin = &sin_m;
    330 	register struct sockaddr_dl *sdl;
    331 	register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
    332 	struct addrinfo hints, *res;
    333 	int gai_error;
    334 	u_char *ea;
    335 	char *host = argv[0], *eaddr = argv[1];
    336 
    337 	getsocket();
    338 	argc -= 2;
    339 	argv += 2;
    340 	sdl_m = blank_sdl;
    341 	sin_m = blank_sin;
    342 
    343 	bzero(&hints, sizeof(hints));
    344 	hints.ai_family = AF_INET6;
    345 	gai_error = getaddrinfo(host, NULL, &hints, &res);
    346 	if (gai_error) {
    347 		fprintf(stderr, "ndp: %s: %s\n", host,
    348 			gai_strerror(gai_error));
    349 		return 1;
    350 	}
    351 	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
    352 	ea = (u_char *)LLADDR(&sdl_m);
    353 	if (ndp_ether_aton(eaddr, ea) == 0)
    354 		sdl_m.sdl_alen = 6;
    355 	flags = expire_time = 0;
    356 	while (argc-- > 0) {
    357 		if (strncmp(argv[0], "temp", 4) == 0) {
    358 			struct timeval time;
    359 			gettimeofday(&time, 0);
    360 			expire_time = time.tv_sec + 20 * 60;
    361 		}
    362 		argv++;
    363 	}
    364 tryagain:
    365 	if (rtmsg(RTM_GET) < 0) {
    366 		perror(host);
    367 		return (1);
    368 	}
    369 	sin = (struct sockaddr_in6 *)(rtm + 1);
    370 	sdl = (struct sockaddr_dl *)(sin->sin6_len + (char *)sin);
    371 	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
    372 		if (sdl->sdl_family == AF_LINK &&
    373 		    (rtm->rtm_flags & RTF_LLINFO) &&
    374 		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
    375 		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
    376 		case IFT_ISO88024: case IFT_ISO88025:
    377 			goto overwrite;
    378 		}
    379 		goto tryagain;
    380 	}
    381 overwrite:
    382 	if (sdl->sdl_family != AF_LINK) {
    383 		printf("cannot intuit interface index and type for %s\n", host);
    384 		return (1);
    385 	}
    386 	sdl_m.sdl_type = sdl->sdl_type;
    387 	sdl_m.sdl_index = sdl->sdl_index;
    388 	return (rtmsg(RTM_ADD));
    389 }
    390 
    391 /*
    392  * Display an individual neighbor cache entry
    393  */
    394 void
    395 get(host)
    396 	char *host;
    397 {
    398 	struct sockaddr_in6 *sin = &sin_m;
    399 	struct addrinfo hints, *res;
    400 	int gai_error;
    401 
    402 	sin_m = blank_sin;
    403 	bzero(&hints, sizeof(hints));
    404 	hints.ai_family = AF_INET6;
    405 	gai_error = getaddrinfo(host, NULL, &hints, &res);
    406 	if (gai_error) {
    407 		fprintf(stderr, "ndp: %s: %s\n", host,
    408 			gai_strerror(gai_error));
    409 		return;
    410 	}
    411 	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
    412 	dump(&sin->sin6_addr);
    413 	if (found_entry == 0) {
    414 		printf("%s (%s) -- no entry\n",
    415 		    host, inet_ntop(AF_INET6, &sin->sin6_addr, ntop_buf,
    416 				sizeof(ntop_buf)));
    417 		exit(1);
    418 	}
    419 }
    420 
    421 /*
    422  * Delete a neighbor cache entry
    423  */
    424 int
    425 delete(host)
    426 	char *host;
    427 {
    428 	register struct sockaddr_in6 *sin = &sin_m;
    429 	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
    430 	struct sockaddr_dl *sdl;
    431 	struct addrinfo hints, *res;
    432 	int gai_error;
    433 
    434 	getsocket();
    435 	sin_m = blank_sin;
    436 
    437 	bzero(&hints, sizeof(hints));
    438 	hints.ai_family = AF_INET6;
    439 	gai_error = getaddrinfo(host, NULL, &hints, &res);
    440 	if (gai_error) {
    441 		fprintf(stderr, "ndp: %s: %s\n", host,
    442 			gai_strerror(gai_error));
    443 		return 1;
    444 	}
    445 	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
    446 /*tryagain:*/
    447 	if (rtmsg(RTM_GET) < 0) {
    448 		perror(host);
    449 		return (1);
    450 	}
    451 	sin = (struct sockaddr_in6 *)(rtm + 1);
    452 	sdl = (struct sockaddr_dl *)(sin->sin6_len + (char *)sin);
    453 	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
    454 		if (sdl->sdl_family == AF_LINK &&
    455 		    (rtm->rtm_flags & RTF_LLINFO) &&
    456 		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
    457 		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
    458 		case IFT_ISO88024: case IFT_ISO88025:
    459 			goto delete;
    460 		}
    461 	}
    462 delete:
    463 	if (sdl->sdl_family != AF_LINK) {
    464 		printf("cannot locate %s\n", host);
    465 		return (1);
    466 	}
    467 	if (rtmsg(RTM_DELETE) == 0)
    468 		printf("%s (%s) deleted\n", host,
    469 			inet_ntop(AF_INET6, &sin->sin6_addr, ntop_buf,
    470 					sizeof(ntop_buf)));
    471 	return 0;
    472 }
    473 
    474 /*
    475  * Dump the entire neighbor cache
    476  */
    477 void
    478 dump(addr)
    479 	struct in6_addr *addr;
    480 {
    481 	int mib[6];
    482 	size_t needed;
    483 	char *host, *lim, *buf, *next;
    484 	struct rt_msghdr *rtm;
    485 	struct sockaddr_in6 *sin;
    486 	struct sockaddr_dl *sdl;
    487 	extern int h_errno;
    488 	struct hostent *hp;
    489 	struct in6_nbrinfo *nbi;
    490 	struct timeval time;
    491 
    492 	/* Print header */
    493 	if (!tflag)
    494 		printf("%-29.29s %-18.18s %6.6s %-9.9s %2s %4s %4s\n",
    495 		       "Neighbor", "Linklayer Address", "Netif", "Expire",
    496 		       "St", "Flgs", "Prbs");
    497 
    498 again:;
    499 	mib[0] = CTL_NET;
    500 	mib[1] = PF_ROUTE;
    501 	mib[2] = 0;
    502 	mib[3] = AF_INET6;
    503 	mib[4] = NET_RT_FLAGS;
    504 	mib[5] = RTF_LLINFO;
    505 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
    506 		quit("route-sysctl-estimate");
    507 	if ((buf = malloc(needed)) == NULL)
    508 		quit("malloc");
    509 	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
    510 		quit("actual retrieval of routing table");
    511 	lim = buf + needed;
    512 
    513 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
    514 		int isrouter = 0, prbs = 0;
    515 
    516 		rtm = (struct rt_msghdr *)next;
    517 		sin = (struct sockaddr_in6 *)(rtm + 1);
    518 		sdl = (struct sockaddr_dl *)(sin + 1);
    519 		if (addr) {
    520 			if (!IN6_ARE_ADDR_EQUAL(addr, &sin->sin6_addr))
    521 				continue;
    522 			found_entry = 1;
    523 		} else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
    524 			continue;
    525 		if (fflag == 1) {
    526 			delete((char *)inet_ntop(AF_INET6, &sin->sin6_addr,
    527 						ntop_buf, sizeof(ntop_buf)));
    528 			continue;
    529 		}
    530 		host = NULL;
    531 		if (nflag == 0) {
    532 			hp = gethostbyaddr((char *)&sin->sin6_addr,
    533 					   sizeof(struct in6_addr), AF_INET6);
    534 			if (hp)
    535 				host = hp->h_name;
    536 		}
    537 		if (host == NULL) {
    538 			inet_ntop(AF_INET6, &sin->sin6_addr,
    539 				  ntop_buf, sizeof(ntop_buf));
    540 			host = ntop_buf;
    541 		}
    542 
    543 		gettimeofday(&time, 0);
    544 		if (tflag)
    545 			ts_print(&time);
    546 
    547 		printf("%-29.29s %-18.18s %6.6s", host,
    548 		       ether_str(sdl),
    549 		       if_indextoname(sdl->sdl_index, ifix_buf));
    550 
    551 		/* Print neighbor discovery specific informations */
    552 		putchar(' ');
    553 		nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index);
    554 		if (nbi) {
    555 			if (nbi->expire > time.tv_sec) {
    556 				printf(" %-9.9s",
    557 				       sec2str(nbi->expire - time.tv_sec));
    558 			}
    559 			else if (nbi->expire == 0)
    560 				printf(" %-9.9s", "permanent");
    561 			else
    562 				printf(" %-9.9s", "expired");
    563 
    564 			switch(nbi->state) {
    565 			 case ND6_LLINFO_NOSTATE:
    566 				 printf(" N");
    567 				 break;
    568 			 case ND6_LLINFO_WAITDELETE:
    569 				 printf(" W");
    570 				 break;
    571 			 case ND6_LLINFO_INCOMPLETE:
    572 				 printf(" I");
    573 				 break;
    574 			 case ND6_LLINFO_REACHABLE:
    575 				 printf(" R");
    576 				 break;
    577 			 case ND6_LLINFO_STALE:
    578 				 printf(" S");
    579 				 break;
    580 			 case ND6_LLINFO_DELAY:
    581 				 printf(" D");
    582 				 break;
    583 			 case ND6_LLINFO_PROBE:
    584 				 printf(" P");
    585 				 break;
    586 			 default:
    587 				 printf(" ?");
    588 				 break;
    589 			}
    590 
    591 			isrouter = nbi->isrouter;
    592 			prbs = nbi->asked;
    593 		}
    594 		else {
    595 			warnx("failed to get neighbor information");
    596 			printf("  ");
    597 		}
    598 
    599 		/* other flags */
    600 		putchar(' ');
    601 		{
    602 			u_char flgbuf[8], *p = flgbuf;
    603 
    604 			flgbuf[0] = '\0';
    605 			if (isrouter)
    606 				p += sprintf((char *)p, "R");
    607 #ifndef RADISH
    608 			if (rtm->rtm_addrs & RTA_NETMASK) {
    609 				sin = (struct sockaddr_in6 *)
    610 					(sdl->sdl_len + (char *)sdl);
    611 				if (!IN6_IS_ADDR_ANY(&sin->sin6_addr))
    612 					p += sprintf((char *)p, "P");
    613 				if (sin->sin6_len != sizeof(struct sockaddr_in6))
    614 					p += sprintf((char *)p, "W");
    615 			}
    616 #endif /*RADISH*/
    617 			printf("%4s", flgbuf);
    618 		}
    619 
    620 		putchar(' ');
    621 		if (prbs)
    622 			printf("% 4d", prbs);
    623 
    624 		printf("\n");
    625 	}
    626 
    627 	if (repeat) {
    628 		printf("\n");
    629 		sleep(repeat);
    630 		goto again;
    631 	}
    632 }
    633 
    634 static struct in6_nbrinfo *
    635 getnbrinfo(addr, ifindex)
    636 	struct in6_addr *addr;
    637 	int ifindex;
    638 {
    639 	static struct in6_nbrinfo nbi;
    640 	int s;
    641 
    642 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
    643 		err(1, "socket");
    644 
    645 	bzero(&nbi, sizeof(nbi));
    646 	if_indextoname(ifindex, nbi.ifname);
    647 	nbi.addr = *addr;
    648 	if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
    649 		warn("ioctl");
    650 		close(s);
    651 		return(NULL);
    652 	}
    653 
    654 	close(s);
    655 	return(&nbi);
    656 }
    657 
    658 static char *
    659 ether_str(sdl)
    660 	struct sockaddr_dl *sdl;
    661 {
    662 	static char ebuf[32];
    663 	u_char *cp;
    664 
    665 	if (sdl->sdl_alen) {
    666 		cp = (u_char *)LLADDR(sdl);
    667 		sprintf(ebuf, "%x:%x:%x:%x:%x:%x",
    668 			cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
    669 	}
    670 	else {
    671 		sprintf(ebuf, "(incomplete)");
    672 	}
    673 
    674 	return(ebuf);
    675 }
    676 
    677 int
    678 ndp_ether_aton(a, n)
    679 	char *a;
    680 	u_char *n;
    681 {
    682 	int i, o[6];
    683 
    684 	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
    685 					   &o[3], &o[4], &o[5]);
    686 	if (i != 6) {
    687 		fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a);
    688 		return (1);
    689 	}
    690 	for (i=0; i<6; i++)
    691 		n[i] = o[i];
    692 	return (0);
    693 }
    694 
    695 void
    696 usage()
    697 {
    698 	printf("usage: ndp hostname\n");
    699 	printf("       ndp -a[nt]\n");
    700 	printf("       ndp [-nt] -A wait\n");
    701 	printf("       ndp -c[nt]\n");
    702 	printf("       ndp -d[nt] hostname\n");
    703 	printf("       ndp -f[nt] filename\n");
    704 	printf("       ndp -i interface\n");
    705 	printf("       ndp -p\n");
    706 	printf("       ndp -r\n");
    707 	printf("       ndp -s hostname ether_addr [temp]\n");
    708 	printf("       ndp -H\n");
    709 	printf("       ndp -P\n");
    710 	printf("       ndp -R\n");
    711 	exit(1);
    712 }
    713 
    714 int
    715 rtmsg(cmd)
    716 	int cmd;
    717 {
    718 	static int seq;
    719 	int rlen;
    720 	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
    721 	register char *cp = m_rtmsg.m_space;
    722 	register int l;
    723 
    724 	errno = 0;
    725 	if (cmd == RTM_DELETE)
    726 		goto doit;
    727 	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
    728 	rtm->rtm_flags = flags;
    729 	rtm->rtm_version = RTM_VERSION;
    730 
    731 	switch (cmd) {
    732 	default:
    733 		fprintf(stderr, "ndp: internal wrong cmd\n");
    734 		exit(1);
    735 	case RTM_ADD:
    736 		rtm->rtm_addrs |= RTA_GATEWAY;
    737 		rtm->rtm_rmx.rmx_expire = expire_time;
    738 		rtm->rtm_inits = RTV_EXPIRE;
    739 		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
    740 		/* FALLTHROUGH */
    741 	case RTM_GET:
    742 		rtm->rtm_addrs |= RTA_DST;
    743 	}
    744 #define NEXTADDR(w, s) \
    745 	if (rtm->rtm_addrs & (w)) { \
    746 		bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
    747 
    748 	NEXTADDR(RTA_DST, sin_m);
    749 	NEXTADDR(RTA_GATEWAY, sdl_m);
    750 	NEXTADDR(RTA_NETMASK, so_mask);
    751 
    752 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
    753 doit:
    754 	l = rtm->rtm_msglen;
    755 	rtm->rtm_seq = ++seq;
    756 	rtm->rtm_type = cmd;
    757 	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
    758 		if (errno != ESRCH || cmd != RTM_DELETE) {
    759 			perror("writing to routing socket");
    760 			return (-1);
    761 		}
    762 	}
    763 	do {
    764 		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
    765 	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
    766 	if (l < 0)
    767 		(void) fprintf(stderr, "ndp: read from routing socket: %s\n",
    768 		    strerror(errno));
    769 	return (0);
    770 }
    771 
    772 void
    773 ifinfo(ifname)
    774 	char *ifname;
    775 {
    776 	struct in6_ndireq nd;
    777 	int s;
    778 
    779 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
    780 		perror("ndp: socket");
    781 		exit(1);
    782 	}
    783 	bzero(&nd, sizeof(nd));
    784 	strcpy(nd.ifname, ifname);
    785 	if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
    786  		perror("ioctl (SIOCGIFINFO_IN6)");
    787  		exit(1);
    788  	}
    789 #define ND nd.ndi
    790 	printf("linkmtu=%d", ND.linkmtu);
    791 	printf(", curhlim=%d", ND.chlim);
    792 	printf(", basereachable=%ds%dms",
    793 	       ND.basereachable / 1000, ND.basereachable % 1000);
    794 	printf(", reachable=%ds", ND.reachable);
    795 	printf(", retrans=%ds%dms\n", ND.retrans / 1000, ND.retrans % 1000);
    796 #undef ND
    797 	close(s);
    798 }
    799 
    800 void
    801 rtrlist()
    802 {
    803 	struct in6_drlist dr;
    804 	int s, i;
    805 	struct timeval time;
    806 
    807 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
    808 		perror("ndp: socket");
    809 		exit(1);
    810 	}
    811 	bzero(&dr, sizeof(dr));
    812 	strcpy(dr.ifname, "lo0"); /* dummy */
    813 	if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) {
    814  		perror("ioctl (SIOCGDRLST_IN6)");
    815  		exit(1);
    816  	}
    817 #define DR dr.defrouter[i]
    818 	for (i = 0 ; DR.if_index && i < PRLSTSIZ ; i++) {
    819 		printf("%s if=%s", inet_ntop(AF_INET6, &DR.rtaddr,
    820 					     ntop_buf, sizeof(ntop_buf)),
    821 		       if_indextoname(DR.if_index, ifix_buf));
    822 		printf(", flags=%s%s",
    823 		       DR.flags & ND_RA_FLAG_MANAGED ? "M" : "",
    824 		       DR.flags & ND_RA_FLAG_OTHER   ? "O" : "");
    825 		gettimeofday(&time, 0);
    826 		if (DR.expire == 0)
    827 			printf(", expire=Never\n");
    828 		else
    829 			printf(", expire=%s\n",
    830 				sec2str(DR.expire - time.tv_sec));
    831 	}
    832 #undef DR
    833 	close(s);
    834 }
    835 
    836 void
    837 plist()
    838 {
    839 	struct in6_prlist pr;
    840 	int s, i;
    841 	struct timeval time;
    842 
    843 	gettimeofday(&time, 0);
    844 
    845 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
    846 		perror("ndp: socket");
    847 		exit(1);
    848 	}
    849 	bzero(&pr, sizeof(pr));
    850 	strcpy(pr.ifname, "lo0"); /* dummy */
    851 	if (ioctl(s, SIOCGPRLST_IN6, (caddr_t)&pr) < 0) {
    852  		perror("ioctl (SIOCGPRLST_IN6)");
    853  		exit(1);
    854  	}
    855 #define PR pr.prefix[i]
    856 	for (i = 0; PR.if_index && i < PRLSTSIZ ; i++) {
    857 		printf("%s/%d if=%s\n",
    858 		       inet_ntop(AF_INET6, &PR.prefix, ntop_buf,
    859 				 sizeof(ntop_buf)), PR.prefixlen,
    860 		       if_indextoname(PR.if_index, ifix_buf));
    861 		gettimeofday(&time, 0);
    862 		printf("  flags=%s%s",
    863 		       PR.raflags.onlink ? "L" : "",
    864 		       PR.raflags.autonomous ? "A" : "");
    865 		if (PR.vltime == ND6_INFINITE_LIFETIME)
    866 			printf(" vltime=infinity");
    867 		else
    868 			printf(" vltime=%ld", (long)PR.vltime);
    869 		if (PR.pltime == ND6_INFINITE_LIFETIME)
    870 			printf(", pltime=infinity");
    871 		else
    872 			printf(", pltime=%ld", (long)PR.pltime);
    873 		if (PR.expire == 0)
    874 			printf(", expire=Never\n");
    875 		else if (PR.expire >= time.tv_sec)
    876 			printf(", expire=%s\n",
    877 				sec2str(PR.expire - time.tv_sec));
    878 		else
    879 			printf(", expired\n");
    880 		if (PR.advrtrs) {
    881 			int j;
    882 			printf("  advertised by\n");
    883 			for (j = 0; j < PR.advrtrs; j++) {
    884 				printf("    %s\n",
    885 				       inet_ntop(AF_INET6, &PR.advrtr[j],
    886 						 ntop_buf,
    887 						 sizeof(ntop_buf)));
    888 			}
    889 			if (PR.advrtrs > DRLSTSIZ)
    890 				printf("    and %d routers\n",
    891 				       PR.advrtrs - DRLSTSIZ);
    892 		}
    893 		else
    894 			printf("  No advertising router\n");
    895 	}
    896 #undef PR
    897 	close(s);
    898 }
    899 
    900 void
    901 pfx_flush()
    902 {
    903 	char dummyif[IFNAMSIZ+8];
    904 	int s;
    905 
    906 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
    907 		err(1, "socket");
    908 	strcpy(dummyif, "lo0"); /* dummy */
    909 	if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
    910  		err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
    911 }
    912 
    913 void
    914 rtr_flush()
    915 {
    916 	char dummyif[IFNAMSIZ+8];
    917 	int s;
    918 
    919 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
    920 		err(1, "socket");
    921 	strcpy(dummyif, "lo0"); /* dummy */
    922 	if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
    923  		err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
    924 }
    925 
    926 void
    927 harmonize_rtr()
    928 {
    929 	char dummyif[IFNAMSIZ+8];
    930 	int s;
    931 
    932 	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
    933 		perror("ndp: socket");
    934 		exit(1);
    935 	}
    936 	strcpy(dummyif, "lo0"); /* dummy */
    937 	if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0) {
    938  		perror("ioctl (SIOCSNDFLUSH_IN6)");
    939  		exit(1);
    940  	}
    941 }
    942 
    943 void
    944 quit(msg)
    945 	char *msg;
    946 {
    947 	fprintf(stderr, "%s\n", msg);
    948 	exit(1);
    949 }
    950 
    951 static char *
    952 sec2str(total)
    953 	time_t total;
    954 {
    955 	static char result[256];
    956 	int days, hours, mins, secs;
    957 	int first = 1;
    958 	char *p = result;
    959 
    960 	days = total / 3600 / 24;
    961 	hours = (total / 3600) % 24;
    962 	mins = (total / 60) % 60;
    963 	secs = total % 60;
    964 
    965 	if (days) {
    966 		first = 0;
    967 		p += sprintf(p, "%dd", days);
    968 	}
    969 	if (!first || hours) {
    970 		first = 0;
    971 		p += sprintf(p, "%dh", hours);
    972 	}
    973 	if (!first || mins) {
    974 		first = 0;
    975 		p += sprintf(p, "%dm", mins);
    976 	}
    977 	sprintf(p, "%ds", secs);
    978 
    979 	return(result);
    980 }
    981 
    982 /*
    983  * Print the timestamp
    984  * from tcpdump/util.c
    985  */
    986 static void
    987 ts_print(tvp)
    988 	const struct timeval *tvp;
    989 {
    990 	int s;
    991 
    992 	/* Default */
    993 	s = (tvp->tv_sec + thiszone) % 86400;
    994 	(void)printf("%02d:%02d:%02d.%06u ",
    995 	    s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec);
    996 }
    997