ndp.c revision 62590
1/*	$FreeBSD: head/usr.sbin/ndp/ndp.c 62590 2000-07-04 16:43:14Z itojun $	*/
2/*	$KAME: ndp.c,v 1.41 2000/07/04 12:54:11 jinmei Exp $	*/
3
4/*
5 * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32/*
33 * Copyright (c) 1984, 1993
34 *	The Regents of the University of California.  All rights reserved.
35 *
36 * This code is derived from software contributed to Berkeley by
37 * Sun Microsystems, Inc.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 *    notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 *    notice, this list of conditions and the following disclaimer in the
46 *    documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 *    must display the following acknowledgement:
49 *	This product includes software developed by the University of
50 *	California, Berkeley and its contributors.
51 * 4. Neither the name of the University nor the names of its contributors
52 *    may be used to endorse or promote products derived from this software
53 *    without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 */
67
68/*
69 * Based on:
70 * "@(#) Copyright (c) 1984, 1993\n\
71 *	The Regents of the University of California.  All rights reserved.\n";
72 *
73 * "@(#)arp.c	8.2 (Berkeley) 1/2/94";
74 */
75
76/*
77 * ndp - display, set, delete and flush neighbor cache
78 */
79
80
81#include <sys/param.h>
82#include <sys/file.h>
83#include <sys/ioctl.h>
84#include <sys/socket.h>
85#include <sys/sysctl.h>
86#include <sys/time.h>
87
88#include <net/if.h>
89#include <net/if_var.h>
90#include <net/if_dl.h>
91#include <net/if_types.h>
92#include <net/route.h>
93
94#include <netinet/in.h>
95#include <netinet/if_ether.h>
96
97#include <netinet/icmp6.h>
98#include <netinet6/in6_var.h>
99#include <netinet6/nd6.h>
100
101#include <arpa/inet.h>
102
103#include <netdb.h>
104#include <errno.h>
105#include <nlist.h>
106#include <stdio.h>
107#include <string.h>
108#include <paths.h>
109#include <err.h>
110#include <stdlib.h>
111#include <fcntl.h>
112#include <unistd.h>
113#include "gmt2local.h"
114
115#ifndef NI_WITHSCOPEID
116#define NI_WITHSCOPEID	0
117#endif
118
119/* packing rule for routing socket */
120#define ROUNDUP(a) \
121	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
122#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
123
124static int pid;
125static int fflag;
126static int nflag;
127static int tflag;
128static int32_t thiszone;	/* time difference with gmt */
129static int s = -1;
130static int repeat = 0;
131static int lflag = 0;
132
133char ntop_buf[INET6_ADDRSTRLEN];	/* inet_ntop() */
134char host_buf[NI_MAXHOST];		/* getnameinfo() */
135char ifix_buf[IFNAMSIZ];		/* if_indextoname() */
136
137int main __P((int, char **));
138int file __P((char *));
139void getsocket __P((void));
140int set __P((int, char **));
141void get __P((char *));
142int delete __P((char *));
143void dump __P((struct in6_addr *));
144static struct in6_nbrinfo *getnbrinfo __P((struct in6_addr *addr,
145					   int ifindex, int));
146static char *ether_str __P((struct sockaddr_dl *));
147int ndp_ether_aton __P((char *, u_char *));
148void usage __P((void));
149int rtmsg __P((int));
150void ifinfo __P((int, char **));
151void rtrlist __P((void));
152void plist __P((void));
153void pfx_flush __P((void));
154void rtrlist __P((void));
155void rtr_flush __P((void));
156void harmonize_rtr __P((void));
157#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
158static void getdefif __P((void));
159static void setdefif __P((char *));
160#endif
161static char *sec2str __P((time_t t));
162static char *ether_str __P((struct sockaddr_dl *sdl));
163static void ts_print __P((const struct timeval *));
164
165int
166main(argc, argv)
167	int argc;
168	char **argv;
169{
170	int ch;
171	int aflag = 0, cflag = 0, dflag = 0, sflag = 0, Hflag = 0,
172		pflag = 0, rflag = 0, Pflag = 0, Rflag = 0;
173
174	pid = getpid();
175	thiszone = gmt2local(0);
176	while ((ch = getopt(argc, argv, "acndfIilprstA:HPR")) != EOF)
177		switch ((char)ch) {
178		case 'a':
179			aflag = 1;
180			break;
181		case 'c':
182			fflag = 1;
183			cflag = 1;
184			break;
185		case 'd':
186			dflag = 1;
187			break;
188		case 'I':
189#ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
190			if (argc > 2)
191				setdefif(argv[2]);
192			getdefif(); /* always call it to print the result */
193			exit(0);
194#else
195			errx(1, "not supported yet");
196			/*NOTREACHED*/
197#endif
198		case 'i' :
199			argc -= optind;
200			argv += optind;
201			if (argc < 1)
202				usage();
203			ifinfo(argc, argv);
204			exit(0);
205		case 'n':
206			nflag = 1;
207			continue;
208		case 'p':
209			pflag = 1;
210			break;
211		case 'f' :
212			if (argc != 3)
213				usage();
214			file(argv[2]);
215			exit(0);
216		case 'l' :
217			lflag = 1;
218			break;
219		case 'r' :
220			rflag = 1;
221			break;
222		case 's':
223			sflag = 1;
224			break;
225		case 't':
226			tflag = 1;
227			break;
228		case 'A':
229			aflag = 1;
230			repeat = atoi(optarg);
231			if (repeat < 0)
232				usage();
233			break;
234		case 'H' :
235			Hflag = 1;
236			break;
237		case 'P':
238			Pflag = 1;
239			break;
240		case 'R':
241			Rflag = 1;
242			break;
243		default:
244			usage();
245		}
246
247	argc -= optind;
248	argv += optind;
249
250	if (aflag || cflag) {
251		dump(0);
252		exit(0);
253	}
254	if (dflag) {
255		if (argc != 1)
256			usage();
257		delete(argv[0]);
258		exit(0);
259	}
260	if (pflag) {
261		plist();
262		exit(0);
263	}
264	if (rflag) {
265		rtrlist();
266		exit(0);
267	}
268	if (sflag) {
269		if (argc < 2 || argc > 4)
270			usage();
271		exit(set(argc, argv) ? 1 : 0);
272	}
273	if (Hflag) {
274		harmonize_rtr();
275		exit(0);
276	}
277	if (Pflag) {
278		pfx_flush();
279		exit(0);
280	}
281	if (Rflag) {
282		rtr_flush();
283		exit(0);
284	}
285
286	if (argc != 1)
287		usage();
288	get(argv[0]);
289	exit(0);
290}
291
292/*
293 * Process a file to set standard ndp entries
294 */
295int
296file(name)
297	char *name;
298{
299	FILE *fp;
300	int i, retval;
301	char line[100], arg[5][50], *args[5];
302
303	if ((fp = fopen(name, "r")) == NULL) {
304		fprintf(stderr, "ndp: cannot open %s\n", name);
305		exit(1);
306	}
307	args[0] = &arg[0][0];
308	args[1] = &arg[1][0];
309	args[2] = &arg[2][0];
310	args[3] = &arg[3][0];
311	args[4] = &arg[4][0];
312	retval = 0;
313	while(fgets(line, 100, fp) != NULL) {
314		i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2],
315		    arg[3], arg[4]);
316		if (i < 2) {
317			fprintf(stderr, "ndp: bad line: %s\n", line);
318			retval = 1;
319			continue;
320		}
321		if (set(i, args))
322			retval = 1;
323	}
324	fclose(fp);
325	return (retval);
326}
327
328void
329getsocket()
330{
331	if (s < 0) {
332		s = socket(PF_ROUTE, SOCK_RAW, 0);
333		if (s < 0) {
334			perror("ndp: socket");
335			exit(1);
336		}
337	}
338}
339
340struct	sockaddr_in6 so_mask = {sizeof(so_mask), AF_INET6 };
341struct	sockaddr_in6 blank_sin = {sizeof(blank_sin), AF_INET6 }, sin_m;
342struct	sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
343int	expire_time, flags, found_entry;
344struct	{
345	struct	rt_msghdr m_rtm;
346	char	m_space[512];
347}	m_rtmsg;
348
349/*
350 * Set an individual neighbor cache entry
351 */
352int
353set(argc, argv)
354	int argc;
355	char **argv;
356{
357	register struct sockaddr_in6 *sin = &sin_m;
358	register struct sockaddr_dl *sdl;
359	register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
360	struct addrinfo hints, *res;
361	int gai_error;
362	u_char *ea;
363	char *host = argv[0], *eaddr = argv[1];
364
365	getsocket();
366	argc -= 2;
367	argv += 2;
368	sdl_m = blank_sdl;
369	sin_m = blank_sin;
370
371	bzero(&hints, sizeof(hints));
372	hints.ai_family = AF_INET6;
373	gai_error = getaddrinfo(host, NULL, &hints, &res);
374	if (gai_error) {
375		fprintf(stderr, "ndp: %s: %s\n", host,
376			gai_strerror(gai_error));
377		return 1;
378	}
379	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
380#ifdef __KAME__
381	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
382		*(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
383			htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
384	}
385#endif
386	ea = (u_char *)LLADDR(&sdl_m);
387	if (ndp_ether_aton(eaddr, ea) == 0)
388		sdl_m.sdl_alen = 6;
389	flags = expire_time = 0;
390	while (argc-- > 0) {
391		if (strncmp(argv[0], "temp", 4) == 0) {
392			struct timeval time;
393			gettimeofday(&time, 0);
394			expire_time = time.tv_sec + 20 * 60;
395		} else if (strncmp(argv[0], "proxy", 5) == 0)
396			flags |= RTF_ANNOUNCE;
397		argv++;
398	}
399	if (rtmsg(RTM_GET) < 0) {
400		perror(host);
401		return (1);
402	}
403	sin = (struct sockaddr_in6 *)(rtm + 1);
404	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
405	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
406		if (sdl->sdl_family == AF_LINK &&
407		    (rtm->rtm_flags & RTF_LLINFO) &&
408		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
409		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
410		case IFT_ISO88024: case IFT_ISO88025:
411			goto overwrite;
412		}
413		/*
414		 * IPv4 arp command retries with sin_other = SIN_PROXY here.
415		 */
416		fprintf(stderr, "set: cannot configure a new entry\n");
417		return 1;
418	}
419
420overwrite:
421	if (sdl->sdl_family != AF_LINK) {
422		printf("cannot intuit interface index and type for %s\n", host);
423		return (1);
424	}
425	sdl_m.sdl_type = sdl->sdl_type;
426	sdl_m.sdl_index = sdl->sdl_index;
427	return (rtmsg(RTM_ADD));
428}
429
430/*
431 * Display an individual neighbor cache entry
432 */
433void
434get(host)
435	char *host;
436{
437	struct sockaddr_in6 *sin = &sin_m;
438	struct addrinfo hints, *res;
439	int gai_error;
440
441	sin_m = blank_sin;
442	bzero(&hints, sizeof(hints));
443	hints.ai_family = AF_INET6;
444	gai_error = getaddrinfo(host, NULL, &hints, &res);
445	if (gai_error) {
446		fprintf(stderr, "ndp: %s: %s\n", host,
447			gai_strerror(gai_error));
448		return;
449	}
450	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
451#ifdef __KAME__
452	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
453		*(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
454			htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
455	}
456#endif
457	dump(&sin->sin6_addr);
458	if (found_entry == 0) {
459		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
460			    sizeof(host_buf), NULL ,0,
461			    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
462		printf("%s (%s) -- no entry\n", host, host_buf);
463		exit(1);
464	}
465}
466
467/*
468 * Delete a neighbor cache entry
469 */
470int
471delete(host)
472	char *host;
473{
474	struct sockaddr_in6 *sin = &sin_m;
475	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
476	struct sockaddr_dl *sdl;
477	struct addrinfo hints, *res;
478	int gai_error;
479
480	getsocket();
481	sin_m = blank_sin;
482
483	bzero(&hints, sizeof(hints));
484	hints.ai_family = AF_INET6;
485	gai_error = getaddrinfo(host, NULL, &hints, &res);
486	if (gai_error) {
487		fprintf(stderr, "ndp: %s: %s\n", host,
488			gai_strerror(gai_error));
489		return 1;
490	}
491	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
492#ifdef __KAME__
493	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
494		*(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
495			htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
496	}
497#endif
498	if (rtmsg(RTM_GET) < 0) {
499		perror(host);
500		return (1);
501	}
502	sin = (struct sockaddr_in6 *)(rtm + 1);
503	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
504	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
505		if (sdl->sdl_family == AF_LINK &&
506		    (rtm->rtm_flags & RTF_LLINFO) &&
507		    !(rtm->rtm_flags & RTF_GATEWAY)) {
508			switch (sdl->sdl_type) {
509			case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
510			case IFT_ISO88024: case IFT_ISO88025:
511				goto delete;
512			}
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