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