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
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
30 #include <sys/ioctl.h>
31 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/sockio.h>
37 #include <net/if_types.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
41 #include <net/if_dl.h>
42 #include <net/route.h>
44 typedef struct sockaddr_in6 sin6_t
;
47 typedef struct rtmsg_pkt
{
48 struct rt_msghdr m_rtm
;
49 char m_space
[BUF_SIZE
];
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";
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
105 #define WORDSEPS " \t\r\n"
108 * Macros borrowed from route(1M) for working with PF_ROUTE messages
110 #define RT_ADVANCE(x, n) ((x) += ndp_salen(n))
111 #define RT_NEXTADDR(cp, w, u) \
113 (void) memmove(cp, u, l); \
117 * Print an error to stderr and then exit non-zero.
120 ndp_fatal(const char *format
, ...)
124 va_start(ap
, format
);
131 * Print out the command usage to stderr, along with any reason why it's being
132 * printed, and then exit non-zero.
135 ndp_usage(const char *reason
, ...)
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");
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"
154 " %s [-n] -A period\n",
155 ndp_progname
, ndp_progname
, ndp_progname
,
156 ndp_progname
, ndp_progname
, ndp_progname
);
161 ndp_badflag(enum ndp_action action
)
166 ndp_usage("Already going to print an entry, "
167 "but extra -%c given", optopt
);
170 ndp_usage("Already going to print all entries (-a), "
171 "but extra -%c given", optopt
);
173 case NDP_A_GET_FOREVER
:
174 ndp_usage("Already going to repeatedly print all entries (-A), "
175 "but extra -%c given", optopt
);
178 ndp_usage("Already going to delete an entry (-d), "
179 "but extra -%c given", optopt
);
182 ndp_usage("Already going to set an entry (-s), "
183 "but extra -%c given", optopt
);
186 ndp_usage("Already going to set from file (-f), "
187 "but extra -%c given", optopt
);
193 ndp_missingarg(char flag
)
197 ndp_usage("Missing time period after -%c", flag
);
200 ndp_usage("Missing node name after -%c", flag
);
203 ndp_usage("Missing filename after -%c", flag
);
206 ndp_usage("Missing node name after -%c", flag
);
209 ndp_usage("Missing interface name after -%c", flag
);
212 ndp_usage("Missing option argument after -%c", flag
);
218 * Run a function that's going to exec in a child process, and don't return
222 ndp_run_in_child(ndp_void_f
*func
)
225 int childstat
= 0, status
= 0;
228 if (child_pid
== (pid_t
)-1) {
229 ndp_fatal("Unable to fork: %s", strerror(errno
));
230 } else if (child_pid
== (pid_t
)0) {
235 while (waitpid(child_pid
, &childstat
, 0) == -1) {
239 ndp_fatal("Failed to wait on child: %s", strerror(errno
));
242 status
= WEXITSTATUS(childstat
);
244 ndp_fatal("Child process exited with %d", status
);
249 * SIGALRM handler to schedule a run.
259 * Prepare signal masks, and install the SIGALRM handler. Return old signal
260 * masks through the first argument.
263 ndp_setup_handler(sigset_t
*oset
)
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",
282 sa
.sa_handler
= ndp_do_run
;
284 if (sigemptyset(&sa
.sa_mask
) != 0) {
285 ndp_fatal("Unable to prepare empty signal set: %s",
289 if (sigaction(SIGALRM
, &sa
, NULL
) != 0) {
290 ndp_fatal("Unable to install timer handler: %s",
296 * Start the printing timer.
299 ndp_start_timer(time_t period
)
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.
320 ndp_run_periodically(time_t period
, ndp_void_f
*func
)
324 ndp_setup_handler(&oset
);
325 ndp_start_timer(period
);
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
));
343 * Given an address, return its size.
346 ndp_salen(const struct sockaddr
*sa
)
348 switch (sa
->sa_family
) {
350 return (sizeof (struct sockaddr_in
));
352 return (sizeof (struct sockaddr_dl
));
354 return (sizeof (struct sockaddr_in6
));
356 warnx("Unrecognized sockaddr with address family %d!",
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.
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
)
377 if (rtm
->rtm_version
!= RTM_VERSION
) {
378 warnx("Routing message version %d not understood",
383 if (rtm
->rtm_errno
!= 0) {
384 warnx("Routing message couldn't be processed: %s",
385 strerror(rtm
->rtm_errno
));
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)
396 sa
= (struct sockaddr
*)cp
;
408 if (sa
->sa_family
== AF_LINK
&&
409 ((struct sockaddr_dl
*)sa
)->sdl_nlen
!= 0)
410 *ifp
= (struct sockaddr_dl
*)sa
;
424 * Given an IPv6 address, use routing information to look up
425 * the destination and interface it would pass through.
428 ndp_rtmsg_get(int fd
, rtmsg_pkt_t
*msg
, struct sockaddr
*sin6p
)
431 struct sockaddr_dl sdl
;
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
) {
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
);
468 warnx("Failed to send routing message: %s",
472 } else if (mlen
< (int)m_rtm
->rtm_msglen
) {
473 warnx("Failed to write all bytes to routing socket");
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.
483 mlen
= read(fd
, (char *)msg
, sizeof (rtmsg_pkt_t
));
485 (m_rtm
->rtm_seq
!= seq
|| m_rtm
->rtm_pid
!= ndp_pid
));
487 warnx("Failed to read from routing socket: %s",
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.
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
;
506 if (ndp_rtmsg_get(fd
, &msg
, sin6p
) != 0) {
510 if (ndp_extract_sockaddrs(&msg
.m_rtm
, &dst
, &gate
,
511 &mask
, &src
, &ifp
) != 0) {
516 warnx("Unable to find appropriate interface for address");
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
);
524 (void) snprintf(buf
, buflen
, "%.*s", ifp
->sdl_nlen
,
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.
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
);
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.");
560 if (ndp_find_interface(route
, sap
, lifr_name
, LIFNAMSIZ
) != 0)
564 (void) memcpy(lnr_addr
, sap
, sizeof (struct sockaddr_storage
));
570 * Take a host identifier, find the corresponding IPv6 addresses and then pass
571 * them to the specified function, along with any desired data.
574 ndp_host_enumerate(char *host
, ndp_addr_f
*addr_func
, void *data
)
577 struct addrinfo hints
, *serverinfo
, *p
;
578 int err
, attempts
= 0;
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
);
590 } else if (err
== EAI_AGAIN
) {
593 warnx("Unable to lookup %s: %s", host
,
599 if (attempts
== MAX_ATTEMPTS
) {
600 warnx("Failed multiple times to lookup %s", host
);
604 inet6
= socket(PF_INET6
, SOCK_DGRAM
, 0);
606 warnx("Failed to open IPv6 socket: %s", strerror(errno
));
610 route
= socket(PF_ROUTE
, SOCK_RAW
, 0);
612 warnx("Failed to open routing socket: %s", strerror(errno
));
617 for (p
= serverinfo
; p
!= NULL
; p
= p
->ai_next
) {
618 if (ndp_initialize_lifreq(route
, &lifr
, p
->ai_addr
)
624 if (addr_func(inet6
, &lifr
, data
) != 0) {
631 if (close(route
) != 0) {
632 warnx("Failed to close routing socket: %s", strerror(errno
));
636 if (close(inet6
) != 0) {
637 warnx("Failed to close IPv6 socket: %s", strerror(errno
));
641 /* Clean up linked list */
642 freeaddrinfo(serverinfo
);
648 ndp_display(struct lifreq
*lifrp
)
650 struct sockaddr_in6
*lnr_addr
;
651 char ipaddr
[INET6_ADDRSTRLEN
];
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",
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",
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
);
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");
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",
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
);
728 (void) printf("%s (%s) -- no entry\n", ipaddr
, hostname
);
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
,
738 (void) snprintf(ipaddr
, buflen
, "(failed to format IP)");
743 * Perform a SIOCLIFGETND and print out information about it
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
));
754 ndp_lifr2ip(lifrp
, ipaddr
, sizeof (ipaddr
));
755 warnx("Couldn't lookup %s: %s",
756 ipaddr
, strerror(errno
));
761 return (ndp_display(lifrp
));
765 * Print out all NDP entries
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
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
);
790 } else if (errno
== EPERM
) {
791 warnx("Permission denied, "
792 "could not delete entry for %s", ipaddr
);
795 warnx("Couldn't delete mapping for %s: %s",
796 ipaddr
, strerror(errno
));
805 * Perform a SIOCLIFSETND ioctl using properties from the example structure.
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
,
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
);
829 warnx("Failed to set mapping for %s: %s",
830 ipaddr
, strerror(errno
));
839 * Given a host identifier, a link-layer address and possible options,
840 * add/update the NDP mappings.
843 ndp_set_nce(char *host
, char *lladdr
, char *opts
[], int optlen
)
845 lif_nd_req_t nd_attrs
;
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
);
858 warnx("Unable to parse link-layer address \"%s\"", lladdr
);
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");
869 (void) memcpy(nd_attrs
.lnr_hdw_addr
, ea
, nd_attrs
.lnr_hdw_len
);
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
++) {
879 if (strcmp(opt
, "temp") == 0) {
881 } else if (strcmp(opt
, "any") == 0) {
883 } else if (strcmp(opt
, "router") == 0) {
885 } else if (strcmp(opt
, "proxy") == 0) {
886 warnx("NDP proxying is currently not supported");
889 warnx("Unrecognized option \"%s\"", opt
);
895 nd_attrs
.lnr_flags
|= NDF_STATIC
;
899 nd_attrs
.lnr_flags
|= NDF_ANYCAST_ON
;
901 nd_attrs
.lnr_flags
|= NDF_ANYCAST_OFF
;
905 nd_attrs
.lnr_flags
|= NDF_ISROUTER_OFF
;
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.
917 ndp_set_file(char *filename
)
919 char *line
= NULL
, *lasts
= NULL
, *curr
;
921 char *opts
[MAX_OPTS
];
922 int optlen
= 0, lineno
= 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
));
933 while (getline(&line
, &cap
, stream
) != -1) {
939 host
= strtok_r(line
, WORDSEPS
, &lasts
);
941 warnx("Line %d incomplete, skipping: "
942 "missing host identifier", lineno
);
943 failed_line
= B_TRUE
;
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
;
955 for (optlen
= 0; optlen
< MAX_OPTS
; optlen
++) {
956 curr
= strtok_r(NULL
, WORDSEPS
, &lasts
);
962 if (ndp_set_nce(host
, lladdr
, opts
, optlen
) != 0) {
963 failed_line
= B_TRUE
;
970 if (errno
!= 0 || ferror(stream
)) {
971 ndp_fatal("Error while reading from file %s: %s", filename
,
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
;
988 int c
, argsleft
, optlen
= 0, err
= 0;
990 enum ndp_action action
= NDP_A_DEFAULT
;
992 setprogname(basename(argv
[0]));
995 ndp_usage("No arguments given.");
998 while ((c
= getopt(argc
, argv
, ":naA:d:f:i:s:")) != -1) {
1001 ndp_noresolve
= B_TRUE
;
1007 if (action
!= NDP_A_DEFAULT
)
1008 ndp_badflag(action
);
1009 action
= NDP_A_SET_NCE
;
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",
1032 if (action
!= NDP_A_DEFAULT
)
1033 ndp_badflag(action
);
1034 action
= NDP_A_GET_ALL
;
1037 if (action
!= NDP_A_DEFAULT
)
1038 ndp_badflag(action
);
1039 action
= NDP_A_GET_FOREVER
;
1043 if (action
!= NDP_A_DEFAULT
)
1044 ndp_badflag(action
);
1045 action
= NDP_A_DELETE
;
1049 if (action
!= NDP_A_DEFAULT
)
1050 ndp_badflag(action
);
1051 action
= NDP_A_SET_FILE
;
1055 ndp_missingarg(optopt
);
1058 ndp_usage("Unrecognized flag \"-%c\"", optopt
);
1064 argsleft
= argc
- optind
;
1067 if (action
!= NDP_A_DEFAULT
&& argsleft
!= 0) {
1068 ndp_usage("Extra arguments leftover after parsing flags");
1074 if (argsleft
!= 1) {
1075 ndp_usage("Multiple arguments given without any flags");
1077 err
= ndp_host_enumerate(argv
[optind
], ndp_get
, NULL
);
1083 case NDP_A_GET_FOREVER
:
1085 period
= strtoll(flagarg
, &endptr
, 10);
1086 if ((period
== 0 && errno
!= 0) ||
1087 (endptr
[0] != '\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
);
1100 err
= ndp_host_enumerate(flagarg
, ndp_delete
, NULL
);
1103 err
= ndp_set_nce(flagarg
, lladdr
, opts
, optlen
);
1105 case NDP_A_SET_FILE
:
1106 err
= ndp_set_file(flagarg
);
1110 return (err
== 0 ? 0 : 1);