xref: /titanic_50/usr/src/cmd/cmd-inet/usr.sbin/ndp.c (revision e7df7762bfed06e996cc80f583cbee2d8ed81d69)
1*e7df7762SCody Peter Mello /*
2*e7df7762SCody Peter Mello  * This file and its contents are supplied under the terms of the
3*e7df7762SCody Peter Mello  * Common Development and Distribution License ("CDDL"), version 1.0.
4*e7df7762SCody Peter Mello  * You may only use this file in accordance with the terms of version
5*e7df7762SCody Peter Mello  * 1.0 of the CDDL.
6*e7df7762SCody Peter Mello  *
7*e7df7762SCody Peter Mello  * A full copy of the text of the CDDL should have accompanied this
8*e7df7762SCody Peter Mello  * source.  A copy of the CDDL is also available via the Internet at
9*e7df7762SCody Peter Mello  * http://www.illumos.org/license/CDDL.
10*e7df7762SCody Peter Mello  */
11*e7df7762SCody Peter Mello 
12*e7df7762SCody Peter Mello /*
13*e7df7762SCody Peter Mello  * Copyright 2015 Joyent, Inc.  All rights reserved.
14*e7df7762SCody Peter Mello  */
15*e7df7762SCody Peter Mello 
16*e7df7762SCody Peter Mello /*
17*e7df7762SCody Peter Mello  * ndp - display and manipulate Neighbor Cache Entries from NDP
18*e7df7762SCody Peter Mello  */
19*e7df7762SCody Peter Mello 
20*e7df7762SCody Peter Mello #include <stdio.h>
21*e7df7762SCody Peter Mello #include <stdarg.h>
22*e7df7762SCody Peter Mello #include <signal.h>
23*e7df7762SCody Peter Mello #include <time.h>
24*e7df7762SCody Peter Mello #include <err.h>
25*e7df7762SCody Peter Mello #include <errno.h>
26*e7df7762SCody Peter Mello #include <stdlib.h>
27*e7df7762SCody Peter Mello #include <strings.h>
28*e7df7762SCody Peter Mello #include <unistd.h>
29*e7df7762SCody Peter Mello #include <libgen.h>
30*e7df7762SCody Peter Mello #include <sys/ioctl.h>
31*e7df7762SCody Peter Mello #include <sys/types.h>
32*e7df7762SCody Peter Mello #include <wait.h>
33*e7df7762SCody Peter Mello #include <sys/mac.h>
34*e7df7762SCody Peter Mello #include <sys/socket.h>
35*e7df7762SCody Peter Mello #include <sys/sockio.h>
36*e7df7762SCody Peter Mello #include <netdb.h>
37*e7df7762SCody Peter Mello #include <net/if_types.h>
38*e7df7762SCody Peter Mello #include <netinet/in.h>
39*e7df7762SCody Peter Mello #include <arpa/inet.h>
40*e7df7762SCody Peter Mello #include <inet/ip.h>
41*e7df7762SCody Peter Mello #include <net/if_dl.h>
42*e7df7762SCody Peter Mello #include <net/route.h>
43*e7df7762SCody Peter Mello 
44*e7df7762SCody Peter Mello typedef	struct	sockaddr_in6	sin6_t;
45*e7df7762SCody Peter Mello 
46*e7df7762SCody Peter Mello #define	BUF_SIZE 2048
47*e7df7762SCody Peter Mello typedef struct rtmsg_pkt {
48*e7df7762SCody Peter Mello 	struct	rt_msghdr m_rtm;
49*e7df7762SCody Peter Mello 	char	m_space[BUF_SIZE];
50*e7df7762SCody Peter Mello } rtmsg_pkt_t;
51*e7df7762SCody Peter Mello 
52*e7df7762SCody Peter Mello enum ndp_action {
53*e7df7762SCody Peter Mello 	NDP_A_DEFAULT,
54*e7df7762SCody Peter Mello 	NDP_A_GET,		/* Show a single NDP entry */
55*e7df7762SCody Peter Mello 	NDP_A_GET_ALL,		/* Show NDP entries */
56*e7df7762SCody Peter Mello 	NDP_A_GET_FOREVER,	/* Repeatedly show entries */
57*e7df7762SCody Peter Mello 	NDP_A_DELETE,		/* Delete an NDP entry */
58*e7df7762SCody Peter Mello 	NDP_A_SET_NCE,		/* Set NDP entry */
59*e7df7762SCody Peter Mello 	NDP_A_SET_FILE		/* Read in & set NDP entries */
60*e7df7762SCody Peter Mello };
61*e7df7762SCody Peter Mello 
62*e7df7762SCody Peter Mello typedef	int	(ndp_addr_f)(int, struct lifreq *, void *);
63*e7df7762SCody Peter Mello typedef	void	(ndp_void_f)(void);
64*e7df7762SCody Peter Mello 
65*e7df7762SCody Peter Mello static	void	ndp_usage(const char *, ...);
66*e7df7762SCody Peter Mello static	void	ndp_fatal(const char *, ...);
67*e7df7762SCody Peter Mello static	void	ndp_badflag(enum ndp_action);
68*e7df7762SCody Peter Mello static	void	ndp_missingarg(char);
69*e7df7762SCody Peter Mello 
70*e7df7762SCody Peter Mello static	void	ndp_run_in_child(ndp_void_f *);
71*e7df7762SCody Peter Mello static	void	ndp_do_run(void);
72*e7df7762SCody Peter Mello static	void	ndp_setup_handler(sigset_t *);
73*e7df7762SCody Peter Mello static	void	ndp_start_timer(time_t period);
74*e7df7762SCody Peter Mello static	void	ndp_run_periodically(time_t, ndp_void_f *);
75*e7df7762SCody Peter Mello 
76*e7df7762SCody Peter Mello static	int	ndp_salen(const struct sockaddr *sa);
77*e7df7762SCody Peter Mello static	int	ndp_extract_sockaddrs(struct rt_msghdr *, struct sockaddr **,
78*e7df7762SCody Peter Mello 		    struct sockaddr **, struct sockaddr **, struct sockaddr **,
79*e7df7762SCody Peter Mello 		    struct sockaddr_dl **);
80*e7df7762SCody Peter Mello static	int	ndp_rtmsg_get(int, rtmsg_pkt_t *, struct sockaddr *);
81*e7df7762SCody Peter Mello static	int	ndp_find_interface(int, struct sockaddr *, char *, int);
82*e7df7762SCody Peter Mello 
83*e7df7762SCody Peter Mello static	int	ndp_initialize_lifreq(int, struct lifreq *, struct sockaddr *);
84*e7df7762SCody Peter Mello static	int	ndp_host_enumerate(char *, ndp_addr_f *, void *);
85*e7df7762SCody Peter Mello 
86*e7df7762SCody Peter Mello static	int	ndp_display(struct lifreq *);
87*e7df7762SCody Peter Mello static	int	ndp_display_missing(struct lifreq *);
88*e7df7762SCody Peter Mello static	void	ndp_lifr2ip(struct lifreq *, char *, int);
89*e7df7762SCody Peter Mello 
90*e7df7762SCody Peter Mello static	int	ndp_get(int, struct lifreq *, void *);
91*e7df7762SCody Peter Mello static	void	ndp_get_all(void);
92*e7df7762SCody Peter Mello static	int	ndp_delete(int, struct lifreq *, void *);
93*e7df7762SCody Peter Mello static	int	ndp_set(int, struct lifreq *, void *);
94*e7df7762SCody Peter Mello static	int	ndp_set_nce(char *, char *, char *[], int);
95*e7df7762SCody Peter Mello static	int	ndp_set_file(char *);
96*e7df7762SCody Peter Mello 
97*e7df7762SCody Peter Mello static	char		*ndp_iface = NULL;
98*e7df7762SCody Peter Mello static	char		*netstat_path = "/usr/bin/netstat";
99*e7df7762SCody Peter Mello static	pid_t		ndp_pid;
100*e7df7762SCody Peter Mello static	boolean_t	ndp_noresolve = B_FALSE; /* Don't lookup addresses */
101*e7df7762SCody Peter Mello static	boolean_t	ndp_run = B_TRUE;
102*e7df7762SCody Peter Mello 
103*e7df7762SCody Peter Mello #define	MAX_ATTEMPTS 5
104*e7df7762SCody Peter Mello #define	MAX_OPTS 5
105*e7df7762SCody Peter Mello #define	WORDSEPS " \t\r\n"
106*e7df7762SCody Peter Mello 
107*e7df7762SCody Peter Mello /*
108*e7df7762SCody Peter Mello  * Macros borrowed from route(1M) for working with PF_ROUTE messages
109*e7df7762SCody Peter Mello  */
110*e7df7762SCody Peter Mello #define	RT_ADVANCE(x, n) ((x) += ndp_salen(n))
111*e7df7762SCody Peter Mello #define	RT_NEXTADDR(cp, w, u) \
112*e7df7762SCody Peter Mello 	l = ndp_salen(u); \
113*e7df7762SCody Peter Mello 	(void) memmove(cp, u, l); \
114*e7df7762SCody Peter Mello 	cp += l;
115*e7df7762SCody Peter Mello 
116*e7df7762SCody Peter Mello /*
117*e7df7762SCody Peter Mello  * Print an error to stderr and then exit non-zero.
118*e7df7762SCody Peter Mello  */
119*e7df7762SCody Peter Mello static void
ndp_fatal(const char * format,...)120*e7df7762SCody Peter Mello ndp_fatal(const char *format, ...)
121*e7df7762SCody Peter Mello {
122*e7df7762SCody Peter Mello 	va_list ap;
123*e7df7762SCody Peter Mello 
124*e7df7762SCody Peter Mello 	va_start(ap, format);
125*e7df7762SCody Peter Mello 	vwarnx(format, ap);
126*e7df7762SCody Peter Mello 	va_end(ap);
127*e7df7762SCody Peter Mello 	exit(EXIT_FAILURE);
128*e7df7762SCody Peter Mello }
129*e7df7762SCody Peter Mello 
130*e7df7762SCody Peter Mello /*
131*e7df7762SCody Peter Mello  * Print out the command usage to stderr, along with any reason why it's being
132*e7df7762SCody Peter Mello  * printed, and then exit non-zero.
133*e7df7762SCody Peter Mello  */
134*e7df7762SCody Peter Mello static void
ndp_usage(const char * reason,...)135*e7df7762SCody Peter Mello ndp_usage(const char *reason, ...)
136*e7df7762SCody Peter Mello {
137*e7df7762SCody Peter Mello 	va_list ap;
138*e7df7762SCody Peter Mello 	const char *ndp_progname = getprogname();
139*e7df7762SCody Peter Mello 
140*e7df7762SCody Peter Mello 	if (reason != NULL) {
141*e7df7762SCody Peter Mello 		va_start(ap, reason);
142*e7df7762SCody Peter Mello 		(void) fprintf(stderr, "%s: ", ndp_progname);
143*e7df7762SCody Peter Mello 		(void) vfprintf(stderr, reason, ap);
144*e7df7762SCody Peter Mello 		(void) fprintf(stderr, "\n");
145*e7df7762SCody Peter Mello 		va_end(ap);
146*e7df7762SCody Peter Mello 	}
147*e7df7762SCody Peter Mello 
148*e7df7762SCody Peter Mello 	(void) fprintf(stderr,
149*e7df7762SCody Peter Mello 	    "Usage: %s [-n] [-i iface] hostname\n"
150*e7df7762SCody Peter Mello 	    "       %s [-n] [-i iface] -s nodeaddr etheraddr [temp] [proxy]\n"
151*e7df7762SCody Peter Mello 	    "       %s [-n] [-i iface] -d nodeaddr\n"
152*e7df7762SCody Peter Mello 	    "       %s [-n] [-i iface] -f filename\n"
153*e7df7762SCody Peter Mello 	    "       %s [-n] -a\n"
154*e7df7762SCody Peter Mello 	    "       %s [-n] -A period\n",
155*e7df7762SCody Peter Mello 	    ndp_progname, ndp_progname, ndp_progname,
156*e7df7762SCody Peter Mello 	    ndp_progname, ndp_progname, ndp_progname);
157*e7df7762SCody Peter Mello 	exit(EXIT_FAILURE);
158*e7df7762SCody Peter Mello }
159*e7df7762SCody Peter Mello 
160*e7df7762SCody Peter Mello static void
ndp_badflag(enum ndp_action action)161*e7df7762SCody Peter Mello ndp_badflag(enum ndp_action action)
162*e7df7762SCody Peter Mello {
163*e7df7762SCody Peter Mello 	switch (action) {
164*e7df7762SCody Peter Mello 	case NDP_A_DEFAULT:
165*e7df7762SCody Peter Mello 	case NDP_A_GET:
166*e7df7762SCody Peter Mello 		ndp_usage("Already going to print an entry, "
167*e7df7762SCody Peter Mello 		    "but extra -%c given", optopt);
168*e7df7762SCody Peter Mello 		break;
169*e7df7762SCody Peter Mello 	case NDP_A_GET_ALL:
170*e7df7762SCody Peter Mello 		ndp_usage("Already going to print all entries (-a), "
171*e7df7762SCody Peter Mello 		    "but extra -%c given", optopt);
172*e7df7762SCody Peter Mello 		break;
173*e7df7762SCody Peter Mello 	case NDP_A_GET_FOREVER:
174*e7df7762SCody Peter Mello 		ndp_usage("Already going to repeatedly print all entries (-A), "
175*e7df7762SCody Peter Mello 		    "but extra -%c given", optopt);
176*e7df7762SCody Peter Mello 		break;
177*e7df7762SCody Peter Mello 	case NDP_A_DELETE:
178*e7df7762SCody Peter Mello 		ndp_usage("Already going to delete an entry (-d), "
179*e7df7762SCody Peter Mello 		    "but extra -%c given", optopt);
180*e7df7762SCody Peter Mello 		break;
181*e7df7762SCody Peter Mello 	case NDP_A_SET_NCE:
182*e7df7762SCody Peter Mello 		ndp_usage("Already going to set an entry (-s), "
183*e7df7762SCody Peter Mello 		    "but extra -%c given", optopt);
184*e7df7762SCody Peter Mello 		break;
185*e7df7762SCody Peter Mello 	case NDP_A_SET_FILE:
186*e7df7762SCody Peter Mello 		ndp_usage("Already going to set from file (-f), "
187*e7df7762SCody Peter Mello 		    "but extra -%c given", optopt);
188*e7df7762SCody Peter Mello 		break;
189*e7df7762SCody Peter Mello 	}
190*e7df7762SCody Peter Mello }
191*e7df7762SCody Peter Mello 
192*e7df7762SCody Peter Mello static void
ndp_missingarg(char flag)193*e7df7762SCody Peter Mello ndp_missingarg(char flag)
194*e7df7762SCody Peter Mello {
195*e7df7762SCody Peter Mello 	switch (flag) {
196*e7df7762SCody Peter Mello 	case 'A':
197*e7df7762SCody Peter Mello 		ndp_usage("Missing time period after -%c", flag);
198*e7df7762SCody Peter Mello 		break;
199*e7df7762SCody Peter Mello 	case 'd':
200*e7df7762SCody Peter Mello 		ndp_usage("Missing node name after -%c", flag);
201*e7df7762SCody Peter Mello 		break;
202*e7df7762SCody Peter Mello 	case 'f':
203*e7df7762SCody Peter Mello 		ndp_usage("Missing filename after -%c", flag);
204*e7df7762SCody Peter Mello 		break;
205*e7df7762SCody Peter Mello 	case 's':
206*e7df7762SCody Peter Mello 		ndp_usage("Missing node name after -%c", flag);
207*e7df7762SCody Peter Mello 		break;
208*e7df7762SCody Peter Mello 	case 'i':
209*e7df7762SCody Peter Mello 		ndp_usage("Missing interface name after -%c", flag);
210*e7df7762SCody Peter Mello 		break;
211*e7df7762SCody Peter Mello 	default:
212*e7df7762SCody Peter Mello 		ndp_usage("Missing option argument after -%c", flag);
213*e7df7762SCody Peter Mello 		break;
214*e7df7762SCody Peter Mello 	}
215*e7df7762SCody Peter Mello }
216*e7df7762SCody Peter Mello 
217*e7df7762SCody Peter Mello /*
218*e7df7762SCody Peter Mello  * Run a function that's going to exec in a child process, and don't return
219*e7df7762SCody Peter Mello  * until it exits.
220*e7df7762SCody Peter Mello  */
221*e7df7762SCody Peter Mello static void
ndp_run_in_child(ndp_void_f * func)222*e7df7762SCody Peter Mello ndp_run_in_child(ndp_void_f *func)
223*e7df7762SCody Peter Mello {
224*e7df7762SCody Peter Mello 	pid_t child_pid;
225*e7df7762SCody Peter Mello 	int childstat = 0, status = 0;
226*e7df7762SCody Peter Mello 
227*e7df7762SCody Peter Mello 	child_pid = fork();
228*e7df7762SCody Peter Mello 	if (child_pid == (pid_t)-1) {
229*e7df7762SCody Peter Mello 		ndp_fatal("Unable to fork: %s", strerror(errno));
230*e7df7762SCody Peter Mello 	} else if (child_pid == (pid_t)0) {
231*e7df7762SCody Peter Mello 		func();
232*e7df7762SCody Peter Mello 		exit(EXIT_FAILURE);
233*e7df7762SCody Peter Mello 	}
234*e7df7762SCody Peter Mello 
235*e7df7762SCody Peter Mello 	while (waitpid(child_pid, &childstat, 0) == -1) {
236*e7df7762SCody Peter Mello 		if (errno == EINTR)
237*e7df7762SCody Peter Mello 			continue;
238*e7df7762SCody Peter Mello 
239*e7df7762SCody Peter Mello 		ndp_fatal("Failed to wait on child: %s", strerror(errno));
240*e7df7762SCody Peter Mello 	}
241*e7df7762SCody Peter Mello 
242*e7df7762SCody Peter Mello 	status = WEXITSTATUS(childstat);
243*e7df7762SCody Peter Mello 	if (status != 0) {
244*e7df7762SCody Peter Mello 		ndp_fatal("Child process exited with %d", status);
245*e7df7762SCody Peter Mello 	}
246*e7df7762SCody Peter Mello }
247*e7df7762SCody Peter Mello 
248*e7df7762SCody Peter Mello /*
249*e7df7762SCody Peter Mello  * SIGALRM handler to schedule a run.
250*e7df7762SCody Peter Mello  */
251*e7df7762SCody Peter Mello static void
ndp_do_run(void)252*e7df7762SCody Peter Mello ndp_do_run(void)
253*e7df7762SCody Peter Mello {
254*e7df7762SCody Peter Mello 	ndp_run = B_TRUE;
255*e7df7762SCody Peter Mello }
256*e7df7762SCody Peter Mello 
257*e7df7762SCody Peter Mello 
258*e7df7762SCody Peter Mello /*
259*e7df7762SCody Peter Mello  * Prepare signal masks, and install the SIGALRM handler. Return old signal
260*e7df7762SCody Peter Mello  * masks through the first argument.
261*e7df7762SCody Peter Mello  */
262*e7df7762SCody Peter Mello static void
ndp_setup_handler(sigset_t * oset)263*e7df7762SCody Peter Mello ndp_setup_handler(sigset_t *oset)
264*e7df7762SCody Peter Mello {
265*e7df7762SCody Peter Mello 	struct sigaction sa;
266*e7df7762SCody Peter Mello 
267*e7df7762SCody Peter Mello 	/*
268*e7df7762SCody Peter Mello 	 * Mask off SIGALRM so we only trigger the handler when we're ready
269*e7df7762SCody Peter Mello 	 * using sigsuspend(3C), in case the child process takes longer to
270*e7df7762SCody Peter Mello 	 * run than the alarm interval.
271*e7df7762SCody Peter Mello 	 */
272*e7df7762SCody Peter Mello 	if (sigprocmask(0, NULL, oset) != 0) {
273*e7df7762SCody Peter Mello 		ndp_fatal("Unable to set signal mask: %s", strerror(errno));
274*e7df7762SCody Peter Mello 	}
275*e7df7762SCody Peter Mello 
276*e7df7762SCody Peter Mello 	if (sighold(SIGALRM) != 0) {
277*e7df7762SCody Peter Mello 		ndp_fatal("Unable to add SIGALRM to signal mask: %s",
278*e7df7762SCody Peter Mello 		    strerror(errno));
279*e7df7762SCody Peter Mello 	}
280*e7df7762SCody Peter Mello 
281*e7df7762SCody Peter Mello 	sa.sa_flags = 0;
282*e7df7762SCody Peter Mello 	sa.sa_handler = ndp_do_run;
283*e7df7762SCody Peter Mello 
284*e7df7762SCody Peter Mello 	if (sigemptyset(&sa.sa_mask) != 0) {
285*e7df7762SCody Peter Mello 		ndp_fatal("Unable to prepare empty signal set: %s",
286*e7df7762SCody Peter Mello 		    strerror(errno));
287*e7df7762SCody Peter Mello 	}
288*e7df7762SCody Peter Mello 
289*e7df7762SCody Peter Mello 	if (sigaction(SIGALRM, &sa, NULL) != 0) {
290*e7df7762SCody Peter Mello 		ndp_fatal("Unable to install timer handler: %s",
291*e7df7762SCody Peter Mello 		    strerror(errno));
292*e7df7762SCody Peter Mello 	}
293*e7df7762SCody Peter Mello }
294*e7df7762SCody Peter Mello 
295*e7df7762SCody Peter Mello /*
296*e7df7762SCody Peter Mello  * Start the printing timer.
297*e7df7762SCody Peter Mello  */
298*e7df7762SCody Peter Mello static void
ndp_start_timer(time_t period)299*e7df7762SCody Peter Mello ndp_start_timer(time_t period)
300*e7df7762SCody Peter Mello {
301*e7df7762SCody Peter Mello 	timer_t timer;
302*e7df7762SCody Peter Mello 	struct itimerspec interval;
303*e7df7762SCody Peter Mello 	interval.it_value.tv_sec  = interval.it_interval.tv_sec  = period;
304*e7df7762SCody Peter Mello 	interval.it_value.tv_nsec = interval.it_interval.tv_nsec = 0;
305*e7df7762SCody Peter Mello 
306*e7df7762SCody Peter Mello 	if (timer_create(CLOCK_REALTIME, NULL, &timer) != 0) {
307*e7df7762SCody Peter Mello 		ndp_fatal("Unable to create timer: %s", strerror(errno));
308*e7df7762SCody Peter Mello 	}
309*e7df7762SCody Peter Mello 
310*e7df7762SCody Peter Mello 	if (timer_settime(timer, 0, &interval, NULL) != 0) {
311*e7df7762SCody Peter Mello 		ndp_fatal("Unable to set time on timer: %s", strerror(errno));
312*e7df7762SCody Peter Mello 	}
313*e7df7762SCody Peter Mello }
314*e7df7762SCody Peter Mello 
315*e7df7762SCody Peter Mello 
316*e7df7762SCody Peter Mello /*
317*e7df7762SCody Peter Mello  * Run a given function forever periodically in a child process.
318*e7df7762SCody Peter Mello  */
319*e7df7762SCody Peter Mello static void
ndp_run_periodically(time_t period,ndp_void_f * func)320*e7df7762SCody Peter Mello ndp_run_periodically(time_t period, ndp_void_f *func)
321*e7df7762SCody Peter Mello {
322*e7df7762SCody Peter Mello 	sigset_t oset;
323*e7df7762SCody Peter Mello 
324*e7df7762SCody Peter Mello 	ndp_setup_handler(&oset);
325*e7df7762SCody Peter Mello 	ndp_start_timer(period);
326*e7df7762SCody Peter Mello 
327*e7df7762SCody Peter Mello 	do {
328*e7df7762SCody Peter Mello 		if (ndp_run) {
329*e7df7762SCody Peter Mello 			ndp_run = B_FALSE;
330*e7df7762SCody Peter Mello 			ndp_run_in_child(func);
331*e7df7762SCody Peter Mello 		}
332*e7df7762SCody Peter Mello 		(void) sigsuspend(&oset);
333*e7df7762SCody Peter Mello 	} while (errno == EINTR);
334*e7df7762SCody Peter Mello 
335*e7df7762SCody Peter Mello 	/*
336*e7df7762SCody Peter Mello 	 * Only an EFAULT should get us here. Abort so we get a core dump.
337*e7df7762SCody Peter Mello 	 */
338*e7df7762SCody Peter Mello 	warnx("Failure while waiting on timer: %s", strerror(errno));
339*e7df7762SCody Peter Mello 	abort();
340*e7df7762SCody Peter Mello }
341*e7df7762SCody Peter Mello 
342*e7df7762SCody Peter Mello /*
343*e7df7762SCody Peter Mello  * Given an address, return its size.
344*e7df7762SCody Peter Mello  */
345*e7df7762SCody Peter Mello static int
ndp_salen(const struct sockaddr * sa)346*e7df7762SCody Peter Mello ndp_salen(const struct sockaddr *sa)
347*e7df7762SCody Peter Mello {
348*e7df7762SCody Peter Mello 	switch (sa->sa_family) {
349*e7df7762SCody Peter Mello 	case AF_INET:
350*e7df7762SCody Peter Mello 		return (sizeof (struct sockaddr_in));
351*e7df7762SCody Peter Mello 	case AF_LINK:
352*e7df7762SCody Peter Mello 		return (sizeof (struct sockaddr_dl));
353*e7df7762SCody Peter Mello 	case AF_INET6:
354*e7df7762SCody Peter Mello 		return (sizeof (struct sockaddr_in6));
355*e7df7762SCody Peter Mello 	default:
356*e7df7762SCody Peter Mello 		warnx("Unrecognized sockaddr with address family %d!",
357*e7df7762SCody Peter Mello 		    sa->sa_family);
358*e7df7762SCody Peter Mello 		abort();
359*e7df7762SCody Peter Mello 	}
360*e7df7762SCody Peter Mello 	/*NOTREACHED*/
361*e7df7762SCody Peter Mello }
362*e7df7762SCody Peter Mello 
363*e7df7762SCody Peter Mello /*
364*e7df7762SCody Peter Mello  * Extract all socket addresses from a routing message, and return them
365*e7df7762SCody Peter Mello  * through the pointers given as arguments to ndp_extract_sockaddrs. None
366*e7df7762SCody Peter Mello  * of the pointers should be null.
367*e7df7762SCody Peter Mello  */
368*e7df7762SCody Peter Mello static int
ndp_extract_sockaddrs(struct rt_msghdr * rtm,struct sockaddr ** dst,struct sockaddr ** gate,struct sockaddr ** mask,struct sockaddr ** src,struct sockaddr_dl ** ifp)369*e7df7762SCody Peter Mello ndp_extract_sockaddrs(struct rt_msghdr *rtm, struct sockaddr **dst,
370*e7df7762SCody Peter Mello     struct sockaddr **gate, struct sockaddr **mask, struct sockaddr **src,
371*e7df7762SCody Peter Mello     struct sockaddr_dl **ifp)
372*e7df7762SCody Peter Mello {
373*e7df7762SCody Peter Mello 	struct sockaddr *sa;
374*e7df7762SCody Peter Mello 	char *cp;
375*e7df7762SCody Peter Mello 	int i;
376*e7df7762SCody Peter Mello 
377*e7df7762SCody Peter Mello 	if (rtm->rtm_version != RTM_VERSION) {
378*e7df7762SCody Peter Mello 		warnx("Routing message version %d not understood",
379*e7df7762SCody Peter Mello 		    rtm->rtm_version);
380*e7df7762SCody Peter Mello 		return (-1);
381*e7df7762SCody Peter Mello 	}
382*e7df7762SCody Peter Mello 
383*e7df7762SCody Peter Mello 	if (rtm->rtm_errno != 0)  {
384*e7df7762SCody Peter Mello 		warnx("Routing message couldn't be processed: %s",
385*e7df7762SCody Peter Mello 		    strerror(rtm->rtm_errno));
386*e7df7762SCody Peter Mello 		return (-1);
387*e7df7762SCody Peter Mello 	}
388*e7df7762SCody Peter Mello 
389*e7df7762SCody Peter Mello 	cp = ((char *)(rtm + 1));
390*e7df7762SCody Peter Mello 	if (rtm->rtm_addrs != 0) {
391*e7df7762SCody Peter Mello 		for (i = 1; i != 0; i <<= 1) {
392*e7df7762SCody Peter Mello 			if ((i & rtm->rtm_addrs) == 0)
393*e7df7762SCody Peter Mello 				continue;
394*e7df7762SCody Peter Mello 
395*e7df7762SCody Peter Mello 			/*LINTED*/
396*e7df7762SCody Peter Mello 			sa = (struct sockaddr *)cp;
397*e7df7762SCody Peter Mello 			switch (i) {
398*e7df7762SCody Peter Mello 			case RTA_DST:
399*e7df7762SCody Peter Mello 				*dst = sa;
400*e7df7762SCody Peter Mello 				break;
401*e7df7762SCody Peter Mello 			case RTA_GATEWAY:
402*e7df7762SCody Peter Mello 				*gate = sa;
403*e7df7762SCody Peter Mello 				break;
404*e7df7762SCody Peter Mello 			case RTA_NETMASK:
405*e7df7762SCody Peter Mello 				*mask = sa;
406*e7df7762SCody Peter Mello 				break;
407*e7df7762SCody Peter Mello 			case RTA_IFP:
408*e7df7762SCody Peter Mello 				if (sa->sa_family == AF_LINK &&
409*e7df7762SCody Peter Mello 				    ((struct sockaddr_dl *)sa)->sdl_nlen != 0)
410*e7df7762SCody Peter Mello 					*ifp = (struct sockaddr_dl *)sa;
411*e7df7762SCody Peter Mello 				break;
412*e7df7762SCody Peter Mello 			case RTA_SRC:
413*e7df7762SCody Peter Mello 				*src = sa;
414*e7df7762SCody Peter Mello 				break;
415*e7df7762SCody Peter Mello 			}
416*e7df7762SCody Peter Mello 			RT_ADVANCE(cp, sa);
417*e7df7762SCody Peter Mello 		}
418*e7df7762SCody Peter Mello 	}
419*e7df7762SCody Peter Mello 
420*e7df7762SCody Peter Mello 	return (0);
421*e7df7762SCody Peter Mello }
422*e7df7762SCody Peter Mello 
423*e7df7762SCody Peter Mello /*
424*e7df7762SCody Peter Mello  * Given an IPv6 address, use routing information to look up
425*e7df7762SCody Peter Mello  * the destination and interface it would pass through.
426*e7df7762SCody Peter Mello  */
427*e7df7762SCody Peter Mello static int
ndp_rtmsg_get(int fd,rtmsg_pkt_t * msg,struct sockaddr * sin6p)428*e7df7762SCody Peter Mello ndp_rtmsg_get(int fd, rtmsg_pkt_t *msg, struct sockaddr *sin6p)
429*e7df7762SCody Peter Mello {
430*e7df7762SCody Peter Mello 	static int seq = 0;
431*e7df7762SCody Peter Mello 	struct sockaddr_dl sdl;
432*e7df7762SCody Peter Mello 	int mlen, l;
433*e7df7762SCody Peter Mello 	char ipaddr[INET6_ADDRSTRLEN];
434*e7df7762SCody Peter Mello 	char *cp = msg->m_space;
435*e7df7762SCody Peter Mello 	struct	rt_msghdr *m_rtm = &msg->m_rtm;
436*e7df7762SCody Peter Mello 
437*e7df7762SCody Peter Mello 	bzero(msg, sizeof (rtmsg_pkt_t));
438*e7df7762SCody Peter Mello 	bzero(&sdl, sizeof (struct sockaddr_dl));
439*e7df7762SCody Peter Mello 
440*e7df7762SCody Peter Mello 	m_rtm->rtm_type = RTM_GET;
441*e7df7762SCody Peter Mello 	m_rtm->rtm_version = RTM_VERSION;
442*e7df7762SCody Peter Mello 	m_rtm->rtm_seq = ++seq;
443*e7df7762SCody Peter Mello 	m_rtm->rtm_addrs = RTA_DST | RTA_IFP;
444*e7df7762SCody Peter Mello 	m_rtm->rtm_msglen = sizeof (rtmsg_pkt_t);
445*e7df7762SCody Peter Mello 
446*e7df7762SCody Peter Mello 	/* Place the address we're looking up after the header */
447*e7df7762SCody Peter Mello 	RT_NEXTADDR(cp, RTA_DST, sin6p);
448*e7df7762SCody Peter Mello 
449*e7df7762SCody Peter Mello 	/* Load an empty link-level address, so we get an interface back */
450*e7df7762SCody Peter Mello 	sdl.sdl_family = AF_LINK;
451*e7df7762SCody Peter Mello 	RT_NEXTADDR(cp, RTA_IFP, (struct sockaddr *)&sdl);
452*e7df7762SCody Peter Mello 
453*e7df7762SCody Peter Mello 	m_rtm->rtm_msglen = cp - (char *)msg;
454*e7df7762SCody Peter Mello 
455*e7df7762SCody Peter Mello 	if ((mlen = write(fd, (char *)msg, m_rtm->rtm_msglen)) < 0) {
456*e7df7762SCody Peter Mello 		if (errno == ESRCH) {
457*e7df7762SCody Peter Mello 			/*LINTED*/
458*e7df7762SCody Peter Mello 			if (inet_ntop(AF_INET6, &((sin6_t *)sin6p)->sin6_addr,
459*e7df7762SCody Peter Mello 			    ipaddr, sizeof (ipaddr)) == NULL) {
460*e7df7762SCody Peter Mello 				(void) snprintf(ipaddr, sizeof (ipaddr),
461*e7df7762SCody Peter Mello 				    "(failed to format IP)");
462*e7df7762SCody Peter Mello 			};
463*e7df7762SCody Peter Mello 			warnx("An appropriate interface for the address %s "
464*e7df7762SCody Peter Mello 			    "is not in the routing table; use -i to force an "
465*e7df7762SCody Peter Mello 			    "interface", ipaddr);
466*e7df7762SCody Peter Mello 			return (-1);
467*e7df7762SCody Peter Mello 		} else {
468*e7df7762SCody Peter Mello 			warnx("Failed to send routing message: %s",
469*e7df7762SCody Peter Mello 			    strerror(errno));
470*e7df7762SCody Peter Mello 			return (-1);
471*e7df7762SCody Peter Mello 		}
472*e7df7762SCody Peter Mello 	} else if (mlen < (int)m_rtm->rtm_msglen) {
473*e7df7762SCody Peter Mello 		warnx("Failed to write all bytes to routing socket");
474*e7df7762SCody Peter Mello 		return (-1);
475*e7df7762SCody Peter Mello 	}
476*e7df7762SCody Peter Mello 
477*e7df7762SCody Peter Mello 	/*
478*e7df7762SCody Peter Mello 	 * Keep reading routing messages until we find the response to the one
479*e7df7762SCody Peter Mello 	 * we just sent. Note that we depend on the sequence number being unique
480*e7df7762SCody Peter Mello 	 * to the running program.
481*e7df7762SCody Peter Mello 	 */
482*e7df7762SCody Peter Mello 	do {
483*e7df7762SCody Peter Mello 		mlen = read(fd, (char *)msg, sizeof (rtmsg_pkt_t));
484*e7df7762SCody Peter Mello 	} while (mlen > 0 &&
485*e7df7762SCody Peter Mello 	    (m_rtm->rtm_seq != seq || m_rtm->rtm_pid != ndp_pid));
486*e7df7762SCody Peter Mello 	if (mlen < 0) {
487*e7df7762SCody Peter Mello 		warnx("Failed to read from routing socket: %s",
488*e7df7762SCody Peter Mello 		    strerror(errno));
489*e7df7762SCody Peter Mello 		return (-1);
490*e7df7762SCody Peter Mello 	}
491*e7df7762SCody Peter Mello 
492*e7df7762SCody Peter Mello 	return (0);
493*e7df7762SCody Peter Mello }
494*e7df7762SCody Peter Mello 
495*e7df7762SCody Peter Mello /*
496*e7df7762SCody Peter Mello  * Find the interface that the IPv6 address would be routed through, and store
497*e7df7762SCody Peter Mello  * the name of the interface in the buffer passed in.
498*e7df7762SCody Peter Mello  */
499*e7df7762SCody Peter Mello static int
ndp_find_interface(int fd,struct sockaddr * sin6p,char * buf,int buflen)500*e7df7762SCody Peter Mello ndp_find_interface(int fd, struct sockaddr *sin6p, char *buf, int buflen)
501*e7df7762SCody Peter Mello {
502*e7df7762SCody Peter Mello 	struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
503*e7df7762SCody Peter Mello 	struct sockaddr_dl *ifp = NULL;
504*e7df7762SCody Peter Mello 	rtmsg_pkt_t msg;
505*e7df7762SCody Peter Mello 
506*e7df7762SCody Peter Mello 	if (ndp_rtmsg_get(fd, &msg, sin6p) != 0) {
507*e7df7762SCody Peter Mello 		return (-1);
508*e7df7762SCody Peter Mello 	}
509*e7df7762SCody Peter Mello 
510*e7df7762SCody Peter Mello 	if (ndp_extract_sockaddrs(&msg.m_rtm, &dst, &gate,
511*e7df7762SCody Peter Mello 	    &mask, &src, &ifp) != 0) {
512*e7df7762SCody Peter Mello 		return (-1);
513*e7df7762SCody Peter Mello 	}
514*e7df7762SCody Peter Mello 
515*e7df7762SCody Peter Mello 	if (ifp == NULL) {
516*e7df7762SCody Peter Mello 		warnx("Unable to find appropriate interface for address");
517*e7df7762SCody Peter Mello 		return (-1);
518*e7df7762SCody Peter Mello 	} else {
519*e7df7762SCody Peter Mello 		if (ifp->sdl_nlen >= buflen) {
520*e7df7762SCody Peter Mello 			warnx("The interface name \"%.*s\" is too big for the "
521*e7df7762SCody Peter Mello 			    "available buffer", ifp->sdl_nlen, ifp->sdl_data);
522*e7df7762SCody Peter Mello 			return (-1);
523*e7df7762SCody Peter Mello 		} else {
524*e7df7762SCody Peter Mello 			(void) snprintf(buf, buflen, "%.*s", ifp->sdl_nlen,
525*e7df7762SCody Peter Mello 			    ifp->sdl_data);
526*e7df7762SCody Peter Mello 		}
527*e7df7762SCody Peter Mello 	}
528*e7df7762SCody Peter Mello 
529*e7df7762SCody Peter Mello 	return (0);
530*e7df7762SCody Peter Mello }
531*e7df7762SCody Peter Mello 
532*e7df7762SCody Peter Mello /*
533*e7df7762SCody Peter Mello  * Zero out a lifreq struct for a SIOCLIF*ND ioctl, set the address, and fetch
534*e7df7762SCody Peter Mello  * the appropriate interface using the given routing socket.
535*e7df7762SCody Peter Mello  */
536*e7df7762SCody Peter Mello static int
ndp_initialize_lifreq(int route,struct lifreq * lifrp,struct sockaddr * sap)537*e7df7762SCody Peter Mello ndp_initialize_lifreq(int route, struct lifreq *lifrp, struct sockaddr *sap)
538*e7df7762SCody Peter Mello {
539*e7df7762SCody Peter Mello 	struct sockaddr_storage *lnr_addr;
540*e7df7762SCody Peter Mello 	/* LINTED E_BAD_PTR_CAST_ALIGN */
541*e7df7762SCody Peter Mello 	struct sockaddr_in6 *sin6p = (sin6_t *)sap;
542*e7df7762SCody Peter Mello 	char *lifr_name = lifrp->lifr_name;
543*e7df7762SCody Peter Mello 
544*e7df7762SCody Peter Mello 	bzero(lifrp, sizeof (struct lifreq));
545*e7df7762SCody Peter Mello 	lnr_addr = &lifrp->lifr_nd.lnr_addr;
546*e7df7762SCody Peter Mello 
547*e7df7762SCody Peter Mello 	if (ndp_iface != NULL) {
548*e7df7762SCody Peter Mello 		(void) strlcpy(lifr_name, ndp_iface, LIFNAMSIZ);
549*e7df7762SCody Peter Mello 	} else if (sin6p->sin6_scope_id != 0) {
550*e7df7762SCody Peter Mello 		int zone_id = sin6p->sin6_scope_id;
551*e7df7762SCody Peter Mello 		if (if_indextoname(zone_id, lifr_name) == NULL) {
552*e7df7762SCody Peter Mello 			warnx("Invalid zone identifier: %d", zone_id);
553*e7df7762SCody Peter Mello 			return (-1);
554*e7df7762SCody Peter Mello 		}
555*e7df7762SCody Peter Mello 	} else if (IN6_IS_ADDR_LINKSCOPE(&sin6p->sin6_addr)) {
556*e7df7762SCody Peter Mello 		warnx("Link-scope addresses should specify an interface with "
557*e7df7762SCody Peter Mello 		    "a zone ID, or with -i.");
558*e7df7762SCody Peter Mello 		return (-1);
559*e7df7762SCody Peter Mello 	} else {
560*e7df7762SCody Peter Mello 		if (ndp_find_interface(route, sap, lifr_name, LIFNAMSIZ) != 0)
561*e7df7762SCody Peter Mello 			return (-1);
562*e7df7762SCody Peter Mello 	}
563*e7df7762SCody Peter Mello 
564*e7df7762SCody Peter Mello 	(void) memcpy(lnr_addr, sap, sizeof (struct sockaddr_storage));
565*e7df7762SCody Peter Mello 
566*e7df7762SCody Peter Mello 	return (0);
567*e7df7762SCody Peter Mello }
568*e7df7762SCody Peter Mello 
569*e7df7762SCody Peter Mello /*
570*e7df7762SCody Peter Mello  * Take a host identifier, find the corresponding IPv6 addresses and then pass
571*e7df7762SCody Peter Mello  * them to the specified function, along with any desired data.
572*e7df7762SCody Peter Mello  */
573*e7df7762SCody Peter Mello static int
ndp_host_enumerate(char * host,ndp_addr_f * addr_func,void * data)574*e7df7762SCody Peter Mello ndp_host_enumerate(char *host, ndp_addr_f *addr_func, void *data)
575*e7df7762SCody Peter Mello {
576*e7df7762SCody Peter Mello 	struct lifreq lifr;
577*e7df7762SCody Peter Mello 	struct addrinfo hints, *serverinfo, *p;
578*e7df7762SCody Peter Mello 	int err, attempts = 0;
579*e7df7762SCody Peter Mello 	int inet6, route;
580*e7df7762SCody Peter Mello 
581*e7df7762SCody Peter Mello 	bzero(&hints, sizeof (struct addrinfo));
582*e7df7762SCody Peter Mello 	hints.ai_family = AF_INET6;
583*e7df7762SCody Peter Mello 	hints.ai_protocol = IPPROTO_IPV6;
584*e7df7762SCody Peter Mello 
585*e7df7762SCody Peter Mello 	while (attempts < MAX_ATTEMPTS) {
586*e7df7762SCody Peter Mello 		err = getaddrinfo(host, NULL, &hints, &serverinfo);
587*e7df7762SCody Peter Mello 
588*e7df7762SCody Peter Mello 		if (err == 0) {
589*e7df7762SCody Peter Mello 			break;
590*e7df7762SCody Peter Mello 		} else if (err == EAI_AGAIN) {
591*e7df7762SCody Peter Mello 			attempts++;
592*e7df7762SCody Peter Mello 		} else {
593*e7df7762SCody Peter Mello 			warnx("Unable to lookup %s: %s", host,
594*e7df7762SCody Peter Mello 			    gai_strerror(err));
595*e7df7762SCody Peter Mello 			return (-1);
596*e7df7762SCody Peter Mello 		}
597*e7df7762SCody Peter Mello 	}
598*e7df7762SCody Peter Mello 
599*e7df7762SCody Peter Mello 	if (attempts == MAX_ATTEMPTS) {
600*e7df7762SCody Peter Mello 		warnx("Failed multiple times to lookup %s", host);
601*e7df7762SCody Peter Mello 		return (-1);
602*e7df7762SCody Peter Mello 	}
603*e7df7762SCody Peter Mello 
604*e7df7762SCody Peter Mello 	inet6 = socket(PF_INET6, SOCK_DGRAM, 0);
605*e7df7762SCody Peter Mello 	if (inet6 < 0) {
606*e7df7762SCody Peter Mello 		warnx("Failed to open IPv6 socket: %s", strerror(errno));
607*e7df7762SCody Peter Mello 		err = -1;
608*e7df7762SCody Peter Mello 	}
609*e7df7762SCody Peter Mello 
610*e7df7762SCody Peter Mello 	route = socket(PF_ROUTE, SOCK_RAW, 0);
611*e7df7762SCody Peter Mello 	if (route < 0) {
612*e7df7762SCody Peter Mello 		warnx("Failed to open routing socket: %s", strerror(errno));
613*e7df7762SCody Peter Mello 		err = -1;
614*e7df7762SCody Peter Mello 	}
615*e7df7762SCody Peter Mello 
616*e7df7762SCody Peter Mello 	if (err == 0) {
617*e7df7762SCody Peter Mello 		for (p = serverinfo; p != NULL; p = p->ai_next) {
618*e7df7762SCody Peter Mello 			if (ndp_initialize_lifreq(route, &lifr, p->ai_addr)
619*e7df7762SCody Peter Mello 			    != 0) {
620*e7df7762SCody Peter Mello 				err = -1;
621*e7df7762SCody Peter Mello 				continue;
622*e7df7762SCody Peter Mello 			}
623*e7df7762SCody Peter Mello 
624*e7df7762SCody Peter Mello 			if (addr_func(inet6, &lifr, data) != 0) {
625*e7df7762SCody Peter Mello 				err = -1;
626*e7df7762SCody Peter Mello 				continue;
627*e7df7762SCody Peter Mello 			}
628*e7df7762SCody Peter Mello 		}
629*e7df7762SCody Peter Mello 	}
630*e7df7762SCody Peter Mello 
631*e7df7762SCody Peter Mello 	if (close(route) != 0) {
632*e7df7762SCody Peter Mello 		warnx("Failed to close routing socket: %s", strerror(errno));
633*e7df7762SCody Peter Mello 		err = -1;
634*e7df7762SCody Peter Mello 	}
635*e7df7762SCody Peter Mello 
636*e7df7762SCody Peter Mello 	if (close(inet6) != 0) {
637*e7df7762SCody Peter Mello 		warnx("Failed to close IPv6 socket: %s", strerror(errno));
638*e7df7762SCody Peter Mello 		err = -1;
639*e7df7762SCody Peter Mello 	}
640*e7df7762SCody Peter Mello 
641*e7df7762SCody Peter Mello 	/* Clean up linked list */
642*e7df7762SCody Peter Mello 	freeaddrinfo(serverinfo);
643*e7df7762SCody Peter Mello 
644*e7df7762SCody Peter Mello 	return (err);
645*e7df7762SCody Peter Mello }
646*e7df7762SCody Peter Mello 
647*e7df7762SCody Peter Mello static int
ndp_display(struct lifreq * lifrp)648*e7df7762SCody Peter Mello ndp_display(struct lifreq *lifrp)
649*e7df7762SCody Peter Mello {
650*e7df7762SCody Peter Mello 	struct sockaddr_in6 *lnr_addr;
651*e7df7762SCody Peter Mello 	char ipaddr[INET6_ADDRSTRLEN];
652*e7df7762SCody Peter Mello 	char *lladdr = NULL;
653*e7df7762SCody Peter Mello 	char hostname[NI_MAXHOST];
654*e7df7762SCody Peter Mello 	int flags, gni_flags;
655*e7df7762SCody Peter Mello 
656*e7df7762SCody Peter Mello 	lnr_addr = (struct sockaddr_in6 *)&lifrp->lifr_nd.lnr_addr;
657*e7df7762SCody Peter Mello 	flags = lifrp->lifr_nd.lnr_flags;
658*e7df7762SCody Peter Mello 
659*e7df7762SCody Peter Mello 	if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr,
660*e7df7762SCody Peter Mello 	    sizeof (ipaddr)) == NULL) {
661*e7df7762SCody Peter Mello 		warnx("Couldn't convert IPv6 address to string: %s",
662*e7df7762SCody Peter Mello 		    strerror(errno));
663*e7df7762SCody Peter Mello 		return (-1);
664*e7df7762SCody Peter Mello 	};
665*e7df7762SCody Peter Mello 
666*e7df7762SCody Peter Mello 	if ((lladdr = _link_ntoa((uchar_t *)lifrp->lifr_nd.lnr_hdw_addr,
667*e7df7762SCody Peter Mello 	    NULL, lifrp->lifr_nd.lnr_hdw_len, IFT_ETHER)) == NULL) {
668*e7df7762SCody Peter Mello 		warnx("Couldn't convert link-layer address to string: %s",
669*e7df7762SCody Peter Mello 		    strerror(errno));
670*e7df7762SCody Peter Mello 		return (-1);
671*e7df7762SCody Peter Mello 	}
672*e7df7762SCody Peter Mello 
673*e7df7762SCody Peter Mello 	gni_flags = ndp_noresolve ? NI_NUMERICHOST : 0;
674*e7df7762SCody Peter Mello 
675*e7df7762SCody Peter Mello 	if (getnameinfo((struct sockaddr *)lnr_addr, sizeof (sin6_t), hostname,
676*e7df7762SCody Peter Mello 	    sizeof (hostname), NULL, 0, gni_flags) != 0) {
677*e7df7762SCody Peter Mello 		warnx("Unable to lookup hostname for %s", ipaddr);
678*e7df7762SCody Peter Mello 		free(lladdr);
679*e7df7762SCody Peter Mello 		return (-1);
680*e7df7762SCody Peter Mello 	}
681*e7df7762SCody Peter Mello 
682*e7df7762SCody Peter Mello 	(void) printf("%s (%s) at %s", ipaddr, hostname, lladdr);
683*e7df7762SCody Peter Mello 
684*e7df7762SCody Peter Mello 	if (flags & NDF_ISROUTER_ON) {
685*e7df7762SCody Peter Mello 		(void) printf(" router");
686*e7df7762SCody Peter Mello 	}
687*e7df7762SCody Peter Mello 
688*e7df7762SCody Peter Mello 	if (flags & NDF_ANYCAST_ON) {
689*e7df7762SCody Peter Mello 		(void) printf(" any");
690*e7df7762SCody Peter Mello 	}
691*e7df7762SCody Peter Mello 
692*e7df7762SCody Peter Mello 	if (!(flags & NDF_STATIC)) {
693*e7df7762SCody Peter Mello 		(void) printf(" temp");
694*e7df7762SCody Peter Mello 	}
695*e7df7762SCody Peter Mello 
696*e7df7762SCody Peter Mello 	if (flags & NDF_PROXY_ON) {
697*e7df7762SCody Peter Mello 		(void) printf(" proxy");
698*e7df7762SCody Peter Mello 	}
699*e7df7762SCody Peter Mello 
700*e7df7762SCody Peter Mello 	(void) printf("\n");
701*e7df7762SCody Peter Mello 
702*e7df7762SCody Peter Mello 	free(lladdr);
703*e7df7762SCody Peter Mello 	return (0);
704*e7df7762SCody Peter Mello }
705*e7df7762SCody Peter Mello 
706*e7df7762SCody Peter Mello static int
ndp_display_missing(struct lifreq * lifrp)707*e7df7762SCody Peter Mello ndp_display_missing(struct lifreq *lifrp)
708*e7df7762SCody Peter Mello {
709*e7df7762SCody Peter Mello 	struct sockaddr_in6 *lnr_addr;
710*e7df7762SCody Peter Mello 	char ipaddr[INET6_ADDRSTRLEN];
711*e7df7762SCody Peter Mello 	char hostname[NI_MAXHOST];
712*e7df7762SCody Peter Mello 	int flags = ndp_noresolve ? NI_NUMERICHOST : 0;
713*e7df7762SCody Peter Mello 	lnr_addr = (struct sockaddr_in6 *)&lifrp->lifr_nd.lnr_addr;
714*e7df7762SCody Peter Mello 
715*e7df7762SCody Peter Mello 	if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr,
716*e7df7762SCody Peter Mello 	    sizeof (ipaddr)) == NULL) {
717*e7df7762SCody Peter Mello 		warnx("Couldn't convert IPv6 address to string: %s",
718*e7df7762SCody Peter Mello 		    strerror(errno));
719*e7df7762SCody Peter Mello 		return (-1);
720*e7df7762SCody Peter Mello 	};
721*e7df7762SCody Peter Mello 
722*e7df7762SCody Peter Mello 	if (getnameinfo((struct sockaddr *)lnr_addr, sizeof (sin6_t), hostname,
723*e7df7762SCody Peter Mello 	    sizeof (hostname), NULL, 0, flags) != 0) {
724*e7df7762SCody Peter Mello 		warnx("Unable to lookup hostname for %s", ipaddr);
725*e7df7762SCody Peter Mello 		return (-1);
726*e7df7762SCody Peter Mello 	}
727*e7df7762SCody Peter Mello 
728*e7df7762SCody Peter Mello 	(void) printf("%s (%s) -- no entry\n", ipaddr, hostname);
729*e7df7762SCody Peter Mello 	return (0);
730*e7df7762SCody Peter Mello }
731*e7df7762SCody Peter Mello 
732*e7df7762SCody Peter Mello static void
ndp_lifr2ip(struct lifreq * lifrp,char * ipaddr,int buflen)733*e7df7762SCody Peter Mello ndp_lifr2ip(struct lifreq *lifrp, char *ipaddr, int buflen)
734*e7df7762SCody Peter Mello {
735*e7df7762SCody Peter Mello 	sin6_t *lnr_addr = (sin6_t *)&lifrp->lifr_nd.lnr_addr;
736*e7df7762SCody Peter Mello 	if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr,
737*e7df7762SCody Peter Mello 	    buflen) == NULL) {
738*e7df7762SCody Peter Mello 		(void) snprintf(ipaddr, buflen, "(failed to format IP)");
739*e7df7762SCody Peter Mello 	};
740*e7df7762SCody Peter Mello }
741*e7df7762SCody Peter Mello 
742*e7df7762SCody Peter Mello /*
743*e7df7762SCody Peter Mello  * Perform a SIOCLIFGETND and print out information about it
744*e7df7762SCody Peter Mello  */
745*e7df7762SCody Peter Mello /*ARGSUSED*/
746*e7df7762SCody Peter Mello static int
ndp_get(int fd,struct lifreq * lifrp,void * unused)747*e7df7762SCody Peter Mello ndp_get(int fd, struct lifreq *lifrp, void *unused)
748*e7df7762SCody Peter Mello {
749*e7df7762SCody Peter Mello 	char ipaddr[INET6_ADDRSTRLEN];
750*e7df7762SCody Peter Mello 	if (ioctl(fd, SIOCLIFGETND, lifrp) < 0) {
751*e7df7762SCody Peter Mello 		if (errno == ESRCH) {
752*e7df7762SCody Peter Mello 			return (ndp_display_missing(lifrp));
753*e7df7762SCody Peter Mello 		} else {
754*e7df7762SCody Peter Mello 			ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr));
755*e7df7762SCody Peter Mello 			warnx("Couldn't lookup %s: %s",
756*e7df7762SCody Peter Mello 			    ipaddr, strerror(errno));
757*e7df7762SCody Peter Mello 			return (-1);
758*e7df7762SCody Peter Mello 		}
759*e7df7762SCody Peter Mello 	}
760*e7df7762SCody Peter Mello 
761*e7df7762SCody Peter Mello 	return (ndp_display(lifrp));
762*e7df7762SCody Peter Mello }
763*e7df7762SCody Peter Mello 
764*e7df7762SCody Peter Mello /*
765*e7df7762SCody Peter Mello  * Print out all NDP entries
766*e7df7762SCody Peter Mello  */
767*e7df7762SCody Peter Mello static void
ndp_get_all(void)768*e7df7762SCody Peter Mello ndp_get_all(void)
769*e7df7762SCody Peter Mello {
770*e7df7762SCody Peter Mello 	(void) execl(netstat_path, "netstat",
771*e7df7762SCody Peter Mello 	    (ndp_noresolve ? "-np" : "-p"),
772*e7df7762SCody Peter Mello 	    "-f", "inet6", (char *)0);
773*e7df7762SCody Peter Mello 	ndp_fatal("Coudn't exec %s: %s", netstat_path, strerror(errno));
774*e7df7762SCody Peter Mello }
775*e7df7762SCody Peter Mello 
776*e7df7762SCody Peter Mello /*
777*e7df7762SCody Peter Mello  * Perform a SIOCLIFDELND ioctl
778*e7df7762SCody Peter Mello  */
779*e7df7762SCody Peter Mello /*ARGSUSED*/
780*e7df7762SCody Peter Mello static int
ndp_delete(int fd,struct lifreq * lifrp,void * unused)781*e7df7762SCody Peter Mello ndp_delete(int fd, struct lifreq *lifrp, void *unused)
782*e7df7762SCody Peter Mello {
783*e7df7762SCody Peter Mello 	char ipaddr[INET6_ADDRSTRLEN];
784*e7df7762SCody Peter Mello 
785*e7df7762SCody Peter Mello 	if (ioctl(fd, SIOCLIFDELND, lifrp) < 0) {
786*e7df7762SCody Peter Mello 		ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr));
787*e7df7762SCody Peter Mello 		if (errno == ESRCH) {
788*e7df7762SCody Peter Mello 			warnx("No entry for %s", ipaddr);
789*e7df7762SCody Peter Mello 			return (-1);
790*e7df7762SCody Peter Mello 		} else if (errno == EPERM) {
791*e7df7762SCody Peter Mello 			warnx("Permission denied, "
792*e7df7762SCody Peter Mello 			    "could not delete entry for %s", ipaddr);
793*e7df7762SCody Peter Mello 			return (-1);
794*e7df7762SCody Peter Mello 		} else {
795*e7df7762SCody Peter Mello 			warnx("Couldn't delete mapping for %s: %s",
796*e7df7762SCody Peter Mello 			    ipaddr, strerror(errno));
797*e7df7762SCody Peter Mello 			return (-1);
798*e7df7762SCody Peter Mello 		}
799*e7df7762SCody Peter Mello 	}
800*e7df7762SCody Peter Mello 
801*e7df7762SCody Peter Mello 	return (0);
802*e7df7762SCody Peter Mello }
803*e7df7762SCody Peter Mello 
804*e7df7762SCody Peter Mello /*
805*e7df7762SCody Peter Mello  * Perform a SIOCLIFSETND ioctl using properties from the example structure.
806*e7df7762SCody Peter Mello  */
807*e7df7762SCody Peter Mello static int
ndp_set(int fd,struct lifreq * lifrp,void * data)808*e7df7762SCody Peter Mello ndp_set(int fd, struct lifreq *lifrp, void *data)
809*e7df7762SCody Peter Mello {
810*e7df7762SCody Peter Mello 	char ipaddr[INET6_ADDRSTRLEN];
811*e7df7762SCody Peter Mello 	const lif_nd_req_t *nd_attrs = data;
812*e7df7762SCody Peter Mello 
813*e7df7762SCody Peter Mello 	(void) memcpy(lifrp->lifr_nd.lnr_hdw_addr, nd_attrs->lnr_hdw_addr,
814*e7df7762SCody Peter Mello 	    ND_MAX_HDW_LEN);
815*e7df7762SCody Peter Mello 	lifrp->lifr_nd.lnr_hdw_len = nd_attrs->lnr_hdw_len;
816*e7df7762SCody Peter Mello 	lifrp->lifr_nd.lnr_flags = nd_attrs->lnr_flags;
817*e7df7762SCody Peter Mello 
818*e7df7762SCody Peter Mello 	lifrp->lifr_nd.lnr_state_create = nd_attrs->lnr_state_create;
819*e7df7762SCody Peter Mello 	lifrp->lifr_nd.lnr_state_same_lla = nd_attrs->lnr_state_same_lla;
820*e7df7762SCody Peter Mello 	lifrp->lifr_nd.lnr_state_diff_lla = nd_attrs->lnr_state_diff_lla;
821*e7df7762SCody Peter Mello 
822*e7df7762SCody Peter Mello 	if (ioctl(fd, SIOCLIFSETND, lifrp) < 0) {
823*e7df7762SCody Peter Mello 		ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr));
824*e7df7762SCody Peter Mello 		if (errno == EPERM) {
825*e7df7762SCody Peter Mello 			warnx("Permission denied, "
826*e7df7762SCody Peter Mello 			    "could not set entry for %s", ipaddr);
827*e7df7762SCody Peter Mello 			return (-1);
828*e7df7762SCody Peter Mello 		} else {
829*e7df7762SCody Peter Mello 			warnx("Failed to set mapping for %s: %s",
830*e7df7762SCody Peter Mello 			    ipaddr, strerror(errno));
831*e7df7762SCody Peter Mello 			return (-1);
832*e7df7762SCody Peter Mello 		}
833*e7df7762SCody Peter Mello 	}
834*e7df7762SCody Peter Mello 
835*e7df7762SCody Peter Mello 	return (0);
836*e7df7762SCody Peter Mello }
837*e7df7762SCody Peter Mello 
838*e7df7762SCody Peter Mello /*
839*e7df7762SCody Peter Mello  * Given a host identifier, a link-layer address and possible options,
840*e7df7762SCody Peter Mello  * add/update the NDP mappings.
841*e7df7762SCody Peter Mello  */
842*e7df7762SCody Peter Mello static int
ndp_set_nce(char * host,char * lladdr,char * opts[],int optlen)843*e7df7762SCody Peter Mello ndp_set_nce(char *host, char *lladdr, char *opts[], int optlen)
844*e7df7762SCody Peter Mello {
845*e7df7762SCody Peter Mello 	lif_nd_req_t nd_attrs;
846*e7df7762SCody Peter Mello 	uchar_t *ea;
847*e7df7762SCody Peter Mello 	char *opt;
848*e7df7762SCody Peter Mello 	int i;
849*e7df7762SCody Peter Mello 	boolean_t temp = B_FALSE;
850*e7df7762SCody Peter Mello 	boolean_t any = B_FALSE;
851*e7df7762SCody Peter Mello 	boolean_t router = B_FALSE;
852*e7df7762SCody Peter Mello 
853*e7df7762SCody Peter Mello 	bzero(&nd_attrs, sizeof (lif_nd_req_t));
854*e7df7762SCody Peter Mello 
855*e7df7762SCody Peter Mello 	ea = _link_aton(lladdr, &nd_attrs.lnr_hdw_len);
856*e7df7762SCody Peter Mello 
857*e7df7762SCody Peter Mello 	if (ea == NULL) {
858*e7df7762SCody Peter Mello 		warnx("Unable to parse link-layer address \"%s\"", lladdr);
859*e7df7762SCody Peter Mello 		return (-1);
860*e7df7762SCody Peter Mello 	}
861*e7df7762SCody Peter Mello 
862*e7df7762SCody Peter Mello 	if (nd_attrs.lnr_hdw_len > sizeof (nd_attrs.lnr_hdw_addr)) {
863*e7df7762SCody Peter Mello 		warnx("The size of the link-layer address is "
864*e7df7762SCody Peter Mello 		    "too large to set\n");
865*e7df7762SCody Peter Mello 		free(ea);
866*e7df7762SCody Peter Mello 		return (-1);
867*e7df7762SCody Peter Mello 	}
868*e7df7762SCody Peter Mello 
869*e7df7762SCody Peter Mello 	(void) memcpy(nd_attrs.lnr_hdw_addr, ea, nd_attrs.lnr_hdw_len);
870*e7df7762SCody Peter Mello 
871*e7df7762SCody Peter Mello 	free(ea);
872*e7df7762SCody Peter Mello 
873*e7df7762SCody Peter Mello 	nd_attrs.lnr_state_create = ND_REACHABLE;
874*e7df7762SCody Peter Mello 	nd_attrs.lnr_state_same_lla = ND_UNCHANGED;
875*e7df7762SCody Peter Mello 	nd_attrs.lnr_state_diff_lla = ND_STALE;
876*e7df7762SCody Peter Mello 
877*e7df7762SCody Peter Mello 	for (i = 0; i < optlen; i++) {
878*e7df7762SCody Peter Mello 		opt = opts[i];
879*e7df7762SCody Peter Mello 		if (strcmp(opt, "temp") == 0) {
880*e7df7762SCody Peter Mello 			temp = B_TRUE;
881*e7df7762SCody Peter Mello 		} else if (strcmp(opt, "any") == 0) {
882*e7df7762SCody Peter Mello 			any = B_TRUE;
883*e7df7762SCody Peter Mello 		} else if (strcmp(opt, "router") == 0) {
884*e7df7762SCody Peter Mello 			router = B_TRUE;
885*e7df7762SCody Peter Mello 		} else if (strcmp(opt, "proxy") == 0) {
886*e7df7762SCody Peter Mello 			warnx("NDP proxying is currently not supported");
887*e7df7762SCody Peter Mello 			return (-1);
888*e7df7762SCody Peter Mello 		} else {
889*e7df7762SCody Peter Mello 			warnx("Unrecognized option \"%s\"", opt);
890*e7df7762SCody Peter Mello 			return (-1);
891*e7df7762SCody Peter Mello 		}
892*e7df7762SCody Peter Mello 	}
893*e7df7762SCody Peter Mello 
894*e7df7762SCody Peter Mello 	if (!temp) {
895*e7df7762SCody Peter Mello 		nd_attrs.lnr_flags |= NDF_STATIC;
896*e7df7762SCody Peter Mello 	}
897*e7df7762SCody Peter Mello 
898*e7df7762SCody Peter Mello 	if (any) {
899*e7df7762SCody Peter Mello 		nd_attrs.lnr_flags |= NDF_ANYCAST_ON;
900*e7df7762SCody Peter Mello 	} else {
901*e7df7762SCody Peter Mello 		nd_attrs.lnr_flags |= NDF_ANYCAST_OFF;
902*e7df7762SCody Peter Mello 	}
903*e7df7762SCody Peter Mello 
904*e7df7762SCody Peter Mello 	if (router) {
905*e7df7762SCody Peter Mello 		nd_attrs.lnr_flags |= NDF_ISROUTER_OFF;
906*e7df7762SCody Peter Mello 	} else {
907*e7df7762SCody Peter Mello 		nd_attrs.lnr_flags |= NDF_ISROUTER_OFF;
908*e7df7762SCody Peter Mello 	}
909*e7df7762SCody Peter Mello 
910*e7df7762SCody Peter Mello 	return (ndp_host_enumerate(host, ndp_set, &nd_attrs));
911*e7df7762SCody Peter Mello }
912*e7df7762SCody Peter Mello 
913*e7df7762SCody Peter Mello /*
914*e7df7762SCody Peter Mello  * Read in a file and set the mappings from each line.
915*e7df7762SCody Peter Mello  */
916*e7df7762SCody Peter Mello static int
ndp_set_file(char * filename)917*e7df7762SCody Peter Mello ndp_set_file(char *filename)
918*e7df7762SCody Peter Mello {
919*e7df7762SCody Peter Mello 	char *line = NULL, *lasts = NULL, *curr;
920*e7df7762SCody Peter Mello 	char *host, *lladdr;
921*e7df7762SCody Peter Mello 	char *opts[MAX_OPTS];
922*e7df7762SCody Peter Mello 	int optlen = 0, lineno = 0;
923*e7df7762SCody Peter Mello 	size_t cap = 0;
924*e7df7762SCody Peter Mello 	boolean_t failed_line = B_FALSE;
925*e7df7762SCody Peter Mello 	FILE *stream = fopen(filename, "r");
926*e7df7762SCody Peter Mello 
927*e7df7762SCody Peter Mello 	if (stream == NULL) {
928*e7df7762SCody Peter Mello 		ndp_fatal("Error while opening file %s: %s",
929*e7df7762SCody Peter Mello 		    filename, strerror(errno));
930*e7df7762SCody Peter Mello 	}
931*e7df7762SCody Peter Mello 
932*e7df7762SCody Peter Mello 	errno = 0;
933*e7df7762SCody Peter Mello 	while (getline(&line, &cap, stream) != -1) {
934*e7df7762SCody Peter Mello 		lineno++;
935*e7df7762SCody Peter Mello 
936*e7df7762SCody Peter Mello 		if (line[0] == '#')
937*e7df7762SCody Peter Mello 			continue;
938*e7df7762SCody Peter Mello 
939*e7df7762SCody Peter Mello 		host = strtok_r(line, WORDSEPS, &lasts);
940*e7df7762SCody Peter Mello 		if (host == NULL) {
941*e7df7762SCody Peter Mello 			warnx("Line %d incomplete, skipping: "
942*e7df7762SCody Peter Mello 			    "missing host identifier", lineno);
943*e7df7762SCody Peter Mello 			failed_line = B_TRUE;
944*e7df7762SCody Peter Mello 			continue;
945*e7df7762SCody Peter Mello 		}
946*e7df7762SCody Peter Mello 
947*e7df7762SCody Peter Mello 		lladdr = strtok_r(NULL, WORDSEPS, &lasts);
948*e7df7762SCody Peter Mello 		if (lladdr == NULL) {
949*e7df7762SCody Peter Mello 			warnx("Line %d incomplete, skipping: "
950*e7df7762SCody Peter Mello 			    "missing link-layer address", lineno);
951*e7df7762SCody Peter Mello 			failed_line = B_TRUE;
952*e7df7762SCody Peter Mello 			continue;
953*e7df7762SCody Peter Mello 		}
954*e7df7762SCody Peter Mello 
955*e7df7762SCody Peter Mello 		for (optlen = 0; optlen < MAX_OPTS; optlen++) {
956*e7df7762SCody Peter Mello 			curr = strtok_r(NULL, WORDSEPS, &lasts);
957*e7df7762SCody Peter Mello 			if (curr == NULL)
958*e7df7762SCody Peter Mello 				break;
959*e7df7762SCody Peter Mello 			opts[optlen] = curr;
960*e7df7762SCody Peter Mello 		}
961*e7df7762SCody Peter Mello 
962*e7df7762SCody Peter Mello 		if (ndp_set_nce(host, lladdr, opts, optlen) != 0) {
963*e7df7762SCody Peter Mello 			failed_line = B_TRUE;
964*e7df7762SCody Peter Mello 			continue;
965*e7df7762SCody Peter Mello 		}
966*e7df7762SCody Peter Mello 	}
967*e7df7762SCody Peter Mello 
968*e7df7762SCody Peter Mello 	free(line);
969*e7df7762SCody Peter Mello 
970*e7df7762SCody Peter Mello 	if (errno != 0 || ferror(stream)) {
971*e7df7762SCody Peter Mello 		ndp_fatal("Error while reading from file %s: %s", filename,
972*e7df7762SCody Peter Mello 		    strerror(errno));
973*e7df7762SCody Peter Mello 	}
974*e7df7762SCody Peter Mello 
975*e7df7762SCody Peter Mello 	if (fclose(stream) != 0) {
976*e7df7762SCody Peter Mello 		ndp_fatal("Error close file %s: %s", filename, strerror(errno));
977*e7df7762SCody Peter Mello 	}
978*e7df7762SCody Peter Mello 
979*e7df7762SCody Peter Mello 	return (failed_line ? -1 : 0);
980*e7df7762SCody Peter Mello }
981*e7df7762SCody Peter Mello 
982*e7df7762SCody Peter Mello int
main(int argc,char * argv[])983*e7df7762SCody Peter Mello main(int argc, char *argv[])
984*e7df7762SCody Peter Mello {
985*e7df7762SCody Peter Mello 	char *flagarg = NULL, *lladdr = NULL;
986*e7df7762SCody Peter Mello 	char **opts;
987*e7df7762SCody Peter Mello 	char *endptr;
988*e7df7762SCody Peter Mello 	int c, argsleft, optlen = 0, err = 0;
989*e7df7762SCody Peter Mello 	long long period;
990*e7df7762SCody Peter Mello 	enum ndp_action action = NDP_A_DEFAULT;
991*e7df7762SCody Peter Mello 
992*e7df7762SCody Peter Mello 	setprogname(basename(argv[0]));
993*e7df7762SCody Peter Mello 
994*e7df7762SCody Peter Mello 	if (argc < 2) {
995*e7df7762SCody Peter Mello 		ndp_usage("No arguments given.");
996*e7df7762SCody Peter Mello 	}
997*e7df7762SCody Peter Mello 
998*e7df7762SCody Peter Mello 	while ((c = getopt(argc, argv, ":naA:d:f:i:s:")) != -1) {
999*e7df7762SCody Peter Mello 		switch (c) {
1000*e7df7762SCody Peter Mello 		case 'n':
1001*e7df7762SCody Peter Mello 			ndp_noresolve = B_TRUE;
1002*e7df7762SCody Peter Mello 			break;
1003*e7df7762SCody Peter Mello 		case 'i':
1004*e7df7762SCody Peter Mello 			ndp_iface = optarg;
1005*e7df7762SCody Peter Mello 			break;
1006*e7df7762SCody Peter Mello 		case 's':
1007*e7df7762SCody Peter Mello 			if (action != NDP_A_DEFAULT)
1008*e7df7762SCody Peter Mello 				ndp_badflag(action);
1009*e7df7762SCody Peter Mello 			action = NDP_A_SET_NCE;
1010*e7df7762SCody Peter Mello 			flagarg = optarg;
1011*e7df7762SCody Peter Mello 
1012*e7df7762SCody Peter Mello 			if ((argc - optind) < 1) {
1013*e7df7762SCody Peter Mello 				ndp_usage("Missing link-layer address after "
1014*e7df7762SCody Peter Mello 				    "the node address, \"%s\"", flagarg);
1015*e7df7762SCody Peter Mello 			}
1016*e7df7762SCody Peter Mello 			lladdr = argv[optind++];
1017*e7df7762SCody Peter Mello 
1018*e7df7762SCody Peter Mello 			/*
1019*e7df7762SCody Peter Mello 			 * Grab any following keywords up to the next flag
1020*e7df7762SCody Peter Mello 			 */
1021*e7df7762SCody Peter Mello 			opts = argv + optind;
1022*e7df7762SCody Peter Mello 			while ((argc - optind) > 0) {
1023*e7df7762SCody Peter Mello 				if (argv[optind][0] == '-')
1024*e7df7762SCody Peter Mello 					ndp_usage("Encountered \"%s\" after "
1025*e7df7762SCody Peter Mello 					    "flag parsing is done",
1026*e7df7762SCody Peter Mello 					    argv[optind]);
1027*e7df7762SCody Peter Mello 				optind++;
1028*e7df7762SCody Peter Mello 				optlen++;
1029*e7df7762SCody Peter Mello 			}
1030*e7df7762SCody Peter Mello 			break;
1031*e7df7762SCody Peter Mello 		case 'a':
1032*e7df7762SCody Peter Mello 			if (action != NDP_A_DEFAULT)
1033*e7df7762SCody Peter Mello 				ndp_badflag(action);
1034*e7df7762SCody Peter Mello 			action = NDP_A_GET_ALL;
1035*e7df7762SCody Peter Mello 			break;
1036*e7df7762SCody Peter Mello 		case 'A':
1037*e7df7762SCody Peter Mello 			if (action != NDP_A_DEFAULT)
1038*e7df7762SCody Peter Mello 				ndp_badflag(action);
1039*e7df7762SCody Peter Mello 			action = NDP_A_GET_FOREVER;
1040*e7df7762SCody Peter Mello 			flagarg = optarg;
1041*e7df7762SCody Peter Mello 			break;
1042*e7df7762SCody Peter Mello 		case 'd':
1043*e7df7762SCody Peter Mello 			if (action != NDP_A_DEFAULT)
1044*e7df7762SCody Peter Mello 				ndp_badflag(action);
1045*e7df7762SCody Peter Mello 			action = NDP_A_DELETE;
1046*e7df7762SCody Peter Mello 			flagarg = optarg;
1047*e7df7762SCody Peter Mello 			break;
1048*e7df7762SCody Peter Mello 		case 'f':
1049*e7df7762SCody Peter Mello 			if (action != NDP_A_DEFAULT)
1050*e7df7762SCody Peter Mello 				ndp_badflag(action);
1051*e7df7762SCody Peter Mello 			action = NDP_A_SET_FILE;
1052*e7df7762SCody Peter Mello 			flagarg = optarg;
1053*e7df7762SCody Peter Mello 			break;
1054*e7df7762SCody Peter Mello 		case ':':
1055*e7df7762SCody Peter Mello 			ndp_missingarg(optopt);
1056*e7df7762SCody Peter Mello 			break;
1057*e7df7762SCody Peter Mello 		case '?':
1058*e7df7762SCody Peter Mello 			ndp_usage("Unrecognized flag \"-%c\"", optopt);
1059*e7df7762SCody Peter Mello 		default:
1060*e7df7762SCody Peter Mello 			ndp_usage(NULL);
1061*e7df7762SCody Peter Mello 		}
1062*e7df7762SCody Peter Mello 	}
1063*e7df7762SCody Peter Mello 
1064*e7df7762SCody Peter Mello 	argsleft = argc - optind;
1065*e7df7762SCody Peter Mello 	ndp_pid = getpid();
1066*e7df7762SCody Peter Mello 
1067*e7df7762SCody Peter Mello 	if (action != NDP_A_DEFAULT && argsleft != 0) {
1068*e7df7762SCody Peter Mello 		ndp_usage("Extra arguments leftover after parsing flags");
1069*e7df7762SCody Peter Mello 	}
1070*e7df7762SCody Peter Mello 
1071*e7df7762SCody Peter Mello 	switch (action) {
1072*e7df7762SCody Peter Mello 	case NDP_A_DEFAULT:
1073*e7df7762SCody Peter Mello 	case NDP_A_GET:
1074*e7df7762SCody Peter Mello 		if (argsleft != 1) {
1075*e7df7762SCody Peter Mello 			ndp_usage("Multiple arguments given without any flags");
1076*e7df7762SCody Peter Mello 		}
1077*e7df7762SCody Peter Mello 		err = ndp_host_enumerate(argv[optind], ndp_get, NULL);
1078*e7df7762SCody Peter Mello 		break;
1079*e7df7762SCody Peter Mello 	case NDP_A_GET_ALL:
1080*e7df7762SCody Peter Mello 		ndp_get_all();
1081*e7df7762SCody Peter Mello 		/*NOTREACHED*/
1082*e7df7762SCody Peter Mello 		break;
1083*e7df7762SCody Peter Mello 	case NDP_A_GET_FOREVER:
1084*e7df7762SCody Peter Mello 		errno = 0;
1085*e7df7762SCody Peter Mello 		period = strtoll(flagarg, &endptr, 10);
1086*e7df7762SCody Peter Mello 		if ((period == 0 && errno != 0) ||
1087*e7df7762SCody Peter Mello 		    (endptr[0] != '\0') ||
1088*e7df7762SCody Peter Mello 		    (period < 0)) {
1089*e7df7762SCody Peter Mello 			ndp_usage("Given period should be a positive integer,"
1090*e7df7762SCody Peter Mello 			    " not \"%s\"", flagarg);
1091*e7df7762SCody Peter Mello 		}
1092*e7df7762SCody Peter Mello 		if (period > 86400) {
1093*e7df7762SCody Peter Mello 			ndp_usage("Given period should be shorter than a day;"
1094*e7df7762SCody Peter Mello 			    " given \"%s\" seconds", flagarg);
1095*e7df7762SCody Peter Mello 		}
1096*e7df7762SCody Peter Mello 		ndp_run_periodically(period, ndp_get_all);
1097*e7df7762SCody Peter Mello 		/*NOTREACHED*/
1098*e7df7762SCody Peter Mello 		break;
1099*e7df7762SCody Peter Mello 	case NDP_A_DELETE:
1100*e7df7762SCody Peter Mello 		err = ndp_host_enumerate(flagarg, ndp_delete, NULL);
1101*e7df7762SCody Peter Mello 		break;
1102*e7df7762SCody Peter Mello 	case NDP_A_SET_NCE:
1103*e7df7762SCody Peter Mello 		err = ndp_set_nce(flagarg, lladdr, opts, optlen);
1104*e7df7762SCody Peter Mello 		break;
1105*e7df7762SCody Peter Mello 	case NDP_A_SET_FILE:
1106*e7df7762SCody Peter Mello 		err = ndp_set_file(flagarg);
1107*e7df7762SCody Peter Mello 		break;
1108*e7df7762SCody Peter Mello 	}
1109*e7df7762SCody Peter Mello 
1110*e7df7762SCody Peter Mello 	return (err == 0 ? 0 : 1);
1111*e7df7762SCody Peter Mello }
1112