162590Sitojun/*	$FreeBSD$	*/
2122615Sume/*	$KAME: ndp.c,v 1.104 2003/06/27 07:48:39 itojun Exp $	*/
362590Sitojun
455505Sshin/*
555505Sshin * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
655505Sshin * All rights reserved.
755505Sshin *
855505Sshin * Redistribution and use in source and binary forms, with or without
955505Sshin * modification, are permitted provided that the following conditions
1055505Sshin * are met:
1155505Sshin * 1. Redistributions of source code must retain the above copyright
1255505Sshin *    notice, this list of conditions and the following disclaimer.
1355505Sshin * 2. Redistributions in binary form must reproduce the above copyright
1455505Sshin *    notice, this list of conditions and the following disclaimer in the
1555505Sshin *    documentation and/or other materials provided with the distribution.
1655505Sshin * 3. Neither the name of the project nor the names of its contributors
1755505Sshin *    may be used to endorse or promote products derived from this software
1855505Sshin *    without specific prior written permission.
1955505Sshin *
2055505Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2155505Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2255505Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2355505Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2455505Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2555505Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2655505Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2755505Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2855505Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2955505Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3055505Sshin * SUCH DAMAGE.
3155505Sshin */
3255505Sshin/*
3355505Sshin * Copyright (c) 1984, 1993
3455505Sshin *	The Regents of the University of California.  All rights reserved.
3555505Sshin *
3655505Sshin * This code is derived from software contributed to Berkeley by
3755505Sshin * Sun Microsystems, Inc.
3855505Sshin *
3955505Sshin * Redistribution and use in source and binary forms, with or without
4055505Sshin * modification, are permitted provided that the following conditions
4155505Sshin * are met:
4255505Sshin * 1. Redistributions of source code must retain the above copyright
4355505Sshin *    notice, this list of conditions and the following disclaimer.
4455505Sshin * 2. Redistributions in binary form must reproduce the above copyright
4555505Sshin *    notice, this list of conditions and the following disclaimer in the
4655505Sshin *    documentation and/or other materials provided with the distribution.
4755505Sshin * 4. Neither the name of the University nor the names of its contributors
4855505Sshin *    may be used to endorse or promote products derived from this software
4955505Sshin *    without specific prior written permission.
5055505Sshin *
5155505Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5255505Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5355505Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5455505Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5555505Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5655505Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5755505Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5855505Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5955505Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6055505Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6155505Sshin * SUCH DAMAGE.
6255505Sshin */
6355505Sshin
6455505Sshin/*
6555505Sshin * Based on:
6655505Sshin * "@(#) Copyright (c) 1984, 1993\n\
6755505Sshin *	The Regents of the University of California.  All rights reserved.\n";
6855505Sshin *
6955505Sshin * "@(#)arp.c	8.2 (Berkeley) 1/2/94";
7055505Sshin */
7155505Sshin
7255505Sshin/*
7355505Sshin * ndp - display, set, delete and flush neighbor cache
7455505Sshin */
7555505Sshin
7655505Sshin
7755505Sshin#include <sys/param.h>
7855505Sshin#include <sys/file.h>
7955505Sshin#include <sys/ioctl.h>
8055505Sshin#include <sys/socket.h>
8155505Sshin#include <sys/sysctl.h>
82253999Shrs#include <sys/time.h>
8378064Sume#include <sys/queue.h>
8455505Sshin
8555505Sshin#include <net/if.h>
8655505Sshin#include <net/if_var.h>
8755505Sshin#include <net/if_dl.h>
8855505Sshin#include <net/if_types.h>
8955505Sshin#include <net/route.h>
9055505Sshin
9155505Sshin#include <netinet/in.h>
9255505Sshin#include <netinet/if_ether.h>
9355505Sshin
9455505Sshin#include <netinet/icmp6.h>
9555505Sshin#include <netinet6/in6_var.h>
9655505Sshin#include <netinet6/nd6.h>
9755505Sshin
9855505Sshin#include <arpa/inet.h>
9955505Sshin
100270655Sae#include <ctype.h>
10155505Sshin#include <netdb.h>
10255505Sshin#include <errno.h>
10355505Sshin#include <nlist.h>
10455505Sshin#include <stdio.h>
10555505Sshin#include <string.h>
10655505Sshin#include <paths.h>
10755505Sshin#include <err.h>
10855505Sshin#include <stdlib.h>
10955505Sshin#include <fcntl.h>
11055505Sshin#include <unistd.h>
11155505Sshin#include "gmt2local.h"
11255505Sshin
11355505Sshin/* packing rule for routing socket */
11462590Sitojun#define ROUNDUP(a) \
11555505Sshin	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
11662590Sitojun#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
11755505Sshin
118186119Sqingli#define NEXTADDR(w, s) \
119186119Sqingli	if (rtm->rtm_addrs & (w)) { \
120196866Sbz		bcopy((char *)&s, cp, sizeof(s)); cp += SA_SIZE(&s);}
121186119Sqingli
122186119Sqingli
123100650Sjmallettstatic pid_t pid;
12462590Sitojunstatic int nflag;
12562590Sitojunstatic int tflag;
12662590Sitojunstatic int32_t thiszone;	/* time difference with gmt */
12762590Sitojunstatic int s = -1;
12862590Sitojunstatic int repeat = 0;
12955505Sshin
130287735Shrsstatic char host_buf[NI_MAXHOST];	/* getnameinfo() */
131287735Shrsstatic char ifix_buf[IFNAMSIZ];		/* if_indextoname() */
13255505Sshin
133270655Saestatic int file(char *);
134287735Shrsstatic void getsocket(void);
135287735Shrsstatic int set(int, char **);
136287735Shrsstatic void get(char *);
137287735Shrsstatic int delete(char *);
138287735Shrsstatic void dump(struct sockaddr_in6 *, int);
139173412Skevlostatic struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int);
140173412Skevlostatic char *ether_str(struct sockaddr_dl *);
141287735Shrsstatic int ndp_ether_aton(char *, u_char *);
142287735Shrsstatic void usage(void);
143287735Shrsstatic int rtmsg(int);
144287735Shrsstatic void ifinfo(char *, int, char **);
145287735Shrsstatic void rtrlist(void);
146287735Shrsstatic void plist(void);
147287735Shrsstatic void pfx_flush(void);
148287735Shrsstatic void rtr_flush(void);
149287735Shrsstatic void harmonize_rtr(void);
15062590Sitojun#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
151173412Skevlostatic void getdefif(void);
152173412Skevlostatic void setdefif(char *);
15362590Sitojun#endif
154173412Skevlostatic char *sec2str(time_t);
155253999Shrsstatic void ts_print(const struct timeval *);
15655505Sshin
15778064Sumestatic char *rtpref_str[] = {
15878064Sume	"medium",		/* 00 */
15978064Sume	"high",			/* 01 */
16078064Sume	"rsv",			/* 10 */
16178064Sume	"low"			/* 11 */
16278064Sume};
16378064Sume
16455505Sshinint
165287735Shrsmain(int argc, char **argv)
16655505Sshin{
167287735Shrs	int ch, mode = 0;
168287735Shrs	char *arg = NULL;
16955505Sshin
17055505Sshin	pid = getpid();
17155505Sshin	thiszone = gmt2local(0);
172122615Sume	while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1)
173121156Sume		switch (ch) {
17455505Sshin		case 'a':
17555505Sshin		case 'c':
176122615Sume		case 'p':
177122615Sume		case 'r':
178122615Sume		case 'H':
179122615Sume		case 'P':
180122615Sume		case 'R':
181122615Sume		case 's':
182122615Sume		case 'I':
183122615Sume			if (mode) {
184122615Sume				usage();
185122615Sume				/*NOTREACHED*/
186122615Sume			}
187122615Sume			mode = ch;
188122615Sume			arg = NULL;
18955505Sshin			break;
190270655Sae		case 'f':
191270655Sae			exit(file(optarg) ? 1 : 0);
19255505Sshin		case 'd':
193270655Sae		case 'i':
194122615Sume			if (mode) {
19555505Sshin				usage();
196122615Sume				/*NOTREACHED*/
197122615Sume			}
198122615Sume			mode = ch;
199122615Sume			arg = optarg;
200122615Sume			break;
20155505Sshin		case 'n':
20255505Sshin			nflag = 1;
20355505Sshin			break;
20455505Sshin		case 't':
20555505Sshin			tflag = 1;
20655505Sshin			break;
20755505Sshin		case 'A':
208122615Sume			if (mode) {
209122615Sume				usage();
210122615Sume				/*NOTREACHED*/
211122615Sume			}
212122615Sume			mode = 'a';
21355505Sshin			repeat = atoi(optarg);
214122615Sume			if (repeat < 0) {
21555505Sshin				usage();
216122615Sume				/*NOTREACHED*/
217122615Sume			}
21855505Sshin			break;
21955505Sshin		default:
22055505Sshin			usage();
22155505Sshin		}
22255505Sshin
22355505Sshin	argc -= optind;
22455505Sshin	argv += optind;
22555505Sshin
226122615Sume	switch (mode) {
227122615Sume	case 'a':
228122615Sume	case 'c':
229122615Sume		if (argc != 0) {
23055505Sshin			usage();
231122615Sume			/*NOTREACHED*/
232122615Sume		}
233122615Sume		dump(0, mode == 'c');
234122615Sume		break;
235122615Sume	case 'd':
236122615Sume		if (argc != 0) {
237122615Sume			usage();
238122615Sume			/*NOTREACHED*/
239122615Sume		}
240122615Sume		delete(arg);
241122615Sume		break;
242122615Sume	case 'I':
243122615Sume#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
244122615Sume		if (argc > 1) {
245122615Sume			usage();
246122615Sume			/*NOTREACHED*/
247122615Sume		} else if (argc == 1) {
248122615Sume			if (strcmp(*argv, "delete") == 0 ||
249122615Sume			    if_nametoindex(*argv))
250122615Sume				setdefif(*argv);
251122615Sume			else
252122615Sume				errx(1, "invalid interface %s", *argv);
253122615Sume		}
254122615Sume		getdefif(); /* always call it to print the result */
255122615Sume		break;
256122615Sume#else
257122615Sume		errx(1, "not supported yet");
258122615Sume		/*NOTREACHED*/
259122615Sume#endif
260122615Sume	case 'p':
261122615Sume		if (argc != 0) {
262122615Sume			usage();
263122615Sume			/*NOTREACHED*/
264122615Sume		}
26555505Sshin		plist();
266122615Sume		break;
267122615Sume	case 'i':
268122615Sume		ifinfo(arg, argc, argv);
269122615Sume		break;
270122615Sume	case 'r':
271122615Sume		if (argc != 0) {
272122615Sume			usage();
273122615Sume			/*NOTREACHED*/
274122615Sume		}
27555505Sshin		rtrlist();
276122615Sume		break;
277122615Sume	case 's':
27855505Sshin		if (argc < 2 || argc > 4)
27955505Sshin			usage();
28055505Sshin		exit(set(argc, argv) ? 1 : 0);
281122615Sume	case 'H':
282122615Sume		if (argc != 0) {
283122615Sume			usage();
284122615Sume			/*NOTREACHED*/
285122615Sume		}
28655505Sshin		harmonize_rtr();
287122615Sume		break;
288122615Sume	case 'P':
289122615Sume		if (argc != 0) {
290122615Sume			usage();
291122615Sume			/*NOTREACHED*/
292122615Sume		}
29355505Sshin		pfx_flush();
294122615Sume		break;
295122615Sume	case 'R':
296122615Sume		if (argc != 0) {
297122615Sume			usage();
298122615Sume			/*NOTREACHED*/
299122615Sume		}
30055505Sshin		rtr_flush();
301122615Sume		break;
302122615Sume	case 0:
303122615Sume		if (argc != 1) {
304122615Sume			usage();
305122615Sume			/*NOTREACHED*/
306122615Sume		}
307122615Sume		get(argv[0]);
308122615Sume		break;
30955505Sshin	}
31055505Sshin	exit(0);
31155505Sshin}
31255505Sshin
31355505Sshin/*
31455505Sshin * Process a file to set standard ndp entries
31555505Sshin */
316270655Saestatic int
317287735Shrsfile(char *name)
31855505Sshin{
31955505Sshin	FILE *fp;
32055505Sshin	int i, retval;
321270655Sae	char line[100], arg[5][50], *args[5], *p;
32255505Sshin
323270655Sae	if ((fp = fopen(name, "r")) == NULL)
324270655Sae		err(1, "cannot open %s", name);
32555505Sshin	args[0] = &arg[0][0];
32655505Sshin	args[1] = &arg[1][0];
32755505Sshin	args[2] = &arg[2][0];
32855505Sshin	args[3] = &arg[3][0];
32955505Sshin	args[4] = &arg[4][0];
33055505Sshin	retval = 0;
331167260Skevlo	while (fgets(line, sizeof(line), fp) != NULL) {
332270655Sae		if ((p = strchr(line, '#')) != NULL)
333270655Sae			*p = '\0';
334270655Sae		for (p = line; isblank(*p); p++);
335270655Sae		if (*p == '\n' || *p == '\0')
336270655Sae			continue;
337122615Sume		i = sscanf(line, "%49s %49s %49s %49s %49s",
338122615Sume		    arg[0], arg[1], arg[2], arg[3], arg[4]);
33955505Sshin		if (i < 2) {
340270655Sae			warnx("bad line: %s", line);
34155505Sshin			retval = 1;
34255505Sshin			continue;
34355505Sshin		}
34455505Sshin		if (set(i, args))
34555505Sshin			retval = 1;
34655505Sshin	}
34755505Sshin	fclose(fp);
34855505Sshin	return (retval);
34955505Sshin}
35055505Sshin
351287735Shrsstatic void
35255505Sshingetsocket()
35355505Sshin{
35455505Sshin	if (s < 0) {
35555505Sshin		s = socket(PF_ROUTE, SOCK_RAW, 0);
35655505Sshin		if (s < 0) {
357121156Sume			err(1, "socket");
358121156Sume			/* NOTREACHED */
35955505Sshin		}
36055505Sshin	}
36155505Sshin}
36255505Sshin
363287735Shrsstatic struct sockaddr_in6 so_mask = {
364287735Shrs	.sin6_len = sizeof(so_mask),
365287735Shrs	.sin6_family = AF_INET6
366287735Shrs};
367287735Shrsstatic struct sockaddr_in6 blank_sin = {
368287735Shrs	.sin6_len = sizeof(blank_sin),
369287735Shrs	.sin6_family = AF_INET6
370287735Shrs};
371287735Shrsstatic struct sockaddr_in6 sin_m;
372287735Shrsstatic struct sockaddr_dl blank_sdl = {
373287735Shrs	.sdl_len = sizeof(blank_sdl),
374287735Shrs	.sdl_family = AF_LINK
375287735Shrs};
376287735Shrsstatic struct sockaddr_dl sdl_m;
377287735Shrsstatic time_t expire_time;
378287735Shrsstatic int flags, found_entry;
379287735Shrsstatic struct {
38055505Sshin	struct	rt_msghdr m_rtm;
38155505Sshin	char	m_space[512];
382287735Shrs} m_rtmsg;
38355505Sshin
38455505Sshin/*
38555505Sshin * Set an individual neighbor cache entry
38655505Sshin */
387287735Shrsstatic int
388287735Shrsset(int argc, char **argv)
38955505Sshin{
39055505Sshin	register struct sockaddr_in6 *sin = &sin_m;
39155505Sshin	register struct sockaddr_dl *sdl;
39255505Sshin	register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
39355505Sshin	struct addrinfo hints, *res;
39455505Sshin	int gai_error;
39555505Sshin	u_char *ea;
39655505Sshin	char *host = argv[0], *eaddr = argv[1];
39755505Sshin
39855505Sshin	getsocket();
39955505Sshin	argc -= 2;
40055505Sshin	argv += 2;
40155505Sshin	sdl_m = blank_sdl;
40255505Sshin	sin_m = blank_sin;
40355505Sshin
40455505Sshin	bzero(&hints, sizeof(hints));
40555505Sshin	hints.ai_family = AF_INET6;
40655505Sshin	gai_error = getaddrinfo(host, NULL, &hints, &res);
40755505Sshin	if (gai_error) {
40855505Sshin		fprintf(stderr, "ndp: %s: %s\n", host,
40955505Sshin			gai_strerror(gai_error));
41055505Sshin		return 1;
41155505Sshin	}
41255505Sshin	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
413243903Shrs	sin->sin6_scope_id =
414243903Shrs	    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
41555505Sshin	ea = (u_char *)LLADDR(&sdl_m);
41655505Sshin	if (ndp_ether_aton(eaddr, ea) == 0)
41755505Sshin		sdl_m.sdl_alen = 6;
41855505Sshin	flags = expire_time = 0;
41955505Sshin	while (argc-- > 0) {
42055505Sshin		if (strncmp(argv[0], "temp", 4) == 0) {
421253999Shrs			struct timeval now;
422121156Sume
423253999Shrs			gettimeofday(&now, 0);
424253970Shrs			expire_time = now.tv_sec + 20 * 60;
42562590Sitojun		} else if (strncmp(argv[0], "proxy", 5) == 0)
42662590Sitojun			flags |= RTF_ANNOUNCE;
42755505Sshin		argv++;
42855505Sshin	}
42955505Sshin	if (rtmsg(RTM_GET) < 0) {
430121156Sume		errx(1, "RTM_GET(%s) failed", host);
431121156Sume		/* NOTREACHED */
43255505Sshin	}
43355505Sshin	sin = (struct sockaddr_in6 *)(rtm + 1);
43455505Sshin	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
43555505Sshin	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
43655505Sshin		if (sdl->sdl_family == AF_LINK &&
437122615Sume		    !(rtm->rtm_flags & RTF_GATEWAY)) {
438122615Sume			switch (sdl->sdl_type) {
439122615Sume			case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
440122615Sume			case IFT_ISO88024: case IFT_ISO88025:
441210936Sjhb			case IFT_L2VLAN: case IFT_BRIDGE:
442122615Sume				goto overwrite;
443122615Sume			}
44455505Sshin		}
44562590Sitojun		fprintf(stderr, "set: cannot configure a new entry\n");
44662590Sitojun		return 1;
44755505Sshin	}
44862590Sitojun
44955505Sshinoverwrite:
45055505Sshin	if (sdl->sdl_family != AF_LINK) {
45155505Sshin		printf("cannot intuit interface index and type for %s\n", host);
45255505Sshin		return (1);
45355505Sshin	}
45455505Sshin	sdl_m.sdl_type = sdl->sdl_type;
45555505Sshin	sdl_m.sdl_index = sdl->sdl_index;
45655505Sshin	return (rtmsg(RTM_ADD));
45755505Sshin}
45855505Sshin
45955505Sshin/*
46055505Sshin * Display an individual neighbor cache entry
46155505Sshin */
462287735Shrsstatic void
463287735Shrsget(char *host)
46455505Sshin{
46555505Sshin	struct sockaddr_in6 *sin = &sin_m;
46655505Sshin	struct addrinfo hints, *res;
46755505Sshin	int gai_error;
46855505Sshin
46955505Sshin	sin_m = blank_sin;
47055505Sshin	bzero(&hints, sizeof(hints));
47155505Sshin	hints.ai_family = AF_INET6;
47255505Sshin	gai_error = getaddrinfo(host, NULL, &hints, &res);
47355505Sshin	if (gai_error) {
47455505Sshin		fprintf(stderr, "ndp: %s: %s\n", host,
475121156Sume		    gai_strerror(gai_error));
47655505Sshin		return;
47755505Sshin	}
47855505Sshin	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
479287735Shrs	sin->sin6_scope_id =
480287735Shrs	    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
481287735Shrs	dump(sin, 0);
48255505Sshin	if (found_entry == 0) {
48355505Sshin		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
484121156Sume		    sizeof(host_buf), NULL ,0,
485121156Sume		    (nflag ? NI_NUMERICHOST : 0));
48655505Sshin		printf("%s (%s) -- no entry\n", host, host_buf);
48755505Sshin		exit(1);
48855505Sshin	}
48955505Sshin}
49055505Sshin
49155505Sshin/*
49255505Sshin * Delete a neighbor cache entry
49355505Sshin */
494287735Shrsstatic int
495287735Shrsdelete(char *host)
49655505Sshin{
49755505Sshin	struct sockaddr_in6 *sin = &sin_m;
49855505Sshin	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
499186119Sqingli	register char *cp = m_rtmsg.m_space;
50055505Sshin	struct sockaddr_dl *sdl;
50155505Sshin	struct addrinfo hints, *res;
50255505Sshin	int gai_error;
50355505Sshin
50455505Sshin	getsocket();
50555505Sshin	sin_m = blank_sin;
50655505Sshin
50755505Sshin	bzero(&hints, sizeof(hints));
50855505Sshin	hints.ai_family = AF_INET6;
50955505Sshin	gai_error = getaddrinfo(host, NULL, &hints, &res);
51055505Sshin	if (gai_error) {
51155505Sshin		fprintf(stderr, "ndp: %s: %s\n", host,
512121156Sume		    gai_strerror(gai_error));
51355505Sshin		return 1;
51455505Sshin	}
51555505Sshin	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
516243903Shrs	sin->sin6_scope_id =
517243903Shrs	    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
51855505Sshin	if (rtmsg(RTM_GET) < 0) {
519121156Sume		errx(1, "RTM_GET(%s) failed", host);
520121156Sume		/* NOTREACHED */
52155505Sshin	}
52255505Sshin	sin = (struct sockaddr_in6 *)(rtm + 1);
52355505Sshin	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
52455505Sshin	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
52555505Sshin		if (sdl->sdl_family == AF_LINK &&
52662590Sitojun		    !(rtm->rtm_flags & RTF_GATEWAY)) {
52778064Sume			goto delete;
52855505Sshin		}
52962590Sitojun		fprintf(stderr, "delete: cannot delete non-NDP entry\n");
53062590Sitojun		return 1;
53155505Sshin	}
53262590Sitojun
53355505Sshindelete:
53455505Sshin	if (sdl->sdl_family != AF_LINK) {
53555505Sshin		printf("cannot locate %s\n", host);
53655505Sshin		return (1);
53755505Sshin	}
538186119Sqingli        /*
539186119Sqingli         * need to reinit the field because it has rt_key
540186119Sqingli         * but we want the actual address
541186119Sqingli         */
542186119Sqingli	NEXTADDR(RTA_DST, sin_m);
543186500Sqingli	rtm->rtm_flags |= RTF_LLDATA;
54455505Sshin	if (rtmsg(RTM_DELETE) == 0) {
545243903Shrs		getnameinfo((struct sockaddr *)sin,
546243903Shrs		    sin->sin6_len, host_buf,
547121156Sume		    sizeof(host_buf), NULL, 0,
548121156Sume		    (nflag ? NI_NUMERICHOST : 0));
54955505Sshin		printf("%s (%s) deleted\n", host, host_buf);
55055505Sshin	}
55155505Sshin
55255505Sshin	return 0;
55355505Sshin}
55455505Sshin
555122615Sume#define W_ADDR	36
55678064Sume#define W_LL	17
55778064Sume#define W_IF	6
55878064Sume
55955505Sshin/*
56055505Sshin * Dump the entire neighbor cache
56155505Sshin */
562287735Shrsstatic void
563287735Shrsdump(struct sockaddr_in6 *addr, int cflag)
56455505Sshin{
56555505Sshin	int mib[6];
56655505Sshin	size_t needed;
56762590Sitojun	char *lim, *buf, *next;
56855505Sshin	struct rt_msghdr *rtm;
56955505Sshin	struct sockaddr_in6 *sin;
57055505Sshin	struct sockaddr_dl *sdl;
57155505Sshin	extern int h_errno;
57255505Sshin	struct in6_nbrinfo *nbi;
573253999Shrs	struct timeval now;
57455505Sshin	int addrwidth;
57578064Sume	int llwidth;
57678064Sume	int ifwidth;
57762590Sitojun	char flgbuf[8];
57878064Sume	char *ifname;
57955505Sshin
58055505Sshin	/* Print header */
58166865Ssumikawa	if (!tflag && !cflag)
582122615Sume		printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n",
58378064Sume		    W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address",
584122615Sume		    W_IF, W_IF, "Netif", "Expire", "S", "Flags");
58555505Sshin
58655505Sshinagain:;
58755505Sshin	mib[0] = CTL_NET;
58855505Sshin	mib[1] = PF_ROUTE;
58955505Sshin	mib[2] = 0;
59055505Sshin	mib[3] = AF_INET6;
59155505Sshin	mib[4] = NET_RT_FLAGS;
592186119Sqingli#ifdef RTF_LLINFO
59355505Sshin	mib[5] = RTF_LLINFO;
594186119Sqingli#else
595186119Sqingli	mib[5] = 0;
596186119Sqingli#endif
59755505Sshin	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
59855505Sshin		err(1, "sysctl(PF_ROUTE estimate)");
59955505Sshin	if (needed > 0) {
60055505Sshin		if ((buf = malloc(needed)) == NULL)
601121156Sume			err(1, "malloc");
60255505Sshin		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
60355505Sshin			err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
60455505Sshin		lim = buf + needed;
60555505Sshin	} else
60655505Sshin		buf = lim = NULL;
60755505Sshin
60855505Sshin	for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
60955505Sshin		int isrouter = 0, prbs = 0;
61055505Sshin
61155505Sshin		rtm = (struct rt_msghdr *)next;
61255505Sshin		sin = (struct sockaddr_in6 *)(rtm + 1);
61355505Sshin		sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len));
61478064Sume
61578064Sume		/*
61678064Sume		 * Some OSes can produce a route that has the LINK flag but
61778064Sume		 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
61878064Sume		 * and BSD/OS, where xx is not the interface identifier on
61978064Sume		 * lo0).  Such routes entry would annoy getnbrinfo() below,
62078064Sume		 * so we skip them.
62178064Sume		 * XXX: such routes should have the GATEWAY flag, not the
622121156Sume		 * LINK flag.  However, there is rotten routing software
62378064Sume		 * that advertises all routes that have the GATEWAY flag.
62478064Sume		 * Thus, KAME kernel intentionally does not set the LINK flag.
62578064Sume		 * What is to be fixed is not ndp, but such routing software
62678064Sume		 * (and the kernel workaround)...
62778064Sume		 */
62878064Sume		if (sdl->sdl_family != AF_LINK)
62978064Sume			continue;
63078064Sume
631122615Sume		if (!(rtm->rtm_flags & RTF_HOST))
632122615Sume			continue;
633122615Sume
63455505Sshin		if (addr) {
635287735Shrs			if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
636287735Shrs			    &sin->sin6_addr) == 0 ||
637287735Shrs			    addr->sin6_scope_id != sin->sin6_scope_id)
63855505Sshin				continue;
63955505Sshin			found_entry = 1;
64055505Sshin		} else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
64155505Sshin			continue;
64255505Sshin		if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
64355505Sshin		    IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
64455505Sshin			/* XXX: should scope id be filled in the kernel? */
64555505Sshin			if (sin->sin6_scope_id == 0)
64655505Sshin				sin->sin6_scope_id = sdl->sdl_index;
64755505Sshin		}
64855505Sshin		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
649121156Sume		    sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0));
650122615Sume		if (cflag) {
65181366Ssumikawa#ifdef RTF_WASCLONED
65281366Ssumikawa			if (rtm->rtm_flags & RTF_WASCLONED)
65381366Ssumikawa				delete(host_buf);
654122615Sume#elif defined(RTF_CLONED)
655122615Sume			if (rtm->rtm_flags & RTF_CLONED)
656122615Sume				delete(host_buf);
65781366Ssumikawa#else
658294203Smelifaro			if (rtm->rtm_flags & RTF_PINNED)
659294203Smelifaro				continue;
66066865Ssumikawa			delete(host_buf);
66181366Ssumikawa#endif
66266865Ssumikawa			continue;
66366865Ssumikawa		}
664253999Shrs		gettimeofday(&now, 0);
66555505Sshin		if (tflag)
666253970Shrs			ts_print(&now);
66755505Sshin
66878064Sume		addrwidth = strlen(host_buf);
66978064Sume		if (addrwidth < W_ADDR)
67078064Sume			addrwidth = W_ADDR;
67178064Sume		llwidth = strlen(ether_str(sdl));
67278064Sume		if (W_ADDR + W_LL - addrwidth > llwidth)
67378064Sume			llwidth = W_ADDR + W_LL - addrwidth;
67478064Sume		ifname = if_indextoname(sdl->sdl_index, ifix_buf);
67578064Sume		if (!ifname)
67678064Sume			ifname = "?";
67778064Sume		ifwidth = strlen(ifname);
67878064Sume		if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
67978064Sume			ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
68055505Sshin
68178064Sume		printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf,
68278064Sume		    llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
68355505Sshin
684293290Sbdrewery		/* Print neighbor discovery specific information */
68562590Sitojun		nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1);
68655505Sshin		if (nbi) {
687253970Shrs			if (nbi->expire > now.tv_sec) {
68855505Sshin				printf(" %-9.9s",
689253970Shrs				    sec2str(nbi->expire - now.tv_sec));
69078064Sume			} else if (nbi->expire == 0)
69155505Sshin				printf(" %-9.9s", "permanent");
69255505Sshin			else
69355505Sshin				printf(" %-9.9s", "expired");
69455505Sshin
695121156Sume			switch (nbi->state) {
696121156Sume			case ND6_LLINFO_NOSTATE:
69755505Sshin				 printf(" N");
69855505Sshin				 break;
69978064Sume#ifdef ND6_LLINFO_WAITDELETE
700121156Sume			case ND6_LLINFO_WAITDELETE:
70155505Sshin				 printf(" W");
70255505Sshin				 break;
70378064Sume#endif
704121156Sume			case ND6_LLINFO_INCOMPLETE:
70555505Sshin				 printf(" I");
70655505Sshin				 break;
707121156Sume			case ND6_LLINFO_REACHABLE:
70855505Sshin				 printf(" R");
70955505Sshin				 break;
710121156Sume			case ND6_LLINFO_STALE:
71155505Sshin				 printf(" S");
71255505Sshin				 break;
713121156Sume			case ND6_LLINFO_DELAY:
71455505Sshin				 printf(" D");
71555505Sshin				 break;
716121156Sume			case ND6_LLINFO_PROBE:
71755505Sshin				 printf(" P");
71855505Sshin				 break;
719121156Sume			default:
72055505Sshin				 printf(" ?");
72155505Sshin				 break;
72255505Sshin			}
72355505Sshin
72455505Sshin			isrouter = nbi->isrouter;
72555505Sshin			prbs = nbi->asked;
72678064Sume		} else {
72755505Sshin			warnx("failed to get neighbor information");
72855505Sshin			printf("  ");
72955505Sshin		}
73055505Sshin
73162590Sitojun		/*
73262590Sitojun		 * other flags. R: router, P: proxy, W: ??
73362590Sitojun		 */
73462590Sitojun		if ((rtm->rtm_addrs & RTA_NETMASK) == 0) {
73562590Sitojun			snprintf(flgbuf, sizeof(flgbuf), "%s%s",
736121156Sume			    isrouter ? "R" : "",
737121156Sume			    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
73862590Sitojun		} else {
73962590Sitojun			sin = (struct sockaddr_in6 *)
740121156Sume			    (sdl->sdl_len + (char *)sdl);
741122615Sume#if 0	/* W and P are mystery even for us */
74262590Sitojun			snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s",
743121156Sume			    isrouter ? "R" : "",
744121156Sume			    !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "P" : "",
745121156Sume			    (sin->sin6_len != sizeof(struct sockaddr_in6)) ? "W" : "",
746121156Sume			    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
747122615Sume#else
748122615Sume			snprintf(flgbuf, sizeof(flgbuf), "%s%s",
749122615Sume			    isrouter ? "R" : "",
750122615Sume			    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
751122615Sume#endif
75255505Sshin		}
753122615Sume		printf(" %s", flgbuf);
75455505Sshin
75555505Sshin		if (prbs)
756122615Sume			printf(" %d", prbs);
75755505Sshin
75855505Sshin		printf("\n");
75955505Sshin	}
76078064Sume	if (buf != NULL)
76178064Sume		free(buf);
76255505Sshin
76355505Sshin	if (repeat) {
76455505Sshin		printf("\n");
765125675Ssumikawa		fflush(stdout);
76655505Sshin		sleep(repeat);
76755505Sshin		goto again;
76855505Sshin	}
76955505Sshin}
77055505Sshin
77155505Sshinstatic struct in6_nbrinfo *
772287735Shrsgetnbrinfo(struct in6_addr *addr, int ifindex, int warning)
77355505Sshin{
77455505Sshin	static struct in6_nbrinfo nbi;
77555505Sshin	int s;
77655505Sshin
77755505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
77855505Sshin		err(1, "socket");
77955505Sshin
78055505Sshin	bzero(&nbi, sizeof(nbi));
78155505Sshin	if_indextoname(ifindex, nbi.ifname);
78255505Sshin	nbi.addr = *addr;
78355505Sshin	if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
78462590Sitojun		if (warning)
78562590Sitojun			warn("ioctl(SIOCGNBRINFO_IN6)");
78655505Sshin		close(s);
78755505Sshin		return(NULL);
78855505Sshin	}
78955505Sshin
79055505Sshin	close(s);
79155505Sshin	return(&nbi);
79255505Sshin}
79355505Sshin
79455505Sshinstatic char *
795217140Sdelphijether_str(struct sockaddr_dl *sdl)
79655505Sshin{
797121156Sume	static char hbuf[NI_MAXHOST];
79855505Sshin
799219819Sjeff	if (sdl->sdl_alen == ETHER_ADDR_LEN) {
800217140Sdelphij		strlcpy(hbuf, ether_ntoa((struct ether_addr *)LLADDR(sdl)),
801217140Sdelphij		    sizeof(hbuf));
802219819Sjeff	} else if (sdl->sdl_alen) {
803219819Sjeff		int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
804219819Sjeff		snprintf(hbuf, sizeof(hbuf), "%s", link_ntoa(sdl) + n);
805219819Sjeff	} else
806121156Sume		snprintf(hbuf, sizeof(hbuf), "(incomplete)");
80755505Sshin
808121156Sume	return(hbuf);
80955505Sshin}
81055505Sshin
811287735Shrsstatic int
812287735Shrsndp_ether_aton(char *a, u_char *n)
81355505Sshin{
81455505Sshin	int i, o[6];
81555505Sshin
81655505Sshin	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
817121156Sume	    &o[3], &o[4], &o[5]);
81855505Sshin	if (i != 6) {
81955505Sshin		fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a);
82055505Sshin		return (1);
82155505Sshin	}
822121156Sume	for (i = 0; i < 6; i++)
82355505Sshin		n[i] = o[i];
82455505Sshin	return (0);
82555505Sshin}
82655505Sshin
827287735Shrsstatic void
82855505Sshinusage()
82955505Sshin{
830122615Sume	printf("usage: ndp [-nt] hostname\n");
831122615Sume	printf("       ndp [-nt] -a | -c | -p | -r | -H | -P | -R\n");
83278064Sume	printf("       ndp [-nt] -A wait\n");
833122615Sume	printf("       ndp [-nt] -d hostname\n");
834122615Sume	printf("       ndp [-nt] -f filename\n");
835122615Sume	printf("       ndp [-nt] -i interface [flags...]\n");
83662590Sitojun#ifdef SIOCSDEFIFACE_IN6
837122615Sume	printf("       ndp [-nt] -I [interface|delete]\n");
83862590Sitojun#endif
839122615Sume	printf("       ndp [-nt] -s nodename etheraddr [temp] [proxy]\n");
84055505Sshin	exit(1);
84155505Sshin}
84255505Sshin
843287735Shrsstatic int
844287735Shrsrtmsg(int cmd)
84555505Sshin{
84655505Sshin	static int seq;
84755505Sshin	int rlen;
84855505Sshin	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
84955505Sshin	register char *cp = m_rtmsg.m_space;
85055505Sshin	register int l;
85155505Sshin
85255505Sshin	errno = 0;
85355505Sshin	if (cmd == RTM_DELETE)
85455505Sshin		goto doit;
85555505Sshin	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
85655505Sshin	rtm->rtm_flags = flags;
85755505Sshin	rtm->rtm_version = RTM_VERSION;
85855505Sshin
85955505Sshin	switch (cmd) {
86055505Sshin	default:
86155505Sshin		fprintf(stderr, "ndp: internal wrong cmd\n");
86255505Sshin		exit(1);
86355505Sshin	case RTM_ADD:
86455505Sshin		rtm->rtm_addrs |= RTA_GATEWAY;
865122615Sume		if (expire_time) {
866122615Sume			rtm->rtm_rmx.rmx_expire = expire_time;
867122615Sume			rtm->rtm_inits = RTV_EXPIRE;
868122615Sume		}
869186500Sqingli		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
870151473Ssuz#if 0 /* we don't support ipv6addr/128 type proxying */
87162590Sitojun		if (rtm->rtm_flags & RTF_ANNOUNCE) {
87262590Sitojun			rtm->rtm_flags &= ~RTF_HOST;
873124241Ssuz			rtm->rtm_addrs |= RTA_NETMASK;
87462590Sitojun		}
875151473Ssuz#endif
87655505Sshin		/* FALLTHROUGH */
87755505Sshin	case RTM_GET:
87855505Sshin		rtm->rtm_addrs |= RTA_DST;
87955505Sshin	}
88055505Sshin
88155505Sshin	NEXTADDR(RTA_DST, sin_m);
88255505Sshin	NEXTADDR(RTA_GATEWAY, sdl_m);
883151473Ssuz#if 0 /* we don't support ipv6addr/128 type proxying */
88462590Sitojun	memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
88555505Sshin	NEXTADDR(RTA_NETMASK, so_mask);
886151473Ssuz#endif
88755505Sshin
88855505Sshin	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
88955505Sshindoit:
89055505Sshin	l = rtm->rtm_msglen;
89155505Sshin	rtm->rtm_seq = ++seq;
89255505Sshin	rtm->rtm_type = cmd;
89355505Sshin	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
89455505Sshin		if (errno != ESRCH || cmd != RTM_DELETE) {
895121156Sume			err(1, "writing to routing socket");
896121156Sume			/* NOTREACHED */
89755505Sshin		}
89855505Sshin	}
89955505Sshin	do {
90055505Sshin		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
90155505Sshin	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
90255505Sshin	if (l < 0)
90355505Sshin		(void) fprintf(stderr, "ndp: read from routing socket: %s\n",
90455505Sshin		    strerror(errno));
90555505Sshin	return (0);
90655505Sshin}
90755505Sshin
908287735Shrsstatic void
909287735Shrsifinfo(char *ifname, int argc, char **argv)
91055505Sshin{
91155505Sshin	struct in6_ndireq nd;
91262590Sitojun	int i, s;
91362590Sitojun	u_int32_t newflags;
91478064Sume#ifdef IPV6CTL_USETEMPADDR
91578064Sume	u_int8_t nullbuf[8];
91678064Sume#endif
91755505Sshin
91855505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
919121156Sume		err(1, "socket");
920121156Sume		/* NOTREACHED */
92155505Sshin	}
92255505Sshin	bzero(&nd, sizeof(nd));
923121156Sume	strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
92455505Sshin	if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
925121156Sume		err(1, "ioctl(SIOCGIFINFO_IN6)");
926121156Sume		/* NOTREACHED */
927122615Sume	}
92862590Sitojun#define ND nd.ndi
92962590Sitojun	newflags = ND.flags;
930122615Sume	for (i = 0; i < argc; i++) {
93162590Sitojun		int clear = 0;
93262590Sitojun		char *cp = argv[i];
93362590Sitojun
93462590Sitojun		if (*cp == '-') {
93562590Sitojun			clear = 1;
93662590Sitojun			cp++;
93762590Sitojun		}
93862590Sitojun
93962590Sitojun#define SETFLAG(s, f) \
94062590Sitojun	do {\
94162590Sitojun		if (strcmp(cp, (s)) == 0) {\
94262590Sitojun			if (clear)\
94362590Sitojun				newflags &= ~(f);\
94462590Sitojun			else\
94562590Sitojun				newflags |= (f);\
94662590Sitojun		}\
94762590Sitojun	} while (0)
948151468Ssuz/*
949151468Ssuz * XXX: this macro is not 100% correct, in that it matches "nud" against
950151468Ssuz *      "nudbogus".  But we just let it go since this is minor.
951151468Ssuz */
952151468Ssuz#define SETVALUE(f, v) \
953151468Ssuz	do { \
954151468Ssuz		char *valptr; \
955151468Ssuz		unsigned long newval; \
956151468Ssuz		v = 0; /* unspecified */ \
957151468Ssuz		if (strncmp(cp, f, strlen(f)) == 0) { \
958151468Ssuz			valptr = strchr(cp, '='); \
959151468Ssuz			if (valptr == NULL) \
960151468Ssuz				err(1, "syntax error in %s field", (f)); \
961151468Ssuz			errno = 0; \
962151468Ssuz			newval = strtoul(++valptr, NULL, 0); \
963151468Ssuz			if (errno) \
964151468Ssuz				err(1, "syntax error in %s's value", (f)); \
965151468Ssuz			v = newval; \
966151468Ssuz		} \
967151468Ssuz	} while (0)
968151468Ssuz
969151474Ssuz		SETFLAG("disabled", ND6_IFF_IFDISABLED);
97062590Sitojun		SETFLAG("nud", ND6_IFF_PERFORMNUD);
971118498Sume#ifdef ND6_IFF_ACCEPT_RTADV
972118498Sume		SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV);
973118498Sume#endif
974197138Shrs#ifdef ND6_IFF_AUTO_LINKLOCAL
975197138Shrs		SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL);
976197138Shrs#endif
977245230Sume#ifdef ND6_IFF_NO_PREFER_IFACE
978245230Sume		SETFLAG("no_prefer_iface", ND6_IFF_NO_PREFER_IFACE);
979245230Sume#endif
980151468Ssuz		SETVALUE("basereachable", ND.basereachable);
981151468Ssuz		SETVALUE("retrans", ND.retrans);
982151468Ssuz		SETVALUE("curhlim", ND.chlim);
98362590Sitojun
98462590Sitojun		ND.flags = newflags;
985151468Ssuz		if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) {
986151468Ssuz			err(1, "ioctl(SIOCSIFINFO_IN6)");
987121156Sume			/* NOTREACHED */
98862590Sitojun		}
98962590Sitojun#undef SETFLAG
990151468Ssuz#undef SETVALUE
99162590Sitojun	}
99262590Sitojun
993121162Sume	if (!ND.initialized) {
994121162Sume		errx(1, "%s: not initialized yet", ifname);
995121162Sume		/* NOTREACHED */
996121162Sume	}
997121162Sume
998151468Ssuz	if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
999151468Ssuz		err(1, "ioctl(SIOCGIFINFO_IN6)");
1000151468Ssuz		/* NOTREACHED */
1001151468Ssuz	}
100255505Sshin	printf("linkmtu=%d", ND.linkmtu);
1003121471Sume	printf(", maxmtu=%d", ND.maxmtu);
100455505Sshin	printf(", curhlim=%d", ND.chlim);
100555505Sshin	printf(", basereachable=%ds%dms",
1006121156Sume	    ND.basereachable / 1000, ND.basereachable % 1000);
100755505Sshin	printf(", reachable=%ds", ND.reachable);
100862590Sitojun	printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000);
100978064Sume#ifdef IPV6CTL_USETEMPADDR
101078064Sume	memset(nullbuf, 0, sizeof(nullbuf));
101178064Sume	if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) {
101278064Sume		int j;
101378064Sume		u_int8_t *rbuf;
101478064Sume
101578064Sume		for (i = 0; i < 3; i++) {
1016121156Sume			switch (i) {
101778064Sume			case 0:
101878064Sume				printf("\nRandom seed(0): ");
101978064Sume				rbuf = ND.randomseed0;
102078064Sume				break;
102178064Sume			case 1:
102278064Sume				printf("\nRandom seed(1): ");
102378064Sume				rbuf = ND.randomseed1;
102478064Sume				break;
102578064Sume			case 2:
102678064Sume				printf("\nRandom ID:      ");
102778064Sume				rbuf = ND.randomid;
102878064Sume				break;
1029151472Ssuz			default:
1030151472Ssuz				errx(1, "impossible case for tempaddr display");
103178064Sume			}
103278064Sume			for (j = 0; j < 8; j++)
103378064Sume				printf("%02x", rbuf[j]);
103478064Sume		}
103578064Sume	}
103678064Sume#endif
103762590Sitojun	if (ND.flags) {
103862590Sitojun		printf("\nFlags: ");
1039151474Ssuz#ifdef ND6_IFF_IFDISABLED
1040151474Ssuz		if ((ND.flags & ND6_IFF_IFDISABLED))
1041151474Ssuz			printf("disabled ");
1042151474Ssuz#endif
1043118498Sume		if ((ND.flags & ND6_IFF_PERFORMNUD))
1044118498Sume			printf("nud ");
1045118498Sume#ifdef ND6_IFF_ACCEPT_RTADV
1046118498Sume		if ((ND.flags & ND6_IFF_ACCEPT_RTADV))
1047118498Sume			printf("accept_rtadv ");
1048118498Sume#endif
1049197138Shrs#ifdef ND6_IFF_AUTO_LINKLOCAL
1050197138Shrs		if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL))
1051197138Shrs			printf("auto_linklocal ");
1052197138Shrs#endif
1053245230Sume#ifdef ND6_IFF_NO_PREFER_IFACE
1054245230Sume		if ((ND.flags & ND6_IFF_NO_PREFER_IFACE))
1055245230Sume			printf("no_prefer_iface ");
1056245230Sume#endif
1057122615Sume	}
105862590Sitojun	putc('\n', stdout);
105955505Sshin#undef ND
1060121156Sume
106155505Sshin	close(s);
106255505Sshin}
106355505Sshin
106478064Sume#ifndef ND_RA_FLAG_RTPREF_MASK	/* XXX: just for compilation on *BSD release */
106578064Sume#define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
106678064Sume#endif
106778064Sume
1068287735Shrsstatic void
106955505Sshinrtrlist()
107055505Sshin{
107178064Sume	int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST };
107278064Sume	char *buf;
107378064Sume	struct in6_defrouter *p, *ep;
107478064Sume	size_t l;
1075253999Shrs	struct timeval now;
107678064Sume
107778064Sume	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
107878064Sume		err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
107978064Sume		/*NOTREACHED*/
108078064Sume	}
1081151472Ssuz	if (l == 0)
1082151472Ssuz		return;
108378064Sume	buf = malloc(l);
108478064Sume	if (!buf) {
1085121156Sume		err(1, "malloc");
108678064Sume		/*NOTREACHED*/
108778064Sume	}
108878064Sume	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
108978064Sume		err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
109078064Sume		/*NOTREACHED*/
109178064Sume	}
109278064Sume
109378064Sume	ep = (struct in6_defrouter *)(buf + l);
109478064Sume	for (p = (struct in6_defrouter *)buf; p < ep; p++) {
109578064Sume		int rtpref;
109678064Sume
109778064Sume		if (getnameinfo((struct sockaddr *)&p->rtaddr,
109878064Sume		    p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0,
1099121156Sume		    (nflag ? NI_NUMERICHOST : 0)) != 0)
110078064Sume			strlcpy(host_buf, "?", sizeof(host_buf));
1101121156Sume
110278064Sume		printf("%s if=%s", host_buf,
1103121156Sume		    if_indextoname(p->if_index, ifix_buf));
110478064Sume		printf(", flags=%s%s",
1105121156Sume		    p->flags & ND_RA_FLAG_MANAGED ? "M" : "",
1106121156Sume		    p->flags & ND_RA_FLAG_OTHER   ? "O" : "");
110778064Sume		rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff;
110878064Sume		printf(", pref=%s", rtpref_str[rtpref]);
1109121156Sume
1110253999Shrs		gettimeofday(&now, 0);
111178064Sume		if (p->expire == 0)
111278064Sume			printf(", expire=Never\n");
111378064Sume		else
111478064Sume			printf(", expire=%s\n",
1115253970Shrs			    sec2str(p->expire - now.tv_sec));
111678064Sume	}
111778064Sume	free(buf);
111855505Sshin}
111955505Sshin
1120287735Shrsstatic void
112155505Sshinplist()
112255505Sshin{
112378064Sume	int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST };
112478064Sume	char *buf;
112578064Sume	struct in6_prefix *p, *ep, *n;
112678064Sume	struct sockaddr_in6 *advrtr;
112778064Sume	size_t l;
1128253999Shrs	struct timeval now;
112978064Sume	const int niflags = NI_NUMERICHOST;
113078064Sume	int ninflags = nflag ? NI_NUMERICHOST : 0;
113178064Sume	char namebuf[NI_MAXHOST];
113278064Sume
113378064Sume	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
113478064Sume		err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
113578064Sume		/*NOTREACHED*/
113678064Sume	}
113778064Sume	buf = malloc(l);
113878064Sume	if (!buf) {
1139121156Sume		err(1, "malloc");
114078064Sume		/*NOTREACHED*/
114178064Sume	}
114278064Sume	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
114378064Sume		err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
114478064Sume		/*NOTREACHED*/
114578064Sume	}
114678064Sume
114778064Sume	ep = (struct in6_prefix *)(buf + l);
114878064Sume	for (p = (struct in6_prefix *)buf; p < ep; p = n) {
114978064Sume		advrtr = (struct sockaddr_in6 *)(p + 1);
115078064Sume		n = (struct in6_prefix *)&advrtr[p->advrtrs];
115178064Sume
115278064Sume		if (getnameinfo((struct sockaddr *)&p->prefix,
115378064Sume		    p->prefix.sin6_len, namebuf, sizeof(namebuf),
115478064Sume		    NULL, 0, niflags) != 0)
115578064Sume			strlcpy(namebuf, "?", sizeof(namebuf));
115678064Sume		printf("%s/%d if=%s\n", namebuf, p->prefixlen,
1157121156Sume		    if_indextoname(p->if_index, ifix_buf));
115878064Sume
1159253999Shrs		gettimeofday(&now, 0);
116078064Sume		/*
116178064Sume		 * meaning of fields, especially flags, is very different
116278064Sume		 * by origin.  notify the difference to the users.
116378064Sume		 */
116478064Sume		printf("flags=%s%s%s%s%s",
1165121156Sume		    p->raflags.onlink ? "L" : "",
1166121156Sume		    p->raflags.autonomous ? "A" : "",
1167121156Sume		    (p->flags & NDPRF_ONLINK) != 0 ? "O" : "",
1168121156Sume		    (p->flags & NDPRF_DETACHED) != 0 ? "D" : "",
116978064Sume#ifdef NDPRF_HOME
1170121156Sume		    (p->flags & NDPRF_HOME) != 0 ? "H" : ""
117178064Sume#else
1172121156Sume		    ""
117378064Sume#endif
1174121156Sume		    );
117578064Sume		if (p->vltime == ND6_INFINITE_LIFETIME)
117678064Sume			printf(" vltime=infinity");
117778064Sume		else
1178122615Sume			printf(" vltime=%lu", (unsigned long)p->vltime);
117978064Sume		if (p->pltime == ND6_INFINITE_LIFETIME)
118078064Sume			printf(", pltime=infinity");
118178064Sume		else
1182122615Sume			printf(", pltime=%lu", (unsigned long)p->pltime);
118378064Sume		if (p->expire == 0)
118478064Sume			printf(", expire=Never");
1185253970Shrs		else if (p->expire >= now.tv_sec)
118678064Sume			printf(", expire=%s",
1187253970Shrs			    sec2str(p->expire - now.tv_sec));
118878064Sume		else
118978064Sume			printf(", expired");
119078064Sume		printf(", ref=%d", p->refcnt);
119178064Sume		printf("\n");
119278064Sume		/*
119378064Sume		 * "advertising router" list is meaningful only if the prefix
119478064Sume		 * information is from RA.
119578064Sume		 */
119678064Sume		if (p->advrtrs) {
119778064Sume			int j;
119878064Sume			struct sockaddr_in6 *sin6;
119978064Sume
1200122615Sume			sin6 = advrtr;
120178064Sume			printf("  advertised by\n");
120278064Sume			for (j = 0; j < p->advrtrs; j++) {
120378064Sume				struct in6_nbrinfo *nbi;
120478064Sume
120578064Sume				if (getnameinfo((struct sockaddr *)sin6,
120678064Sume				    sin6->sin6_len, namebuf, sizeof(namebuf),
120778064Sume				    NULL, 0, ninflags) != 0)
120878064Sume					strlcpy(namebuf, "?", sizeof(namebuf));
120978064Sume				printf("    %s", namebuf);
121078064Sume
1211121156Sume				nbi = getnbrinfo(&sin6->sin6_addr,
1212121156Sume				    p->if_index, 0);
121378064Sume				if (nbi) {
1214121156Sume					switch (nbi->state) {
121578064Sume					case ND6_LLINFO_REACHABLE:
121678064Sume					case ND6_LLINFO_STALE:
121778064Sume					case ND6_LLINFO_DELAY:
121878064Sume					case ND6_LLINFO_PROBE:
121978064Sume						printf(" (reachable)\n");
122078064Sume						break;
122178064Sume					default:
122278064Sume						printf(" (unreachable)\n");
122378064Sume					}
122478064Sume				} else
122578064Sume					printf(" (no neighbor state)\n");
122678064Sume				sin6++;
122778064Sume			}
122878064Sume		} else
122978064Sume			printf("  No advertising router\n");
123078064Sume	}
123178064Sume	free(buf);
123255505Sshin}
123355505Sshin
1234287735Shrsstatic void
123555505Sshinpfx_flush()
123655505Sshin{
123755505Sshin	char dummyif[IFNAMSIZ+8];
123855505Sshin	int s;
123955505Sshin
124055505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
124155505Sshin		err(1, "socket");
1242121156Sume	strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
124355505Sshin	if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
1244121156Sume		err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
124555505Sshin}
124655505Sshin
1247287735Shrsstatic void
124855505Sshinrtr_flush()
124955505Sshin{
125055505Sshin	char dummyif[IFNAMSIZ+8];
125155505Sshin	int s;
125255505Sshin
125355505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
125455505Sshin		err(1, "socket");
1255121156Sume	strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
125655505Sshin	if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
1257121156Sume		err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
125862590Sitojun
125962590Sitojun	close(s);
126055505Sshin}
126155505Sshin
1262287735Shrsstatic void
126355505Sshinharmonize_rtr()
126455505Sshin{
126555505Sshin	char dummyif[IFNAMSIZ+8];
126655505Sshin	int s;
126755505Sshin
126862590Sitojun	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
126962590Sitojun		err(1, "socket");
1270121156Sume	strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
127162590Sitojun	if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0)
1272121156Sume		err(1, "ioctl(SIOCSNDFLUSH_IN6)");
127362590Sitojun
127462590Sitojun	close(s);
127555505Sshin}
127655505Sshin
127762590Sitojun#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
127862590Sitojunstatic void
1279287735Shrssetdefif(char *ifname)
128062590Sitojun{
128162590Sitojun	struct in6_ndifreq ndifreq;
128262590Sitojun	unsigned int ifindex;
128362590Sitojun
128462590Sitojun	if (strcasecmp(ifname, "delete") == 0)
128562590Sitojun		ifindex = 0;
128662590Sitojun	else {
128762590Sitojun		if ((ifindex = if_nametoindex(ifname)) == 0)
128862590Sitojun			err(1, "failed to resolve i/f index for %s", ifname);
128962590Sitojun	}
129062590Sitojun
129162590Sitojun	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
129262590Sitojun		err(1, "socket");
129362590Sitojun
1294121156Sume	strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
129562590Sitojun	ndifreq.ifindex = ifindex;
129662590Sitojun
129762590Sitojun	if (ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1298121156Sume		err(1, "ioctl(SIOCSDEFIFACE_IN6)");
129962590Sitojun
130062590Sitojun	close(s);
130162590Sitojun}
130262590Sitojun
130362590Sitojunstatic void
130462590Sitojungetdefif()
130562590Sitojun{
130662590Sitojun	struct in6_ndifreq ndifreq;
130762590Sitojun	char ifname[IFNAMSIZ+8];
130862590Sitojun
130962590Sitojun	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
131062590Sitojun		err(1, "socket");
131162590Sitojun
131262590Sitojun	memset(&ndifreq, 0, sizeof(ndifreq));
1313121156Sume	strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
131462590Sitojun
131562590Sitojun	if (ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1316121156Sume		err(1, "ioctl(SIOCGDEFIFACE_IN6)");
131762590Sitojun
131862590Sitojun	if (ndifreq.ifindex == 0)
131962590Sitojun		printf("No default interface.\n");
132062590Sitojun	else {
132162590Sitojun		if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL)
132262590Sitojun			err(1, "failed to resolve ifname for index %lu",
132362590Sitojun			    ndifreq.ifindex);
132462590Sitojun		printf("ND default interface = %s\n", ifname);
132562590Sitojun	}
132662590Sitojun
132762590Sitojun	close(s);
132862590Sitojun}
132962590Sitojun#endif
133062590Sitojun
133155505Sshinstatic char *
1332287735Shrssec2str(time_t total)
133355505Sshin{
133455505Sshin	static char result[256];
133555505Sshin	int days, hours, mins, secs;
133655505Sshin	int first = 1;
133755505Sshin	char *p = result;
1338121156Sume	char *ep = &result[sizeof(result)];
1339121156Sume	int n;
134055505Sshin
134155505Sshin	days = total / 3600 / 24;
134255505Sshin	hours = (total / 3600) % 24;
134355505Sshin	mins = (total / 60) % 60;
134455505Sshin	secs = total % 60;
134555505Sshin
134655505Sshin	if (days) {
134755505Sshin		first = 0;
1348121156Sume		n = snprintf(p, ep - p, "%dd", days);
1349121156Sume		if (n < 0 || n >= ep - p)
1350121156Sume			return "?";
1351121156Sume		p += n;
135255505Sshin	}
135355505Sshin	if (!first || hours) {
135455505Sshin		first = 0;
1355121156Sume		n = snprintf(p, ep - p, "%dh", hours);
1356121156Sume		if (n < 0 || n >= ep - p)
1357121156Sume			return "?";
1358121156Sume		p += n;
135955505Sshin	}
136055505Sshin	if (!first || mins) {
136155505Sshin		first = 0;
1362121156Sume		n = snprintf(p, ep - p, "%dm", mins);
1363121156Sume		if (n < 0 || n >= ep - p)
1364121156Sume			return "?";
1365121156Sume		p += n;
136655505Sshin	}
1367121156Sume	snprintf(p, ep - p, "%ds", secs);
136855505Sshin
136955505Sshin	return(result);
137055505Sshin}
137155505Sshin
137255505Sshin/*
137355505Sshin * Print the timestamp
137455505Sshin * from tcpdump/util.c
137555505Sshin */
137655505Sshinstatic void
1377287735Shrsts_print(const struct timeval *tvp)
137855505Sshin{
137955505Sshin	int s;
138055505Sshin
138155505Sshin	/* Default */
1382253999Shrs	s = (tvp->tv_sec + thiszone) % 86400;
138355505Sshin	(void)printf("%02d:%02d:%02d.%06u ",
1384253999Shrs	    s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec);
138555505Sshin}
1386186119Sqingli
1387186119Sqingli#undef NEXTADDR
1388