ndp.c revision 62590
138494Sobrien/*	$FreeBSD: head/usr.sbin/ndp/ndp.c 62590 2000-07-04 16:43:14Z itojun $	*/
2174294Sobrien/*	$KAME: ndp.c,v 1.41 2000/07/04 12:54:11 jinmei Exp $	*/
338494Sobrien
438494Sobrien/*
538494Sobrien * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * Redistribution and use in source and binary forms, with or without
938494Sobrien * modification, are permitted provided that the following conditions
1038494Sobrien * are met:
1138494Sobrien * 1. Redistributions of source code must retain the above copyright
1238494Sobrien *    notice, this list of conditions and the following disclaimer.
1338494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1438494Sobrien *    notice, this list of conditions and the following disclaimer in the
1538494Sobrien *    documentation and/or other materials provided with the distribution.
1638494Sobrien * 3. Neither the name of the project nor the names of its contributors
1738494Sobrien *    may be used to endorse or promote products derived from this software
1838494Sobrien *    without specific prior written permission.
1938494Sobrien *
2042629Sobrien * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2138494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2238494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2338494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2438494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2538494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2638494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2738494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2838494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2938494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3038494Sobrien * SUCH DAMAGE.
3138494Sobrien */
3238494Sobrien/*
3338494Sobrien * Copyright (c) 1984, 1993
3438494Sobrien *	The Regents of the University of California.  All rights reserved.
3538494Sobrien *
3638494Sobrien * This code is derived from software contributed to Berkeley by
3738494Sobrien * Sun Microsystems, Inc.
3838494Sobrien *
3938494Sobrien * Redistribution and use in source and binary forms, with or without
40174294Sobrien * modification, are permitted provided that the following conditions
4138494Sobrien * are met:
4238494Sobrien * 1. Redistributions of source code must retain the above copyright
4338494Sobrien *    notice, this list of conditions and the following disclaimer.
4438494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
4538494Sobrien *    notice, this list of conditions and the following disclaimer in the
4638494Sobrien *    documentation and/or other materials provided with the distribution.
4738494Sobrien * 3. All advertising materials mentioning features or use of this software
4838494Sobrien *    must display the following acknowledgement:
4938494Sobrien *	This product includes software developed by the University of
5038494Sobrien *	California, Berkeley and its contributors.
5138494Sobrien * 4. Neither the name of the University nor the names of its contributors
5238494Sobrien *    may be used to endorse or promote products derived from this software
5338494Sobrien *    without specific prior written permission.
5438494Sobrien *
5538494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5638494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5738494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5838494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5938494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6038494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6138494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6238494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6338494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6438494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6538494Sobrien * SUCH DAMAGE.
6638494Sobrien */
6738494Sobrien
6838494Sobrien/*
69174294Sobrien * Based on:
7038494Sobrien * "@(#) Copyright (c) 1984, 1993\n\
7138494Sobrien *	The Regents of the University of California.  All rights reserved.\n";
7238494Sobrien *
7338494Sobrien * "@(#)arp.c	8.2 (Berkeley) 1/2/94";
74174294Sobrien */
75174294Sobrien
7638494Sobrien/*
7738494Sobrien * ndp - display, set, delete and flush neighbor cache
7838494Sobrien */
7938494Sobrien
8038494Sobrien
8138494Sobrien#include <sys/param.h>
8238494Sobrien#include <sys/file.h>
8338494Sobrien#include <sys/ioctl.h>
8438494Sobrien#include <sys/socket.h>
8538494Sobrien#include <sys/sysctl.h>
86174294Sobrien#include <sys/time.h>
87174294Sobrien
88174294Sobrien#include <net/if.h>
89174294Sobrien#include <net/if_var.h>
9038494Sobrien#include <net/if_dl.h>
9138494Sobrien#include <net/if_types.h>
9238494Sobrien#include <net/route.h>
9338494Sobrien
9438494Sobrien#include <netinet/in.h>
95174294Sobrien#include <netinet/if_ether.h>
96174294Sobrien
97174294Sobrien#include <netinet/icmp6.h>
98174294Sobrien#include <netinet6/in6_var.h>
99174294Sobrien#include <netinet6/nd6.h>
10038494Sobrien
10138494Sobrien#include <arpa/inet.h>
10238494Sobrien
10338494Sobrien#include <netdb.h>
10438494Sobrien#include <errno.h>
10538494Sobrien#include <nlist.h>
10638494Sobrien#include <stdio.h>
10738494Sobrien#include <string.h>
10838494Sobrien#include <paths.h>
10938494Sobrien#include <err.h>
11038494Sobrien#include <stdlib.h>
11138494Sobrien#include <fcntl.h>
11238494Sobrien#include <unistd.h>
11338494Sobrien#include "gmt2local.h"
11438494Sobrien
11538494Sobrien#ifndef NI_WITHSCOPEID
11638494Sobrien#define NI_WITHSCOPEID	0
11738494Sobrien#endif
11838494Sobrien
11938494Sobrien/* packing rule for routing socket */
12038494Sobrien#define ROUNDUP(a) \
12138494Sobrien	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
12238494Sobrien#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
123174294Sobrien
12438494Sobrienstatic int pid;
12538494Sobrienstatic int fflag;
12638494Sobrienstatic int nflag;
12738494Sobrienstatic int tflag;
12838494Sobrienstatic int32_t thiszone;	/* time difference with gmt */
12938494Sobrienstatic int s = -1;
13038494Sobrienstatic int repeat = 0;
13138494Sobrienstatic int lflag = 0;
13238494Sobrien
13338494Sobrienchar ntop_buf[INET6_ADDRSTRLEN];	/* inet_ntop() */
13438494Sobrienchar host_buf[NI_MAXHOST];		/* getnameinfo() */
13538494Sobrienchar ifix_buf[IFNAMSIZ];		/* if_indextoname() */
13638494Sobrien
13738494Sobrienint main __P((int, char **));
13838494Sobrienint file __P((char *));
13938494Sobrienvoid getsocket __P((void));
14038494Sobrienint set __P((int, char **));
14138494Sobrienvoid get __P((char *));
14238494Sobrienint delete __P((char *));
14338494Sobrienvoid dump __P((struct in6_addr *));
14438494Sobrienstatic struct in6_nbrinfo *getnbrinfo __P((struct in6_addr *addr,
14538494Sobrien					   int ifindex, int));
14638494Sobrienstatic char *ether_str __P((struct sockaddr_dl *));
14738494Sobrienint ndp_ether_aton __P((char *, u_char *));
14838494Sobrienvoid usage __P((void));
14938494Sobrienint rtmsg __P((int));
15038494Sobrienvoid ifinfo __P((int, char **));
15138494Sobrienvoid rtrlist __P((void));
15238494Sobrienvoid plist __P((void));
15382794Sobrienvoid pfx_flush __P((void));
15438494Sobrienvoid rtrlist __P((void));
15538494Sobrienvoid rtr_flush __P((void));
15638494Sobrienvoid harmonize_rtr __P((void));
15738494Sobrien#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
15838494Sobrienstatic void getdefif __P((void));
15938494Sobrienstatic void setdefif __P((char *));
16038494Sobrien#endif
161174294Sobrienstatic char *sec2str __P((time_t t));
16238494Sobrienstatic char *ether_str __P((struct sockaddr_dl *sdl));
16338494Sobrienstatic void ts_print __P((const struct timeval *));
16438494Sobrien
16538494Sobrienint
16638494Sobrienmain(argc, argv)
16738494Sobrien	int argc;
16838494Sobrien	char **argv;
16938494Sobrien{
17038494Sobrien	int ch;
17138494Sobrien	int aflag = 0, cflag = 0, dflag = 0, sflag = 0, Hflag = 0,
17238494Sobrien		pflag = 0, rflag = 0, Pflag = 0, Rflag = 0;
17338494Sobrien
17438494Sobrien	pid = getpid();
17538494Sobrien	thiszone = gmt2local(0);
17638494Sobrien	while ((ch = getopt(argc, argv, "acndfIilprstA:HPR")) != EOF)
17738494Sobrien		switch ((char)ch) {
17838494Sobrien		case 'a':
17938494Sobrien			aflag = 1;
18038494Sobrien			break;
18138494Sobrien		case 'c':
18238494Sobrien			fflag = 1;
18338494Sobrien			cflag = 1;
18438494Sobrien			break;
18538494Sobrien		case 'd':
18638494Sobrien			dflag = 1;
18738494Sobrien			break;
18838494Sobrien		case 'I':
18938494Sobrien#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
19038494Sobrien			if (argc > 2)
19138494Sobrien				setdefif(argv[2]);
19238494Sobrien			getdefif(); /* always call it to print the result */
19338494Sobrien			exit(0);
19438494Sobrien#else
19538494Sobrien			errx(1, "not supported yet");
19638494Sobrien			/*NOTREACHED*/
19738494Sobrien#endif
19838494Sobrien		case 'i' :
19938494Sobrien			argc -= optind;
20038494Sobrien			argv += optind;
20138494Sobrien			if (argc < 1)
20238494Sobrien				usage();
20338494Sobrien			ifinfo(argc, argv);
204174294Sobrien			exit(0);
20538494Sobrien		case 'n':
20638494Sobrien			nflag = 1;
20738494Sobrien			continue;
20838494Sobrien		case 'p':
20938494Sobrien			pflag = 1;
21038494Sobrien			break;
21138494Sobrien		case 'f' :
21238494Sobrien			if (argc != 3)
21338494Sobrien				usage();
21438494Sobrien			file(argv[2]);
215174294Sobrien			exit(0);
216174294Sobrien		case 'l' :
21738494Sobrien			lflag = 1;
21838494Sobrien			break;
219174294Sobrien		case 'r' :
22038494Sobrien			rflag = 1;
22138494Sobrien			break;
22238494Sobrien		case 's':
22338494Sobrien			sflag = 1;
224174294Sobrien			break;
22538494Sobrien		case 't':
22638494Sobrien			tflag = 1;
22738494Sobrien			break;
22838494Sobrien		case 'A':
22938494Sobrien			aflag = 1;
23038494Sobrien			repeat = atoi(optarg);
23138494Sobrien			if (repeat < 0)
23238494Sobrien				usage();
23338494Sobrien			break;
23438494Sobrien		case 'H' :
23538494Sobrien			Hflag = 1;
23638494Sobrien			break;
23738494Sobrien		case 'P':
23838494Sobrien			Pflag = 1;
23938494Sobrien			break;
24038494Sobrien		case 'R':
24138494Sobrien			Rflag = 1;
24238494Sobrien			break;
24338494Sobrien		default:
24438494Sobrien			usage();
24538494Sobrien		}
24638494Sobrien
24738494Sobrien	argc -= optind;
24838494Sobrien	argv += optind;
24938494Sobrien
25038494Sobrien	if (aflag || cflag) {
251174294Sobrien		dump(0);
252174294Sobrien		exit(0);
25338494Sobrien	}
25438494Sobrien	if (dflag) {
25538494Sobrien		if (argc != 1)
25638494Sobrien			usage();
25738494Sobrien		delete(argv[0]);
25838494Sobrien		exit(0);
25938494Sobrien	}
26038494Sobrien	if (pflag) {
26138494Sobrien		plist();
26238494Sobrien		exit(0);
26338494Sobrien	}
26438494Sobrien	if (rflag) {
26538494Sobrien		rtrlist();
26638494Sobrien		exit(0);
26738494Sobrien	}
26838494Sobrien	if (sflag) {
26938494Sobrien		if (argc < 2 || argc > 4)
27038494Sobrien			usage();
27138494Sobrien		exit(set(argc, argv) ? 1 : 0);
27238494Sobrien	}
27338494Sobrien	if (Hflag) {
27438494Sobrien		harmonize_rtr();
27538494Sobrien		exit(0);
27638494Sobrien	}
27738494Sobrien	if (Pflag) {
27838494Sobrien		pfx_flush();
279174294Sobrien		exit(0);
280174294Sobrien	}
281174294Sobrien	if (Rflag) {
28238494Sobrien		rtr_flush();
28338494Sobrien		exit(0);
28438494Sobrien	}
28538494Sobrien
28638494Sobrien	if (argc != 1)
28738494Sobrien		usage();
28838494Sobrien	get(argv[0]);
28938494Sobrien	exit(0);
29038494Sobrien}
29138494Sobrien
29238494Sobrien/*
29338494Sobrien * Process a file to set standard ndp entries
294174294Sobrien */
29538494Sobrienint
29638494Sobrienfile(name)
29738494Sobrien	char *name;
29838494Sobrien{
29938494Sobrien	FILE *fp;
30038494Sobrien	int i, retval;
30138494Sobrien	char line[100], arg[5][50], *args[5];
30238494Sobrien
30338494Sobrien	if ((fp = fopen(name, "r")) == NULL) {
304174294Sobrien		fprintf(stderr, "ndp: cannot open %s\n", name);
30538494Sobrien		exit(1);
306174294Sobrien	}
30738494Sobrien	args[0] = &arg[0][0];
308174294Sobrien	args[1] = &arg[1][0];
30938494Sobrien	args[2] = &arg[2][0];
31038494Sobrien	args[3] = &arg[3][0];
31138494Sobrien	args[4] = &arg[4][0];
31238494Sobrien	retval = 0;
31338494Sobrien	while(fgets(line, 100, fp) != NULL) {
31438494Sobrien		i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2],
31538494Sobrien		    arg[3], arg[4]);
31638494Sobrien		if (i < 2) {
317174294Sobrien			fprintf(stderr, "ndp: bad line: %s\n", line);
31838494Sobrien			retval = 1;
31938494Sobrien			continue;
32038494Sobrien		}
32138494Sobrien		if (set(i, args))
32238494Sobrien			retval = 1;
32338494Sobrien	}
32438494Sobrien	fclose(fp);
32538494Sobrien	return (retval);
32638494Sobrien}
32738494Sobrien
32838494Sobrienvoid
32938494Sobriengetsocket()
33038494Sobrien{
33138494Sobrien	if (s < 0) {
33238494Sobrien		s = socket(PF_ROUTE, SOCK_RAW, 0);
33338494Sobrien		if (s < 0) {
33438494Sobrien			perror("ndp: socket");
33538494Sobrien			exit(1);
33638494Sobrien		}
33738494Sobrien	}
33838494Sobrien}
33938494Sobrien
34038494Sobrienstruct	sockaddr_in6 so_mask = {sizeof(so_mask), AF_INET6 };
34138494Sobrienstruct	sockaddr_in6 blank_sin = {sizeof(blank_sin), AF_INET6 }, sin_m;
342174294Sobrienstruct	sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
34338494Sobrienint	expire_time, flags, found_entry;
34438494Sobrienstruct	{
34538494Sobrien	struct	rt_msghdr m_rtm;
34638494Sobrien	char	m_space[512];
34738494Sobrien}	m_rtmsg;
34838494Sobrien
349174294Sobrien/*
350174294Sobrien * Set an individual neighbor cache entry
35138494Sobrien */
35238494Sobrienint
35338494Sobrienset(argc, argv)
35438494Sobrien	int argc;
355174294Sobrien	char **argv;
35638494Sobrien{
35738494Sobrien	register struct sockaddr_in6 *sin = &sin_m;
358174294Sobrien	register struct sockaddr_dl *sdl;
359174294Sobrien	register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
36038494Sobrien	struct addrinfo hints, *res;
36138494Sobrien	int gai_error;
362174294Sobrien	u_char *ea;
36338494Sobrien	char *host = argv[0], *eaddr = argv[1];
36438494Sobrien
36538494Sobrien	getsocket();
36638494Sobrien	argc -= 2;
36738494Sobrien	argv += 2;
36838494Sobrien	sdl_m = blank_sdl;
369174294Sobrien	sin_m = blank_sin;
37038494Sobrien
37138494Sobrien	bzero(&hints, sizeof(hints));
37238494Sobrien	hints.ai_family = AF_INET6;
37338494Sobrien	gai_error = getaddrinfo(host, NULL, &hints, &res);
37438494Sobrien	if (gai_error) {
375174294Sobrien		fprintf(stderr, "ndp: %s: %s\n", host,
376174294Sobrien			gai_strerror(gai_error));
37738494Sobrien		return 1;
37838494Sobrien	}
37938494Sobrien	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
38038494Sobrien#ifdef __KAME__
38138494Sobrien	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
38238494Sobrien		*(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
38338494Sobrien			htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
384174294Sobrien	}
385174294Sobrien#endif
386174294Sobrien	ea = (u_char *)LLADDR(&sdl_m);
387174294Sobrien	if (ndp_ether_aton(eaddr, ea) == 0)
388174294Sobrien		sdl_m.sdl_alen = 6;
389174294Sobrien	flags = expire_time = 0;
390174294Sobrien	while (argc-- > 0) {
391174294Sobrien		if (strncmp(argv[0], "temp", 4) == 0) {
392174294Sobrien			struct timeval time;
393174294Sobrien			gettimeofday(&time, 0);
39438494Sobrien			expire_time = time.tv_sec + 20 * 60;
395174294Sobrien		} else if (strncmp(argv[0], "proxy", 5) == 0)
396174294Sobrien			flags |= RTF_ANNOUNCE;
397174294Sobrien		argv++;
398174294Sobrien	}
39938494Sobrien	if (rtmsg(RTM_GET) < 0) {
40038494Sobrien		perror(host);
401174294Sobrien		return (1);
402174294Sobrien	}
403174294Sobrien	sin = (struct sockaddr_in6 *)(rtm + 1);
404174294Sobrien	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
405174294Sobrien	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
406174294Sobrien		if (sdl->sdl_family == AF_LINK &&
407174294Sobrien		    (rtm->rtm_flags & RTF_LLINFO) &&
408174294Sobrien		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
409174294Sobrien		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
410174294Sobrien		case IFT_ISO88024: case IFT_ISO88025:
411174294Sobrien			goto overwrite;
41238494Sobrien		}
41338494Sobrien		/*
414174294Sobrien		 * IPv4 arp command retries with sin_other = SIN_PROXY here.
415174294Sobrien		 */
416174294Sobrien		fprintf(stderr, "set: cannot configure a new entry\n");
41738494Sobrien		return 1;
41838494Sobrien	}
41938494Sobrien
42038494Sobrienoverwrite:
42138494Sobrien	if (sdl->sdl_family != AF_LINK) {
42238494Sobrien		printf("cannot intuit interface index and type for %s\n", host);
42338494Sobrien		return (1);
42438494Sobrien	}
42538494Sobrien	sdl_m.sdl_type = sdl->sdl_type;
42638494Sobrien	sdl_m.sdl_index = sdl->sdl_index;
42738494Sobrien	return (rtmsg(RTM_ADD));
42838494Sobrien}
42938494Sobrien
430174294Sobrien/*
43138494Sobrien * Display an individual neighbor cache entry
432174294Sobrien */
43338494Sobrienvoid
43438494Sobrienget(host)
43538494Sobrien	char *host;
43638494Sobrien{
43738494Sobrien	struct sockaddr_in6 *sin = &sin_m;
43838494Sobrien	struct addrinfo hints, *res;
43938494Sobrien	int gai_error;
44038494Sobrien
44138494Sobrien	sin_m = blank_sin;
442174294Sobrien	bzero(&hints, sizeof(hints));
44338494Sobrien	hints.ai_family = AF_INET6;
44438494Sobrien	gai_error = getaddrinfo(host, NULL, &hints, &res);
44538494Sobrien	if (gai_error) {
44638494Sobrien		fprintf(stderr, "ndp: %s: %s\n", host,
44738494Sobrien			gai_strerror(gai_error));
44838494Sobrien		return;
44938494Sobrien	}
45038494Sobrien	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
45138494Sobrien#ifdef __KAME__
45238494Sobrien	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
45338494Sobrien		*(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
45438494Sobrien			htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
45538494Sobrien	}
45638494Sobrien#endif
45738494Sobrien	dump(&sin->sin6_addr);
45842629Sobrien	if (found_entry == 0) {
45938494Sobrien		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
46038494Sobrien			    sizeof(host_buf), NULL ,0,
46138494Sobrien			    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
46238494Sobrien		printf("%s (%s) -- no entry\n", host, host_buf);
46338494Sobrien		exit(1);
46438494Sobrien	}
465174294Sobrien}
46638494Sobrien
46738494Sobrien/*
46838494Sobrien * Delete a neighbor cache entry
46938494Sobrien */
47038494Sobrienint
47138494Sobriendelete(host)
47238494Sobrien	char *host;
47338494Sobrien{
47438494Sobrien	struct sockaddr_in6 *sin = &sin_m;
47538494Sobrien	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
47638494Sobrien	struct sockaddr_dl *sdl;
47738494Sobrien	struct addrinfo hints, *res;
47838494Sobrien	int gai_error;
47938494Sobrien
48038494Sobrien	getsocket();
48138494Sobrien	sin_m = blank_sin;
48238494Sobrien
483174294Sobrien	bzero(&hints, sizeof(hints));
48438494Sobrien	hints.ai_family = AF_INET6;
48538494Sobrien	gai_error = getaddrinfo(host, NULL, &hints, &res);
48638494Sobrien	if (gai_error) {
48738494Sobrien		fprintf(stderr, "ndp: %s: %s\n", host,
48838494Sobrien			gai_strerror(gai_error));
48938494Sobrien		return 1;
49038494Sobrien	}
49138494Sobrien	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
49238494Sobrien#ifdef __KAME__
49338494Sobrien	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
49438494Sobrien		*(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
49538494Sobrien			htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
496174294Sobrien	}
497174294Sobrien#endif
49838494Sobrien	if (rtmsg(RTM_GET) < 0) {
49938494Sobrien		perror(host);
500174294Sobrien		return (1);
50138494Sobrien	}
50238494Sobrien	sin = (struct sockaddr_in6 *)(rtm + 1);
50338494Sobrien	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
50438494Sobrien	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
50538494Sobrien		if (sdl->sdl_family == AF_LINK &&
50638494Sobrien		    (rtm->rtm_flags & RTF_LLINFO) &&
50738494Sobrien		    !(rtm->rtm_flags & RTF_GATEWAY)) {
50838494Sobrien			switch (sdl->sdl_type) {
50938494Sobrien			case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
51038494Sobrien			case IFT_ISO88024: case IFT_ISO88025:
51138494Sobrien				goto delete;
51238494Sobrien			}
513		}
514		/*
515		 * IPv4 arp command retries with sin_other = SIN_PROXY here.
516		 */
517		fprintf(stderr, "delete: cannot delete non-NDP entry\n");
518		return 1;
519	}
520
521delete:
522	if (sdl->sdl_family != AF_LINK) {
523		printf("cannot locate %s\n", host);
524		return (1);
525	}
526	if (rtmsg(RTM_DELETE) == 0) {
527		struct sockaddr_in6 s6 = *sin; /* XXX: for safety */
528
529#ifdef __KAME__
530		if (IN6_IS_ADDR_LINKLOCAL(&s6.sin6_addr)) {
531			s6.sin6_scope_id = ntohs(*(u_int16_t *)&s6.sin6_addr.s6_addr[2]);
532			*(u_int16_t *)&s6.sin6_addr.s6_addr[2] = 0;
533		}
534#endif
535		getnameinfo((struct sockaddr *)&s6,
536			    s6.sin6_len, host_buf,
537			    sizeof(host_buf), NULL, 0,
538			    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
539		printf("%s (%s) deleted\n", host, host_buf);
540	}
541
542	return 0;
543}
544
545/*
546 * Dump the entire neighbor cache
547 */
548void
549dump(addr)
550	struct in6_addr *addr;
551{
552	int mib[6];
553	size_t needed;
554	char *lim, *buf, *next;
555	struct rt_msghdr *rtm;
556	struct sockaddr_in6 *sin;
557	struct sockaddr_dl *sdl;
558	extern int h_errno;
559	struct in6_nbrinfo *nbi;
560	struct timeval time;
561	int addrwidth;
562	char flgbuf[8];
563
564	/* Print header */
565	if (!tflag)
566		printf("%-31.31s %-17.17s %6.6s %-9.9s %2s %4s %4s\n",
567		       "Neighbor", "Linklayer Address", "Netif", "Expire",
568		       "St", "Flgs", "Prbs");
569
570again:;
571	mib[0] = CTL_NET;
572	mib[1] = PF_ROUTE;
573	mib[2] = 0;
574	mib[3] = AF_INET6;
575	mib[4] = NET_RT_FLAGS;
576	mib[5] = RTF_LLINFO;
577	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
578		err(1, "sysctl(PF_ROUTE estimate)");
579	if (needed > 0) {
580		if ((buf = malloc(needed)) == NULL)
581			errx(1, "malloc");
582		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
583			err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
584		lim = buf + needed;
585	} else
586		buf = lim = NULL;
587
588	for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
589		int isrouter = 0, prbs = 0;
590
591		rtm = (struct rt_msghdr *)next;
592		sin = (struct sockaddr_in6 *)(rtm + 1);
593		sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len));
594		if (addr) {
595			if (!IN6_ARE_ADDR_EQUAL(addr, &sin->sin6_addr))
596				continue;
597			found_entry = 1;
598		} else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
599			continue;
600		if (fflag == 1) {
601			delete((char *)inet_ntop(AF_INET6, &sin->sin6_addr,
602						 ntop_buf, sizeof(ntop_buf)));
603			continue;
604		}
605
606		if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
607		    IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
608			/* XXX: should scope id be filled in the kernel? */
609			if (sin->sin6_scope_id == 0)
610				sin->sin6_scope_id = sdl->sdl_index;
611
612			/* XXX: KAME specific hack; removed the embedded id */
613			*(u_int16_t *)&sin->sin6_addr.s6_addr[2] = 0;
614		}
615		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
616			    sizeof(host_buf), NULL, 0,
617			    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
618		gettimeofday(&time, 0);
619		if (tflag)
620			ts_print(&time);
621
622		if (lflag) {
623			addrwidth = strlen(host_buf);
624			if (addrwidth < 31)
625				addrwidth = 31;
626		} else
627			addrwidth = 31;
628
629		printf("%-*.*s %-17.17s %6.6s", addrwidth, addrwidth, host_buf,
630		       ether_str(sdl),
631		       if_indextoname(sdl->sdl_index, ifix_buf));
632
633		/* Print neighbor discovery specific informations */
634		nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1);
635		if (nbi) {
636			if (nbi->expire > time.tv_sec) {
637				printf(" %-9.9s",
638				       sec2str(nbi->expire - time.tv_sec));
639			}
640			else if (nbi->expire == 0)
641				printf(" %-9.9s", "permanent");
642			else
643				printf(" %-9.9s", "expired");
644
645			switch(nbi->state) {
646			 case ND6_LLINFO_NOSTATE:
647				 printf(" N");
648				 break;
649			 case ND6_LLINFO_WAITDELETE:
650				 printf(" W");
651				 break;
652			 case ND6_LLINFO_INCOMPLETE:
653				 printf(" I");
654				 break;
655			 case ND6_LLINFO_REACHABLE:
656				 printf(" R");
657				 break;
658			 case ND6_LLINFO_STALE:
659				 printf(" S");
660				 break;
661			 case ND6_LLINFO_DELAY:
662				 printf(" D");
663				 break;
664			 case ND6_LLINFO_PROBE:
665				 printf(" P");
666				 break;
667			 default:
668				 printf(" ?");
669				 break;
670			}
671
672			isrouter = nbi->isrouter;
673			prbs = nbi->asked;
674		}
675		else {
676			warnx("failed to get neighbor information");
677			printf("  ");
678		}
679		putchar(' ');
680
681		/*
682		 * other flags. R: router, P: proxy, W: ??
683		 */
684		if ((rtm->rtm_addrs & RTA_NETMASK) == 0) {
685			snprintf(flgbuf, sizeof(flgbuf), "%s%s",
686				isrouter ? "R" : "",
687				(rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
688		} else {
689			sin = (struct sockaddr_in6 *)
690				(sdl->sdl_len + (char *)sdl);
691			snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s",
692				isrouter ? "R" : "",
693				!IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr)
694					? "P" : "",
695				(sin->sin6_len != sizeof(struct sockaddr_in6))
696					? "W" : "",
697				(rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
698		}
699		printf(" %-4.4s", flgbuf);
700
701		if (prbs)
702			printf(" %4d", prbs);
703
704		printf("\n");
705	}
706
707	if (repeat) {
708		printf("\n");
709		sleep(repeat);
710		goto again;
711	}
712}
713
714static struct in6_nbrinfo *
715getnbrinfo(addr, ifindex, warning)
716	struct in6_addr *addr;
717	int ifindex;
718	int warning;
719{
720	static struct in6_nbrinfo nbi;
721	int s;
722
723	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
724		err(1, "socket");
725
726	bzero(&nbi, sizeof(nbi));
727	if_indextoname(ifindex, nbi.ifname);
728	nbi.addr = *addr;
729	if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
730		if (warning)
731			warn("ioctl(SIOCGNBRINFO_IN6)");
732		close(s);
733		return(NULL);
734	}
735
736	close(s);
737	return(&nbi);
738}
739
740static char *
741ether_str(sdl)
742	struct sockaddr_dl *sdl;
743{
744	static char ebuf[32];
745	u_char *cp;
746
747	if (sdl->sdl_alen) {
748		cp = (u_char *)LLADDR(sdl);
749		sprintf(ebuf, "%x:%x:%x:%x:%x:%x",
750			cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
751	}
752	else {
753		sprintf(ebuf, "(incomplete)");
754	}
755
756	return(ebuf);
757}
758
759int
760ndp_ether_aton(a, n)
761	char *a;
762	u_char *n;
763{
764	int i, o[6];
765
766	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
767					   &o[3], &o[4], &o[5]);
768	if (i != 6) {
769		fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a);
770		return (1);
771	}
772	for (i=0; i<6; i++)
773		n[i] = o[i];
774	return (0);
775}
776
777void
778usage()
779{
780	printf("usage: ndp hostname\n");
781	printf("       ndp -a[ntl]\n");
782	printf("       ndp [-ntl] -A wait\n");
783	printf("       ndp -c[nt]\n");
784	printf("       ndp -d[nt] hostname\n");
785	printf("       ndp -f[nt] filename\n");
786	printf("       ndp -i interface [flags...]\n");
787#ifdef SIOCSDEFIFACE_IN6
788	printf("       ndp -I [interface|delete]\n");
789#endif
790	printf("       ndp -p\n");
791	printf("       ndp -r\n");
792	printf("       ndp -s hostname ether_addr [temp] [proxy]\n");
793	printf("       ndp -H\n");
794	printf("       ndp -P\n");
795	printf("       ndp -R\n");
796	exit(1);
797}
798
799int
800rtmsg(cmd)
801	int cmd;
802{
803	static int seq;
804	int rlen;
805	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
806	register char *cp = m_rtmsg.m_space;
807	register int l;
808
809	errno = 0;
810	if (cmd == RTM_DELETE)
811		goto doit;
812	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
813	rtm->rtm_flags = flags;
814	rtm->rtm_version = RTM_VERSION;
815
816	switch (cmd) {
817	default:
818		fprintf(stderr, "ndp: internal wrong cmd\n");
819		exit(1);
820	case RTM_ADD:
821		rtm->rtm_addrs |= RTA_GATEWAY;
822		rtm->rtm_rmx.rmx_expire = expire_time;
823		rtm->rtm_inits = RTV_EXPIRE;
824		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
825		if (rtm->rtm_flags & RTF_ANNOUNCE) {
826			rtm->rtm_flags &= ~RTF_HOST;
827			rtm->rtm_flags |= RTA_NETMASK;
828		}
829		/* FALLTHROUGH */
830	case RTM_GET:
831		rtm->rtm_addrs |= RTA_DST;
832	}
833#define NEXTADDR(w, s) \
834	if (rtm->rtm_addrs & (w)) { \
835		bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
836
837	NEXTADDR(RTA_DST, sin_m);
838	NEXTADDR(RTA_GATEWAY, sdl_m);
839	memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
840	NEXTADDR(RTA_NETMASK, so_mask);
841
842	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
843doit:
844	l = rtm->rtm_msglen;
845	rtm->rtm_seq = ++seq;
846	rtm->rtm_type = cmd;
847	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
848		if (errno != ESRCH || cmd != RTM_DELETE) {
849			perror("writing to routing socket");
850			return (-1);
851		}
852	}
853	do {
854		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
855	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
856	if (l < 0)
857		(void) fprintf(stderr, "ndp: read from routing socket: %s\n",
858		    strerror(errno));
859	return (0);
860}
861
862void
863ifinfo(argc, argv)
864	int argc;
865	char **argv;
866{
867	struct in6_ndireq nd;
868	int i, s;
869	char *ifname = argv[0];
870	u_int32_t newflags;
871
872	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
873		perror("ndp: socket");
874		exit(1);
875	}
876	bzero(&nd, sizeof(nd));
877	strcpy(nd.ifname, ifname);
878	if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
879 		perror("ioctl (SIOCGIFINFO_IN6)");
880 		exit(1);
881 	}
882#define ND nd.ndi
883	newflags = ND.flags;
884	for (i = 1; i < argc; i++) {
885		int clear = 0;
886		char *cp = argv[i];
887
888		if (*cp == '-') {
889			clear = 1;
890			cp++;
891		}
892
893#define SETFLAG(s, f) \
894	do {\
895		if (strcmp(cp, (s)) == 0) {\
896			if (clear)\
897				newflags &= ~(f);\
898			else\
899				newflags |= (f);\
900		}\
901	} while (0)
902		SETFLAG("nud", ND6_IFF_PERFORMNUD);
903
904		ND.flags = newflags;
905		if (ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd) < 0) {
906			perror("ioctl(SIOCSIFINFO_FLAGS)");
907			exit(1);
908		}
909#undef SETFLAG
910	}
911
912	printf("linkmtu=%d", ND.linkmtu);
913	printf(", curhlim=%d", ND.chlim);
914	printf(", basereachable=%ds%dms",
915	       ND.basereachable / 1000, ND.basereachable % 1000);
916	printf(", reachable=%ds", ND.reachable);
917	printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000);
918	if (ND.flags) {
919		printf("\nFlags: ");
920		if ((ND.flags & ND6_IFF_PERFORMNUD) != 0)
921			printf("PERFORMNUD ");
922	}
923	putc('\n', stdout);
924#undef ND
925
926	close(s);
927}
928
929void
930rtrlist()
931{
932	struct in6_drlist dr;
933	int s, i;
934	struct timeval time;
935
936	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
937		perror("ndp: socket");
938		exit(1);
939	}
940	bzero(&dr, sizeof(dr));
941	strcpy(dr.ifname, "lo0"); /* dummy */
942	if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) {
943 		perror("ioctl (SIOCGDRLST_IN6)");
944 		exit(1);
945 	}
946#define DR dr.defrouter[i]
947	for (i = 0 ; DR.if_index && i < PRLSTSIZ ; i++) {
948		struct sockaddr_in6 sin6;
949
950		bzero(&sin6, sizeof(sin6));
951		sin6.sin6_family = AF_INET6;
952		sin6.sin6_len = sizeof(sin6);
953		sin6.sin6_addr = DR.rtaddr;
954		getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, host_buf,
955			    sizeof(host_buf), NULL, 0,
956			    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
957
958		printf("%s if=%s", host_buf,
959		       if_indextoname(DR.if_index, ifix_buf));
960		printf(", flags=%s%s",
961		       DR.flags & ND_RA_FLAG_MANAGED ? "M" : "",
962		       DR.flags & ND_RA_FLAG_OTHER   ? "O" : "");
963		gettimeofday(&time, 0);
964		if (DR.expire == 0)
965			printf(", expire=Never\n");
966		else
967			printf(", expire=%s\n",
968				sec2str(DR.expire - time.tv_sec));
969	}
970#undef DR
971	close(s);
972}
973
974void
975plist()
976{
977	struct in6_prlist pr;
978	int s, i;
979	struct timeval time;
980
981	gettimeofday(&time, 0);
982
983	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
984		perror("ndp: socket");
985		exit(1);
986	}
987	bzero(&pr, sizeof(pr));
988	strcpy(pr.ifname, "lo0"); /* dummy */
989	if (ioctl(s, SIOCGPRLST_IN6, (caddr_t)&pr) < 0) {
990 		perror("ioctl (SIOCGPRLST_IN6)");
991 		exit(1);
992 	}
993#define PR pr.prefix[i]
994	for (i = 0; PR.if_index && i < PRLSTSIZ ; i++) {
995		printf("%s/%d if=%s\n",
996		       inet_ntop(AF_INET6, &PR.prefix, ntop_buf,
997				 sizeof(ntop_buf)), PR.prefixlen,
998		       if_indextoname(PR.if_index, ifix_buf));
999		gettimeofday(&time, 0);
1000		/*
1001		 * meaning of fields, especially flags, is very different
1002		 * by origin.  notify the difference to the users.
1003		 */
1004		printf("  %s", PR.origin == PR_ORIG_RA ? "" : "advertise: ");
1005		printf("flags=%s%s",
1006		       PR.raflags.onlink ? "L" : "",
1007		       PR.raflags.autonomous ? "A" : "");
1008		if (PR.vltime == ND6_INFINITE_LIFETIME)
1009			printf(" vltime=infinity");
1010		else
1011			printf(" vltime=%ld", (long)PR.vltime);
1012		if (PR.pltime == ND6_INFINITE_LIFETIME)
1013			printf(", pltime=infinity");
1014		else
1015			printf(", pltime=%ld", (long)PR.pltime);
1016		if (PR.expire == 0)
1017			printf(", expire=Never");
1018		else if (PR.expire >= time.tv_sec)
1019			printf(", expire=%s",
1020				sec2str(PR.expire - time.tv_sec));
1021		else
1022			printf(", expired");
1023		switch (PR.origin) {
1024		case PR_ORIG_RA:
1025			printf(", origin=RA");
1026			break;
1027		case PR_ORIG_RR:
1028			printf(", origin=RR");
1029			break;
1030		case PR_ORIG_STATIC:
1031			printf(", origin=static");
1032			break;
1033		case PR_ORIG_KERNEL:
1034			printf(", origin=kernel");
1035			break;
1036		default:
1037			printf(", origin=?");
1038			break;
1039		}
1040		printf("\n");
1041		/*
1042		 * "advertising router" list is meaningful only if the prefix
1043		 * information is from RA.
1044		 */
1045		if (PR.origin != PR_ORIG_RA)
1046			;
1047		else if (PR.advrtrs) {
1048			int j;
1049			printf("  advertised by\n");
1050			for (j = 0; j < PR.advrtrs; j++) {
1051				struct sockaddr_in6 sin6;
1052				struct in6_nbrinfo *nbi;
1053
1054				bzero(&sin6, sizeof(sin6));
1055				sin6.sin6_family = AF_INET6;
1056				sin6.sin6_len = sizeof(sin6);
1057				sin6.sin6_addr = PR.advrtr[j];
1058				sin6.sin6_scope_id = PR.if_index; /* XXX */
1059				getnameinfo((struct sockaddr *)&sin6,
1060					    sin6.sin6_len, host_buf,
1061					    sizeof(host_buf), NULL, 0,
1062					    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
1063				printf("    %s", host_buf);
1064
1065				nbi = getnbrinfo(&sin6.sin6_addr, PR.if_index,
1066						 0);
1067				if (nbi) {
1068					switch(nbi->state) {
1069					 case ND6_LLINFO_REACHABLE:
1070					 case ND6_LLINFO_STALE:
1071					 case ND6_LLINFO_DELAY:
1072					 case ND6_LLINFO_PROBE:
1073						 printf(" (reachable)\n");
1074						 break;
1075					 default:
1076						 printf(" (unreachable)\n");
1077					}
1078				}
1079				else
1080					printf(" (no neighbor state)\n");
1081			}
1082			if (PR.advrtrs > DRLSTSIZ)
1083				printf("    and %d routers\n",
1084				       PR.advrtrs - DRLSTSIZ);
1085		} else
1086			printf("  No advertising router\n");
1087	}
1088#undef PR
1089	close(s);
1090}
1091
1092void
1093pfx_flush()
1094{
1095	char dummyif[IFNAMSIZ+8];
1096	int s;
1097
1098	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1099		err(1, "socket");
1100	strcpy(dummyif, "lo0"); /* dummy */
1101	if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
1102 		err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
1103}
1104
1105void
1106rtr_flush()
1107{
1108	char dummyif[IFNAMSIZ+8];
1109	int s;
1110
1111	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1112		err(1, "socket");
1113	strcpy(dummyif, "lo0"); /* dummy */
1114	if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
1115 		err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
1116
1117	close(s);
1118}
1119
1120void
1121harmonize_rtr()
1122{
1123	char dummyif[IFNAMSIZ+8];
1124	int s;
1125
1126	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1127		err(1, "socket");
1128	strcpy(dummyif, "lo0"); /* dummy */
1129	if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0)
1130 		err(1, "ioctl (SIOCSNDFLUSH_IN6)");
1131
1132	close(s);
1133}
1134
1135#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
1136static void
1137setdefif(ifname)
1138	char *ifname;
1139{
1140	struct in6_ndifreq ndifreq;
1141	unsigned int ifindex;
1142
1143	if (strcasecmp(ifname, "delete") == 0)
1144		ifindex = 0;
1145	else {
1146		if ((ifindex = if_nametoindex(ifname)) == 0)
1147			err(1, "failed to resolve i/f index for %s", ifname);
1148	}
1149
1150	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1151		err(1, "socket");
1152
1153	strcpy(ndifreq.ifname, "lo0"); /* dummy */
1154	ndifreq.ifindex = ifindex;
1155
1156	if (ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1157 		err(1, "ioctl (SIOCSDEFIFACE_IN6)");
1158
1159	close(s);
1160}
1161
1162static void
1163getdefif()
1164{
1165	struct in6_ndifreq ndifreq;
1166	char ifname[IFNAMSIZ+8];
1167
1168	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
1169		err(1, "socket");
1170
1171	memset(&ndifreq, 0, sizeof(ndifreq));
1172	strcpy(ndifreq.ifname, "lo0"); /* dummy */
1173
1174	if (ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
1175 		err(1, "ioctl (SIOCGDEFIFACE_IN6)");
1176
1177	if (ndifreq.ifindex == 0)
1178		printf("No default interface.\n");
1179	else {
1180		if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL)
1181			err(1, "failed to resolve ifname for index %lu",
1182			    ndifreq.ifindex);
1183		printf("ND default interface = %s\n", ifname);
1184	}
1185
1186	close(s);
1187}
1188#endif
1189
1190static char *
1191sec2str(total)
1192	time_t total;
1193{
1194	static char result[256];
1195	int days, hours, mins, secs;
1196	int first = 1;
1197	char *p = result;
1198
1199	days = total / 3600 / 24;
1200	hours = (total / 3600) % 24;
1201	mins = (total / 60) % 60;
1202	secs = total % 60;
1203
1204	if (days) {
1205		first = 0;
1206		p += sprintf(p, "%dd", days);
1207	}
1208	if (!first || hours) {
1209		first = 0;
1210		p += sprintf(p, "%dh", hours);
1211	}
1212	if (!first || mins) {
1213		first = 0;
1214		p += sprintf(p, "%dm", mins);
1215	}
1216	sprintf(p, "%ds", secs);
1217
1218	return(result);
1219}
1220
1221/*
1222 * Print the timestamp
1223 * from tcpdump/util.c
1224 */
1225static void
1226ts_print(tvp)
1227	const struct timeval *tvp;
1228{
1229	int s;
1230
1231	/* Default */
1232	s = (tvp->tv_sec + thiszone) % 86400;
1233	(void)printf("%02d:%02d:%02d.%06u ",
1234	    s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec);
1235}
1236