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