summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/Makefile10
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ndp.c1111
-rw-r--r--usr/src/man/man1m/Makefile1
-rw-r--r--usr/src/man/man1m/arp.1m9
-rw-r--r--usr/src/man/man1m/in.ndpd.1m17
-rw-r--r--usr/src/man/man1m/ndp.1m215
-rw-r--r--usr/src/man/man1m/netstat.1m2
-rw-r--r--usr/src/man/man7p/Makefile4
-rw-r--r--usr/src/man/man7p/arp.7p57
-rw-r--r--usr/src/man/man7p/if_tcp.7p6
-rw-r--r--usr/src/man/man7p/ndp.7p345
-rw-r--r--usr/src/man/man7p/route.7p8
-rw-r--r--usr/src/pkg/manifests/SUNWcs.man1m.inc1
-rw-r--r--usr/src/pkg/manifests/SUNWcs.mf1
-rw-r--r--usr/src/pkg/manifests/system-kernel.man7p.inc2
-rw-r--r--usr/src/uts/common/inet/ip/ip_ndp.c2
-rw-r--r--usr/src/uts/common/net/if.h4
17 files changed, 1719 insertions, 76 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/Makefile
index 3c8672d154..972b9e497f 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/Makefile
+++ b/usr/src/cmd/cmd-inet/usr.sbin/Makefile
@@ -29,7 +29,7 @@ SYNCPROG= syncinit syncloop syncstat
PROG= 6to4relay arp if_mpadm \
in.comsat in.fingerd in.rarpd in.rexecd in.rlogind \
in.rshd in.rwhod in.telnetd in.tftpd ipaddrsel \
- ndd $(SYNCPROG) wanbootutil
+ ndd ndp $(SYNCPROG) wanbootutil
MANIFEST= rarp.xml telnet.xml comsat.xml finger.xml \
login.xml shell.xml rexec.xml socket-filter-kssl.xml
@@ -43,10 +43,10 @@ AUDITPROG= in.rexecd in.rlogind in.rshd in.telnetd
PAMPROG= in.rexecd in.rlogind in.rshd in.telnetd
SOCKETPROG= 6to4relay arp hostconfig if_mpadm in.comsat \
in.fingerd in.rarpd in.rexecd in.rlogind in.rshd \
- in.rwhod in.telnetd in.tftpd ipaddrsel route
+ in.rwhod in.telnetd in.tftpd ipaddrsel ndp route
NSLPROG= 6to4relay arp hostconfig in.comsat in.rarpd \
in.rexecd in.rlogind in.rshd in.rwhod in.telnetd \
- in.tftpd ipaddrsel route
+ in.tftpd ipaddrsel ndp route
CMDPROG= in.telnetd
K5PROGS= in.telnetd in.rlogind in.rshd
TSNETPROG= route
@@ -74,7 +74,7 @@ MSGSUBDIRS= bootconfchk ifconfig ilbadm in.routed in.talkd \
# Eventually this hack should go away, and all in PROG should be
# lint-clean.
LINTCLEAN= 6to4relay arp in.rlogind in.rshd in.telnetd in.tftpd \
- ipaddrsel route \
+ ipaddrsel ndp route \
in.rarpd if_mpadm $(SYNCPROG)
# Likewise, as subdirs get lint-clean, add them here. Once
# they're all clean, replace the dependency of the lint target
@@ -269,5 +269,7 @@ lint: $(LINTSUBDIRS)
$(LINT.c) syncstat.c $(LDLIBS) -ldlpi
$(LINT.c) -erroff=E_NAME_USED_NOT_DEF2 in.rarpd.c $(LDLIBS) \
-lsocket -lnsl
+ $(LINT.c) ndp.c $(LDLIBS) \
+ -lsocket -lnsl
$(LINT.c) in.tftpd.c ../usr.bin/tftp/tftpsubs.c $(LDLIBS) \
-lsocket -lnsl
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ndp.c b/usr/src/cmd/cmd-inet/usr.sbin/ndp.c
new file mode 100644
index 0000000000..23b940c686
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ndp.c
@@ -0,0 +1,1111 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * ndp - display and manipulate Neighbor Cache Entries from NDP
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <time.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <wait.h>
+#include <sys/mac.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <netdb.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <inet/ip.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+typedef struct sockaddr_in6 sin6_t;
+
+#define BUF_SIZE 2048
+typedef struct rtmsg_pkt {
+ struct rt_msghdr m_rtm;
+ char m_space[BUF_SIZE];
+} rtmsg_pkt_t;
+
+enum ndp_action {
+ NDP_A_DEFAULT,
+ NDP_A_GET, /* Show a single NDP entry */
+ NDP_A_GET_ALL, /* Show NDP entries */
+ NDP_A_GET_FOREVER, /* Repeatedly show entries */
+ NDP_A_DELETE, /* Delete an NDP entry */
+ NDP_A_SET_NCE, /* Set NDP entry */
+ NDP_A_SET_FILE /* Read in & set NDP entries */
+};
+
+typedef int (ndp_addr_f)(int, struct lifreq *, void *);
+typedef void (ndp_void_f)(void);
+
+static void ndp_usage(const char *, ...);
+static void ndp_fatal(const char *, ...);
+static void ndp_badflag(enum ndp_action);
+static void ndp_missingarg(char);
+
+static void ndp_run_in_child(ndp_void_f *);
+static void ndp_do_run(void);
+static void ndp_setup_handler(sigset_t *);
+static void ndp_start_timer(time_t period);
+static void ndp_run_periodically(time_t, ndp_void_f *);
+
+static int ndp_salen(const struct sockaddr *sa);
+static int ndp_extract_sockaddrs(struct rt_msghdr *, struct sockaddr **,
+ struct sockaddr **, struct sockaddr **, struct sockaddr **,
+ struct sockaddr_dl **);
+static int ndp_rtmsg_get(int, rtmsg_pkt_t *, struct sockaddr *);
+static int ndp_find_interface(int, struct sockaddr *, char *, int);
+
+static int ndp_initialize_lifreq(int, struct lifreq *, struct sockaddr *);
+static int ndp_host_enumerate(char *, ndp_addr_f *, void *);
+
+static int ndp_display(struct lifreq *);
+static int ndp_display_missing(struct lifreq *);
+static void ndp_lifr2ip(struct lifreq *, char *, int);
+
+static int ndp_get(int, struct lifreq *, void *);
+static void ndp_get_all(void);
+static int ndp_delete(int, struct lifreq *, void *);
+static int ndp_set(int, struct lifreq *, void *);
+static int ndp_set_nce(char *, char *, char *[], int);
+static int ndp_set_file(char *);
+
+static char *ndp_iface = NULL;
+static char *netstat_path = "/usr/bin/netstat";
+static pid_t ndp_pid;
+static boolean_t ndp_noresolve = B_FALSE; /* Don't lookup addresses */
+static boolean_t ndp_run = B_TRUE;
+
+#define MAX_ATTEMPTS 5
+#define MAX_OPTS 5
+#define WORDSEPS " \t\r\n"
+
+/*
+ * Macros borrowed from route(1M) for working with PF_ROUTE messages
+ */
+#define RT_ADVANCE(x, n) ((x) += ndp_salen(n))
+#define RT_NEXTADDR(cp, w, u) \
+ l = ndp_salen(u); \
+ (void) memmove(cp, u, l); \
+ cp += l;
+
+/*
+ * Print an error to stderr and then exit non-zero.
+ */
+static void
+ndp_fatal(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vwarnx(format, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * Print out the command usage to stderr, along with any reason why it's being
+ * printed, and then exit non-zero.
+ */
+static void
+ndp_usage(const char *reason, ...)
+{
+ va_list ap;
+ const char *ndp_progname = getprogname();
+
+ if (reason != NULL) {
+ va_start(ap, reason);
+ (void) fprintf(stderr, "%s: ", ndp_progname);
+ (void) vfprintf(stderr, reason, ap);
+ (void) fprintf(stderr, "\n");
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr,
+ "Usage: %s [-n] [-i iface] hostname\n"
+ " %s [-n] [-i iface] -s nodeaddr etheraddr [temp] [proxy]\n"
+ " %s [-n] [-i iface] -d nodeaddr\n"
+ " %s [-n] [-i iface] -f filename\n"
+ " %s [-n] -a\n"
+ " %s [-n] -A period\n",
+ ndp_progname, ndp_progname, ndp_progname,
+ ndp_progname, ndp_progname, ndp_progname);
+ exit(EXIT_FAILURE);
+}
+
+static void
+ndp_badflag(enum ndp_action action)
+{
+ switch (action) {
+ case NDP_A_DEFAULT:
+ case NDP_A_GET:
+ ndp_usage("Already going to print an entry, "
+ "but extra -%c given", optopt);
+ break;
+ case NDP_A_GET_ALL:
+ ndp_usage("Already going to print all entries (-a), "
+ "but extra -%c given", optopt);
+ break;
+ case NDP_A_GET_FOREVER:
+ ndp_usage("Already going to repeatedly print all entries (-A), "
+ "but extra -%c given", optopt);
+ break;
+ case NDP_A_DELETE:
+ ndp_usage("Already going to delete an entry (-d), "
+ "but extra -%c given", optopt);
+ break;
+ case NDP_A_SET_NCE:
+ ndp_usage("Already going to set an entry (-s), "
+ "but extra -%c given", optopt);
+ break;
+ case NDP_A_SET_FILE:
+ ndp_usage("Already going to set from file (-f), "
+ "but extra -%c given", optopt);
+ break;
+ }
+}
+
+static void
+ndp_missingarg(char flag)
+{
+ switch (flag) {
+ case 'A':
+ ndp_usage("Missing time period after -%c", flag);
+ break;
+ case 'd':
+ ndp_usage("Missing node name after -%c", flag);
+ break;
+ case 'f':
+ ndp_usage("Missing filename after -%c", flag);
+ break;
+ case 's':
+ ndp_usage("Missing node name after -%c", flag);
+ break;
+ case 'i':
+ ndp_usage("Missing interface name after -%c", flag);
+ break;
+ default:
+ ndp_usage("Missing option argument after -%c", flag);
+ break;
+ }
+}
+
+/*
+ * Run a function that's going to exec in a child process, and don't return
+ * until it exits.
+ */
+static void
+ndp_run_in_child(ndp_void_f *func)
+{
+ pid_t child_pid;
+ int childstat = 0, status = 0;
+
+ child_pid = fork();
+ if (child_pid == (pid_t)-1) {
+ ndp_fatal("Unable to fork: %s", strerror(errno));
+ } else if (child_pid == (pid_t)0) {
+ func();
+ exit(EXIT_FAILURE);
+ }
+
+ while (waitpid(child_pid, &childstat, 0) == -1) {
+ if (errno == EINTR)
+ continue;
+
+ ndp_fatal("Failed to wait on child: %s", strerror(errno));
+ }
+
+ status = WEXITSTATUS(childstat);
+ if (status != 0) {
+ ndp_fatal("Child process exited with %d", status);
+ }
+}
+
+/*
+ * SIGALRM handler to schedule a run.
+ */
+static void
+ndp_do_run(void)
+{
+ ndp_run = B_TRUE;
+}
+
+
+/*
+ * Prepare signal masks, and install the SIGALRM handler. Return old signal
+ * masks through the first argument.
+ */
+static void
+ndp_setup_handler(sigset_t *oset)
+{
+ struct sigaction sa;
+
+ /*
+ * Mask off SIGALRM so we only trigger the handler when we're ready
+ * using sigsuspend(3C), in case the child process takes longer to
+ * run than the alarm interval.
+ */
+ if (sigprocmask(0, NULL, oset) != 0) {
+ ndp_fatal("Unable to set signal mask: %s", strerror(errno));
+ }
+
+ if (sighold(SIGALRM) != 0) {
+ ndp_fatal("Unable to add SIGALRM to signal mask: %s",
+ strerror(errno));
+ }
+
+ sa.sa_flags = 0;
+ sa.sa_handler = ndp_do_run;
+
+ if (sigemptyset(&sa.sa_mask) != 0) {
+ ndp_fatal("Unable to prepare empty signal set: %s",
+ strerror(errno));
+ }
+
+ if (sigaction(SIGALRM, &sa, NULL) != 0) {
+ ndp_fatal("Unable to install timer handler: %s",
+ strerror(errno));
+ }
+}
+
+/*
+ * Start the printing timer.
+ */
+static void
+ndp_start_timer(time_t period)
+{
+ timer_t timer;
+ struct itimerspec interval;
+ interval.it_value.tv_sec = interval.it_interval.tv_sec = period;
+ interval.it_value.tv_nsec = interval.it_interval.tv_nsec = 0;
+
+ if (timer_create(CLOCK_REALTIME, NULL, &timer) != 0) {
+ ndp_fatal("Unable to create timer: %s", strerror(errno));
+ }
+
+ if (timer_settime(timer, 0, &interval, NULL) != 0) {
+ ndp_fatal("Unable to set time on timer: %s", strerror(errno));
+ }
+}
+
+
+/*
+ * Run a given function forever periodically in a child process.
+ */
+static void
+ndp_run_periodically(time_t period, ndp_void_f *func)
+{
+ sigset_t oset;
+
+ ndp_setup_handler(&oset);
+ ndp_start_timer(period);
+
+ do {
+ if (ndp_run) {
+ ndp_run = B_FALSE;
+ ndp_run_in_child(func);
+ }
+ (void) sigsuspend(&oset);
+ } while (errno == EINTR);
+
+ /*
+ * Only an EFAULT should get us here. Abort so we get a core dump.
+ */
+ warnx("Failure while waiting on timer: %s", strerror(errno));
+ abort();
+}
+
+/*
+ * Given an address, return its size.
+ */
+static int
+ndp_salen(const struct sockaddr *sa)
+{
+ switch (sa->sa_family) {
+ case AF_INET:
+ return (sizeof (struct sockaddr_in));
+ case AF_LINK:
+ return (sizeof (struct sockaddr_dl));
+ case AF_INET6:
+ return (sizeof (struct sockaddr_in6));
+ default:
+ warnx("Unrecognized sockaddr with address family %d!",
+ sa->sa_family);
+ abort();
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Extract all socket addresses from a routing message, and return them
+ * through the pointers given as arguments to ndp_extract_sockaddrs. None
+ * of the pointers should be null.
+ */
+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)
+{
+ struct sockaddr *sa;
+ char *cp;
+ int i;
+
+ if (rtm->rtm_version != RTM_VERSION) {
+ warnx("Routing message version %d not understood",
+ rtm->rtm_version);
+ return (-1);
+ }
+
+ if (rtm->rtm_errno != 0) {
+ warnx("Routing message couldn't be processed: %s",
+ strerror(rtm->rtm_errno));
+ return (-1);
+ }
+
+ cp = ((char *)(rtm + 1));
+ if (rtm->rtm_addrs != 0) {
+ for (i = 1; i != 0; i <<= 1) {
+ if ((i & rtm->rtm_addrs) == 0)
+ continue;
+
+ /*LINTED*/
+ sa = (struct sockaddr *)cp;
+ switch (i) {
+ case RTA_DST:
+ *dst = sa;
+ break;
+ case RTA_GATEWAY:
+ *gate = sa;
+ break;
+ case RTA_NETMASK:
+ *mask = sa;
+ break;
+ case RTA_IFP:
+ if (sa->sa_family == AF_LINK &&
+ ((struct sockaddr_dl *)sa)->sdl_nlen != 0)
+ *ifp = (struct sockaddr_dl *)sa;
+ break;
+ case RTA_SRC:
+ *src = sa;
+ break;
+ }
+ RT_ADVANCE(cp, sa);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Given an IPv6 address, use routing information to look up
+ * the destination and interface it would pass through.
+ */
+static int
+ndp_rtmsg_get(int fd, rtmsg_pkt_t *msg, struct sockaddr *sin6p)
+{
+ static int seq = 0;
+ struct sockaddr_dl sdl;
+ int mlen, l;
+ char ipaddr[INET6_ADDRSTRLEN];
+ char *cp = msg->m_space;
+ struct rt_msghdr *m_rtm = &msg->m_rtm;
+
+ bzero(msg, sizeof (rtmsg_pkt_t));
+ bzero(&sdl, sizeof (struct sockaddr_dl));
+
+ m_rtm->rtm_type = RTM_GET;
+ m_rtm->rtm_version = RTM_VERSION;
+ m_rtm->rtm_seq = ++seq;
+ m_rtm->rtm_addrs = RTA_DST | RTA_IFP;
+ m_rtm->rtm_msglen = sizeof (rtmsg_pkt_t);
+
+ /* Place the address we're looking up after the header */
+ RT_NEXTADDR(cp, RTA_DST, sin6p);
+
+ /* Load an empty link-level address, so we get an interface back */
+ sdl.sdl_family = AF_LINK;
+ RT_NEXTADDR(cp, RTA_IFP, (struct sockaddr *)&sdl);
+
+ m_rtm->rtm_msglen = cp - (char *)msg;
+
+ if ((mlen = write(fd, (char *)msg, m_rtm->rtm_msglen)) < 0) {
+ if (errno == ESRCH) {
+ /*LINTED*/
+ if (inet_ntop(AF_INET6, &((sin6_t *)sin6p)->sin6_addr,
+ ipaddr, sizeof (ipaddr)) == NULL) {
+ (void) snprintf(ipaddr, sizeof (ipaddr),
+ "(failed to format IP)");
+ };
+ warnx("An appropriate interface for the address %s "
+ "is not in the routing table; use -i to force an "
+ "interface", ipaddr);
+ return (-1);
+ } else {
+ warnx("Failed to send routing message: %s",
+ strerror(errno));
+ return (-1);
+ }
+ } else if (mlen < (int)m_rtm->rtm_msglen) {
+ warnx("Failed to write all bytes to routing socket");
+ return (-1);
+ }
+
+ /*
+ * Keep reading routing messages until we find the response to the one
+ * we just sent. Note that we depend on the sequence number being unique
+ * to the running program.
+ */
+ do {
+ mlen = read(fd, (char *)msg, sizeof (rtmsg_pkt_t));
+ } while (mlen > 0 &&
+ (m_rtm->rtm_seq != seq || m_rtm->rtm_pid != ndp_pid));
+ if (mlen < 0) {
+ warnx("Failed to read from routing socket: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Find the interface that the IPv6 address would be routed through, and store
+ * the name of the interface in the buffer passed in.
+ */
+static int
+ndp_find_interface(int fd, struct sockaddr *sin6p, char *buf, int buflen)
+{
+ struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
+ struct sockaddr_dl *ifp = NULL;
+ rtmsg_pkt_t msg;
+
+ if (ndp_rtmsg_get(fd, &msg, sin6p) != 0) {
+ return (-1);
+ }
+
+ if (ndp_extract_sockaddrs(&msg.m_rtm, &dst, &gate,
+ &mask, &src, &ifp) != 0) {
+ return (-1);
+ }
+
+ if (ifp == NULL) {
+ warnx("Unable to find appropriate interface for address");
+ return (-1);
+ } else {
+ if (ifp->sdl_nlen >= buflen) {
+ warnx("The interface name \"%.*s\" is too big for the "
+ "available buffer", ifp->sdl_nlen, ifp->sdl_data);
+ return (-1);
+ } else {
+ (void) snprintf(buf, buflen, "%.*s", ifp->sdl_nlen,
+ ifp->sdl_data);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Zero out a lifreq struct for a SIOCLIF*ND ioctl, set the address, and fetch
+ * the appropriate interface using the given routing socket.
+ */
+static int
+ndp_initialize_lifreq(int route, struct lifreq *lifrp, struct sockaddr *sap)
+{
+ struct sockaddr_storage *lnr_addr;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct sockaddr_in6 *sin6p = (sin6_t *)sap;
+ char *lifr_name = lifrp->lifr_name;
+
+ bzero(lifrp, sizeof (struct lifreq));
+ lnr_addr = &lifrp->lifr_nd.lnr_addr;
+
+ if (ndp_iface != NULL) {
+ (void) strlcpy(lifr_name, ndp_iface, LIFNAMSIZ);
+ } else if (sin6p->sin6_scope_id != 0) {
+ int zone_id = sin6p->sin6_scope_id;
+ if (if_indextoname(zone_id, lifr_name) == NULL) {
+ warnx("Invalid zone identifier: %d", zone_id);
+ return (-1);
+ }
+ } else if (IN6_IS_ADDR_LINKSCOPE(&sin6p->sin6_addr)) {
+ warnx("Link-scope addresses should specify an interface with "
+ "a zone ID, or with -i.");
+ return (-1);
+ } else {
+ if (ndp_find_interface(route, sap, lifr_name, LIFNAMSIZ) != 0)
+ return (-1);
+ }
+
+ (void) memcpy(lnr_addr, sap, sizeof (struct sockaddr_storage));
+
+ return (0);
+}
+
+/*
+ * Take a host identifier, find the corresponding IPv6 addresses and then pass
+ * them to the specified function, along with any desired data.
+ */
+static int
+ndp_host_enumerate(char *host, ndp_addr_f *addr_func, void *data)
+{
+ struct lifreq lifr;
+ struct addrinfo hints, *serverinfo, *p;
+ int err, attempts = 0;
+ int inet6, route;
+
+ bzero(&hints, sizeof (struct addrinfo));
+ hints.ai_family = AF_INET6;
+ hints.ai_protocol = IPPROTO_IPV6;
+
+ while (attempts < MAX_ATTEMPTS) {
+ err = getaddrinfo(host, NULL, &hints, &serverinfo);
+
+ if (err == 0) {
+ break;
+ } else if (err == EAI_AGAIN) {
+ attempts++;
+ } else {
+ warnx("Unable to lookup %s: %s", host,
+ gai_strerror(err));
+ return (-1);
+ }
+ }
+
+ if (attempts == MAX_ATTEMPTS) {
+ warnx("Failed multiple times to lookup %s", host);
+ return (-1);
+ }
+
+ inet6 = socket(PF_INET6, SOCK_DGRAM, 0);
+ if (inet6 < 0) {
+ warnx("Failed to open IPv6 socket: %s", strerror(errno));
+ err = -1;
+ }
+
+ route = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (route < 0) {
+ warnx("Failed to open routing socket: %s", strerror(errno));
+ err = -1;
+ }
+
+ if (err == 0) {
+ for (p = serverinfo; p != NULL; p = p->ai_next) {
+ if (ndp_initialize_lifreq(route, &lifr, p->ai_addr)
+ != 0) {
+ err = -1;
+ continue;
+ }
+
+ if (addr_func(inet6, &lifr, data) != 0) {
+ err = -1;
+ continue;
+ }
+ }
+ }
+
+ if (close(route) != 0) {
+ warnx("Failed to close routing socket: %s", strerror(errno));
+ err = -1;
+ }
+
+ if (close(inet6) != 0) {
+ warnx("Failed to close IPv6 socket: %s", strerror(errno));
+ err = -1;
+ }
+
+ /* Clean up linked list */
+ freeaddrinfo(serverinfo);
+
+ return (err);
+}
+
+static int
+ndp_display(struct lifreq *lifrp)
+{
+ struct sockaddr_in6 *lnr_addr;
+ char ipaddr[INET6_ADDRSTRLEN];
+ char *lladdr = NULL;
+ char hostname[NI_MAXHOST];
+ int flags, gni_flags;
+
+ lnr_addr = (struct sockaddr_in6 *)&lifrp->lifr_nd.lnr_addr;
+ flags = lifrp->lifr_nd.lnr_flags;
+
+ if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr,
+ sizeof (ipaddr)) == NULL) {
+ warnx("Couldn't convert IPv6 address to string: %s",
+ strerror(errno));
+ return (-1);
+ };
+
+ if ((lladdr = _link_ntoa((uchar_t *)lifrp->lifr_nd.lnr_hdw_addr,
+ NULL, lifrp->lifr_nd.lnr_hdw_len, IFT_ETHER)) == NULL) {
+ warnx("Couldn't convert link-layer address to string: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ gni_flags = ndp_noresolve ? NI_NUMERICHOST : 0;
+
+ if (getnameinfo((struct sockaddr *)lnr_addr, sizeof (sin6_t), hostname,
+ sizeof (hostname), NULL, 0, gni_flags) != 0) {
+ warnx("Unable to lookup hostname for %s", ipaddr);
+ free(lladdr);
+ return (-1);
+ }
+
+ (void) printf("%s (%s) at %s", ipaddr, hostname, lladdr);
+
+ if (flags & NDF_ISROUTER_ON) {
+ (void) printf(" router");
+ }
+
+ if (flags & NDF_ANYCAST_ON) {
+ (void) printf(" any");
+ }
+
+ if (!(flags & NDF_STATIC)) {
+ (void) printf(" temp");
+ }
+
+ if (flags & NDF_PROXY_ON) {
+ (void) printf(" proxy");
+ }
+
+ (void) printf("\n");
+
+ free(lladdr);
+ return (0);
+}
+
+static int
+ndp_display_missing(struct lifreq *lifrp)
+{
+ struct sockaddr_in6 *lnr_addr;
+ char ipaddr[INET6_ADDRSTRLEN];
+ char hostname[NI_MAXHOST];
+ int flags = ndp_noresolve ? NI_NUMERICHOST : 0;
+ lnr_addr = (struct sockaddr_in6 *)&lifrp->lifr_nd.lnr_addr;
+
+ if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr,
+ sizeof (ipaddr)) == NULL) {
+ warnx("Couldn't convert IPv6 address to string: %s",
+ strerror(errno));
+ return (-1);
+ };
+
+ if (getnameinfo((struct sockaddr *)lnr_addr, sizeof (sin6_t), hostname,
+ sizeof (hostname), NULL, 0, flags) != 0) {
+ warnx("Unable to lookup hostname for %s", ipaddr);
+ return (-1);
+ }
+
+ (void) printf("%s (%s) -- no entry\n", ipaddr, hostname);
+ return (0);
+}
+
+static void
+ndp_lifr2ip(struct lifreq *lifrp, char *ipaddr, int buflen)
+{
+ sin6_t *lnr_addr = (sin6_t *)&lifrp->lifr_nd.lnr_addr;
+ if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr,
+ buflen) == NULL) {
+ (void) snprintf(ipaddr, buflen, "(failed to format IP)");
+ };
+}
+
+/*
+ * Perform a SIOCLIFGETND and print out information about it
+ */
+/*ARGSUSED*/
+static int
+ndp_get(int fd, struct lifreq *lifrp, void *unused)
+{
+ char ipaddr[INET6_ADDRSTRLEN];
+ if (ioctl(fd, SIOCLIFGETND, lifrp) < 0) {
+ if (errno == ESRCH) {
+ return (ndp_display_missing(lifrp));
+ } else {
+ ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr));
+ warnx("Couldn't lookup %s: %s",
+ ipaddr, strerror(errno));
+ return (-1);
+ }
+ }
+
+ return (ndp_display(lifrp));
+}
+
+/*
+ * Print out all NDP entries
+ */
+static void
+ndp_get_all(void)
+{
+ (void) execl(netstat_path, "netstat",
+ (ndp_noresolve ? "-np" : "-p"),
+ "-f", "inet6", (char *)0);
+ ndp_fatal("Coudn't exec %s: %s", netstat_path, strerror(errno));
+}
+
+/*
+ * Perform a SIOCLIFDELND ioctl
+ */
+/*ARGSUSED*/
+static int
+ndp_delete(int fd, struct lifreq *lifrp, void *unused)
+{
+ char ipaddr[INET6_ADDRSTRLEN];
+
+ if (ioctl(fd, SIOCLIFDELND, lifrp) < 0) {
+ ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr));
+ if (errno == ESRCH) {
+ warnx("No entry for %s", ipaddr);
+ return (-1);
+ } else if (errno == EPERM) {
+ warnx("Permission denied, "
+ "could not delete entry for %s", ipaddr);
+ return (-1);
+ } else {
+ warnx("Couldn't delete mapping for %s: %s",
+ ipaddr, strerror(errno));
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Perform a SIOCLIFSETND ioctl using properties from the example structure.
+ */
+static int
+ndp_set(int fd, struct lifreq *lifrp, void *data)
+{
+ char ipaddr[INET6_ADDRSTRLEN];
+ const lif_nd_req_t *nd_attrs = data;
+
+ (void) memcpy(lifrp->lifr_nd.lnr_hdw_addr, nd_attrs->lnr_hdw_addr,
+ ND_MAX_HDW_LEN);
+ lifrp->lifr_nd.lnr_hdw_len = nd_attrs->lnr_hdw_len;
+ lifrp->lifr_nd.lnr_flags = nd_attrs->lnr_flags;
+
+ lifrp->lifr_nd.lnr_state_create = nd_attrs->lnr_state_create;
+ lifrp->lifr_nd.lnr_state_same_lla = nd_attrs->lnr_state_same_lla;
+ lifrp->lifr_nd.lnr_state_diff_lla = nd_attrs->lnr_state_diff_lla;
+
+ if (ioctl(fd, SIOCLIFSETND, lifrp) < 0) {
+ ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr));
+ if (errno == EPERM) {
+ warnx("Permission denied, "
+ "could not set entry for %s", ipaddr);
+ return (-1);
+ } else {
+ warnx("Failed to set mapping for %s: %s",
+ ipaddr, strerror(errno));
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Given a host identifier, a link-layer address and possible options,
+ * add/update the NDP mappings.
+ */
+static int
+ndp_set_nce(char *host, char *lladdr, char *opts[], int optlen)
+{
+ lif_nd_req_t nd_attrs;
+ uchar_t *ea;
+ char *opt;
+ int i;
+ boolean_t temp = B_FALSE;
+ boolean_t any = B_FALSE;
+ boolean_t router = B_FALSE;
+
+ bzero(&nd_attrs, sizeof (lif_nd_req_t));
+
+ ea = _link_aton(lladdr, &nd_attrs.lnr_hdw_len);
+
+ if (ea == NULL) {
+ warnx("Unable to parse link-layer address \"%s\"", lladdr);
+ return (-1);
+ }
+
+ if (nd_attrs.lnr_hdw_len > sizeof (nd_attrs.lnr_hdw_addr)) {
+ warnx("The size of the link-layer address is "
+ "too large to set\n");
+ free(ea);
+ return (-1);
+ }
+
+ (void) memcpy(nd_attrs.lnr_hdw_addr, ea, nd_attrs.lnr_hdw_len);
+
+ free(ea);
+
+ nd_attrs.lnr_state_create = ND_REACHABLE;
+ nd_attrs.lnr_state_same_lla = ND_UNCHANGED;
+ nd_attrs.lnr_state_diff_lla = ND_STALE;
+
+ for (i = 0; i < optlen; i++) {
+ opt = opts[i];
+ if (strcmp(opt, "temp") == 0) {
+ temp = B_TRUE;
+ } else if (strcmp(opt, "any") == 0) {
+ any = B_TRUE;
+ } else if (strcmp(opt, "router") == 0) {
+ router = B_TRUE;
+ } else if (strcmp(opt, "proxy") == 0) {
+ warnx("NDP proxying is currently not supported");
+ return (-1);
+ } else {
+ warnx("Unrecognized option \"%s\"", opt);
+ return (-1);
+ }
+ }
+
+ if (!temp) {
+ nd_attrs.lnr_flags |= NDF_STATIC;
+ }
+
+ if (any) {
+ nd_attrs.lnr_flags |= NDF_ANYCAST_ON;
+ } else {
+ nd_attrs.lnr_flags |= NDF_ANYCAST_OFF;
+ }
+
+ if (router) {
+ nd_attrs.lnr_flags |= NDF_ISROUTER_OFF;
+ } else {
+ nd_attrs.lnr_flags |= NDF_ISROUTER_OFF;
+ }
+
+ return (ndp_host_enumerate(host, ndp_set, &nd_attrs));
+}
+
+/*
+ * Read in a file and set the mappings from each line.
+ */
+static int
+ndp_set_file(char *filename)
+{
+ char *line = NULL, *lasts = NULL, *curr;
+ char *host, *lladdr;
+ char *opts[MAX_OPTS];
+ int optlen = 0, lineno = 0;
+ size_t cap = 0;
+ boolean_t failed_line = B_FALSE;
+ FILE *stream = fopen(filename, "r");
+
+ if (stream == NULL) {
+ ndp_fatal("Error while opening file %s: %s",
+ filename, strerror(errno));
+ }
+
+ errno = 0;
+ while (getline(&line, &cap, stream) != -1) {
+ lineno++;
+
+ if (line[0] == '#')
+ continue;
+
+ host = strtok_r(line, WORDSEPS, &lasts);
+ if (host == NULL) {
+ warnx("Line %d incomplete, skipping: "
+ "missing host identifier", lineno);
+ failed_line = B_TRUE;
+ continue;
+ }
+
+ lladdr = strtok_r(NULL, WORDSEPS, &lasts);
+ if (lladdr == NULL) {
+ warnx("Line %d incomplete, skipping: "
+ "missing link-layer address", lineno);
+ failed_line = B_TRUE;
+ continue;
+ }
+
+ for (optlen = 0; optlen < MAX_OPTS; optlen++) {
+ curr = strtok_r(NULL, WORDSEPS, &lasts);
+ if (curr == NULL)
+ break;
+ opts[optlen] = curr;
+ }
+
+ if (ndp_set_nce(host, lladdr, opts, optlen) != 0) {
+ failed_line = B_TRUE;
+ continue;
+ }
+ }
+
+ free(line);
+
+ if (errno != 0 || ferror(stream)) {
+ ndp_fatal("Error while reading from file %s: %s", filename,
+ strerror(errno));
+ }
+
+ if (fclose(stream) != 0) {
+ ndp_fatal("Error close file %s: %s", filename, strerror(errno));
+ }
+
+ return (failed_line ? -1 : 0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *flagarg = NULL, *lladdr = NULL;
+ char **opts;
+ char *endptr;
+ int c, argsleft, optlen = 0, err = 0;
+ long long period;
+ enum ndp_action action = NDP_A_DEFAULT;
+
+ setprogname(basename(argv[0]));
+
+ if (argc < 2) {
+ ndp_usage("No arguments given.");
+ }
+
+ while ((c = getopt(argc, argv, ":naA:d:f:i:s:")) != -1) {
+ switch (c) {
+ case 'n':
+ ndp_noresolve = B_TRUE;
+ break;
+ case 'i':
+ ndp_iface = optarg;
+ break;
+ case 's':
+ if (action != NDP_A_DEFAULT)
+ ndp_badflag(action);
+ action = NDP_A_SET_NCE;
+ flagarg = optarg;
+
+ if ((argc - optind) < 1) {
+ ndp_usage("Missing link-layer address after "
+ "the node address, \"%s\"", flagarg);
+ }
+ lladdr = argv[optind++];
+
+ /*
+ * Grab any following keywords up to the next flag
+ */
+ opts = argv + optind;
+ while ((argc - optind) > 0) {
+ if (argv[optind][0] == '-')
+ ndp_usage("Encountered \"%s\" after "
+ "flag parsing is done",
+ argv[optind]);
+ optind++;
+ optlen++;
+ }
+ break;
+ case 'a':
+ if (action != NDP_A_DEFAULT)
+ ndp_badflag(action);
+ action = NDP_A_GET_ALL;
+ break;
+ case 'A':
+ if (action != NDP_A_DEFAULT)
+ ndp_badflag(action);
+ action = NDP_A_GET_FOREVER;
+ flagarg = optarg;
+ break;
+ case 'd':
+ if (action != NDP_A_DEFAULT)
+ ndp_badflag(action);
+ action = NDP_A_DELETE;
+ flagarg = optarg;
+ break;
+ case 'f':
+ if (action != NDP_A_DEFAULT)
+ ndp_badflag(action);
+ action = NDP_A_SET_FILE;
+ flagarg = optarg;
+ break;
+ case ':':
+ ndp_missingarg(optopt);
+ break;
+ case '?':
+ ndp_usage("Unrecognized flag \"-%c\"", optopt);
+ default:
+ ndp_usage(NULL);
+ }
+ }
+
+ argsleft = argc - optind;
+ ndp_pid = getpid();
+
+ if (action != NDP_A_DEFAULT && argsleft != 0) {
+ ndp_usage("Extra arguments leftover after parsing flags");
+ }
+
+ switch (action) {
+ case NDP_A_DEFAULT:
+ case NDP_A_GET:
+ if (argsleft != 1) {
+ ndp_usage("Multiple arguments given without any flags");
+ }
+ err = ndp_host_enumerate(argv[optind], ndp_get, NULL);
+ break;
+ case NDP_A_GET_ALL:
+ ndp_get_all();
+ /*NOTREACHED*/
+ break;
+ case NDP_A_GET_FOREVER:
+ errno = 0;
+ period = strtoll(flagarg, &endptr, 10);
+ if ((period == 0 && errno != 0) ||
+ (endptr[0] != '\0') ||
+ (period < 0)) {
+ ndp_usage("Given period should be a positive integer,"
+ " not \"%s\"", flagarg);
+ }
+ if (period > 86400) {
+ ndp_usage("Given period should be shorter than a day;"
+ " given \"%s\" seconds", flagarg);
+ }
+ ndp_run_periodically(period, ndp_get_all);
+ /*NOTREACHED*/
+ break;
+ case NDP_A_DELETE:
+ err = ndp_host_enumerate(flagarg, ndp_delete, NULL);
+ break;
+ case NDP_A_SET_NCE:
+ err = ndp_set_nce(flagarg, lladdr, opts, optlen);
+ break;
+ case NDP_A_SET_FILE:
+ err = ndp_set_file(flagarg);
+ break;
+ }
+
+ return (err == 0 ? 0 : 1);
+}
diff --git a/usr/src/man/man1m/Makefile b/usr/src/man/man1m/Makefile
index 8dbf7331b1..c5f7db2cbc 100644
--- a/usr/src/man/man1m/Makefile
+++ b/usr/src/man/man1m/Makefile
@@ -335,6 +335,7 @@ _MANFILES= 6to4relay.1m \
ncheck.1m \
ncheck_ufs.1m \
ndd.1m \
+ ndp.1m \
ndmpadm.1m \
ndmpd.1m \
ndmpstat.1m \
diff --git a/usr/src/man/man1m/arp.1m b/usr/src/man/man1m/arp.1m
index 11fa0164f0..ca4e20bdc1 100644
--- a/usr/src/man/man1m/arp.1m
+++ b/usr/src/man/man1m/arp.1m
@@ -4,7 +4,7 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH ARP 1M "Jan 5, 2009"
+.TH ARP 1M "Sep 02, 2015"
.SH NAME
arp \- address resolution display and control
.SH SYNOPSIS
@@ -35,7 +35,6 @@ arp \- address resolution display and control
.fi
.SH DESCRIPTION
-.sp
.LP
The \fBarp\fR program displays and modifies the Internet-to-MAC address
translation tables used by the address resolution protocol (see \fBarp\fR(7P)).
@@ -48,9 +47,8 @@ dot notation.
.LP
Options that modify the ARP translation tables (\fB-d\fR, \fB-f\fR, and
\fB-s\fR) can be used only when the invoked command is granted the
-\fBsys_net_config\fR privilege. See \fBprivileges\fR(5).
+\fBPRIV_SYS_NET_CONFIG\fR privilege. See \fBprivileges\fR(5).
.SH OPTIONS
-.sp
.ne 2
.na
\fB\fB-a\fR\fR
@@ -244,7 +242,6 @@ kernel and thus cannot be changed.
.RE
.SH SEE ALSO
-.sp
.LP
-\fBifconfig\fR(1M), \fBnetstat\fR(1M), \fBattributes\fR(5),
+\fBifconfig\fR(1M), \fBnetstat\fR(1M), \fBndp\fR(1M), \fBattributes\fR(5),
\fBprivileges\fR(5), \fBarp\fR(7P)
diff --git a/usr/src/man/man1m/in.ndpd.1m b/usr/src/man/man1m/in.ndpd.1m
index 5693425a89..2ce2643405 100644
--- a/usr/src/man/man1m/in.ndpd.1m
+++ b/usr/src/man/man1m/in.ndpd.1m
@@ -3,7 +3,7 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH IN.NDPD 1M "Jan 10, 2007"
+.TH IN.NDPD 1M "Sep 02, 2015"
.SH NAME
in.ndpd \- daemon for IPv6 autoconfiguration
.SH SYNOPSIS
@@ -13,7 +13,6 @@ in.ndpd \- daemon for IPv6 autoconfiguration
.fi
.SH DESCRIPTION
-.sp
.LP
\fBin.ndpd\fR provides both the host and router autoconfiguration components of
Neighbor Discovery for IPv6 and Stateless and Stateful Address
@@ -151,7 +150,6 @@ can only be used for communication with nodes on the same link.
.LP
For information on how to enable IPv6 address autoconfiguration, see \fI\fR
.SH OPTIONS
-.sp
.LP
Supported options and equivalent SMF service properties are listed below. SMF
service properties are set using a command of the form:
@@ -223,7 +221,6 @@ controlling terminal. As such, this option cannot be run under the SMF.
.RE
.SH FILES
-.sp
.ne 2
.na
\fB\fB/etc/inet/ndpd.conf\fR\fR
@@ -235,14 +232,10 @@ the hosts.
.RE
.SH SEE ALSO
-.sp
.LP
-\fBdhcpagent\fR(1M), \fBifconfig\fR(1M), \fBrouteadm\fR(1M), \fBsvcadm\fR(1M),
-\fBndpd.conf\fR(4), \fBattributes\fR(5), \fBicmp6\fR(7P),\fBip6\fR(7P),
-\fBattributes\fR(5), \fBsmf\fR(5)
-.sp
-.LP
-\fI\fR
+\fBdhcpagent\fR(1M), \fBifconfig\fR(1M), \fBndp\fR(1M), \fBrouteadm\fR(1M),
+\fBsvcadm\fR(1M), \fBndpd.conf\fR(4), \fBattributes\fR(5), \fBsmf\fR(5),
+\fBicmp6\fR(7P), \fBip6\fR(7P), \fBndp\fR(7P)
.sp
.LP
Narten, T., Nordmark, E., Simpson, W.\fIRFC 2461, Neighbor Discovery for IP
@@ -256,12 +249,10 @@ Autoconfiguration\fR. The Internet Society. December 1998.
Narten, T., and Draves, R. RFC 3041, Privacy Extensions for Stateless Address
Autoconfiguration in IPv6. The Internet Society. January 2001.
.SH DIAGNOSTICS
-.sp
.LP
Receipt of a \fBSIGHUP\fR signal will make \fBin.ndpd\fR restart and reread
\fB/etc/inet/ndpd.conf\fR.
.SH NOTES
-.sp
.LP
The \fBin.ndpd\fR daemon service is managed by the service management facility,
\fBsmf\fR(5), under the service identifier:
diff --git a/usr/src/man/man1m/ndp.1m b/usr/src/man/man1m/ndp.1m
new file mode 100644
index 0000000000..6301181f5f
--- /dev/null
+++ b/usr/src/man/man1m/ndp.1m
@@ -0,0 +1,215 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright (c) 2015, Joyent, Inc. All rights reserved.
+.\"
+.Dd Sep 02, 2015
+.Dt NDP 1M
+.Os
+.Sh NAME
+.Nm ndp
+.Nd manipulate IPv6 Neighbor Discovery Protocol mappings
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Fl a
+.Nm
+.Op Fl n
+.Fl A Ar period
+.Nm
+.Op Fl n
+.Op Fl i Ar iface
+.Ar hostname
+.Nm
+.Op Fl n
+.Op Fl i Ar iface
+.Fl d Ar hostname
+.Nm
+.Op Fl n
+.Op Fl i Ar iface
+.Fl f Ar filename
+.Nm
+.Op Fl n
+.Op Fl i Ar iface
+.Fl s Ar hostname lladdr
+.Op Cm temp
+.Op Cm any
+.Op Cm router
+.Sh DESCRIPTION
+The
+.Nm
+command displays and modifies the IPv6-to-MAC address translation
+tables used by the Neighbor Discovery Protocol
+.Po see Xr ndp 7P Pc .
+.Pp
+Given just a hostname,
+.Nm
+will display the current entry. Note that when getting, setting or deleting,
+if a hostname refers to multiple IPv6 addresses, the operation will apply to
+all of them.
+.Pp
+The NDP translation tables can be modified with
+.Fl d ,
+.Fl s ,
+or
+.Fl f .
+These flags can only be used when
+.Nm
+is given the
+.Sy PRIV_SYS_NET_CONFIG
+privilege. See
+.Xr privileges 5
+for further information.
+.Pp
+Note that NDP entries for IPMP (IP Network Multipathing) data and
+test addresses are managed by the kernel and therefore cannot be
+modified or deleted.
+.Sh OPTIONS
+.Bl -tag -width 6m
+.It Fl a
+Display all NDP entries. Entries can be one of several types:
+.Bl -tag -offset indent -width 7n
+.It Sy dynamic
+This is a normal NDP mapping and will eventually expire. This is the most
+common type of mapping for non-local addresses that will be displayed.
+.It Sy local
+The IPv6 address is local to the machine.
+.It Sy other
+The mapping is a multicast or broadcast address, or the system is acting
+as proxy for the address.
+.It Sy static
+The mapping is static and will not be removed from the machine over time.
+.El
+.Pp
+Entries also exist in one of the following states:
+.Bl -tag -offset indent -width 12m
+.It Sy INCOMPLETE
+Address resolution is in progress
+.It Sy REACHABLE
+This address has recently been reachable
+.It Sy STALE
+This address may be unreachable
+.It Sy DELAY
+Waiting to send out reachability probes
+.It Sy PROBE
+Sending out probes for the address
+.It Sy UNREACHABLE
+The address is unreachable, and will be deleted
+.It Sy UNKNOWN
+The state of the entry is unknown
+.El
+.Pp
+Using the
+.Fl a
+flag is equivalent to:
+.sp
+.Dl # netstat -p -f inet6
+.sp
+If
+.Fl n
+is passed to
+.Nm ,
+then it will be passed along to
+.Nm netstat .
+.It Fl A
+Display all NDP entries every
+.Ar period
+seconds.
+.It Fl d
+Delete NDP mappings for the host called
+.Ar hostname .
+.It Fl f
+Read in the lines from
+.Ar filename
+and use each one to set a mapping. The syntax of each line is the
+same as the arguments to
+.Fl s .
+Lines beginning with `#' will be ignored.
+.It Fl i
+By default,
+.Nm
+will use the routing table to determine the appropriate interface to place the
+mapping on. This flag allows forcing a specific interface
+.Ar iface .
+This argument will be ignored when using the
+.Fl a
+or
+.Fl A
+flags.
+.It Fl n
+Disable the default translation of numeric IP addresses to host names when
+printing.
+.It Fl s
+Add or update an NDP mapping, and set the desired properties for the entry. The
+list of flags should be the full set of flags desired on the entry, i.e., not
+listing a flag will remove it if it already exists. The following flags can be
+used:
+.Bl -tag -offset indent -width Ds
+.It Cm temp
+The entry should be temporary and eventually expire like a normal NDP
+entry. By default, all entries created with the
+.Nm
+command are static, and will not be deleted. To make a static entry temporary,
+it should be deleted and recreated with the
+.Cm temp
+flag.
+.It Cm any
+The address should be treated like an anycast address. This will prevent the
+system from sending Neighbor Advertisements with the Override flag.
+.It Cm router
+The address should be treated like a router address. This cause the system to
+send Neighbor Advertisements with the Router flag.
+.El
+.El
+.Sh EXAMPLES
+.Ss Example 1: Setting a mapping
+To create a temporary mapping for an address:
+.Bd -literal -offset indent
+# ndp -s fd00::1 90:b8:d0:4b:c7:3b temp
+.Ed
+.Ss Example 2: Getting a mapping
+An entry can be fetched via its IP address:
+.Bd -literal -offset indent
+# ndp fd00::1
+fd00::1 (fd00::1) at 90:b8:d0:4b:c7:3b temp
+.Ed
+.sp
+Or via a name that resolves to that address:
+.Bd -literal -offset indent
+# echo fd00::1 epicac >> /etc/hosts
+# echo fd00::2 epicac >> /etc/hosts
+# ndp epicac
+fd00::1 (epicac) at 90:b8:d0:4b:c7:3b temp
+fd00::2 (epicac) -- no entry
+.Ed
+.Ss Example 3: Deleting a mapping
+An entry can be removed with the
+.Fl d
+flag:
+.Bd -literal -offset indent
+# ndp -d fd00::1
+.Ed
+.sp
+Attempting to remove it again will print an error message:
+.Bd -literal -offset indent
+# ndp -d fd00::1
+ndp: No entry for fd00::1
+.Ed
+.Sh STABILITY
+The command line options for this command are stable, but the output format
+and diagnostic messages are not.
+.Sh SEE ALSO
+.Xr arp 1M ,
+.Xr ifconfig 1M ,
+.Xr netstat 1M ,
+.Xr attributes 5 ,
+.Xr privileges 5 ,
+.Xr ndp 7P
diff --git a/usr/src/man/man1m/netstat.1m b/usr/src/man/man1m/netstat.1m
index f9d02d18e0..b1f6d0e22f 100644
--- a/usr/src/man/man1m/netstat.1m
+++ b/usr/src/man/man1m/netstat.1m
@@ -1028,7 +1028,7 @@ future lease.
.SH SEE ALSO
.LP
\fBarp\fR(1M), \fBdhcpinfo\fR(1), \fBdhcpagent\fR(1M), \fBifconfig\fR(1M),
-\fBiostat\fR(1M), \fBkstat\fR(1M), \fBmibiisa\fR(1M), \fBsavecore\fR(1M),
+\fBiostat\fR(1M), \fBkstat\fR(1M), \fBmibiisa\fR(1M), \fBndp\fR(1M), \fBsavecore\fR(1M),
\fBvmstat\fR(1M), \fBhosts\fR(4), \fBinet_type\fR(4), \fBnetworks\fR(4),
\fBprotocols\fR(4), \fBservices\fR(4), \fBattributes\fR(5), \fBdhcp\fR(5),
\fBkstat\fR(7D), \fBinet\fR(7P), \fBinet6\fR(7P)
diff --git a/usr/src/man/man7p/Makefile b/usr/src/man/man7p/Makefile
index bbcf5ecf1d..13cb58770d 100644
--- a/usr/src/man/man7p/Makefile
+++ b/usr/src/man/man7p/Makefile
@@ -30,6 +30,7 @@ MANFILES= arp.7p \
ipsec.7p \
ipsecah.7p \
ipsecesp.7p \
+ ndp.7p \
pf_key.7p \
rarp.7p \
route.7p \
@@ -45,6 +46,7 @@ MANLINKS= AH.7p \
ESP.7p \
ICMP.7p \
IP.7p \
+ NDP.7p \
RARP.7p \
SCTP.7p \
TCP.7p \
@@ -63,6 +65,8 @@ AH.7p := LINKSRC = ipsecah.7p
ESP.7p := LINKSRC = ipsecesp.7p
+NDP.7p := LINKSRC = ndp.7p
+
RARP.7p := LINKSRC = rarp.7p
SCTP.7p := LINKSRC = sctp.7p
diff --git a/usr/src/man/man7p/arp.7p b/usr/src/man/man7p/arp.7p
index 5489ce3aca..d7055fcb0a 100644
--- a/usr/src/man/man7p/arp.7p
+++ b/usr/src/man/man7p/arp.7p
@@ -4,40 +4,25 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH ARP 7P "Feb 5, 2009"
+.TH ARP 7P "Sep 02, 2015"
.SH NAME
arp, ARP \- Address Resolution Protocol
.SH SYNOPSIS
.LP
.nf
\fB#include <sys/fcntl.h>\fR
-.fi
-
-.LP
-.nf
\fB#include <sys/socket.h>\fR
-.fi
-
-.LP
-.nf
\fB#include <net/if_arp.h>\fR
-.fi
-
-.LP
-.nf
\fB#include <netinet/in.h>\fR
.fi
-
.LP
.nf
-\fBs = socket(AF_INET, SOCK_DGRAM, 0);\fR
+\fBs = socket(PF_INET, SOCK_DGRAM, 0);\fR
.fi
-
.LP
.nf
\fBd = open ("/dev/arp", \fIoflag\fR);\fR
.fi
-
.SH DESCRIPTION
.LP
ARP is a protocol used to map dynamically between Internet Protocol (IP) and
@@ -64,8 +49,8 @@ interface.
.LP
To facilitate communications with systems that do not use ARP, ioctl()
requests are provided to enter and delete entries in the IP-to-link
-address tables. Ioctls that change the table contents require sys_net_config
-privilege. See \fBprivileges\fR(5).
+address tables. Ioctls that change the table contents require the
+\fBPRIV_SYS_NET_CONFIG\fR privilege. See \fBprivileges\fR(5).
.sp
.in +2
.nf
@@ -109,10 +94,8 @@ Each \fBioctl()\fR request takes the same structure as an argument.
entry, and \fBSIOCD[X]ARP\fR deletes an \fBARP\fR entry. These \fBioctl()\fR
requests may be applied to any Internet family socket descriptor\fIs\fR, or to
a descriptor for the \fBARP\fR device. Note that \fBSIOCS[X]ARP\fR and
-\fBSIOCD[X]ARP\fR require a privileged user, while \fBSIOCG[X]ARP\fR
-.sp
-.LP
-does not.
+\fBSIOCD[X]ARP\fR require the user to have the \fBPRIV_SYS_NET_CONFIG\fR
+privilege, while \fBSIOCG[X]ARP\fR does not.
.sp
.LP
The \fBarpreq\fR structure contains
@@ -155,8 +138,8 @@ struct xarpreq {
.sp
.LP
The address family for the [x]arp_pa sockaddr must be \fBAF_INET\fR. The
-\fBATF_COM\fR flag bits ([x]arp_flags) cannot be altered.
-\fBATF_USETRAILERS\fR is not implemented on Solaris and is retained for
+\fBATF_COM\fR flag bits ([x]arp_flags) cannot be altered. \fBATF_USETRAILERS\fR
+is not implemented by the operating system and is retained for
compatibility only. \fBATF_PERM\fR makes the entry permanent (disables aging)
if the \fBioctl()\fR request succeeds. \fBATF_PUBL\fR specifies that the system
should respond to ARP requests for the indicated protocol address coming from
@@ -206,11 +189,11 @@ interface).
On return from the kernel on a \fBSIOCGXARP\fR ioctl, the kernel fills in the
name of the interface (excluding terminating NULL) and its hardware address,
one after another, in the \fBsdl_data/LLADDR\fR area; if the two are larger
-than can be held in the 244 byte \fBsdl_data[\fR] area, an \fBENOSPC\fR error
+than can be held in the 244 byte \fBsdl_data[\fR] area, an \fBEINVAL\fR error
is returned. Assuming it fits, the kernel will also set \fBsdl_alen\fR with the
-length of hardware address, \fBsdl_nlen\fR with the length of name of the
+length of the hardware address, \fBsdl_nlen\fR with the length of the name of the
interface (excluding terminating NULL), \fBsdl_type\fR with an IFT_* value to
-indicate the type of the media, \fBsdl_slen\fR with 0, sdl_family with
+indicate the type of the media, \fBsdl_slen\fR with 0, \fBsdl_family\fR with
\fBAF_LINK\fR and \fBsdl_index\fR (which if not 0) with system given index for
the interface. The information returned is very similar to that returned via
routing sockets on an \fBRTM_IFINFO\fR message.
@@ -239,7 +222,7 @@ corresponding IPMP IP interface.
.TP
.ie t \(bu
.el o
-roxy ARP mappings for an IPMP group are automatically managed by the kernel.
+Proxy ARP mappings for an IPMP group are automatically managed by the kernel.
Specifically, if the hardware address in a \fBSIOCSARP\fR or \fBSIOCSXARP\fR
request matches the hardware address of an IP interface in an IPMP group and
the IP address is not local to the system, the kernel regards this as a IPMP
@@ -247,18 +230,6 @@ Proxy ARP entry. This IPMP Proxy ARP entry will have its hardware address
automatically adjusted in order to keep the IP address reachable (provided
the IPMP group has not entirely failed).
.RE
-.br
-.in +2
-\(em
-.in -2
-.br
-.in +2
-\(em
-.in -2
-.br
-.in +2
-\(emP
-.in -2
.sp
.LP
\fBARP\fR performs duplicate address detection for local addresses. When a
@@ -285,7 +256,7 @@ passing it the value \fBNHF_ARP\fR. Through this interface, two packet events
are supported:
.sp
.LP
-Physical in - ARP packets received via a network inter face
+Physical in - ARP packets received via a network interface
.sp
.LP
Physical out - ARP packets to be sent out via a network interface
@@ -321,7 +292,7 @@ Identifier indicating the outbound interface for packets received with the
.ad
.sp .6
.RS 4n
-Pointer to the start of the ARP header (not the ethernet header).
+Pointer to the start of the ARP header (not the Ethernet header).
.RE
.sp
diff --git a/usr/src/man/man7p/if_tcp.7p b/usr/src/man/man7p/if_tcp.7p
index 6f811bd12c..acba34c8ae 100644
--- a/usr/src/man/man7p/if_tcp.7p
+++ b/usr/src/man/man7p/if_tcp.7p
@@ -4,7 +4,7 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
.\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with
.\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH IF_TCP 7P "Sep 14, 2009"
+.TH IF_TCP 7P "Sep 02, 2015"
.SH NAME
if_tcp, if \- general properties of Internet Protocol network interfaces
.SH DESCRIPTION
@@ -60,7 +60,7 @@ struct lifreq {
int lifru_metric;
uint_t lifru_mtu;
int lif_muxid[2]; /* mux id's for arp & ip */
- struct lif_nd_req lifru_nd_req;
+ struct lif_nd_req lifru_nd_req; /* SIOCLIF*ND */
struct lif_ifinfo_req lifru_ifinfo_req;
zoneid_t lifru_zone; /* SIOC[GS]LIFZONE */
} lifr_lifru;
@@ -869,4 +869,4 @@ specified by \fBlifr_ppa\fR plumbed.
.LP
\fBifconfig\fR(1M), \fBin.routed\fR(1M), \fBioctl\fR(2),
\fBsockaddr\fR(3SOCKET), \fBstreamio\fR(7I), \fBarp\fR(7P), \fBdlpi\fR(7P),
-\fBip\fR(7P), \fBip6\fR(7P)
+\fBip\fR(7P), \fBip6\fR(7P), \fBndp\fR(7P)
diff --git a/usr/src/man/man7p/ndp.7p b/usr/src/man/man7p/ndp.7p
new file mode 100644
index 0000000000..ef57b1b3d6
--- /dev/null
+++ b/usr/src/man/man7p/ndp.7p
@@ -0,0 +1,345 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright (c) 2015, Joyent, Inc. All rights reserved.
+.\"
+.Dd Sep 02, 2015
+.Dt NDP 7P
+.Os
+.Sh NAME
+.Nm ndp ,
+.Nm NDP
+.Nd Neighbor Discovery Protocol
+.Sh SYNOPSIS
+.In sys/socket.h
+.In sys/sockio.h
+.In netinet/in.h
+.In net/if.h
+.Bd -literal
+s = socket(PF_INET6, SOCK_DGRAM, 0);
+
+struct lifreq lifr;
+ioctl(s, SIOCLIFGETND, &lifr);
+ioctl(s, SIOCLIFSETND, &lifr);
+ioctl(s, SIOCLIFDELND, &lifr);
+.Ed
+.Sh DESCRIPTION
+The Neighbor Discovery Protocol (NDP) is a protocol used to distribute and request
+information about neighboring IPv6 systems on the local network, much like
+.Xr ARP 7P
+for IPv4. NDP is also responsible for spreading information about the network
+gateway and how hosts should configure themselves
+.Pq see Xr in.ndpd 1M for more on how this happens .
+.Sh APPLICATION PROGRAMMING INTERFACE
+The operating system provides several ioctls to help manipulate the mappings
+obtained through NDP. They are
+.Sy SIOCLIFGETND ,
+.Sy SIOCLIFSETND ,
+and
+.Sy SIOCLIFDELND ,
+for getting, setting, and deleting respectively. Each of these ioctls takes a
+.Vt struct lifreq
+.Pq see Xr if 7P for details ,
+where the
+.Fa lifr_lifru
+field is of type
+.Vt struct lif_nd_req :
+.Bd -literal -offset 2m
+typedef struct lif_nd_req {
+ struct sockaddr_storage lnr_addr;
+ uint8_t lnr_state_create;
+ uint8_t lnr_state_same_lla;
+ uint8_t lnr_state_diff_lla;
+ int lnr_hdw_len;
+ int lnr_flags;
+ int lnr_pad0;
+ char lnr_hdw_addr[ND_MAX_HDW_LEN];
+} lif_nd_req_t;
+.Ed
+.Pp
+The
+.Fa lnr_addr
+field should be filled in with an IPv6 address
+.Pq see Xr sockaddr_in6 3SOCKET ,
+and the
+.Fa lnr_hdw_addr
+is the link-layer address of length
+.Fa lnr_hdw_len .
+.Pp
+State flags for
+.Fa lnr_state_create ,
+.Fa lnr_state_same_lla ,
+and
+.Fa lnr_state_diff_lla
+can be set to one of the following values:
+.Bl -tag -offset indent -width 16m
+.It Sy ND_UNCHANGED
+For ioctls that don't modify state
+.It Sy ND_INCOMPLETE
+Address resolution is currently in progress
+.It Sy ND_REACHABLE
+The link-layer address has recently been reachable
+.It Sy ND_STALE
+The link-layer address may be unreachable, and the system shouldn't do anything
+.It Sy ND_DELAY
+This entry hasn't yet started sending Neighbor Solicitations
+.It Sy ND_PROBE
+The operating system is currently sending out Neighbor Solicitations for the address
+.It Sy ND_UNREACHABLE
+The link-layer address is unreachable, and this entry is going to be deleted.
+.El
+.sp
+When creating a new entry, the only valid values for
+.Fa lnr_state_create
+are
+.Sy ND_REACHABLE
+and
+.Sy ND_STALE .
+Any other value will return
+.Sy EINVAL .
+The
+.Fa lnr_state_same_lla
+and
+.Fa lnr_state_diff_lla
+fields are reserved for future use and can be safely set to
+.Sy ND_UNCHANGED
+and
+.Sy ND_STALE
+respectively.
+.Pp
+Flags that can be placed in
+.Fa lnr_flags
+are:
+.Bl -tag -offset indent -width 16m
+.It Sy NDF_ISROUTER_ON
+Mark this entry as being a router. This will cause Neighbor Advertisements for
+this address to be sent with the R-bit (Router).
+.It Sy NDF_ISROUTER_OFF
+If this entry was flagged as being a router, remove the flag.
+.It Sy NDF_ANYCAST_ON
+Mark this entry as being for an anycast address. This prevents sending Neighbor
+Advertisements with the O-bit (Override).
+.It Sy NDF_ANYCAST_OFF
+If this entry was flagged as an anycast address, remove the flag.
+.It Sy NDF_STATIC
+Prevent this entry from being deleted by the system.
+.El
+.sp
+When using
+.Sy SIOCLIFGETND ,
+these flags represent the current state of the corresponding Neighbor Cache
+Entry. When using
+.Sy SIOCLIFSETND ,
+these flags represent what changes should be applied to the underlying entry.
+.Pp
+The only fields that need to be set for the
+.Sy SIOCLIFGETND
+or
+.Sy SIOCLIFDELND
+ioctls are
+.Fa lifr_name
+and
+.Fa lnr_addr .
+All other fields should be zeroed out. After successfully getting an entry, the
+other fields will be filled in. When using
+.Sy SIOCLIFSETND ,
+all fields should be set to an appropriate value, as described above, with the
+exception of
+.Fa lnr_pad0 ,
+which is unused and only exists for padding purposes.
+.Pp
+After performing the ioctl, the following errors may be returned through the
+global
+.Sy errno
+variable:
+.Bl -tag -offset indent -width 16m
+.It Sy EAFNOSUPPORT
+A non-IPv6 socket was used to perform the ioctl.
+.It Sy EINVAL
+The request contents were bad. This could be because conflicting flags were
+used, the specified interface wasn't logical unit zero, or another reason.
+.It Sy ENOMEM
+The system ran out of memory for internal data structures.
+.It Sy ENXIO
+The specified interface does not exist.
+.It Sy EPERM
+The caller does not have permission to modify the Neighbor Cache Entries
+associated with this interface. They may be lacking the
+.Sy PRIV_SYS_NET_CONFIG
+privilege
+.Po see Xr privileges 5 Pc ,
+or the interface is managed by IPMP (IP Network Multipathing).
+.It Sy ESRCH
+There is no entry matching the specified address.
+.El
+.Sh EXAMPLES
+The following examples demonstrate how to get and set NDP mappings using the
+provided ioctls. They can be compiled by using a C compiler and linking against
+the sockets library.
+.Ss Example 1: Getting a mapping
+.Bd -literal -offset indent
+$ gcc -Wall -lsocket -o get get.c
+$ cat get.c
+/*
+ * Example of getting a mapping for a node name.
+ */
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <net/if.h>
+
+int get(char *host) {
+ struct lifreq lifr;
+ struct addrinfo hints, *serverinfo, *p;
+ int err, s;
+
+ bzero(&hints, sizeof (struct addrinfo));
+ hints.ai_family = PF_INET6;
+ hints.ai_protocol = IPPROTO_IPV6;
+
+ if ((err = getaddrinfo(host, NULL, &hints, &serverinfo)) != 0) {
+ (void) fprintf(stderr, "Unable to lookup %s: %s\\n", host,
+ gai_strerror(err));
+ return (1);
+ }
+
+ s = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("Failed to open IPv6 socket");
+ return (1);
+ }
+
+ for (p = serverinfo; p != NULL; p = p->ai_next) {
+ /* Zero out structure */
+ bzero(&lifr, sizeof (struct lifreq));
+ (void) strlcpy(lifr.lifr_name, "net0",
+ sizeof (lifr.lifr_name));
+ (void) memcpy(&lifr.lifr_nd.lnr_addr, p->ai_addr,
+ sizeof (struct sockaddr_storage));
+
+ /* Get mapping */
+ if (ioctl(s, SIOCLIFGETND, &lifr) < 0) {
+ perror("Unable to get NDP mapping");
+ continue;
+ }
+
+ /*
+ * lifr.lifr_nd.lnr_hdw_addr now contains the MAC address,
+ * and can be used as desired.
+ */
+ }
+
+ /*
+ * Clean up linked list.
+ */
+ freeaddrinfo(serverinfo);
+ return (0);
+}
+
+int main(int argc, char *argv[]) {
+ if (argc < 2)
+ exit(1);
+ return (get(argv[1]));
+}
+.Ed
+.sp
+Deleting a mapping would work similarly, except that instead of using
+.Sy SIOCLIFGETND ,
+you would instead use the
+.Sy SIOCLIFDELND
+ioctl.
+.Ss Example 2: Adding a mapping
+.Bd -literal -offset indent
+$ gcc -Wall -lsocket -o set set.c
+$ cat set.c
+/*
+ * Example of setting a mapping to an all-zero Ethernet address.
+ */
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <net/if.h>
+
+int set(char *host) {
+ struct lifreq lifr;
+ struct addrinfo hints, *serverinfo, *p;
+ int err, s;
+
+ bzero(&hints, sizeof (struct addrinfo));
+ hints.ai_family = PF_INET6;
+ hints.ai_protocol = IPPROTO_IPV6;
+
+ if ((err = getaddrinfo(host, NULL, &hints, &serverinfo)) != 0) {
+ (void) fprintf(stderr, "Unable to lookup %s: %s\\n", host,
+ gai_strerror(err));
+ return (1);
+ }
+
+ s = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("Failed to open IPv6 socket");
+ return (1);
+ }
+
+ for (p = serverinfo; p != NULL; p = p->ai_next) {
+ /* Zero out structure */
+ bzero(&lifr, sizeof (struct lifreq));
+ (void) strlcpy(lifr.lifr_name, "net0",
+ sizeof (lifr.lifr_name));
+ (void) memcpy(&lifr.lifr_nd.lnr_addr, p->ai_addr,
+ sizeof (struct sockaddr_storage));
+
+ lifr.lifr_nd.lnr_state_create = ND_REACHABLE;
+ lifr.lifr_nd.lnr_flags = NDF_STATIC;
+
+ /* Get mapping */
+ if (ioctl(s, SIOCLIFSETND, &lifr) < 0) {
+ perror("Unable to set NDP mapping");
+ continue;
+ }
+ }
+
+ /*
+ * Clean up linked list.
+ */
+ freeaddrinfo(serverinfo);
+ return (0);
+}
+
+int main(int argc, char *argv[]) {
+ if (argc < 2)
+ exit(1);
+ return (set(argv[1]));
+}
+.Ed
+.Sh SEE ALSO
+.Xr in.ndpd 1M ,
+.Xr ndp 1M ,
+.Xr ifconfig 1M ,
+.Xr sockaddr_in6 3SOCKET ,
+.Xr privileges 5
+.Rs
+.%A Narten, T.
+.%A Nordmark, E.
+.%A Simpson, W.
+.%A Soliman, H.
+.%R Neighbor Discovery for IP version 6
+.%T RFC 4861
+.%D September 2007
+.Re
diff --git a/usr/src/man/man7p/route.7p b/usr/src/man/man7p/route.7p
index ba193fef11..bbd74fd740 100644
--- a/usr/src/man/man7p/route.7p
+++ b/usr/src/man/man7p/route.7p
@@ -7,7 +7,7 @@
.\" OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
.\" IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" Portions Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved.
-.TH ROUTE 7P "Feb 5, 2009"
+.TH ROUTE 7P "Sep 02, 2015"
.SH NAME
route \- kernel packet forwarding database
.SH SYNOPSIS
@@ -252,8 +252,8 @@ struct rt_msghdr {
int rtm_use; /* from rtentry */
uint_t rtm_inits; /* which values we are initializing */
-struct rt_metrics rtm_rmx; /* metrics themselves */
- };
+ struct rt_metrics rtm_rmx; /* metrics themselves */
+};
.fi
.in -2
@@ -274,7 +274,7 @@ struct rt_metrics {
uint32_t rmx_rtt; /* estimated round trip time */
uint32_t rmx_rttvar; /* estimated rtt variance */
uint32_t rmx_pksent; /* packets sent using this route */
- };
+};
/* Flags include the values */
diff --git a/usr/src/pkg/manifests/SUNWcs.man1m.inc b/usr/src/pkg/manifests/SUNWcs.man1m.inc
index 6437bb2bf9..c8d0a1c3fb 100644
--- a/usr/src/pkg/manifests/SUNWcs.man1m.inc
+++ b/usr/src/pkg/manifests/SUNWcs.man1m.inc
@@ -166,6 +166,7 @@ file path=usr/share/man/man1m/mvdir.1m
file path=usr/share/man/man1m/ncheck.1m
file path=usr/share/man/man1m/ncheck_ufs.1m
file path=usr/share/man/man1m/ndd.1m
+file path=usr/share/man/man1m/ndp.1m
file path=usr/share/man/man1m/netstat.1m
file path=usr/share/man/man1m/netstrategy.1m
file path=usr/share/man/man1m/newfs.1m
diff --git a/usr/src/pkg/manifests/SUNWcs.mf b/usr/src/pkg/manifests/SUNWcs.mf
index 908111a784..85285ae7cd 100644
--- a/usr/src/pkg/manifests/SUNWcs.mf
+++ b/usr/src/pkg/manifests/SUNWcs.mf
@@ -1348,6 +1348,7 @@ file path=usr/sbin/mountall group=sys mode=0555
file path=usr/sbin/msgid mode=0555
file path=usr/sbin/mvdir mode=0555
file path=usr/sbin/ndd mode=0555
+file path=usr/sbin/ndp mode=0555
file path=usr/sbin/nlsadmin group=adm mode=0755
file path=usr/sbin/nscd mode=0555
file path=usr/sbin/nwamadm mode=0555
diff --git a/usr/src/pkg/manifests/system-kernel.man7p.inc b/usr/src/pkg/manifests/system-kernel.man7p.inc
index 5d9471375d..77260928e8 100644
--- a/usr/src/pkg/manifests/system-kernel.man7p.inc
+++ b/usr/src/pkg/manifests/system-kernel.man7p.inc
@@ -26,6 +26,7 @@ file path=usr/share/man/man7p/ip6.7p
file path=usr/share/man/man7p/ipsec.7p
file path=usr/share/man/man7p/ipsecah.7p
file path=usr/share/man/man7p/ipsecesp.7p
+file path=usr/share/man/man7p/ndp.7p
file path=usr/share/man/man7p/pf_key.7p
file path=usr/share/man/man7p/route.7p
file path=usr/share/man/man7p/routing.7p
@@ -37,6 +38,7 @@ link path=usr/share/man/man7p/ARP.7p target=arp.7p
link path=usr/share/man/man7p/ESP.7p target=ipsecesp.7p
link path=usr/share/man/man7p/ICMP.7p target=icmp.7p
link path=usr/share/man/man7p/IP.7p target=ip.7p
+link path=usr/share/man/man7p/NDP.7p target=ndp.7p
link path=usr/share/man/man7p/SCTP.7p target=sctp.7p
link path=usr/share/man/man7p/TCP.7p target=tcp.7p
link path=usr/share/man/man7p/UDP.7p target=udp.7p
diff --git a/usr/src/uts/common/inet/ip/ip_ndp.c b/usr/src/uts/common/inet/ip/ip_ndp.c
index c6dee0247a..2cb91f8c41 100644
--- a/usr/src/uts/common/inet/ip/ip_ndp.c
+++ b/usr/src/uts/common/inet/ip/ip_ndp.c
@@ -1097,6 +1097,8 @@ ndp_query(ill_t *ill, struct lif_nd_req *lnr)
lnr->lnr_flags = NDF_ISROUTER_ON;
if (ncec->ncec_flags & NCE_F_ANYCAST)
lnr->lnr_flags |= NDF_ANYCAST_ON;
+ if (ncec->ncec_flags & NCE_F_STATIC)
+ lnr->lnr_flags |= NDF_STATIC;
ncec_refrele(ncec);
return (0);
}
diff --git a/usr/src/uts/common/net/if.h b/usr/src/uts/common/net/if.h
index 81f34c5659..b184fcdda9 100644
--- a/usr/src/uts/common/net/if.h
+++ b/usr/src/uts/common/net/if.h
@@ -279,7 +279,7 @@ struct ifaddr {
* The entry already exists and the link-layer address is the same
* The entry already exists and the link-layer address differs
*
- * Use ND_UNCHANGED and ND_ISROUTER_UNCHANGED to not change any state.
+ * Use ND_UNCHANGED to not change any state.
*/
#define ND_MAX_HDW_LEN 64
typedef struct lif_nd_req {
@@ -380,7 +380,7 @@ struct lifreq {
int lifru_metric;
uint_t lifru_mtu;
int lif_muxid[2]; /* mux id's for arp and ip */
- struct lif_nd_req lifru_nd_req;
+ struct lif_nd_req lifru_nd_req; /* SIOCLIF*ND */
struct lif_ifinfo_req lifru_ifinfo_req;
char lifru_groupname[LIFGRNAMSIZ]; /* SIOC[GS]LIFGROUPNAME */
char lifru_binding[LIFNAMSIZ]; /* SIOCGLIFBINDING */