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