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