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