ndp.c revision 57919
1136849Sscottl/*
2136849Sscottl * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
3149871Sscottl * All rights reserved.
4136849Sscottl *
5136849Sscottl * Redistribution and use in source and binary forms, with or without
6136849Sscottl * modification, are permitted provided that the following conditions
7319182Sngie * are met:
8142988Sscottl * 1. Redistributions of source code must retain the above copyright
9136849Sscottl *    notice, this list of conditions and the following disclaimer.
10136849Sscottl * 2. Redistributions in binary form must reproduce the above copyright
11136849Sscottl *    notice, this list of conditions and the following disclaimer in the
12136849Sscottl *    documentation and/or other materials provided with the distribution.
13149871Sscottl * 3. Neither the name of the project nor the names of its contributors
14136856Sscottl *    may be used to endorse or promote products derived from this software
15136849Sscottl *    without specific prior written permission.
16211690Simp *
17149871Sscottl * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18149871Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19149871Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20149871Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21136849Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22149871Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23149871Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24149871Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25136849Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26136849Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27136849Sscottl * SUCH DAMAGE.
28136849Sscottl *
29136849Sscottl * $FreeBSD: head/usr.sbin/ndp/ndp.c 57919 2000-03-11 20:57:31Z shin $
30136849Sscottl */
31136849Sscottl/*
32136849Sscottl * Copyright (c) 1984, 1993
33136849Sscottl *	The Regents of the University of California.  All rights reserved.
34136849Sscottl *
35136849Sscottl * This code is derived from software contributed to Berkeley by
36136849Sscottl * Sun Microsystems, Inc.
37136849Sscottl *
38136849Sscottl * Redistribution and use in source and binary forms, with or without
39136849Sscottl * modification, are permitted provided that the following conditions
40136849Sscottl * are met:
41136849Sscottl * 1. Redistributions of source code must retain the above copyright
42136849Sscottl *    notice, this list of conditions and the following disclaimer.
43136849Sscottl * 2. Redistributions in binary form must reproduce the above copyright
44136849Sscottl *    notice, this list of conditions and the following disclaimer in the
45136849Sscottl *    documentation and/or other materials provided with the distribution.
46136849Sscottl * 3. All advertising materials mentioning features or use of this software
47136849Sscottl *    must display the following acknowledgement:
48136849Sscottl *	This product includes software developed by the University of
49136849Sscottl *	California, Berkeley and its contributors.
50149871Sscottl * 4. Neither the name of the University nor the names of its contributors
51211690Simp *    may be used to endorse or promote products derived from this software
52136849Sscottl *    without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 */
66
67/*
68 * Based on:
69 * "@(#) Copyright (c) 1984, 1993\n\
70 *	The Regents of the University of California.  All rights reserved.\n";
71 *
72 * "@(#)arp.c	8.2 (Berkeley) 1/2/94";
73 */
74
75/*
76 * ndp - display, set, delete and flush neighbor cache
77 */
78
79
80#include <sys/param.h>
81#include <sys/file.h>
82#include <sys/ioctl.h>
83#include <sys/socket.h>
84#include <sys/sysctl.h>
85#include <sys/time.h>
86
87#include <net/if.h>
88#include <net/if_var.h>
89#include <net/if_dl.h>
90#include <net/if_types.h>
91#include <net/route.h>
92
93#include <netinet/in.h>
94#include <netinet/if_ether.h>
95
96#include <netinet/icmp6.h>
97#include <netinet6/in6_var.h>
98#include <netinet6/nd6.h>
99
100#include <arpa/inet.h>
101
102#include <netdb.h>
103#include <errno.h>
104#include <nlist.h>
105#include <stdio.h>
106#include <string.h>
107#include <paths.h>
108#include <err.h>
109#include <stdlib.h>
110#include <fcntl.h>
111#include <unistd.h>
112#include "gmt2local.h"
113
114/* packing rule for routing socket */
115#define	ROUNDUP(a) \
116	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
117#define	ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
118
119extern int	 errno;
120static int	pid;
121static int	fflag;
122static int	nflag;
123static int	tflag;
124static int32_t	thiszone;	/* time difference with gmt */
125static int	s = -1;
126static int	repeat = 0;
127static int	lflag = 0;
128
129char	ntop_buf[INET6_ADDRSTRLEN];	/* inet_ntop() */
130char	host_buf[NI_MAXHOST];		/* getnameinfo() */
131char	ifix_buf[IFNAMSIZ];		/* if_indextoname() */
132
133int	main __P((int, char **));
134int	file __P((char *));
135void	getsocket __P((void));
136int	set __P((int, char **));
137void	get __P((char *));
138int	delete __P((char *));
139void	dump __P((struct in6_addr *));
140static struct	in6_nbrinfo *getnbrinfo __P((struct in6_addr *addr, int ifindex));
141static char	*ether_str __P((struct sockaddr_dl *));
142int	ndp_ether_aton __P((char *, u_char *));
143void	usage __P((void));
144int	rtmsg __P((int));
145void	ifinfo __P((char *));
146void	list __P((void));
147void	plist __P((void));
148void	pfx_flush __P((void));
149void	rtr_flush __P((void));
150void	harmonize_rtr __P((void));
151static char	*sec2str __P((time_t t));
152static char	*ether_str __P((struct sockaddr_dl *sdl));
153static void	ts_print __P((const struct timeval *));
154
155int
156main(argc, argv)
157	int argc;
158	char **argv;
159{
160	int ch;
161	int aflag = 0, cflag = 0, dflag = 0, sflag = 0, Hflag = 0,
162		pflag = 0, rflag = 0, Pflag = 0, Rflag = 0;
163	extern char *optarg;
164	extern int optind;
165
166	pid = getpid();
167	thiszone = gmt2local(0);
168	while ((ch = getopt(argc, argv, "acndfilprstA:HPR")) != EOF)
169		switch ((char)ch) {
170		case 'a':
171			aflag = 1;
172			break;
173		case 'c':
174			fflag = 1;
175			cflag = 1;
176			break;
177		case 'd':
178			dflag = 1;
179			break;
180		case 'i' :
181			if (argc != 3)
182				usage();
183			ifinfo(argv[2]);
184			exit(0);
185		case 'n':
186			nflag = 1;
187			continue;
188		case 'p':
189			pflag = 1;
190			break;
191		case 'f' :
192			if (argc != 3)
193				usage();
194			file(argv[2]);
195			exit(0);
196		case 'l' :
197			lflag = 1;
198			break;
199		case 'r' :
200			rflag = 1;
201			break;
202		case 's':
203			sflag = 1;
204			break;
205		case 't':
206			tflag = 1;
207			break;
208		case 'A':
209			aflag = 1;
210			repeat = atoi(optarg);
211			if (repeat < 0)
212				usage();
213			break;
214		case 'H' :
215			Hflag = 1;
216			break;
217		case 'P':
218			Pflag = 1;
219			break;
220		case 'R':
221			Rflag = 1;
222			break;
223		default:
224			usage();
225		}
226
227	argc -= optind;
228	argv += optind;
229
230	if (aflag || cflag) {
231		dump(0);
232		exit(0);
233	}
234	if (dflag) {
235		if (argc != 1)
236			usage();
237		delete(argv[0]);
238	}
239	if (pflag) {
240		plist();
241		exit(0);
242	}
243	if (rflag) {
244		rtrlist();
245		exit(0);
246	}
247	if (sflag) {
248		if (argc < 2 || argc > 4)
249			usage();
250		exit(set(argc, argv) ? 1 : 0);
251	}
252	if (Hflag) {
253		harmonize_rtr();
254		exit(0);
255	}
256	if (Pflag) {
257		pfx_flush();
258		exit(0);
259	}
260	if (Rflag) {
261		rtr_flush();
262		exit(0);
263	}
264
265	if (argc != 1)
266		usage();
267	get(argv[0]);
268	exit(0);
269}
270
271/*
272 * Process a file to set standard ndp entries
273 */
274int
275file(name)
276	char *name;
277{
278	FILE *fp;
279	int i, retval;
280	char line[100], arg[5][50], *args[5];
281
282	if ((fp = fopen(name, "r")) == NULL) {
283		fprintf(stderr, "ndp: cannot open %s\n", name);
284		exit(1);
285	}
286	args[0] = &arg[0][0];
287	args[1] = &arg[1][0];
288	args[2] = &arg[2][0];
289	args[3] = &arg[3][0];
290	args[4] = &arg[4][0];
291	retval = 0;
292	while(fgets(line, 100, fp) != NULL) {
293		i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2],
294		    arg[3], arg[4]);
295		if (i < 2) {
296			fprintf(stderr, "ndp: bad line: %s\n", line);
297			retval = 1;
298			continue;
299		}
300		if (set(i, args))
301			retval = 1;
302	}
303	fclose(fp);
304	return (retval);
305}
306
307void
308getsocket()
309{
310	if (s < 0) {
311		s = socket(PF_ROUTE, SOCK_RAW, 0);
312		if (s < 0) {
313			perror("ndp: socket");
314			exit(1);
315		}
316	}
317}
318
319struct	sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
320struct	sockaddr_in6 blank_sin = {sizeof(blank_sin), AF_INET6 }, sin_m;
321struct	sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
322int	expire_time, flags, found_entry;
323struct	{
324	struct	rt_msghdr m_rtm;
325	char	m_space[512];
326}	m_rtmsg;
327
328/*
329 * Set an individual neighbor cache entry
330 */
331int
332set(argc, argv)
333	int argc;
334	char **argv;
335{
336	register struct sockaddr_in6 *sin = &sin_m;
337	register struct sockaddr_dl *sdl;
338	register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
339	struct addrinfo hints, *res;
340	int gai_error;
341	u_char *ea;
342	char *host = argv[0], *eaddr = argv[1];
343
344	getsocket();
345	argc -= 2;
346	argv += 2;
347	sdl_m = blank_sdl;
348	sin_m = blank_sin;
349
350	bzero(&hints, sizeof(hints));
351	hints.ai_family = AF_INET6;
352	gai_error = getaddrinfo(host, NULL, &hints, &res);
353	if (gai_error) {
354		fprintf(stderr, "ndp: %s: %s\n", host,
355			gai_strerror(gai_error));
356		return 1;
357	}
358	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
359	ea = (u_char *)LLADDR(&sdl_m);
360	if (ndp_ether_aton(eaddr, ea) == 0)
361		sdl_m.sdl_alen = 6;
362	flags = expire_time = 0;
363	while (argc-- > 0) {
364		if (strncmp(argv[0], "temp", 4) == 0) {
365			struct timeval time;
366			gettimeofday(&time, 0);
367			expire_time = time.tv_sec + 20 * 60;
368		}
369		argv++;
370	}
371tryagain:
372	if (rtmsg(RTM_GET) < 0) {
373		perror(host);
374		return (1);
375	}
376	sin = (struct sockaddr_in6 *)(rtm + 1);
377	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
378	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
379		if (sdl->sdl_family == AF_LINK &&
380		    (rtm->rtm_flags & RTF_LLINFO) &&
381		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
382		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
383		case IFT_ISO88024: case IFT_ISO88025:
384			goto overwrite;
385		}
386		goto tryagain;
387	}
388overwrite:
389	if (sdl->sdl_family != AF_LINK) {
390		printf("cannot intuit interface index and type for %s\n", host);
391		return (1);
392	}
393	sdl_m.sdl_type = sdl->sdl_type;
394	sdl_m.sdl_index = sdl->sdl_index;
395	return (rtmsg(RTM_ADD));
396}
397
398/*
399 * Display an individual neighbor cache entry
400 */
401void
402get(host)
403	char *host;
404{
405	struct sockaddr_in6 *sin = &sin_m;
406	struct addrinfo hints, *res;
407	int gai_error;
408
409	sin_m = blank_sin;
410	bzero(&hints, sizeof(hints));
411	hints.ai_family = AF_INET6;
412	gai_error = getaddrinfo(host, NULL, &hints, &res);
413	if (gai_error) {
414		fprintf(stderr, "ndp: %s: %s\n", host,
415			gai_strerror(gai_error));
416		return;
417	}
418	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
419	dump(&sin->sin6_addr);
420	if (found_entry == 0) {
421		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
422			    sizeof(host_buf), NULL ,0,
423			    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
424		printf("%s (%s) -- no entry\n", host, host_buf);
425		exit(1);
426	}
427}
428
429/*
430 * Delete a neighbor cache entry
431 */
432int
433delete(host)
434	char *host;
435{
436	struct sockaddr_in6 *sin = &sin_m;
437	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
438	struct sockaddr_dl *sdl;
439	struct addrinfo hints, *res;
440	int gai_error;
441
442	getsocket();
443	sin_m = blank_sin;
444
445	bzero(&hints, sizeof(hints));
446	hints.ai_family = AF_INET6;
447	gai_error = getaddrinfo(host, NULL, &hints, &res);
448	if (gai_error) {
449		fprintf(stderr, "ndp: %s: %s\n", host,
450			gai_strerror(gai_error));
451		return 1;
452	}
453	sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
454/*tryagain:*/
455	if (rtmsg(RTM_GET) < 0) {
456		perror(host);
457		return (1);
458	}
459	sin = (struct sockaddr_in6 *)(rtm + 1);
460	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
461	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
462		if (sdl->sdl_family == AF_LINK &&
463		    (rtm->rtm_flags & RTF_LLINFO) &&
464		    !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
465		case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
466		case IFT_ISO88024: case IFT_ISO88025:
467			goto delete;
468		}
469	}
470	return 0;
471delete:
472	if (sdl->sdl_family != AF_LINK) {
473		printf("cannot locate %s\n", host);
474		return (1);
475	}
476	if (rtmsg(RTM_DELETE) == 0) {
477	       getnameinfo((struct sockaddr *)sin,
478			   sin->sin6_len, host_buf,
479			   sizeof(host_buf), NULL, 0,
480			   NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
481		printf("%s (%s) deleted\n", host, host_buf);
482	}
483
484	return 0;
485}
486
487/*
488 * Dump the entire neighbor cache
489 */
490void
491dump(addr)
492	struct in6_addr *addr;
493{
494	int mib[6];
495	size_t needed;
496	char *host, *lim, *buf, *next;
497	struct rt_msghdr *rtm;
498	struct sockaddr_in6 *sin;
499	struct sockaddr_dl *sdl;
500	extern int h_errno;
501	struct hostent *hp;
502	struct in6_nbrinfo *nbi;
503	struct timeval time;
504	int addrwidth;
505
506	/* Print header */
507	if (!tflag)
508		printf("%-29.29s %-18.18s %6.6s %-9.9s %2s %4s %4s\n",
509		       "Neighbor", "Linklayer Address", "Netif", "Expire",
510		       "St", "Flgs", "Prbs");
511
512again:;
513	mib[0] = CTL_NET;
514	mib[1] = PF_ROUTE;
515	mib[2] = 0;
516	mib[3] = AF_INET6;
517	mib[4] = NET_RT_FLAGS;
518	mib[5] = RTF_LLINFO;
519	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
520		err(1, "sysctl(PF_ROUTE estimate)");
521	if (needed > 0) {
522		if ((buf = malloc(needed)) == NULL)
523			errx(1, "malloc");
524		if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
525			err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
526		lim = buf + needed;
527	} else
528		buf = lim = NULL;
529
530	for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
531		int isrouter = 0, prbs = 0;
532
533		rtm = (struct rt_msghdr *)next;
534		sin = (struct sockaddr_in6 *)(rtm + 1);
535		sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len));
536		if (addr) {
537			if (!IN6_ARE_ADDR_EQUAL(addr, &sin->sin6_addr))
538				continue;
539			found_entry = 1;
540		} else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
541			continue;
542		if (fflag == 1) {
543			delete((char *)inet_ntop(AF_INET6, &sin->sin6_addr,
544						 ntop_buf, sizeof(ntop_buf)));
545			continue;
546		}
547
548		if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
549		    IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
550			/* XXX: should scope id be filled in the kernel? */
551			if (sin->sin6_scope_id == 0)
552				sin->sin6_scope_id = sdl->sdl_index;
553
554			/* XXX: KAME specific hack; removed the embedded id */
555			*(u_int16_t *)&sin->sin6_addr.s6_addr[2] = 0;
556		}
557		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
558			    sizeof(host_buf), NULL, 0,
559			    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
560		gettimeofday(&time, 0);
561		if (tflag)
562			ts_print(&time);
563
564		if (lflag) {
565			addrwidth = strlen(host_buf);
566			if (addrwidth < 29)
567				addrwidth = 29;
568		} else
569			addrwidth = 29;
570
571		printf("%-*.*s %-18.18s %6.6s", addrwidth, addrwidth, host_buf,
572		       ether_str(sdl),
573		       if_indextoname(sdl->sdl_index, ifix_buf));
574
575		/* Print neighbor discovery specific informations */
576		putchar(' ');
577		nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index);
578		if (nbi) {
579			if (nbi->expire > time.tv_sec) {
580				printf(" %-9.9s",
581				       sec2str(nbi->expire - time.tv_sec));
582			}
583			else if (nbi->expire == 0)
584				printf(" %-9.9s", "permanent");
585			else
586				printf(" %-9.9s", "expired");
587
588			switch(nbi->state) {
589			 case ND6_LLINFO_NOSTATE:
590				 printf(" N");
591				 break;
592			 case ND6_LLINFO_WAITDELETE:
593				 printf(" W");
594				 break;
595			 case ND6_LLINFO_INCOMPLETE:
596				 printf(" I");
597				 break;
598			 case ND6_LLINFO_REACHABLE:
599				 printf(" R");
600				 break;
601			 case ND6_LLINFO_STALE:
602				 printf(" S");
603				 break;
604			 case ND6_LLINFO_DELAY:
605				 printf(" D");
606				 break;
607			 case ND6_LLINFO_PROBE:
608				 printf(" P");
609				 break;
610			 default:
611				 printf(" ?");
612				 break;
613			}
614
615			isrouter = nbi->isrouter;
616			prbs = nbi->asked;
617		}
618		else {
619			warnx("failed to get neighbor information");
620			printf("  ");
621		}
622
623		/* other flags */
624		putchar(' ');
625		{
626			u_char flgbuf[8], *p = flgbuf;
627
628			flgbuf[0] = '\0';
629			if (isrouter)
630				p += sprintf((char *)p, "R");
631#ifndef RADISH
632			if (rtm->rtm_addrs & RTA_NETMASK) {
633				sin = (struct sockaddr_in6 *)
634					(sdl->sdl_len + (char *)sdl);
635				if (!IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr))
636					p += sprintf((char *)p, "P");
637				if (sin->sin6_len != sizeof(struct sockaddr_in6))
638					p += sprintf((char *)p, "W");
639			}
640#endif /*RADISH*/
641			printf("%4s", flgbuf);
642		}
643
644		putchar(' ');
645		if (prbs)
646			printf("% 4d", prbs);
647
648		printf("\n");
649	}
650
651	if (repeat) {
652		printf("\n");
653		sleep(repeat);
654		goto again;
655	}
656}
657
658static struct in6_nbrinfo *
659getnbrinfo(addr, ifindex)
660	struct in6_addr *addr;
661	int ifindex;
662{
663	static struct in6_nbrinfo nbi;
664	int s;
665
666	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
667		err(1, "socket");
668
669	bzero(&nbi, sizeof(nbi));
670	if_indextoname(ifindex, nbi.ifname);
671	nbi.addr = *addr;
672	if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
673		warn("ioctl");
674		close(s);
675		return(NULL);
676	}
677
678	close(s);
679	return(&nbi);
680}
681
682static char *
683ether_str(sdl)
684	struct sockaddr_dl *sdl;
685{
686	static char ebuf[32];
687	u_char *cp;
688
689	if (sdl->sdl_alen) {
690		cp = (u_char *)LLADDR(sdl);
691		sprintf(ebuf, "%x:%x:%x:%x:%x:%x",
692			cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
693	}
694	else {
695		sprintf(ebuf, "(incomplete)");
696	}
697
698	return(ebuf);
699}
700
701int
702ndp_ether_aton(a, n)
703	char *a;
704	u_char *n;
705{
706	int i, o[6];
707
708	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
709					   &o[3], &o[4], &o[5]);
710	if (i != 6) {
711		fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a);
712		return (1);
713	}
714	for (i=0; i<6; i++)
715		n[i] = o[i];
716	return (0);
717}
718
719void
720usage()
721{
722	printf("usage: ndp hostname\n");
723	printf("       ndp -a[ntl]\n");
724	printf("       ndp [-ntl] -A wait\n");
725	printf("       ndp -c[nt]\n");
726	printf("       ndp -d[nt] hostname\n");
727	printf("       ndp -f[nt] filename\n");
728	printf("       ndp -i interface\n");
729	printf("       ndp -p\n");
730	printf("       ndp -r\n");
731	printf("       ndp -s hostname ether_addr [temp]\n");
732	printf("       ndp -H\n");
733	printf("       ndp -P\n");
734	printf("       ndp -R\n");
735	exit(1);
736}
737
738int
739rtmsg(cmd)
740	int cmd;
741{
742	static int seq;
743	int rlen;
744	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
745	register char *cp = m_rtmsg.m_space;
746	register int l;
747
748	errno = 0;
749	if (cmd == RTM_DELETE)
750		goto doit;
751	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
752	rtm->rtm_flags = flags;
753	rtm->rtm_version = RTM_VERSION;
754
755	switch (cmd) {
756	default:
757		fprintf(stderr, "ndp: internal wrong cmd\n");
758		exit(1);
759	case RTM_ADD:
760		rtm->rtm_addrs |= RTA_GATEWAY;
761		rtm->rtm_rmx.rmx_expire = expire_time;
762		rtm->rtm_inits = RTV_EXPIRE;
763		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
764		/* FALLTHROUGH */
765	case RTM_GET:
766		rtm->rtm_addrs |= RTA_DST;
767	}
768#define	NEXTADDR(w, s) \
769	if (rtm->rtm_addrs & (w)) { \
770		bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
771
772	NEXTADDR(RTA_DST, sin_m);
773	NEXTADDR(RTA_GATEWAY, sdl_m);
774	NEXTADDR(RTA_NETMASK, so_mask);
775
776	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
777doit:
778	l = rtm->rtm_msglen;
779	rtm->rtm_seq = ++seq;
780	rtm->rtm_type = cmd;
781	if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
782		if (errno != ESRCH || cmd != RTM_DELETE) {
783			perror("writing to routing socket");
784			return (-1);
785		}
786	}
787	do {
788		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
789	} while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
790	if (l < 0)
791		(void) fprintf(stderr, "ndp: read from routing socket: %s\n",
792		    strerror(errno));
793	return (0);
794}
795
796void
797ifinfo(ifname)
798	char *ifname;
799{
800	struct in6_ndireq nd;
801	int s;
802
803	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
804		perror("ndp: socket");
805		exit(1);
806	}
807	bzero(&nd, sizeof(nd));
808	strcpy(nd.ifname, ifname);
809	if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
810 		perror("ioctl (SIOCGIFINFO_IN6)");
811 		exit(1);
812 	}
813#define	ND nd.ndi
814	printf("linkmtu=%d", ND.linkmtu);
815	printf(", curhlim=%d", ND.chlim);
816	printf(", basereachable=%ds%dms",
817	       ND.basereachable / 1000, ND.basereachable % 1000);
818	printf(", reachable=%ds", ND.reachable);
819	printf(", retrans=%ds%dms\n", ND.retrans / 1000, ND.retrans % 1000);
820#undef ND
821	close(s);
822}
823
824void
825rtrlist()
826{
827	struct in6_drlist dr;
828	int s, i;
829	struct timeval time;
830
831	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
832		perror("ndp: socket");
833		exit(1);
834	}
835	bzero(&dr, sizeof(dr));
836	strcpy(dr.ifname, "lo0"); /* dummy */
837	if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) {
838 		perror("ioctl (SIOCGDRLST_IN6)");
839 		exit(1);
840 	}
841#define	DR dr.defrouter[i]
842	for (i = 0 ; DR.if_index && i < PRLSTSIZ ; i++) {
843		struct sockaddr_in6 sin6;
844
845		bzero(&sin6, sizeof(sin6));
846		sin6.sin6_family = AF_INET6;
847		sin6.sin6_len = sizeof(sin6);
848		sin6.sin6_addr = DR.rtaddr;
849		getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, host_buf,
850			    sizeof(host_buf), NULL, 0,
851			    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
852
853		printf("%s if=%s", host_buf,
854		       if_indextoname(DR.if_index, ifix_buf));
855		printf(", flags=%s%s",
856		       DR.flags & ND_RA_FLAG_MANAGED ? "M" : "",
857		       DR.flags & ND_RA_FLAG_OTHER   ? "O" : "");
858		gettimeofday(&time, 0);
859		if (DR.expire == 0)
860			printf(", expire=Never\n");
861		else
862			printf(", expire=%s\n",
863				sec2str(DR.expire - time.tv_sec));
864	}
865#undef DR
866	close(s);
867}
868
869void
870plist()
871{
872	struct in6_prlist pr;
873	int s, i;
874	struct timeval time;
875
876	gettimeofday(&time, 0);
877
878	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
879		perror("ndp: socket");
880		exit(1);
881	}
882	bzero(&pr, sizeof(pr));
883	strcpy(pr.ifname, "lo0"); /* dummy */
884	if (ioctl(s, SIOCGPRLST_IN6, (caddr_t)&pr) < 0) {
885 		perror("ioctl (SIOCGPRLST_IN6)");
886 		exit(1);
887 	}
888#define	PR pr.prefix[i]
889	for (i = 0; PR.if_index && i < PRLSTSIZ ; i++) {
890		printf("%s/%d if=%s\n",
891		       inet_ntop(AF_INET6, &PR.prefix, ntop_buf,
892				 sizeof(ntop_buf)), PR.prefixlen,
893		       if_indextoname(PR.if_index, ifix_buf));
894		gettimeofday(&time, 0);
895		printf("  flags=%s%s",
896		       PR.raflags.onlink ? "L" : "",
897		       PR.raflags.autonomous ? "A" : "");
898		if (PR.vltime == ND6_INFINITE_LIFETIME)
899			printf(" vltime=infinity");
900		else
901			printf(" vltime=%ld", (long)PR.vltime);
902		if (PR.pltime == ND6_INFINITE_LIFETIME)
903			printf(", pltime=infinity");
904		else
905			printf(", pltime=%ld", (long)PR.pltime);
906		if (PR.expire == 0)
907			printf(", expire=Never\n");
908		else if (PR.expire >= time.tv_sec)
909			printf(", expire=%s\n",
910				sec2str(PR.expire - time.tv_sec));
911		else
912			printf(", expired\n");
913		if (PR.advrtrs) {
914			int j;
915			printf("  advertised by\n");
916			for (j = 0; j < PR.advrtrs; j++) {
917				struct sockaddr_in6 sin6;
918
919				bzero(&sin6, sizeof(sin6));
920				sin6.sin6_family = AF_INET6;
921				sin6.sin6_len = sizeof(sin6);
922				sin6.sin6_addr = PR.advrtr[j];
923				getnameinfo((struct sockaddr *)&sin6,
924					    sin6.sin6_len, host_buf,
925					    sizeof(host_buf), NULL, 0,
926					    NI_WITHSCOPEID | (nflag ? NI_NUMERICHOST : 0));
927
928				printf("    %s\n", host_buf);
929			}
930			if (PR.advrtrs > DRLSTSIZ)
931				printf("    and %d routers\n",
932				       PR.advrtrs - DRLSTSIZ);
933		}
934		else
935			printf("  No advertising router\n");
936	}
937#undef PR
938	close(s);
939}
940
941void
942pfx_flush()
943{
944	char dummyif[IFNAMSIZ+8];
945	int s;
946
947	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
948		err(1, "socket");
949	strcpy(dummyif, "lo0"); /* dummy */
950	if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
951 		err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
952}
953
954void
955rtr_flush()
956{
957	char dummyif[IFNAMSIZ+8];
958	int s;
959
960	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
961		err(1, "socket");
962	strcpy(dummyif, "lo0"); /* dummy */
963	if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
964 		err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
965}
966
967void
968harmonize_rtr()
969{
970	char dummyif[IFNAMSIZ+8];
971	int s;
972
973	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
974		perror("ndp: socket");
975		exit(1);
976	}
977	strcpy(dummyif, "lo0"); /* dummy */
978	if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0) {
979 		perror("ioctl (SIOCSNDFLUSH_IN6)");
980 		exit(1);
981 	}
982}
983
984static char *
985sec2str(total)
986	time_t total;
987{
988	static char result[256];
989	int days, hours, mins, secs;
990	int first = 1;
991	char *p = result;
992
993	days = total / 3600 / 24;
994	hours = (total / 3600) % 24;
995	mins = (total / 60) % 60;
996	secs = total % 60;
997
998	if (days) {
999		first = 0;
1000		p += sprintf(p, "%dd", days);
1001	}
1002	if (!first || hours) {
1003		first = 0;
1004		p += sprintf(p, "%dh", hours);
1005	}
1006	if (!first || mins) {
1007		first = 0;
1008		p += sprintf(p, "%dm", mins);
1009	}
1010	sprintf(p, "%ds", secs);
1011
1012	return(result);
1013}
1014
1015/*
1016 * Print the timestamp
1017 * from tcpdump/util.c
1018 */
1019static void
1020ts_print(tvp)
1021	const struct timeval *tvp;
1022{
1023	int s;
1024
1025	/* Default */
1026	s = (tvp->tv_sec + thiszone) % 86400;
1027	(void)printf("%02d:%02d:%02d.%06u ",
1028	    s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec);
1029}
1030