Home | History | Annotate | Line # | Download | only in ndp
      1 /*	$NetBSD: ndp.c,v 1.60 2023/08/18 13:07:38 tnn Exp $	*/
      2 /*	$KAME: ndp.c,v 1.121 2005/07/13 11:30:13 keiichi 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  * Copyright (c) 1997
     66  *	The Regents of the University of California.  All rights reserved.
     67  *
     68  * Redistribution and use in source and binary forms, with or without
     69  * modification, are permitted provided that: (1) source code distributions
     70  * retain the above copyright notice and this paragraph in its entirety, (2)
     71  * distributions including binary code include the above copyright notice and
     72  * this paragraph in its entirety in the documentation or other materials
     73  * provided with the distribution, and (3) all advertising materials mentioning
     74  * features or use of this software display the following acknowledgement:
     75  * ``This product includes software developed by the University of California,
     76  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
     77  * the University nor the names of its contributors may be used to endorse
     78  * or promote products derived from this software without specific prior
     79  * written permission.
     80  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
     81  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
     82  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     83  */
     84 
     85 /*
     86  * Based on:
     87  * "@(#) Copyright (c) 1984, 1993\n\
     88  *	The Regents of the University of California.  All rights reserved.\n";
     89  *
     90  * "@(#)arp.c	8.2 (Berkeley) 1/2/94";
     91  */
     92 
     93 /*
     94  * ndp - display, set, delete and flush neighbor cache
     95  */
     96 
     97 
     98 #include <sys/param.h>
     99 #include <sys/file.h>
    100 #include <sys/ioctl.h>
    101 #include <sys/socket.h>
    102 #include <sys/sysctl.h>
    103 #include <sys/time.h>
    104 
    105 #include <net/if.h>
    106 #include <net/if_dl.h>
    107 #include <net/if_types.h>
    108 #include <net/route.h>
    109 
    110 #include <netinet/in.h>
    111 
    112 #include <netinet/icmp6.h>
    113 #include <netinet6/in6_var.h>
    114 #include <netinet6/nd6.h>
    115 
    116 #include <arpa/inet.h>
    117 
    118 #include <netdb.h>
    119 #include <errno.h>
    120 #include <nlist.h>
    121 #include <stdio.h>
    122 #include <string.h>
    123 #include <paths.h>
    124 #include <err.h>
    125 #include <stdlib.h>
    126 #include <fcntl.h>
    127 #include <unistd.h>
    128 
    129 #include "prog_ops.h"
    130 
    131 static pid_t pid;
    132 static int nflag;
    133 static int tflag;
    134 static int32_t thiszone;	/* time difference with gmt */
    135 static int my_s = -1;
    136 static unsigned int repeat = 0;
    137 
    138 
    139 static char host_buf[NI_MAXHOST];		/* getnameinfo() */
    140 static char ifix_buf[IFNAMSIZ];		/* if_indextoname() */
    141 
    142 static void getsocket(void);
    143 static int set(int, char **);
    144 static void get(char *);
    145 static int delete(struct rt_msghdr *, char *);
    146 static void delete_one(char *);
    147 static void do_foreach(struct in6_addr *, char *, int);
    148 static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, unsigned int, int);
    149 static char *ether_str(struct sockaddr_dl *);
    150 static int ndp_ether_aton(char *, u_char *);
    151 __dead static void usage(void);
    152 static int rtmsg(int, struct rt_msghdr *);
    153 static void ifinfo(char *, int, char **);
    154 static const char *sec2str(time_t);
    155 static char *ether_str(struct sockaddr_dl *);
    156 static void ts_print(const struct timeval *);
    157 static int32_t gmt2local(time_t t);
    158 
    159 #define NDP_F_CLEAR	1
    160 #define NDP_F_DELETE	2
    161 
    162 static int mode = 0;
    163 static char *arg = NULL;
    164 
    165 int
    166 main(int argc, char **argv)
    167 {
    168 	int ch;
    169 
    170 	while ((ch = getopt(argc, argv, "acd:f:i:nstA:")) != -1)
    171 		switch (ch) {
    172 		case 'a':
    173 		case 'c':
    174 		case 's':
    175 		case 'd':
    176 		case 'f':
    177 		case 'i' :
    178 			if (mode) {
    179 				usage();
    180 				/*NOTREACHED*/
    181 			}
    182 			mode = ch;
    183 			arg = optarg;
    184 			break;
    185 		case 'n':
    186 			nflag = 1;
    187 			break;
    188 		case 't':
    189 			tflag = 1;
    190 			break;
    191 		case 'A':
    192 			if (mode) {
    193 				usage();
    194 				/*NOTREACHED*/
    195 			}
    196 			mode = 'a';
    197 			repeat = atoi(optarg);
    198 			break;
    199 		default:
    200 			usage();
    201 		}
    202 
    203 	argc -= optind;
    204 	argv += optind;
    205 
    206 	if (prog_init && prog_init() == -1)
    207 		err(1, "init failed");
    208 
    209 	pid = prog_getpid();
    210 	thiszone = gmt2local(0L);
    211 
    212 	switch (mode) {
    213 	case 'a':
    214 	case 'c':
    215 		if (argc != 0) {
    216 			usage();
    217 			/*NOTREACHED*/
    218 		}
    219 		do_foreach(0, NULL, mode == 'c' ? NDP_F_CLEAR : 0);
    220 		break;
    221 	case 'd':
    222 		if (argc != 0) {
    223 			usage();
    224 			/*NOTREACHED*/
    225 		}
    226 		delete_one(arg);
    227 		break;
    228 	case 'i':
    229 		ifinfo(arg, argc, argv);
    230 		break;
    231 	case 's':
    232 		if (argc < 2 || argc > 4)
    233 			usage();
    234 		return(set(argc, argv) ? 1 : 0);
    235 	case 0:
    236 		if (argc != 1) {
    237 			usage();
    238 			/*NOTREACHED*/
    239 		}
    240 		get(argv[0]);
    241 		break;
    242 	}
    243 	return(0);
    244 }
    245 
    246 static void
    247 makeaddr(struct sockaddr_in6 *mysin, const void *resp)
    248 {
    249 	const struct sockaddr_in6 *res = resp;
    250 	mysin->sin6_addr = res->sin6_addr;
    251 	mysin->sin6_scope_id = res->sin6_scope_id;
    252 	inet6_putscopeid(mysin, INET6_IS_ADDR_LINKLOCAL);
    253 }
    254 
    255 static void
    256 getsocket(void)
    257 {
    258 	if (my_s < 0) {
    259 		my_s = prog_socket(PF_ROUTE, SOCK_RAW, 0);
    260 		if (my_s < 0)
    261 			err(1, "socket");
    262 	}
    263 }
    264 
    265 #ifdef notdef
    266 static struct sockaddr_in6 so_mask = {
    267 	.sin6_len = sizeof(so_mask),
    268 	.sin6_family = AF_INET6
    269 };
    270 #endif
    271 static struct sockaddr_in6 blank_sin = {
    272 	.sin6_len = sizeof(blank_sin),
    273 	.sin6_family = AF_INET6
    274 };
    275 static struct sockaddr_in6 sin_m;
    276 static struct sockaddr_dl blank_sdl = {
    277 	.sdl_len = sizeof(blank_sdl),
    278 	.sdl_family = AF_LINK,
    279 };
    280 static struct sockaddr_dl sdl_m;
    281 static int expire_time, flags, found_entry;
    282 static struct {
    283 	struct	rt_msghdr m_rtm;
    284 	char	m_space[512];
    285 } m_rtmsg;
    286 
    287 /*
    288  * Set an individual neighbor cache entry
    289  */
    290 static int
    291 set(int argc, char **argv)
    292 {
    293 	register struct sockaddr_in6 *mysin = &sin_m;
    294 	register struct sockaddr_dl *sdl;
    295 	register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
    296 	struct addrinfo hints, *res;
    297 	int gai_error;
    298 	u_char *ea;
    299 	char *host = argv[0], *eaddr = argv[1];
    300 
    301 	getsocket();
    302 	argc -= 2;
    303 	argv += 2;
    304 	sdl_m = blank_sdl;
    305 	sin_m = blank_sin;
    306 
    307 	(void)memset(&hints, 0, sizeof(hints));
    308 	hints.ai_family = AF_INET6;
    309 	gai_error = getaddrinfo(host, NULL, &hints, &res);
    310 	if (gai_error) {
    311 		warnx("%s: %s", host, gai_strerror(gai_error));
    312 		return 1;
    313 	}
    314 	makeaddr(mysin, res->ai_addr);
    315 	freeaddrinfo(res);
    316 	ea = (u_char *)LLADDR(&sdl_m);
    317 	if (ndp_ether_aton(eaddr, ea) == 0)
    318 		sdl_m.sdl_alen = 6;
    319 	flags = expire_time = 0;
    320 	while (argc-- > 0) {
    321 		if (strncmp(argv[0], "temp", 4) == 0) {
    322 			struct timeval tim;
    323 
    324 			(void)gettimeofday(&tim, 0);
    325 			expire_time = tim.tv_sec + 20 * 60;
    326 		} else if (strncmp(argv[0], "proxy", 5) == 0)
    327 			flags |= RTF_ANNOUNCE;
    328 		argv++;
    329 	}
    330 	if (rtmsg(RTM_GET, NULL) < 0) {
    331 		errx(1, "RTM_GET(%s) failed", host);
    332 		/* NOTREACHED */
    333 	}
    334 	mysin = (struct sockaddr_in6 *)(void *)(rtm + 1);
    335 	sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(mysin->sin6_len) + (char *)(void *)mysin);
    336 	if (IN6_ARE_ADDR_EQUAL(&mysin->sin6_addr, &sin_m.sin6_addr)) {
    337 		if (sdl->sdl_family == AF_LINK &&
    338 		    !(rtm->rtm_flags & RTF_GATEWAY)) {
    339 			switch (sdl->sdl_type) {
    340 			case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
    341 			case IFT_ISO88024: case IFT_ISO88025:
    342 				goto overwrite;
    343 			}
    344 		}
    345 		/*
    346 		 * IPv4 arp command retries with sin_other = SIN_PROXY here.
    347 		 */
    348 		(void)fprintf(stderr, "set: cannot configure a new entry\n");
    349 		return 1;
    350 	}
    351 
    352 overwrite:
    353 	if (sdl->sdl_family != AF_LINK) {
    354 		warnx("cannot intuit interface index and type for %s", host);
    355 		return (1);
    356 	}
    357 	sdl_m.sdl_type = sdl->sdl_type;
    358 	sdl_m.sdl_index = sdl->sdl_index;
    359 	return (rtmsg(RTM_ADD, NULL));
    360 }
    361 
    362 /*
    363  * Display an individual neighbor cache entry
    364  */
    365 static void
    366 get(char *host)
    367 {
    368 	struct sockaddr_in6 *mysin = &sin_m;
    369 	struct addrinfo hints, *res;
    370 	int gai_error;
    371 
    372 	sin_m = blank_sin;
    373 	(void)memset(&hints, 0, sizeof(hints));
    374 	hints.ai_family = AF_INET6;
    375 	gai_error = getaddrinfo(host, NULL, &hints, &res);
    376 	if (gai_error) {
    377 		warnx("%s: %s", host, gai_strerror(gai_error));
    378 		return;
    379 	}
    380 	makeaddr(mysin, res->ai_addr);
    381 	freeaddrinfo(res);
    382 	do_foreach(&mysin->sin6_addr, host, 0);
    383 	if (found_entry == 0) {
    384 		(void)getnameinfo((struct sockaddr *)(void *)mysin,
    385 		    (socklen_t)mysin->sin6_len,
    386 		    host_buf, sizeof(host_buf), NULL ,0,
    387 		    (nflag ? NI_NUMERICHOST : 0));
    388 		errx(1, "%s (%s) -- no entry", host, host_buf);
    389 	}
    390 }
    391 
    392 static void
    393 delete_one(char *host)
    394 {
    395 	struct sockaddr_in6 *mysin = &sin_m;
    396 	struct addrinfo hints, *res;
    397 	int gai_error;
    398 
    399 	sin_m = blank_sin;
    400 	(void)memset(&hints, 0, sizeof(hints));
    401 	hints.ai_family = AF_INET6;
    402 	gai_error = getaddrinfo(host, NULL, &hints, &res);
    403 	if (gai_error) {
    404 		warnx("%s: %s", host, gai_strerror(gai_error));
    405 		return;
    406 	}
    407 	makeaddr(mysin, res->ai_addr);
    408 	freeaddrinfo(res);
    409 	do_foreach(&mysin->sin6_addr, host, NDP_F_DELETE);
    410 }
    411 
    412 /*
    413  * Delete a neighbor cache entry
    414  */
    415 static int
    416 delete(struct rt_msghdr *rtm, char *host)
    417 {
    418 	char delete_host_buf[NI_MAXHOST];
    419 	struct sockaddr_in6 *mysin = &sin_m;
    420 	struct sockaddr_dl *sdl;
    421 
    422 	getsocket();
    423 	mysin = (struct sockaddr_in6 *)(void *)(rtm + 1);
    424 	sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(mysin->sin6_len) +
    425 	    (char *)(void *)mysin);
    426 
    427 	if (sdl->sdl_family != AF_LINK) {
    428 		(void)printf("cannot locate %s\n", host);
    429 		return (1);
    430 	}
    431 	if (rtmsg(RTM_DELETE, rtm) == 0) {
    432 		struct sockaddr_in6 s6 = *mysin; /* XXX: for safety */
    433 
    434 		s6.sin6_scope_id = 0;
    435 		inet6_putscopeid(&s6, INET6_IS_ADDR_LINKLOCAL);
    436 		(void)getnameinfo((struct sockaddr *)(void *)&s6,
    437 		    (socklen_t)s6.sin6_len, delete_host_buf,
    438 		    sizeof(delete_host_buf), NULL, 0,
    439 		    (nflag ? NI_NUMERICHOST : 0));
    440 		(void)printf("%s (%s) deleted\n", host, delete_host_buf);
    441 	}
    442 
    443 	return 0;
    444 }
    445 
    446 #define W_ADDR	(8 * 4 + 7)
    447 #define W_LL	17
    448 #define W_IF	6
    449 
    450 /*
    451  * Iterate on neighbor caches and do
    452  * - dump all caches,
    453  * - clear all caches (NDP_F_CLEAR) or
    454  * - remove matched caches (NDP_F_DELETE)
    455  */
    456 static void
    457 do_foreach(struct in6_addr *addr, char *host, int _flags)
    458 {
    459 	int mib[6];
    460 	size_t needed;
    461 	char *lim, *buf, *next;
    462 	struct rt_msghdr *rtm;
    463 	struct sockaddr_in6 *mysin;
    464 	struct sockaddr_dl *sdl;
    465 	struct in6_nbrinfo *nbi;
    466 	struct timeval tim;
    467 	int addrwidth;
    468 	int llwidth;
    469 	int ifwidth;
    470 	char flgbuf[8], *fl;
    471 	const char *ifname;
    472 	int cflag = _flags == NDP_F_CLEAR;
    473 	int dflag = _flags == NDP_F_DELETE;
    474 
    475 	/* Print header */
    476 	if (!tflag && !cflag)
    477 		(void)printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %2s\n",
    478 		    W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address",
    479 		    W_IF, W_IF, "Netif", "Expire", "S", "Fl");
    480 
    481 again:;
    482 	mib[0] = CTL_NET;
    483 	mib[1] = PF_ROUTE;
    484 	mib[2] = 0;
    485 	mib[3] = AF_INET6;
    486 	mib[4] = NET_RT_FLAGS;
    487 	mib[5] = RTF_LLDATA;
    488 	if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
    489 		err(1, "sysctl(PF_ROUTE estimate)");
    490 	if (needed > 0) {
    491 		if ((buf = malloc(needed)) == NULL)
    492 			err(1, "malloc");
    493 		if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
    494 			free(buf);
    495 			if (errno == ENOBUFS)
    496 				goto again;
    497 			err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
    498 		}
    499 		lim = buf + needed;
    500 	} else
    501 		buf = lim = NULL;
    502 
    503 	for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
    504 		int isrouter = 0, prbs = 0;
    505 
    506 		rtm = (struct rt_msghdr *)(void *)next;
    507 		mysin = (struct sockaddr_in6 *)(void *)(rtm + 1);
    508 		sdl = (struct sockaddr_dl *)(void *)((char *)(void *)mysin + RT_ROUNDUP(mysin->sin6_len));
    509 
    510 		/*
    511 		 * Some OSes can produce a route that has the LINK flag but
    512 		 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
    513 		 * and BSD/OS, where xx is not the interface identifier on
    514 		 * lo0).  Such routes entry would annoy getnbrinfo() below,
    515 		 * so we skip them.
    516 		 * XXX: such routes should have the GATEWAY flag, not the
    517 		 * LINK flag.  However, there is rotten routing software
    518 		 * that advertises all routes that have the GATEWAY flag.
    519 		 * Thus, KAME kernel intentionally does not set the LINK flag.
    520 		 * What is to be fixed is not ndp, but such routing software
    521 		 * (and the kernel workaround)...
    522 		 */
    523 		if (sdl->sdl_family != AF_LINK)
    524 			continue;
    525 
    526 		if (!(rtm->rtm_flags & RTF_HOST))
    527 			continue;
    528 
    529 		if (addr) {
    530 			if (!IN6_ARE_ADDR_EQUAL(addr, &mysin->sin6_addr))
    531 				continue;
    532 			found_entry = 1;
    533 		} else if (IN6_IS_ADDR_MULTICAST(&mysin->sin6_addr))
    534 			continue;
    535 		if (dflag) {
    536 			(void)delete(rtm, host_buf);
    537 			continue;
    538 		}
    539 		if (IN6_IS_ADDR_LINKLOCAL(&mysin->sin6_addr) ||
    540 		    IN6_IS_ADDR_MC_LINKLOCAL(&mysin->sin6_addr)) {
    541 			uint16_t scopeid = mysin->sin6_scope_id;
    542 			inet6_getscopeid(mysin, INET6_IS_ADDR_LINKLOCAL|
    543 			    INET6_IS_ADDR_MC_LINKLOCAL);
    544 			if (scopeid == 0)
    545 				mysin->sin6_scope_id = sdl->sdl_index;
    546 		}
    547 		(void)getnameinfo((struct sockaddr *)(void *)mysin,
    548 		    (socklen_t)mysin->sin6_len,
    549 		    host_buf, sizeof(host_buf), NULL, 0,
    550 		    (nflag ? NI_NUMERICHOST : 0));
    551 		if (cflag) {
    552 			/* Restore scopeid */
    553 			if (IN6_IS_ADDR_LINKLOCAL(&mysin->sin6_addr) ||
    554 			    IN6_IS_ADDR_MC_LINKLOCAL(&mysin->sin6_addr))
    555 				inet6_putscopeid(mysin, INET6_IS_ADDR_LINKLOCAL|
    556 				    INET6_IS_ADDR_MC_LINKLOCAL);
    557 			if ((rtm->rtm_flags & RTF_STATIC) == 0)
    558 				(void)delete(rtm, host_buf);
    559 			continue;
    560 		}
    561 		(void)gettimeofday(&tim, 0);
    562 		if (tflag)
    563 			ts_print(&tim);
    564 
    565 		addrwidth = strlen(host_buf);
    566 		if (addrwidth < W_ADDR)
    567 			addrwidth = W_ADDR;
    568 		llwidth = strlen(ether_str(sdl));
    569 		if (W_ADDR + W_LL - addrwidth > llwidth)
    570 			llwidth = W_ADDR + W_LL - addrwidth;
    571 		ifname = if_indextoname((unsigned int)sdl->sdl_index, ifix_buf);
    572 		if (!ifname)
    573 			ifname = "?";
    574 		ifwidth = strlen(ifname);
    575 		if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
    576 			ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
    577 
    578 		(void)printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth,
    579 		    host_buf, llwidth, llwidth, ether_str(sdl), ifwidth,
    580 		    ifwidth, ifname);
    581 
    582 		/* Print neighbor discovery specific informations */
    583 		nbi = getnbrinfo(&mysin->sin6_addr,
    584 		    (unsigned int)sdl->sdl_index, 1);
    585 		if (nbi) {
    586 			if (nbi->expire > tim.tv_sec) {
    587 				(void)printf(" %-9.9s",
    588 				    sec2str(nbi->expire - tim.tv_sec));
    589 			} else if (nbi->expire == 0)
    590 				(void)printf(" %-9.9s", "permanent");
    591 			else
    592 				(void)printf(" %-9.9s", "expired");
    593 
    594 			switch (nbi->state) {
    595 			case ND_LLINFO_NOSTATE:
    596 				 (void)printf(" N");
    597 				 break;
    598 			case ND_LLINFO_WAITDELETE:
    599 				 (void)printf(" W");
    600 				 break;
    601 			case ND_LLINFO_INCOMPLETE:
    602 				 (void)printf(" I");
    603 				 break;
    604 			case ND_LLINFO_REACHABLE:
    605 				 (void)printf(" R");
    606 				 break;
    607 			case ND_LLINFO_STALE:
    608 				 (void)printf(" S");
    609 				 break;
    610 			case ND_LLINFO_DELAY:
    611 				 (void)printf(" D");
    612 				 break;
    613 			case ND_LLINFO_PROBE:
    614 				 (void)printf(" P");
    615 				 break;
    616 			case ND_LLINFO_UNREACHABLE:
    617 				 (void)printf(" U");
    618 				 break;
    619 			default:
    620 				 (void)printf(" ?");
    621 				 break;
    622 			}
    623 
    624 			isrouter = nbi->isrouter;
    625 			prbs = nbi->asked;
    626 		} else {
    627 			warnx("failed to get neighbor information");
    628 			(void)printf("  ");
    629 		}
    630 
    631 		/*
    632 		 * other flags. R: router, P: proxy, W: ??
    633 		 */
    634 		fl = flgbuf;
    635 		if (isrouter)
    636 			*fl++ = 'R';
    637 		if (rtm->rtm_flags & RTF_ANNOUNCE)
    638 			*fl++ = 'p';
    639 		*fl++ = '\0';
    640 		(void)printf(" %s", flgbuf);
    641 
    642 		if (prbs)
    643 			(void)printf(" %d", prbs);
    644 
    645 		(void)printf("\n");
    646 	}
    647 	if (buf != NULL)
    648 		free(buf);
    649 
    650 	if (repeat) {
    651 		(void)printf("\n");
    652 		(void)fflush(stdout);
    653 		(void)sleep(repeat);
    654 		goto again;
    655 	}
    656 }
    657 
    658 static struct in6_nbrinfo *
    659 getnbrinfo(struct in6_addr *addr, unsigned int ifindex, int warning)
    660 {
    661 	static struct in6_nbrinfo nbi;
    662 	int s;
    663 
    664 	if ((s = prog_socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
    665 		err(1, "socket");
    666 
    667 	(void)memset(&nbi, 0, sizeof(nbi));
    668 	(void)if_indextoname(ifindex, nbi.ifname);
    669 	nbi.addr = *addr;
    670 	if (prog_ioctl(s, SIOCGNBRINFO_IN6, &nbi) < 0) {
    671 		if (warning)
    672 			warn("ioctl(SIOCGNBRINFO_IN6)");
    673 		(void)prog_close(s);
    674 		return(NULL);
    675 	}
    676 
    677 	(void)prog_close(s);
    678 	return(&nbi);
    679 }
    680 
    681 static char *
    682 ether_str(struct sockaddr_dl *sdl)
    683 {
    684 	static char hbuf[NI_MAXHOST];
    685 
    686 	if (sdl->sdl_alen) {
    687 		if (getnameinfo((struct sockaddr *)(void *)sdl,
    688 		    (socklen_t)sdl->sdl_len,
    689 		    hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
    690 			(void)snprintf(hbuf, sizeof(hbuf), "<invalid>");
    691 	} else
    692 		(void)snprintf(hbuf, sizeof(hbuf), "(incomplete)");
    693 
    694 	return(hbuf);
    695 }
    696 
    697 static int
    698 ndp_ether_aton(char *a, u_char *n)
    699 {
    700 	int i, o[6];
    701 
    702 	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
    703 	    &o[3], &o[4], &o[5]);
    704 	if (i != 6) {
    705 		warnx("invalid Ethernet address '%s'", a);
    706 		return (1);
    707 	}
    708 	for (i = 0; i < 6; i++)
    709 		n[i] = o[i];
    710 	return (0);
    711 }
    712 
    713 static void
    714 usage(void)
    715 {
    716 	const char *pn = getprogname();
    717 
    718 	(void)fprintf(stderr, "Usage: %s [-nt] hostname\n", pn);
    719 	(void)fprintf(stderr,
    720 	    "       %s [-nt] -a | -c\n", pn);
    721 	(void)fprintf(stderr, "       %s [-nt] -A wait\n", pn);
    722 	(void)fprintf(stderr, "       %s [-nt] -d hostname\n", pn);
    723 	(void)fprintf(stderr, "       %s [-nt] -f filename\n", pn);
    724 	(void)fprintf(stderr, "       %s [-nt] -i interface [flags...]\n", pn);
    725 	(void)fprintf(stderr,
    726 	    "       %s [-nt] -s nodename etheraddr [temp] [proxy]\n", pn);
    727 	exit(1);
    728 }
    729 
    730 static int
    731 rtmsg(int cmd, struct rt_msghdr *_rtm)
    732 {
    733 	static int seq;
    734 	register struct rt_msghdr *rtm = _rtm;
    735 	register char *cp = m_rtmsg.m_space;
    736 	register int l;
    737 
    738 	errno = 0;
    739 	if (rtm != NULL) {
    740 		memcpy(&m_rtmsg, rtm, rtm->rtm_msglen);
    741 		rtm = &m_rtmsg.m_rtm;
    742 		goto doit;
    743 	}
    744 	(void)memset(&m_rtmsg, 0, sizeof(m_rtmsg));
    745 	rtm = &m_rtmsg.m_rtm;
    746 	rtm->rtm_flags = flags;
    747 	rtm->rtm_version = RTM_VERSION;
    748 
    749 	switch (cmd) {
    750 	default:
    751 		errx(1, "internal wrong cmd");
    752 		/*NOTREACHED*/
    753 	case RTM_ADD:
    754 		rtm->rtm_addrs |= RTA_GATEWAY;
    755 		if (expire_time) {
    756 			rtm->rtm_rmx.rmx_expire = expire_time;
    757 			rtm->rtm_inits = RTV_EXPIRE;
    758 		}
    759 		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
    760 #ifdef notdef	/* we don't support ipv6addr/128 type proxying. */
    761 		if (rtm->rtm_flags & RTF_ANNOUNCE) {
    762 			rtm->rtm_flags &= ~RTF_HOST;
    763 			rtm->rtm_addrs |= RTA_NETMASK;
    764 		}
    765 #endif
    766 		rtm->rtm_addrs |= RTA_DST;
    767 		break;
    768 	case RTM_GET:
    769 		rtm->rtm_flags |= RTF_LLDATA;
    770 		rtm->rtm_addrs |= RTA_DST | RTA_GATEWAY;
    771 	}
    772 #define NEXTADDR(w, s) \
    773 	if (rtm->rtm_addrs & (w)) { \
    774 		(void)memcpy(cp, &s, sizeof(s)); \
    775 		RT_ADVANCE(cp, (struct sockaddr *)(void *)&s); \
    776 	}
    777 
    778 	NEXTADDR(RTA_DST, sin_m);
    779 	NEXTADDR(RTA_GATEWAY, sdl_m);
    780 #ifdef notdef	/* we don't support ipv6addr/128 type proxying. */
    781 	(void)memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
    782 	NEXTADDR(RTA_NETMASK, so_mask);
    783 #endif
    784 
    785 	rtm->rtm_msglen = cp - (char *)(void *)&m_rtmsg;
    786 doit:
    787 	l = rtm->rtm_msglen;
    788 	rtm->rtm_seq = ++seq;
    789 	rtm->rtm_type = cmd;
    790 	if (prog_write(my_s, &m_rtmsg, (size_t)l) == -1) {
    791 		if (errno != ESRCH || cmd != RTM_DELETE)
    792 			err(1, "writing to routing socket");
    793 	}
    794 	do {
    795 		l = prog_read(my_s, &m_rtmsg, sizeof(m_rtmsg));
    796 	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
    797 	if (l < 0)
    798 		warn("read from routing socket");
    799 	return (0);
    800 }
    801 
    802 static void
    803 ifinfo(char *ifname, int argc, char **argv)
    804 {
    805 	struct in6_ndireq nd;
    806 	int i, s;
    807 	u_int32_t newflags;
    808 	bool valset = false, flagset = false;
    809 
    810 	if ((s = prog_socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
    811 		err(1, "socket");
    812 	(void)memset(&nd, 0, sizeof(nd));
    813 	(void)strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
    814 	if (prog_ioctl(s, SIOCGIFINFO_IN6, &nd) < 0)
    815 		err(1, "ioctl(SIOCGIFINFO_IN6)");
    816 #define ND nd.ndi
    817 	newflags = ND.flags;
    818 	for (i = 0; i < argc; i++) {
    819 		int clear = 0;
    820 		char *cp = argv[i];
    821 
    822 		if (*cp == '-') {
    823 			clear = 1;
    824 			cp++;
    825 		}
    826 
    827 #define SETFLAG(s, f) \
    828 	do {\
    829 		if (strcmp(cp, (s)) == 0) {\
    830 			if (clear)\
    831 				newflags &= ~(f);\
    832 			else\
    833 				newflags |= (f);\
    834 			flagset = true; \
    835 		}\
    836 	} while (0)
    837 /*
    838  * XXX: this macro is not 100% correct, in that it matches "nud" against
    839  *      "nudbogus".  But we just let it go since this is minor.
    840  */
    841 #define SETVALUE(f, v) \
    842 	do { \
    843 		char *valptr; \
    844 		unsigned long newval; \
    845 		v = 0; /* unspecified */ \
    846 		if (strncmp(cp, f, strlen(f)) == 0) { \
    847 			valptr = strchr(cp, '='); \
    848 			if (valptr == NULL) \
    849 				err(1, "syntax error in %s field", (f)); \
    850 			errno = 0; \
    851 			newval = strtoul(++valptr, NULL, 0); \
    852 			if (errno) \
    853 				err(1, "syntax error in %s's value", (f)); \
    854 			v = newval; \
    855 			valset = true; \
    856 		} \
    857 	} while (0)
    858 
    859 #ifdef ND6_IFF_IFDISABLED
    860 		SETFLAG("disabled", ND6_IFF_IFDISABLED);
    861 #endif
    862 		SETFLAG("nud", ND6_IFF_PERFORMNUD);
    863 #ifdef ND6_IFF_ACCEPT_RTADV
    864 		SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV);
    865 #endif
    866 #ifdef ND6_IFF_OVERRIDE_RTADV
    867 		SETFLAG("override_rtadv", ND6_IFF_OVERRIDE_RTADV);
    868 #endif
    869 #ifdef ND6_IFF_AUTO_LINKLOCAL
    870 		SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL);
    871 #endif
    872 #ifdef ND6_IFF_PREFER_SOURCE
    873 		SETFLAG("prefer_source", ND6_IFF_PREFER_SOURCE);
    874 #endif
    875 #ifdef ND6_IFF_DONT_SET_IFROUTE
    876 		SETFLAG("dont_set_ifroute", ND6_IFF_DONT_SET_IFROUTE);
    877 #endif
    878 		SETVALUE("basereachable", ND.basereachable);
    879 		SETVALUE("retrans", ND.retrans);
    880 		SETVALUE("curhlim", ND.chlim);
    881 
    882 		ND.flags = newflags;
    883 #ifdef SIOCSIFINFO_IN6
    884 		if (valset && prog_ioctl(s, SIOCSIFINFO_IN6, &nd) < 0)
    885 			err(1, "ioctl(SIOCSIFINFO_IN6)");
    886 #endif
    887 		if (flagset && prog_ioctl(s, SIOCSIFINFO_FLAGS, &nd) < 0)
    888 			err(1, "ioctl(SIOCSIFINFO_FLAGS)");
    889 #undef SETFLAG
    890 #undef SETVALUE
    891 	}
    892 
    893 	if (prog_ioctl(s, SIOCGIFINFO_IN6, &nd) < 0)
    894 		err(1, "ioctl(SIOCGIFINFO_IN6)");
    895 	(void)printf("curhlim=%d", ND.chlim);
    896 	(void)printf(", basereachable=%ds%dms",
    897 	    ND.basereachable / 1000, ND.basereachable % 1000);
    898 	(void)printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000);
    899 	if (ND.flags) {
    900 		(void)printf("\nFlags: ");
    901 		if ((ND.flags & ND6_IFF_PERFORMNUD))
    902 			(void)printf("nud ");
    903 #ifdef ND6_IFF_IFDISABLED
    904 		if ((ND.flags & ND6_IFF_IFDISABLED))
    905 			(void)printf("disabled ");
    906 #endif
    907 #ifdef ND6_IFF_AUTO_LINKLOCAL
    908 		if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL))
    909 			(void)printf("auto_linklocal ");
    910 #endif
    911 #ifdef ND6_IFF_PREFER_SOURCE
    912 		if ((ND.flags & ND6_IFF_PREFER_SOURCE))
    913 			(void)printf("prefer_source ");
    914 #endif
    915 	}
    916 	(void)putc('\n', stdout);
    917 #undef ND
    918 
    919 	(void)prog_close(s);
    920 }
    921 
    922 static const char *
    923 sec2str(time_t total)
    924 {
    925 	static char result[256];
    926 	int days, hours, mins, secs;
    927 	int first = 1;
    928 	char *p = result;
    929 	char *ep = &result[sizeof(result)];
    930 	int n;
    931 
    932 	days = total / 3600 / 24;
    933 	hours = (total / 3600) % 24;
    934 	mins = (total / 60) % 60;
    935 	secs = total % 60;
    936 
    937 	if (days) {
    938 		first = 0;
    939 		n = snprintf(p, (size_t)(ep - p), "%dd", days);
    940 		if (n < 0 || n >= ep - p)
    941 			return "?";
    942 		p += n;
    943 	}
    944 	if (!first || hours) {
    945 		first = 0;
    946 		n = snprintf(p, (size_t)(ep - p), "%dh", hours);
    947 		if (n < 0 || n >= ep - p)
    948 			return "?";
    949 		p += n;
    950 	}
    951 	if (!first || mins) {
    952 		first = 0;
    953 		n = snprintf(p, (size_t)(ep - p), "%dm", mins);
    954 		if (n < 0 || n >= ep - p)
    955 			return "?";
    956 		p += n;
    957 	}
    958 	(void)snprintf(p, (size_t)(ep - p), "%ds", secs);
    959 
    960 	return(result);
    961 }
    962 
    963 /*
    964  * Print the timestamp
    965  * from tcpdump/util.c
    966  */
    967 static void
    968 ts_print(const struct timeval *tvp)
    969 {
    970 	int s;
    971 
    972 	/* Default */
    973 	s = (tvp->tv_sec + thiszone) % 86400;
    974 	(void)printf("%02d:%02d:%02d.%06u ",
    975 	    s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec);
    976 }
    977 
    978 /*
    979  * Returns the difference between gmt and local time in seconds.
    980  * Use gmtime() and localtime() to keep things simple.
    981  */
    982 static int32_t
    983 gmt2local(time_t t)
    984 {
    985 	int dt, dir;
    986 	struct tm *gmt, *loc;
    987 	struct tm sgmt;
    988 
    989 	if (t == 0)
    990 		t = time(NULL);
    991 	gmt = &sgmt;
    992 	*gmt = *gmtime(&t);
    993 	loc = localtime(&t);
    994 	dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 +
    995 	    (loc->tm_min - gmt->tm_min) * 60;
    996 
    997 	/*
    998 	 * If the year or julian day is different, we span 00:00 GMT
    999 	 * and must add or subtract a day. Check the year first to
   1000 	 * avoid problems when the julian day wraps.
   1001 	 */
   1002 	dir = loc->tm_year - gmt->tm_year;
   1003 	if (dir == 0)
   1004 		dir = loc->tm_yday - gmt->tm_yday;
   1005 	dt += dir * 24 * 60 * 60;
   1006 
   1007 	return (dt);
   1008 }
   1009