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