162590Sitojun/*	$FreeBSD: stable/11/usr.sbin/ndp/ndp.c 314041 2017-02-21 12:45:56Z garga $	*/
2122615Sume/*	$KAME: ndp.c,v 1.104 2003/06/27 07:48:39 itojun Exp $	*/
362590Sitojun
4331722Seadler/*
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_dl.h>
8755505Sshin#include <net/if_types.h>
8855505Sshin#include <net/route.h>
8955505Sshin
9055505Sshin#include <netinet/in.h>
9155505Sshin#include <netinet/if_ether.h>
9255505Sshin
9355505Sshin#include <netinet/icmp6.h>
9455505Sshin#include <netinet6/in6_var.h>
9555505Sshin#include <netinet6/nd6.h>
9655505Sshin
9755505Sshin#include <arpa/inet.h>
9855505Sshin
99265778Smelifaro#include <ctype.h>
10055505Sshin#include <netdb.h>
10155505Sshin#include <errno.h>
10255505Sshin#include <nlist.h>
10355505Sshin#include <stdio.h>
10455505Sshin#include <string.h>
10555505Sshin#include <paths.h>
10655505Sshin#include <err.h>
10755505Sshin#include <stdlib.h>
10855505Sshin#include <fcntl.h>
10955505Sshin#include <unistd.h>
11055505Sshin#include "gmt2local.h"
11155505Sshin
112314041Sgarga#define	NEXTADDR(w, s)					\
113314041Sgarga	if (rtm->rtm_addrs & (w)) {			\
114314041Sgarga		bcopy((char *)&s, cp, sizeof(s));	\
115314041Sgarga		cp += SA_SIZE(&s);			\
116314041Sgarga	}
117186119Sqingli
118100650Sjmallettstatic pid_t pid;
11962590Sitojunstatic int nflag;
12062590Sitojunstatic int tflag;
12162590Sitojunstatic int32_t thiszone;	/* time difference with gmt */
12262590Sitojunstatic int s = -1;
12362590Sitojunstatic int repeat = 0;
12455505Sshin
125287097Shrsstatic char host_buf[NI_MAXHOST];	/* getnameinfo() */
126287097Shrsstatic char ifix_buf[IFNAMSIZ];		/* if_indextoname() */
12755505Sshin
128265778Smelifarostatic int file(char *);
129287097Shrsstatic void getsocket(void);
130287097Shrsstatic int set(int, char **);
131287097Shrsstatic void get(char *);
132287097Shrsstatic int delete(char *);
133287097Shrsstatic void dump(struct sockaddr_in6 *, int);
134173412Skevlostatic struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int);
135173412Skevlostatic char *ether_str(struct sockaddr_dl *);
136287097Shrsstatic int ndp_ether_aton(char *, u_char *);
137287097Shrsstatic void usage(void);
138287097Shrsstatic int rtmsg(int);
139287097Shrsstatic void ifinfo(char *, int, char **);
140287097Shrsstatic void rtrlist(void);
141287097Shrsstatic void plist(void);
142287097Shrsstatic void pfx_flush(void);
143287097Shrsstatic void rtr_flush(void);
144287097Shrsstatic void harmonize_rtr(void);
14562590Sitojun#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
146173412Skevlostatic void getdefif(void);
147173412Skevlostatic void setdefif(char *);
14862590Sitojun#endif
149173412Skevlostatic char *sec2str(time_t);
150253999Shrsstatic void ts_print(const struct timeval *);
15155505Sshin
15278064Sumestatic char *rtpref_str[] = {
15378064Sume	"medium",		/* 00 */
15478064Sume	"high",			/* 01 */
15578064Sume	"rsv",			/* 10 */
15678064Sume	"low"			/* 11 */
15778064Sume};
15878064Sume
15955505Sshinint
160259169Saemain(int argc, char **argv)
16155505Sshin{
162287097Shrs	int ch, mode = 0;
163287097Shrs	char *arg = NULL;
16455505Sshin
16555505Sshin	pid = getpid();
16655505Sshin	thiszone = gmt2local(0);
167122615Sume	while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1)
168121156Sume		switch (ch) {
16955505Sshin		case 'a':
17055505Sshin		case 'c':
171122615Sume		case 'p':
172122615Sume		case 'r':
173122615Sume		case 'H':
174122615Sume		case 'P':
175122615Sume		case 'R':
176122615Sume		case 's':
177122615Sume		case 'I':
178122615Sume			if (mode) {
179122615Sume				usage();
180122615Sume				/*NOTREACHED*/
181122615Sume			}
182122615Sume			mode = ch;
183122615Sume			arg = NULL;
18455505Sshin			break;
185122615Sume		case 'f':
186265778Smelifaro			exit(file(optarg) ? 1 : 0);
187268827Speter		case 'd':
188265778Smelifaro		case 'i':
189122615Sume			if (mode) {
19055505Sshin				usage();
191122615Sume				/*NOTREACHED*/
192122615Sume			}
193122615Sume			mode = ch;
194122615Sume			arg = optarg;
195122615Sume			break;
19655505Sshin		case 'n':
19755505Sshin			nflag = 1;
19855505Sshin			break;
19955505Sshin		case 't':
20055505Sshin			tflag = 1;
20155505Sshin			break;
20255505Sshin		case 'A':
203122615Sume			if (mode) {
204122615Sume				usage();
205122615Sume				/*NOTREACHED*/
206122615Sume			}
207122615Sume			mode = 'a';
20855505Sshin			repeat = atoi(optarg);
209122615Sume			if (repeat < 0) {
21055505Sshin				usage();
211122615Sume				/*NOTREACHED*/
212122615Sume			}
21355505Sshin			break;
21455505Sshin		default:
21555505Sshin			usage();
21655505Sshin		}
21755505Sshin
21855505Sshin	argc -= optind;
21955505Sshin	argv += optind;
22055505Sshin
221122615Sume	switch (mode) {
222122615Sume	case 'a':
223122615Sume	case 'c':
224122615Sume		if (argc != 0) {
22555505Sshin			usage();
226122615Sume			/*NOTREACHED*/
227122615Sume		}
228122615Sume		dump(0, mode == 'c');
229122615Sume		break;
230122615Sume	case 'd':
231122615Sume		if (argc != 0) {
232122615Sume			usage();
233122615Sume			/*NOTREACHED*/
234122615Sume		}
235122615Sume		delete(arg);
236122615Sume		break;
237122615Sume	case 'I':
238122615Sume#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
239122615Sume		if (argc > 1) {
240122615Sume			usage();
241122615Sume			/*NOTREACHED*/
242122615Sume		} else if (argc == 1) {
243122615Sume			if (strcmp(*argv, "delete") == 0 ||
244122615Sume			    if_nametoindex(*argv))
245122615Sume				setdefif(*argv);
246122615Sume			else
247122615Sume				errx(1, "invalid interface %s", *argv);
248122615Sume		}
249122615Sume		getdefif(); /* always call it to print the result */
250122615Sume		break;
251122615Sume#else
252122615Sume		errx(1, "not supported yet");
253122615Sume		/*NOTREACHED*/
254122615Sume#endif
255122615Sume	case 'p':
256122615Sume		if (argc != 0) {
257122615Sume			usage();
258122615Sume			/*NOTREACHED*/
259122615Sume		}
26055505Sshin		plist();
261122615Sume		break;
262122615Sume	case 'i':
263122615Sume		ifinfo(arg, argc, argv);
264122615Sume		break;
265122615Sume	case 'r':
266122615Sume		if (argc != 0) {
267122615Sume			usage();
268122615Sume			/*NOTREACHED*/
269122615Sume		}
27055505Sshin		rtrlist();
271122615Sume		break;
272122615Sume	case 's':
27355505Sshin		if (argc < 2 || argc > 4)
27455505Sshin			usage();
27555505Sshin		exit(set(argc, argv) ? 1 : 0);
276122615Sume	case 'H':
277122615Sume		if (argc != 0) {
278122615Sume			usage();
279122615Sume			/*NOTREACHED*/
280122615Sume		}
28155505Sshin		harmonize_rtr();
282122615Sume		break;
283122615Sume	case 'P':
284122615Sume		if (argc != 0) {
285122615Sume			usage();
286122615Sume			/*NOTREACHED*/
287122615Sume		}
28855505Sshin		pfx_flush();
289122615Sume		break;
290122615Sume	case 'R':
291122615Sume		if (argc != 0) {
292122615Sume			usage();
293122615Sume			/*NOTREACHED*/
294122615Sume		}
29555505Sshin		rtr_flush();
296122615Sume		break;
297122615Sume	case 0:
298122615Sume		if (argc != 1) {
299122615Sume			usage();
300122615Sume			/*NOTREACHED*/
301122615Sume		}
302122615Sume		get(argv[0]);
303122615Sume		break;
30455505Sshin	}
30555505Sshin	exit(0);
30655505Sshin}
30755505Sshin
30855505Sshin/*
30955505Sshin * Process a file to set standard ndp entries
31055505Sshin */
311265778Smelifarostatic int
312259169Saefile(char *name)
31355505Sshin{
31455505Sshin	FILE *fp;
31555505Sshin	int i, retval;
316265778Smelifaro	char line[100], arg[5][50], *args[5], *p;
31755505Sshin
318265778Smelifaro	if ((fp = fopen(name, "r")) == NULL)
319265778Smelifaro		err(1, "cannot open %s", name);
32055505Sshin	args[0] = &arg[0][0];
32155505Sshin	args[1] = &arg[1][0];
32255505Sshin	args[2] = &arg[2][0];
32355505Sshin	args[3] = &arg[3][0];
32455505Sshin	args[4] = &arg[4][0];
32555505Sshin	retval = 0;
326167260Skevlo	while (fgets(line, sizeof(line), fp) != NULL) {
327265778Smelifaro		if ((p = strchr(line, '#')) != NULL)
328265778Smelifaro			*p = '\0';
329265778Smelifaro		for (p = line; isblank(*p); p++);
330265778Smelifaro		if (*p == '\n' || *p == '\0')
331265778Smelifaro			continue;
332122615Sume		i = sscanf(line, "%49s %49s %49s %49s %49s",
333122615Sume		    arg[0], arg[1], arg[2], arg[3], arg[4]);
33455505Sshin		if (i < 2) {
335265778Smelifaro			warnx("bad line: %s", line);
33655505Sshin			retval = 1;
33755505Sshin			continue;
33855505Sshin		}
33955505Sshin		if (set(i, args))
34055505Sshin			retval = 1;
34155505Sshin	}
34255505Sshin	fclose(fp);
34355505Sshin	return (retval);
34455505Sshin}
34555505Sshin
346287097Shrsstatic void
34755505Sshingetsocket()
34855505Sshin{
34955505Sshin	if (s < 0) {
35055505Sshin		s = socket(PF_ROUTE, SOCK_RAW, 0);
35155505Sshin		if (s < 0) {
352121156Sume			err(1, "socket");
353121156Sume			/* NOTREACHED */
35455505Sshin		}
35555505Sshin	}
35655505Sshin}
35755505Sshin
358287097Shrsstatic struct sockaddr_in6 so_mask = {
359287097Shrs	.sin6_len = sizeof(so_mask),
360287097Shrs	.sin6_family = AF_INET6
361287097Shrs};
362287097Shrsstatic struct sockaddr_in6 blank_sin = {
363287097Shrs	.sin6_len = sizeof(blank_sin),
364287097Shrs	.sin6_family = AF_INET6
365287097Shrs};
366287097Shrsstatic struct sockaddr_in6 sin_m;
367287097Shrsstatic struct sockaddr_dl blank_sdl = {
368287097Shrs	.sdl_len = sizeof(blank_sdl),
369287097Shrs	.sdl_family = AF_LINK
370287097Shrs};
371287097Shrsstatic struct sockaddr_dl sdl_m;
372287097Shrsstatic time_t expire_time;
373287097Shrsstatic int flags, found_entry;
374287097Shrsstatic struct {
37555505Sshin	struct	rt_msghdr m_rtm;
37655505Sshin	char	m_space[512];
377287097Shrs} m_rtmsg;
37855505Sshin
37955505Sshin/*
38055505Sshin * Set an individual neighbor cache entry
38155505Sshin */
382287097Shrsstatic int
383259169Saeset(int argc, char **argv)
38455505Sshin{
38555505Sshin	register struct sockaddr_in6 *sin = &sin_m;
38655505Sshin	register struct sockaddr_dl *sdl;
38755505Sshin	register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
38855505Sshin	struct addrinfo hints, *res;
38955505Sshin	int gai_error;
39055505Sshin	u_char *ea;
39155505Sshin	char *host = argv[0], *eaddr = argv[1];
39255505Sshin
39355505Sshin	getsocket();
39455505Sshin	argc -= 2;
39555505Sshin	argv += 2;
39655505Sshin	sdl_m = blank_sdl;
39755505Sshin	sin_m = blank_sin;
39855505Sshin
39955505Sshin	bzero(&hints, sizeof(hints));
40055505Sshin	hints.ai_family = AF_INET6;
40155505Sshin	gai_error = getaddrinfo(host, NULL, &hints, &res);
40255505Sshin	if (gai_error) {
40355505Sshin		fprintf(stderr, "ndp: %s: %s\n", host,
40455505Sshin			gai_strerror(gai_error));
40555505Sshin		return 1;
40655505Sshin	}
40755505Sshin	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
408243903Shrs	sin->sin6_scope_id =
409243903Shrs	    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
41055505Sshin	ea = (u_char *)LLADDR(&sdl_m);
41155505Sshin	if (ndp_ether_aton(eaddr, ea) == 0)
41255505Sshin		sdl_m.sdl_alen = 6;
41355505Sshin	flags = expire_time = 0;
41455505Sshin	while (argc-- > 0) {
41555505Sshin		if (strncmp(argv[0], "temp", 4) == 0) {
416253999Shrs			struct timeval now;
417121156Sume
418253999Shrs			gettimeofday(&now, 0);
419253970Shrs			expire_time = now.tv_sec + 20 * 60;
42062590Sitojun		} else if (strncmp(argv[0], "proxy", 5) == 0)
42162590Sitojun			flags |= RTF_ANNOUNCE;
42255505Sshin		argv++;
42355505Sshin	}
42455505Sshin	if (rtmsg(RTM_GET) < 0) {
425121156Sume		errx(1, "RTM_GET(%s) failed", host);
426121156Sume		/* NOTREACHED */
42755505Sshin	}
42855505Sshin	sin = (struct sockaddr_in6 *)(rtm + 1);
429259171Sae	sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin);
43055505Sshin	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
43155505Sshin		if (sdl->sdl_family == AF_LINK &&
432122615Sume		    !(rtm->rtm_flags & RTF_GATEWAY)) {
433122615Sume			switch (sdl->sdl_type) {
434122615Sume			case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
435122615Sume			case IFT_ISO88024: case IFT_ISO88025:
436210936Sjhb			case IFT_L2VLAN: case IFT_BRIDGE:
437122615Sume				goto overwrite;
438122615Sume			}
43955505Sshin		}
44062590Sitojun		fprintf(stderr, "set: cannot configure a new entry\n");
44162590Sitojun		return 1;
44255505Sshin	}
44362590Sitojun
44455505Sshinoverwrite:
44555505Sshin	if (sdl->sdl_family != AF_LINK) {
44655505Sshin		printf("cannot intuit interface index and type for %s\n", host);
44755505Sshin		return (1);
44855505Sshin	}
44955505Sshin	sdl_m.sdl_type = sdl->sdl_type;
45055505Sshin	sdl_m.sdl_index = sdl->sdl_index;
45155505Sshin	return (rtmsg(RTM_ADD));
45255505Sshin}
45355505Sshin
45455505Sshin/*
45555505Sshin * Display an individual neighbor cache entry
45655505Sshin */
457287097Shrsstatic void
458259169Saeget(char *host)
45955505Sshin{
46055505Sshin	struct sockaddr_in6 *sin = &sin_m;
46155505Sshin	struct addrinfo hints, *res;
46255505Sshin	int gai_error;
46355505Sshin
46455505Sshin	sin_m = blank_sin;
46555505Sshin	bzero(&hints, sizeof(hints));
46655505Sshin	hints.ai_family = AF_INET6;
46755505Sshin	gai_error = getaddrinfo(host, NULL, &hints, &res);
46855505Sshin	if (gai_error) {
46955505Sshin		fprintf(stderr, "ndp: %s: %s\n", host,
470121156Sume		    gai_strerror(gai_error));
47155505Sshin		return;
47255505Sshin	}
47355505Sshin	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
474259176Sae	sin->sin6_scope_id =
475259176Sae	    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
476259176Sae	dump(sin, 0);
47755505Sshin	if (found_entry == 0) {
47855505Sshin		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
479121156Sume		    sizeof(host_buf), NULL ,0,
480121156Sume		    (nflag ? NI_NUMERICHOST : 0));
48155505Sshin		printf("%s (%s) -- no entry\n", host, host_buf);
48255505Sshin		exit(1);
48355505Sshin	}
48455505Sshin}
48555505Sshin
48655505Sshin/*
48755505Sshin * Delete a neighbor cache entry
48855505Sshin */
489287097Shrsstatic int
490259169Saedelete(char *host)
49155505Sshin{
49255505Sshin	struct sockaddr_in6 *sin = &sin_m;
49355505Sshin	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
494186119Sqingli	register char *cp = m_rtmsg.m_space;
49555505Sshin	struct sockaddr_dl *sdl;
49655505Sshin	struct addrinfo hints, *res;
49755505Sshin	int gai_error;
49855505Sshin
49955505Sshin	getsocket();
50055505Sshin	sin_m = blank_sin;
50155505Sshin
50255505Sshin	bzero(&hints, sizeof(hints));
50355505Sshin	hints.ai_family = AF_INET6;
50455505Sshin	gai_error = getaddrinfo(host, NULL, &hints, &res);
50555505Sshin	if (gai_error) {
50655505Sshin		fprintf(stderr, "ndp: %s: %s\n", host,
507121156Sume		    gai_strerror(gai_error));
50855505Sshin		return 1;
50955505Sshin	}
51055505Sshin	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
511243903Shrs	sin->sin6_scope_id =
512243903Shrs	    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
51355505Sshin	if (rtmsg(RTM_GET) < 0) {
514121156Sume		errx(1, "RTM_GET(%s) failed", host);
515121156Sume		/* NOTREACHED */
51655505Sshin	}
51755505Sshin	sin = (struct sockaddr_in6 *)(rtm + 1);
518259171Sae	sdl = (struct sockaddr_dl *)(ALIGN(sin->sin6_len) + (char *)sin);
51955505Sshin	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
52055505Sshin		if (sdl->sdl_family == AF_LINK &&
52162590Sitojun		    !(rtm->rtm_flags & RTF_GATEWAY)) {
52278064Sume			goto delete;
52355505Sshin		}
52462590Sitojun		fprintf(stderr, "delete: cannot delete non-NDP entry\n");
52562590Sitojun		return 1;
52655505Sshin	}
52762590Sitojun
52855505Sshindelete:
52955505Sshin	if (sdl->sdl_family != AF_LINK) {
53055505Sshin		printf("cannot locate %s\n", host);
53155505Sshin		return (1);
53255505Sshin	}
533314041Sgarga	/*
534314041Sgarga	 * need to reinit the field because it has rt_key
535314041Sgarga	 * but we want the actual address
536314041Sgarga	 */
537186119Sqingli	NEXTADDR(RTA_DST, sin_m);
538186500Sqingli	rtm->rtm_flags |= RTF_LLDATA;
53955505Sshin	if (rtmsg(RTM_DELETE) == 0) {
540243903Shrs		getnameinfo((struct sockaddr *)sin,
541243903Shrs		    sin->sin6_len, host_buf,
542121156Sume		    sizeof(host_buf), NULL, 0,
543121156Sume		    (nflag ? NI_NUMERICHOST : 0));
54455505Sshin		printf("%s (%s) deleted\n", host, host_buf);
54555505Sshin	}
54655505Sshin
54755505Sshin	return 0;
54855505Sshin}
54955505Sshin
550122615Sume#define W_ADDR	36
55178064Sume#define W_LL	17
55278064Sume#define W_IF	6
55378064Sume
55455505Sshin/*
55555505Sshin * Dump the entire neighbor cache
55655505Sshin */
557287097Shrsstatic void
558259176Saedump(struct sockaddr_in6 *addr, int cflag)
55955505Sshin{
56055505Sshin	int mib[6];
56155505Sshin	size_t needed;
56262590Sitojun	char *lim, *buf, *next;
56355505Sshin	struct rt_msghdr *rtm;
56455505Sshin	struct sockaddr_in6 *sin;
56555505Sshin	struct sockaddr_dl *sdl;
56655505Sshin	extern int h_errno;
567253999Shrs	struct timeval now;
568292333Smelifaro	u_long expire;
56955505Sshin	int addrwidth;
57078064Sume	int llwidth;
57178064Sume	int ifwidth;
57262590Sitojun	char flgbuf[8];
57378064Sume	char *ifname;
57455505Sshin
57555505Sshin	/* Print header */
57666865Ssumikawa	if (!tflag && !cflag)
577122615Sume		printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n",
57878064Sume		    W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address",
579122615Sume		    W_IF, W_IF, "Netif", "Expire", "S", "Flags");
58055505Sshin
58155505Sshinagain:;
58255505Sshin	mib[0] = CTL_NET;
58355505Sshin	mib[1] = PF_ROUTE;
58455505Sshin	mib[2] = 0;
58555505Sshin	mib[3] = AF_INET6;
58655505Sshin	mib[4] = NET_RT_FLAGS;
587186119Sqingli#ifdef RTF_LLINFO
58855505Sshin	mib[5] = RTF_LLINFO;
589186119Sqingli#else
590186119Sqingli	mib[5] = 0;
591186119Sqingli#endif
59255505Sshin	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
59355505Sshin		err(1, "sysctl(PF_ROUTE estimate)");
59455505Sshin	if (needed > 0) {
59555505Sshin		if ((buf = malloc(needed)) == NULL)
596121156Sume			err(1, "malloc");
59755505Sshin		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
59855505Sshin			err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
59955505Sshin		lim = buf + needed;
60055505Sshin	} else
60155505Sshin		buf = lim = NULL;
60255505Sshin
60355505Sshin	for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
60455505Sshin		int isrouter = 0, prbs = 0;
60555505Sshin
60655505Sshin		rtm = (struct rt_msghdr *)next;
60755505Sshin		sin = (struct sockaddr_in6 *)(rtm + 1);
608314041Sgarga		sdl = (struct sockaddr_dl *)((char *)sin +
609314041Sgarga		    ALIGN(sin->sin6_len));
61078064Sume
61178064Sume		/*
61278064Sume		 * Some OSes can produce a route that has the LINK flag but
61378064Sume		 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
61478064Sume		 * and BSD/OS, where xx is not the interface identifier on
61578064Sume		 * lo0).  Such routes entry would annoy getnbrinfo() below,
61678064Sume		 * so we skip them.
61778064Sume		 * XXX: such routes should have the GATEWAY flag, not the
618121156Sume		 * LINK flag.  However, there is rotten routing software
61978064Sume		 * that advertises all routes that have the GATEWAY flag.
62078064Sume		 * Thus, KAME kernel intentionally does not set the LINK flag.
62178064Sume		 * What is to be fixed is not ndp, but such routing software
62278064Sume		 * (and the kernel workaround)...
62378064Sume		 */
62478064Sume		if (sdl->sdl_family != AF_LINK)
62578064Sume			continue;
62678064Sume
627122615Sume		if (!(rtm->rtm_flags & RTF_HOST))
628122615Sume			continue;
629122615Sume
63055505Sshin		if (addr) {
631259176Sae			if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
632259176Sae			    &sin->sin6_addr) == 0 ||
633259176Sae			    addr->sin6_scope_id != sin->sin6_scope_id)
63455505Sshin				continue;
63555505Sshin			found_entry = 1;
63655505Sshin		} else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
63755505Sshin			continue;
63855505Sshin		if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
63955505Sshin		    IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
64055505Sshin			/* XXX: should scope id be filled in the kernel? */
64155505Sshin			if (sin->sin6_scope_id == 0)
64255505Sshin				sin->sin6_scope_id = sdl->sdl_index;
64355505Sshin		}
64455505Sshin		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
645121156Sume		    sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0));
646122615Sume		if (cflag) {
64781366Ssumikawa#ifdef RTF_WASCLONED
64881366Ssumikawa			if (rtm->rtm_flags & RTF_WASCLONED)
64981366Ssumikawa				delete(host_buf);
650122615Sume#elif defined(RTF_CLONED)
651122615Sume			if (rtm->rtm_flags & RTF_CLONED)
652122615Sume				delete(host_buf);
65381366Ssumikawa#else
654288297Smelifaro			if (rtm->rtm_flags & RTF_PINNED)
655288297Smelifaro				continue;
65666865Ssumikawa			delete(host_buf);
65781366Ssumikawa#endif
65866865Ssumikawa			continue;
65966865Ssumikawa		}
660253999Shrs		gettimeofday(&now, 0);
66155505Sshin		if (tflag)
662253970Shrs			ts_print(&now);
66355505Sshin
66478064Sume		addrwidth = strlen(host_buf);
66578064Sume		if (addrwidth < W_ADDR)
66678064Sume			addrwidth = W_ADDR;
66778064Sume		llwidth = strlen(ether_str(sdl));
66878064Sume		if (W_ADDR + W_LL - addrwidth > llwidth)
66978064Sume			llwidth = W_ADDR + W_LL - addrwidth;
67078064Sume		ifname = if_indextoname(sdl->sdl_index, ifix_buf);
67178064Sume		if (!ifname)
67278064Sume			ifname = "?";
67378064Sume		ifwidth = strlen(ifname);
67478064Sume		if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
67578064Sume			ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
67655505Sshin
67778064Sume		printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf,
67878064Sume		    llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
67955505Sshin
680289677Seadler		/* Print neighbor discovery specific information */
681292333Smelifaro		expire = rtm->rtm_rmx.rmx_expire;
682292333Smelifaro		if (expire > now.tv_sec)
683292333Smelifaro			printf(" %-9.9s", sec2str(expire - now.tv_sec));
684292333Smelifaro		else if (expire == 0)
685292333Smelifaro			printf(" %-9.9s", "permanent");
686292333Smelifaro		else
687292333Smelifaro			printf(" %-9.9s", "expired");
68855505Sshin
689292333Smelifaro		switch (rtm->rtm_rmx.rmx_state) {
690292333Smelifaro		case ND6_LLINFO_NOSTATE:
691292465Sume			printf(" N");
692292465Sume			break;
69378064Sume#ifdef ND6_LLINFO_WAITDELETE
694292333Smelifaro		case ND6_LLINFO_WAITDELETE:
695292465Sume			printf(" W");
696292465Sume			break;
69778064Sume#endif
698292333Smelifaro		case ND6_LLINFO_INCOMPLETE:
699292465Sume			printf(" I");
700292465Sume			break;
701292333Smelifaro		case ND6_LLINFO_REACHABLE:
702292465Sume			printf(" R");
703292465Sume			break;
704292333Smelifaro		case ND6_LLINFO_STALE:
705292465Sume			printf(" S");
706292465Sume			break;
707292333Smelifaro		case ND6_LLINFO_DELAY:
708292465Sume			printf(" D");
709292465Sume			break;
710292333Smelifaro		case ND6_LLINFO_PROBE:
711292465Sume			printf(" P");
712292465Sume			break;
713292333Smelifaro		default:
714292465Sume			printf(" ?");
715292465Sume			break;
71655505Sshin		}
71755505Sshin
718292333Smelifaro		isrouter = rtm->rtm_flags & RTF_GATEWAY;
719292333Smelifaro		prbs = rtm->rtm_rmx.rmx_pksent;
720292333Smelifaro
72162590Sitojun		/*
72262590Sitojun		 * other flags. R: router, P: proxy, W: ??
72362590Sitojun		 */
72462590Sitojun		if ((rtm->rtm_addrs & RTA_NETMASK) == 0) {
72562590Sitojun			snprintf(flgbuf, sizeof(flgbuf), "%s%s",
726121156Sume			    isrouter ? "R" : "",
727121156Sume			    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
72862590Sitojun		} else {
729314041Sgarga#if 0			/* W and P are mystery even for us */
73062590Sitojun			sin = (struct sockaddr_in6 *)
731121156Sume			    (sdl->sdl_len + (char *)sdl);
73262590Sitojun			snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s",
733121156Sume			    isrouter ? "R" : "",
734121156Sume			    !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "P" : "",
735121156Sume			    (sin->sin6_len != sizeof(struct sockaddr_in6)) ? "W" : "",
736121156Sume			    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
737122615Sume#else
738122615Sume			snprintf(flgbuf, sizeof(flgbuf), "%s%s",
739122615Sume			    isrouter ? "R" : "",
740122615Sume			    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
741122615Sume#endif
74255505Sshin		}
743122615Sume		printf(" %s", flgbuf);
74455505Sshin
74555505Sshin		if (prbs)
746122615Sume			printf(" %d", prbs);
74755505Sshin
74855505Sshin		printf("\n");
74955505Sshin	}
75078064Sume	if (buf != NULL)
75178064Sume		free(buf);
75255505Sshin
75355505Sshin	if (repeat) {
75455505Sshin		printf("\n");
755125675Ssumikawa		fflush(stdout);
75655505Sshin		sleep(repeat);
75755505Sshin		goto again;
75855505Sshin	}
75955505Sshin}
76055505Sshin
76155505Sshinstatic struct in6_nbrinfo *
762259169Saegetnbrinfo(struct in6_addr *addr, int ifindex, int warning)
76355505Sshin{
76455505Sshin	static struct in6_nbrinfo nbi;
76555505Sshin	int s;
76655505Sshin
76755505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
76855505Sshin		err(1, "socket");
76955505Sshin
77055505Sshin	bzero(&nbi, sizeof(nbi));
77155505Sshin	if_indextoname(ifindex, nbi.ifname);
77255505Sshin	nbi.addr = *addr;
77355505Sshin	if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
77462590Sitojun		if (warning)
77562590Sitojun			warn("ioctl(SIOCGNBRINFO_IN6)");
77655505Sshin		close(s);
77755505Sshin		return(NULL);
77855505Sshin	}
77955505Sshin
78055505Sshin	close(s);
78155505Sshin	return(&nbi);
78255505Sshin}
78355505Sshin
78455505Sshinstatic char *
785217140Sdelphijether_str(struct sockaddr_dl *sdl)
78655505Sshin{
787121156Sume	static char hbuf[NI_MAXHOST];
78855505Sshin
789219819Sjeff	if (sdl->sdl_alen == ETHER_ADDR_LEN) {
790217140Sdelphij		strlcpy(hbuf, ether_ntoa((struct ether_addr *)LLADDR(sdl)),
791217140Sdelphij		    sizeof(hbuf));
792219819Sjeff	} else if (sdl->sdl_alen) {
793219819Sjeff		int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
794219819Sjeff		snprintf(hbuf, sizeof(hbuf), "%s", link_ntoa(sdl) + n);
795219819Sjeff	} else
796121156Sume		snprintf(hbuf, sizeof(hbuf), "(incomplete)");
79755505Sshin
798121156Sume	return(hbuf);
79955505Sshin}
80055505Sshin
801287097Shrsstatic int
802259169Saendp_ether_aton(char *a, u_char *n)
80355505Sshin{
80455505Sshin	int i, o[6];
80555505Sshin
80655505Sshin	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
807121156Sume	    &o[3], &o[4], &o[5]);
80855505Sshin	if (i != 6) {
80955505Sshin		fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a);
81055505Sshin		return (1);
81155505Sshin	}
812121156Sume	for (i = 0; i < 6; i++)
81355505Sshin		n[i] = o[i];
81455505Sshin	return (0);
81555505Sshin}
81655505Sshin
817287097Shrsstatic void
81855505Sshinusage()
81955505Sshin{
820122615Sume	printf("usage: ndp [-nt] hostname\n");
821122615Sume	printf("       ndp [-nt] -a | -c | -p | -r | -H | -P | -R\n");
82278064Sume	printf("       ndp [-nt] -A wait\n");
823122615Sume	printf("       ndp [-nt] -d hostname\n");
824122615Sume	printf("       ndp [-nt] -f filename\n");
825122615Sume	printf("       ndp [-nt] -i interface [flags...]\n");
82662590Sitojun#ifdef SIOCSDEFIFACE_IN6
827122615Sume	printf("       ndp [-nt] -I [interface|delete]\n");
82862590Sitojun#endif
829122615Sume	printf("       ndp [-nt] -s nodename etheraddr [temp] [proxy]\n");
83055505Sshin	exit(1);
83155505Sshin}
83255505Sshin
833287097Shrsstatic int
834259169Saertmsg(int cmd)
83555505Sshin{
83655505Sshin	static int seq;
83755505Sshin	int rlen;
83855505Sshin	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
83955505Sshin	register char *cp = m_rtmsg.m_space;
84055505Sshin	register int l;
84155505Sshin
84255505Sshin	errno = 0;
84355505Sshin	if (cmd == RTM_DELETE)
84455505Sshin		goto doit;
84555505Sshin	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
84655505Sshin	rtm->rtm_flags = flags;
84755505Sshin	rtm->rtm_version = RTM_VERSION;
84855505Sshin
84955505Sshin	switch (cmd) {
85055505Sshin	default:
85155505Sshin		fprintf(stderr, "ndp: internal wrong cmd\n");
85255505Sshin		exit(1);
85355505Sshin	case RTM_ADD:
85455505Sshin		rtm->rtm_addrs |= RTA_GATEWAY;
855122615Sume		if (expire_time) {
856122615Sume			rtm->rtm_rmx.rmx_expire = expire_time;
857122615Sume			rtm->rtm_inits = RTV_EXPIRE;
858122615Sume		}
859186500Sqingli		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
860314041Sgarga#if 0		/* we don't support ipv6addr/128 type proxying */
86162590Sitojun		if (rtm->rtm_flags & RTF_ANNOUNCE) {
86262590Sitojun			rtm->rtm_flags &= ~RTF_HOST;
863124241Ssuz			rtm->rtm_addrs |= RTA_NETMASK;
86462590Sitojun		}
865151473Ssuz#endif
86655505Sshin		/* FALLTHROUGH */
86755505Sshin	case RTM_GET:
86855505Sshin		rtm->rtm_addrs |= RTA_DST;
86955505Sshin	}
87055505Sshin
87155505Sshin	NEXTADDR(RTA_DST, sin_m);
87255505Sshin	NEXTADDR(RTA_GATEWAY, sdl_m);
873314041Sgarga#if 0	/* we don't support ipv6addr/128 type proxying */
87462590Sitojun	memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
87555505Sshin	NEXTADDR(RTA_NETMASK, so_mask);
876151473Ssuz#endif
87755505Sshin
87855505Sshin	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
87955505Sshindoit:
88055505Sshin	l = rtm->rtm_msglen;
88155505Sshin	rtm->rtm_seq = ++seq;
88255505Sshin	rtm->rtm_type = cmd;
88355505Sshin	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
88455505Sshin		if (errno != ESRCH || cmd != RTM_DELETE) {
885121156Sume			err(1, "writing to routing socket");
886121156Sume			/* NOTREACHED */
88755505Sshin		}
88855505Sshin	}
88955505Sshin	do {
89055505Sshin		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
89155505Sshin	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
89255505Sshin	if (l < 0)
89355505Sshin		(void) fprintf(stderr, "ndp: read from routing socket: %s\n",
89455505Sshin		    strerror(errno));
89555505Sshin	return (0);
89655505Sshin}
89755505Sshin
898287097Shrsstatic void
899259169Saeifinfo(char *ifname, int argc, char **argv)
90055505Sshin{
90155505Sshin	struct in6_ndireq nd;
90262590Sitojun	int i, s;
90362590Sitojun	u_int32_t newflags;
90478064Sume#ifdef IPV6CTL_USETEMPADDR
90578064Sume	u_int8_t nullbuf[8];
90678064Sume#endif
90755505Sshin
90855505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
909121156Sume		err(1, "socket");
910121156Sume		/* NOTREACHED */
91155505Sshin	}
91255505Sshin	bzero(&nd, sizeof(nd));
913121156Sume	strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
91455505Sshin	if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
915121156Sume		err(1, "ioctl(SIOCGIFINFO_IN6)");
916121156Sume		/* NOTREACHED */
917122615Sume	}
918314041Sgarga#define	ND nd.ndi
91962590Sitojun	newflags = ND.flags;
920122615Sume	for (i = 0; i < argc; i++) {
92162590Sitojun		int clear = 0;
92262590Sitojun		char *cp = argv[i];
92362590Sitojun
92462590Sitojun		if (*cp == '-') {
92562590Sitojun			clear = 1;
92662590Sitojun			cp++;
92762590Sitojun		}
92862590Sitojun
929314041Sgarga#define	SETFLAG(s, f) do {			\
930314041Sgarga	if (strcmp(cp, (s)) == 0) {		\
931314041Sgarga		if (clear)			\
932314041Sgarga			newflags &= ~(f);	\
933314041Sgarga		else				\
934314041Sgarga			newflags |= (f);	\
935314041Sgarga	}					\
936314041Sgarga} while (0)
937151468Ssuz/*
938151468Ssuz * XXX: this macro is not 100% correct, in that it matches "nud" against
939151468Ssuz *      "nudbogus".  But we just let it go since this is minor.
940151468Ssuz */
941314041Sgarga#define	SETVALUE(f, v) do {						\
942314041Sgarga	char *valptr;							\
943314041Sgarga	unsigned long newval;						\
944314041Sgarga	v = 0; /* unspecified */					\
945314041Sgarga	if (strncmp(cp, f, strlen(f)) == 0) {				\
946314041Sgarga		valptr = strchr(cp, '=');				\
947314041Sgarga		if (valptr == NULL)					\
948314041Sgarga			err(1, "syntax error in %s field", (f));	\
949314041Sgarga		errno = 0;						\
950314041Sgarga		newval = strtoul(++valptr, NULL, 0);			\
951314041Sgarga		if (errno)						\
952314041Sgarga			err(1, "syntax error in %s's value", (f));	\
953314041Sgarga		v = newval;						\
954314041Sgarga	}								\
955314041Sgarga} while (0)
956151468Ssuz
957151474Ssuz		SETFLAG("disabled", ND6_IFF_IFDISABLED);
95862590Sitojun		SETFLAG("nud", ND6_IFF_PERFORMNUD);
959118498Sume#ifdef ND6_IFF_ACCEPT_RTADV
960118498Sume		SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV);
961118498Sume#endif
962197138Shrs#ifdef ND6_IFF_AUTO_LINKLOCAL
963197138Shrs		SETFLAG("auto_linklocal", ND6_IFF_AUTO_LINKLOCAL);
964197138Shrs#endif
965245230Sume#ifdef ND6_IFF_NO_PREFER_IFACE
966245230Sume		SETFLAG("no_prefer_iface", ND6_IFF_NO_PREFER_IFACE);
967245230Sume#endif
968151468Ssuz		SETVALUE("basereachable", ND.basereachable);
969151468Ssuz		SETVALUE("retrans", ND.retrans);
970151468Ssuz		SETVALUE("curhlim", ND.chlim);
97162590Sitojun
97262590Sitojun		ND.flags = newflags;
973151468Ssuz		if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) {
974151468Ssuz			err(1, "ioctl(SIOCSIFINFO_IN6)");
975121156Sume			/* NOTREACHED */
97662590Sitojun		}
97762590Sitojun#undef SETFLAG
978151468Ssuz#undef SETVALUE
97962590Sitojun	}
98062590Sitojun
981121162Sume	if (!ND.initialized) {
982121162Sume		errx(1, "%s: not initialized yet", ifname);
983121162Sume		/* NOTREACHED */
984121162Sume	}
985121162Sume
986151468Ssuz	if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
987151468Ssuz		err(1, "ioctl(SIOCGIFINFO_IN6)");
988151468Ssuz		/* NOTREACHED */
989151468Ssuz	}
99055505Sshin	printf("linkmtu=%d", ND.linkmtu);
991121471Sume	printf(", maxmtu=%d", ND.maxmtu);
99255505Sshin	printf(", curhlim=%d", ND.chlim);
99355505Sshin	printf(", basereachable=%ds%dms",
994121156Sume	    ND.basereachable / 1000, ND.basereachable % 1000);
99555505Sshin	printf(", reachable=%ds", ND.reachable);
99662590Sitojun	printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000);
99778064Sume#ifdef IPV6CTL_USETEMPADDR
99878064Sume	memset(nullbuf, 0, sizeof(nullbuf));
99978064Sume	if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) {
100078064Sume		int j;
100178064Sume		u_int8_t *rbuf;
100278064Sume
100378064Sume		for (i = 0; i < 3; i++) {
1004121156Sume			switch (i) {
100578064Sume			case 0:
100678064Sume				printf("\nRandom seed(0): ");
100778064Sume				rbuf = ND.randomseed0;
100878064Sume				break;
100978064Sume			case 1:
101078064Sume				printf("\nRandom seed(1): ");
101178064Sume				rbuf = ND.randomseed1;
101278064Sume				break;
101378064Sume			case 2:
101478064Sume				printf("\nRandom ID:      ");
101578064Sume				rbuf = ND.randomid;
101678064Sume				break;
1017151472Ssuz			default:
1018151472Ssuz				errx(1, "impossible case for tempaddr display");
101978064Sume			}
102078064Sume			for (j = 0; j < 8; j++)
102178064Sume				printf("%02x", rbuf[j]);
102278064Sume		}
102378064Sume	}
1024314041Sgarga#endif /* IPV6CTL_USETEMPADDR */
102562590Sitojun	if (ND.flags) {
102662590Sitojun		printf("\nFlags: ");
1027151474Ssuz#ifdef ND6_IFF_IFDISABLED
1028151474Ssuz		if ((ND.flags & ND6_IFF_IFDISABLED))
1029151474Ssuz			printf("disabled ");
1030151474Ssuz#endif
1031118498Sume		if ((ND.flags & ND6_IFF_PERFORMNUD))
1032118498Sume			printf("nud ");
1033118498Sume#ifdef ND6_IFF_ACCEPT_RTADV
1034118498Sume		if ((ND.flags & ND6_IFF_ACCEPT_RTADV))
1035118498Sume			printf("accept_rtadv ");
1036118498Sume#endif
1037197138Shrs#ifdef ND6_IFF_AUTO_LINKLOCAL
1038197138Shrs		if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL))
1039197138Shrs			printf("auto_linklocal ");
1040197138Shrs#endif
1041245230Sume#ifdef ND6_IFF_NO_PREFER_IFACE
1042245230Sume		if ((ND.flags & ND6_IFF_NO_PREFER_IFACE))
1043245230Sume			printf("no_prefer_iface ");
1044245230Sume#endif
1045122615Sume	}
104662590Sitojun	putc('\n', stdout);
104755505Sshin#undef ND
1048121156Sume
104955505Sshin	close(s);
105055505Sshin}
105155505Sshin
105278064Sume#ifndef ND_RA_FLAG_RTPREF_MASK	/* XXX: just for compilation on *BSD release */
105378064Sume#define ND_RA_FLAG_RTPREF_MASK	0x18 /* 00011000 */
105478064Sume#endif
105578064Sume
1056287097Shrsstatic void
105755505Sshinrtrlist()
105855505Sshin{
105978064Sume	int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST };
106078064Sume	char *buf;
106178064Sume	struct in6_defrouter *p, *ep;
106278064Sume	size_t l;
1063253999Shrs	struct timeval now;
106478064Sume
106578064Sume	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
106678064Sume		err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
106778064Sume		/*NOTREACHED*/
106878064Sume	}
1069151472Ssuz	if (l == 0)
1070151472Ssuz		return;
107178064Sume	buf = malloc(l);
107278064Sume	if (!buf) {
1073121156Sume		err(1, "malloc");
107478064Sume		/*NOTREACHED*/
107578064Sume	}
107678064Sume	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
107778064Sume		err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
107878064Sume		/*NOTREACHED*/
107978064Sume	}
108078064Sume
108178064Sume	ep = (struct in6_defrouter *)(buf + l);
108278064Sume	for (p = (struct in6_defrouter *)buf; p < ep; p++) {
108378064Sume		int rtpref;
108478064Sume
108578064Sume		if (getnameinfo((struct sockaddr *)&p->rtaddr,
108678064Sume		    p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0,
1087121156Sume		    (nflag ? NI_NUMERICHOST : 0)) != 0)
108878064Sume			strlcpy(host_buf, "?", sizeof(host_buf));
1089121156Sume
109078064Sume		printf("%s if=%s", host_buf,
1091121156Sume		    if_indextoname(p->if_index, ifix_buf));
109278064Sume		printf(", flags=%s%s",
1093121156Sume		    p->flags & ND_RA_FLAG_MANAGED ? "M" : "",
1094121156Sume		    p->flags & ND_RA_FLAG_OTHER   ? "O" : "");
109578064Sume		rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff;
109678064Sume		printf(", pref=%s", rtpref_str[rtpref]);
1097121156Sume
1098253999Shrs		gettimeofday(&now, 0);
109978064Sume		if (p->expire == 0)
110078064Sume			printf(", expire=Never\n");
110178064Sume		else
110278064Sume			printf(", expire=%s\n",
1103253970Shrs			    sec2str(p->expire - now.tv_sec));
110478064Sume	}
110578064Sume	free(buf);
110655505Sshin}
110755505Sshin
1108287097Shrsstatic void
110955505Sshinplist()
111055505Sshin{
111178064Sume	int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST };
111278064Sume	char *buf;
111378064Sume	struct in6_prefix *p, *ep, *n;
111478064Sume	struct sockaddr_in6 *advrtr;
111578064Sume	size_t l;
1116253999Shrs	struct timeval now;
111778064Sume	const int niflags = NI_NUMERICHOST;
111878064Sume	int ninflags = nflag ? NI_NUMERICHOST : 0;
111978064Sume	char namebuf[NI_MAXHOST];
112078064Sume
112178064Sume	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
112278064Sume		err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
112378064Sume		/*NOTREACHED*/
112478064Sume	}
112578064Sume	buf = malloc(l);
112678064Sume	if (!buf) {
1127121156Sume		err(1, "malloc");
112878064Sume		/*NOTREACHED*/
112978064Sume	}
113078064Sume	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
113178064Sume		err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
113278064Sume		/*NOTREACHED*/
113378064Sume	}
113478064Sume
113578064Sume	ep = (struct in6_prefix *)(buf + l);
113678064Sume	for (p = (struct in6_prefix *)buf; p < ep; p = n) {
113778064Sume		advrtr = (struct sockaddr_in6 *)(p + 1);
113878064Sume		n = (struct in6_prefix *)&advrtr[p->advrtrs];
113978064Sume
114078064Sume		if (getnameinfo((struct sockaddr *)&p->prefix,
114178064Sume		    p->prefix.sin6_len, namebuf, sizeof(namebuf),
114278064Sume		    NULL, 0, niflags) != 0)
114378064Sume			strlcpy(namebuf, "?", sizeof(namebuf));
114478064Sume		printf("%s/%d if=%s\n", namebuf, p->prefixlen,
1145121156Sume		    if_indextoname(p->if_index, ifix_buf));
114678064Sume
1147253999Shrs		gettimeofday(&now, 0);
114878064Sume		/*
114978064Sume		 * meaning of fields, especially flags, is very different
115078064Sume		 * by origin.  notify the difference to the users.
115178064Sume		 */
115278064Sume		printf("flags=%s%s%s%s%s",
1153121156Sume		    p->raflags.onlink ? "L" : "",
1154121156Sume		    p->raflags.autonomous ? "A" : "",
1155121156Sume		    (p->flags & NDPRF_ONLINK) != 0 ? "O" : "",
1156121156Sume		    (p->flags & NDPRF_DETACHED) != 0 ? "D" : "",
115778064Sume#ifdef NDPRF_HOME
1158121156Sume		    (p->flags & NDPRF_HOME) != 0 ? "H" : ""
115978064Sume#else
1160121156Sume		    ""
116178064Sume#endif
1162121156Sume		    );
116378064Sume		if (p->vltime == ND6_INFINITE_LIFETIME)
116478064Sume			printf(" vltime=infinity");
116578064Sume		else
1166122615Sume			printf(" vltime=%lu", (unsigned long)p->vltime);
116778064Sume		if (p->pltime == ND6_INFINITE_LIFETIME)
116878064Sume			printf(", pltime=infinity");
116978064Sume		else
1170122615Sume			printf(", pltime=%lu", (unsigned long)p->pltime);
117178064Sume		if (p->expire == 0)
117278064Sume			printf(", expire=Never");
1173253970Shrs		else if (p->expire >= now.tv_sec)
117478064Sume			printf(", expire=%s",
1175253970Shrs			    sec2str(p->expire - now.tv_sec));
117678064Sume		else
117778064Sume			printf(", expired");
117878064Sume		printf(", ref=%d", p->refcnt);
117978064Sume		printf("\n");
118078064Sume		/*
118178064Sume		 * "advertising router" list is meaningful only if the prefix
118278064Sume		 * information is from RA.
118378064Sume		 */
118478064Sume		if (p->advrtrs) {
118578064Sume			int j;
118678064Sume			struct sockaddr_in6 *sin6;
118778064Sume
1188122615Sume			sin6 = advrtr;
118978064Sume			printf("  advertised by\n");
119078064Sume			for (j = 0; j < p->advrtrs; j++) {
119178064Sume				struct in6_nbrinfo *nbi;
119278064Sume
119378064Sume				if (getnameinfo((struct sockaddr *)sin6,
119478064Sume				    sin6->sin6_len, namebuf, sizeof(namebuf),
119578064Sume				    NULL, 0, ninflags) != 0)
119678064Sume					strlcpy(namebuf, "?", sizeof(namebuf));
119778064Sume				printf("    %s", namebuf);
119878064Sume
1199121156Sume				nbi = getnbrinfo(&sin6->sin6_addr,
1200121156Sume				    p->if_index, 0);
120178064Sume				if (nbi) {
1202121156Sume					switch (nbi->state) {
120378064Sume					case ND6_LLINFO_REACHABLE:
120478064Sume					case ND6_LLINFO_STALE:
120578064Sume					case ND6_LLINFO_DELAY:
120678064Sume					case ND6_LLINFO_PROBE:
120778064Sume						printf(" (reachable)\n");
120878064Sume						break;
120978064Sume					default:
121078064Sume						printf(" (unreachable)\n");
121178064Sume					}
121278064Sume				} else
121378064Sume					printf(" (no neighbor state)\n");
121478064Sume				sin6++;
121578064Sume			}
121678064Sume		} else
121778064Sume			printf("  No advertising router\n");
121878064Sume	}
121978064Sume	free(buf);
122055505Sshin}
122155505Sshin
1222287097Shrsstatic void
122355505Sshinpfx_flush()
122455505Sshin{
122555505Sshin	char dummyif[IFNAMSIZ+8];
122655505Sshin	int s;
122755505Sshin
122855505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
122955505Sshin		err(1, "socket");
1230121156Sume	strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
123155505Sshin	if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
1232121156Sume		err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
123355505Sshin}
123455505Sshin
1235287097Shrsstatic void
123655505Sshinrtr_flush()
123755505Sshin{
123855505Sshin	char dummyif[IFNAMSIZ+8];
123955505Sshin	int s;
124055505Sshin
124155505Sshin	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
124255505Sshin		err(1, "socket");
1243121156Sume	strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
124455505Sshin	if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
1245121156Sume		err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
124662590Sitojun
124762590Sitojun	close(s);
124855505Sshin}
124955505Sshin
1250287097Shrsstatic void
125155505Sshinharmonize_rtr()
125255505Sshin{
125355505Sshin	char dummyif[IFNAMSIZ+8];
125455505Sshin	int s;
125555505Sshin
125662590Sitojun	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
125762590Sitojun		err(1, "socket");
1258121156Sume	strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
125962590Sitojun	if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0)
1260121156Sume		err(1, "ioctl(SIOCSNDFLUSH_IN6)");
126162590Sitojun
126262590Sitojun	close(s);
126355505Sshin}
126455505Sshin
126562590Sitojun#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
126662590Sitojunstatic void
1267259169Saesetdefif(char *ifname)
126862590Sitojun{
126962590Sitojun	struct in6_ndifreq ndifreq;
127062590Sitojun	unsigned int ifindex;
127162590Sitojun
127262590Sitojun	if (strcasecmp(ifname, "delete") == 0)
127362590Sitojun		ifindex = 0;
127462590Sitojun	else {
127562590Sitojun		if ((ifindex = if_nametoindex(ifname)) == 0)
127662590Sitojun			err(1, "failed to resolve i/f index for %s", ifname);
127762590Sitojun	}
127862590Sitojun
127962590Sitojun	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
128062590Sitojun		err(1, "socket");
128162590Sitojun
1282121156Sume	strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
128362590Sitojun	ndifreq.ifindex = ifindex;
128462590Sitojun
128562590Sitojun	if (ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1286121156Sume		err(1, "ioctl(SIOCSDEFIFACE_IN6)");
128762590Sitojun
128862590Sitojun	close(s);
128962590Sitojun}
129062590Sitojun
129162590Sitojunstatic void
129262590Sitojungetdefif()
129362590Sitojun{
129462590Sitojun	struct in6_ndifreq ndifreq;
129562590Sitojun	char ifname[IFNAMSIZ+8];
129662590Sitojun
129762590Sitojun	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
129862590Sitojun		err(1, "socket");
129962590Sitojun
130062590Sitojun	memset(&ndifreq, 0, sizeof(ndifreq));
1301121156Sume	strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
130262590Sitojun
130362590Sitojun	if (ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1304121156Sume		err(1, "ioctl(SIOCGDEFIFACE_IN6)");
130562590Sitojun
130662590Sitojun	if (ndifreq.ifindex == 0)
130762590Sitojun		printf("No default interface.\n");
130862590Sitojun	else {
130962590Sitojun		if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL)
131062590Sitojun			err(1, "failed to resolve ifname for index %lu",
131162590Sitojun			    ndifreq.ifindex);
131262590Sitojun		printf("ND default interface = %s\n", ifname);
131362590Sitojun	}
131462590Sitojun
131562590Sitojun	close(s);
131662590Sitojun}
1317314041Sgarga#endif /* SIOCSDEFIFACE_IN6 */
131862590Sitojun
131955505Sshinstatic char *
1320259169Saesec2str(time_t total)
132155505Sshin{
132255505Sshin	static char result[256];
132355505Sshin	int days, hours, mins, secs;
132455505Sshin	int first = 1;
132555505Sshin	char *p = result;
1326121156Sume	char *ep = &result[sizeof(result)];
1327121156Sume	int n;
132855505Sshin
132955505Sshin	days = total / 3600 / 24;
133055505Sshin	hours = (total / 3600) % 24;
133155505Sshin	mins = (total / 60) % 60;
133255505Sshin	secs = total % 60;
133355505Sshin
133455505Sshin	if (days) {
133555505Sshin		first = 0;
1336121156Sume		n = snprintf(p, ep - p, "%dd", days);
1337121156Sume		if (n < 0 || n >= ep - p)
1338121156Sume			return "?";
1339121156Sume		p += n;
134055505Sshin	}
134155505Sshin	if (!first || hours) {
134255505Sshin		first = 0;
1343121156Sume		n = snprintf(p, ep - p, "%dh", hours);
1344121156Sume		if (n < 0 || n >= ep - p)
1345121156Sume			return "?";
1346121156Sume		p += n;
134755505Sshin	}
134855505Sshin	if (!first || mins) {
134955505Sshin		first = 0;
1350121156Sume		n = snprintf(p, ep - p, "%dm", mins);
1351121156Sume		if (n < 0 || n >= ep - p)
1352121156Sume			return "?";
1353121156Sume		p += n;
135455505Sshin	}
1355121156Sume	snprintf(p, ep - p, "%ds", secs);
135655505Sshin
135755505Sshin	return(result);
135855505Sshin}
135955505Sshin
136055505Sshin/*
136155505Sshin * Print the timestamp
136255505Sshin * from tcpdump/util.c
136355505Sshin */
136455505Sshinstatic void
1365259169Saets_print(const struct timeval *tvp)
136655505Sshin{
136755505Sshin	int s;
136855505Sshin
136955505Sshin	/* Default */
1370253999Shrs	s = (tvp->tv_sec + thiszone) % 86400;
137155505Sshin	(void)printf("%02d:%02d:%02d.%06u ",
1372253999Shrs	    s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec);
137355505Sshin}
1374186119Sqingli
1375186119Sqingli#undef NEXTADDR
1376