Commit Diff


commit - e3c4ca9685cb6c571a1dff20cd7ffdd9fc13ef59
commit + 1b67eebf6758eab11b454ef83f99448d3e38a961
blob - 93411b244a0d3fc845477ababa63c6718527dd80
blob + b5b00dd625472ce17096c3c1b73f1aa142a4c0d4
--- Makefile
+++ Makefile
@@ -16,7 +16,7 @@ SRCS=arp.c compile.c main.c genget.c commands.c stats.
 SRCS+=ctl.c show.c if.c version.c route.c conf.c complete.c ieee80211.c
 SRCS+=bridge.c tunnel.c media.c sysctl.c passwd.c pfsync.c carp.c
 SRCS+=trunk.c who.c more.c stringlist.c utils.c sqlite3.c ppp.c
-SRCS+=nopt.c pflow.c wg.c nameserver.c
+SRCS+=nopt.c pflow.c wg.c nameserver.c ndp.c
 CLEANFILES+=compile.c
 LDADD=-ledit -ltermcap -lsqlite3 -L/usr/local/lib #-static
 
blob - ed26584762cdc7374793119c88039eb03e0df8ac
blob + 81e527b15bf334d83848c0a41170935fb559e1f4
--- arp.c
+++ arp.c
@@ -74,9 +74,8 @@ void nuke_entry(struct sockaddr_dl *sdl,
 	struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
 void conf_arp_entry(FILE *, char *, struct sockaddr_dl *,
 	struct sockaddr_inarp *, struct rt_msghdr *);
-static char *ether_str(struct sockaddr_dl *);
 int getinetaddr(const char *, struct in_addr *);
-int getsocket(void);
+static int getsocket(void);
 int rtmsg_arp(int, int, int, int);
 
 static int s = -1;
@@ -88,7 +87,7 @@ extern int h_errno;
 #define F_FILESET	3
 #define F_DELETE	4
 
-int
+static int
 getsocket(void)
 {
 	socklen_t len = sizeof(cli_rtable);
@@ -108,11 +107,11 @@ getsocket(void)
 	return s;
 }
 
-struct sockaddr_in	so_1mask = { 8, 0, 0, { 0xffffffff } };
-struct sockaddr_inarp	blank_sin = { sizeof(blank_sin), AF_INET }, sin_m;
-struct sockaddr_dl	blank_sdl = { sizeof(blank_sdl), AF_LINK }, sdl_m;
-struct sockaddr_dl	ifp_m = { sizeof(ifp_m), AF_LINK };
-time_t			expire_time;
+static struct sockaddr_in	so_1mask = { 8, 0, 0, { 0xffffffff } };
+static struct sockaddr_inarp	blank_sin = { sizeof(blank_sin), AF_INET }, sin_m;
+static struct sockaddr_dl	blank_sdl = { sizeof(blank_sdl), AF_LINK }, sdl_m;
+static struct sockaddr_dl	ifp_m = { sizeof(ifp_m), AF_LINK };
+static time_t			expire_time;
 
 int
 rtget(struct sockaddr_inarp **sinp, struct sockaddr_dl **sdlp,
@@ -523,7 +522,7 @@ nuke_entry(struct sockaddr_dl *sdl, struct sockaddr_in
 	arpdelete(ip, NULL);
 }
 
-static char *
+char *
 ether_str(struct sockaddr_dl *sdl)
 {
 	static char hbuf[NI_MAXHOST];
blob - 15abe7ebc4e9d72213231f45677a2d032235c5dc
blob + 9de8d9a66405e5604734ee27af9681d8f639a925
--- commands.c
+++ commands.c
@@ -93,6 +93,7 @@ static int	nsh_setrtable(int);
 static int	pr_routes(int, char **);
 static int	pr_routes6(int, char **);
 static int	pr_arp(int, char **);
+static int	pr_ndp(int, char **);
 static int	pr_sadb(int, char **);
 static int	pr_kernel(int, char **);
 static int	pr_prot1(int, char **);
@@ -109,6 +110,7 @@ static int	flush_help(void);
 static int	flush_line(char *);
 static int	flush_ip_routes(void);
 static int	flush_arp_cache(void);
+static int	flush_ndp_cache(void);
 static int	flush_history(void);
 static int	is_bad_input(const char *, size_t);
 static int	read_command_line(EditLine *, History *);
@@ -164,6 +166,7 @@ Menu showlist[] = {
 	{ "route6",	"IPv6 route table or route lookup", CMPL0 0, 0, 0, 1, pr_routes6 },
 	{ "sadb",	"Security Association Database", CMPL0 0, 0, 0, 0, pr_sadb },
 	{ "arp",	"ARP table",		CMPL0 0, 0, 0, 1, pr_arp },
+	{ "ndp",	"NDP table",		CMPL0 0, 0, 0, 1, pr_ndp },
 	{ "kernel",	"Kernel statistics",	CMPL(ta) (char **)stts, sizeof(struct stt), 0, 1, pr_kernel },
 	{ "bgp",	"BGP information",	CMPL(ta) (char **)bgcs, sizeof(struct prot1), 0, 4, pr_prot1 },
 	{ "ospf",	"OSPF information",	CMPL(ta) (char **)oscs, sizeof(struct prot1), 0, 3, pr_prot1 },
@@ -440,6 +443,7 @@ sysctlhelp(int unused1, char **unused2, char **unused3
 Menu flushlist[] = {
 	{ "routes",	"IP routes", CMPL0 0, 0, 0, 0, flush_ip_routes },
 	{ "arp",	"ARP cache", CMPL0 0, 0, 0, 0, flush_arp_cache },
+	{ "ndp",	"NDP cache", CMPL0 0, 0, 0, 0, flush_ndp_cache },
 	{ "line",	"Active user", CMPL0 0, 0, 1, 1, flush_line },
 	{ "bridge-dyn",	"Dynamically learned bridge addresses", CMPL0 0, 0, 1, 1, flush_bridgedyn },
 	{ "bridge-all",	"Dynamic and static bridge addresses", CMPL0 0, 0, 1, 1, flush_bridgeall },
@@ -923,6 +927,7 @@ static char
 #ifdef notyet
 	parphelp[] =	"Proxy ARP set",
 #endif
+	ndphelp[] = 	"Static NDP set",
 	nameserverhelp[] ="set or remove static DNS nameservers",
 	pfhelp[] =	"Packet filter control",
 	ospfhelp[] =	"OSPF control",
@@ -989,6 +994,7 @@ Command cmdtab[] = {
 	{ "rtable",	rtablehelp,	CMPL0 0, 0, rtable,	0, 1, 2 },
 	{ "group",	grouphelp,	CMPL0 0, 0, group,	1, 1, 0 },
 	{ "arp",	arphelp,	CMPL0 0, 0, arpset,	1, 1, 0 },
+	{ "ndp",	ndphelp,	CMPL0 0, 0, ndpset,	1, 1, 0 },
 	{ "nameserver",	nameserverhelp,	CMPL0 0, 0, nameserverset,1, 1, 0 },
 	{ "bridge",	bridgehelp,	CMPL(i) 0, 0, interface, 1, 1, 1 },
 	{ "show",	showhelp,	CMPL(ta) (char **)showlist, sizeof(Menu), showcmd,	0, 0, 0 },
@@ -2079,7 +2085,14 @@ int
 flush_arp_cache(void)
 {
 	flushroutes(AF_INET, AF_LINK);
+
+	return(0);
+}
 
+int
+flush_ndp_cache(void)
+{
+	ndpdump(NULL, 1);
 	return(0);
 }
 
@@ -2173,6 +2186,22 @@ pr_arp(int argc, char **argv)
 }
 
 int
+pr_ndp(int argc, char **argv)
+{
+	switch(argc) {
+	case 2:
+		/* show ndp table */
+		ndpdump(NULL, 0);
+		break;
+	case 3:
+		/* specific address */
+		ndpget(argv[2]);
+		break;
+	}
+	return 0;
+}
+
+int
 pr_sadb(int argc, char **argv)
 {
 	p_rttables(PF_KEY, 0, 0);
blob - da7b5a30346564f6d74d6cb28708265c35898d35
blob + 1404080d6ef7efbdeb48972108535f51a2dfdf27
--- externs.h
+++ externs.h
@@ -512,7 +512,16 @@ int arpset(int, char **);
 void arpdump(void);
 void conf_arp(FILE *, char *);
 char *sec2str(time_t);
+struct sockaddr_dl;
+char *ether_str(struct sockaddr_dl *);
 
+/* ndp.c */
+int ndpset(int, char **);
+void ndpget(const char *);
+int ndpdelete(const char *);
+struct sockaddr_in6;
+void ndpdump(struct sockaddr_in6 *, int);
+
 /* nameserver.c */
 int nameserverset(int, char **);
 void conf_nameserver(FILE *);
blob - /dev/null
blob + 415f24d9a8522b7b8b5ba95cc3fa5134917b344f (mode 644)
--- /dev/null
+++ ndp.c
@@ -0,0 +1,739 @@
+/*	$OpenBSD: ndp.c,v 1.107 2022/12/28 21:30:17 jmc Exp $	*/
+/*	$KAME: ndp.c,v 1.101 2002/07/17 08:46:33 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT 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.
+ */
+/*
+ * Copyright (c) 1984, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Sun Microsystems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS 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.
+ */
+
+/*
+ * Based on:
+ * "@(#) Copyright (c) 1984, 1993\n\
+ *	The Regents of the University of California.  All rights reserved.\n";
+ *
+ * "@(#)arp.c	8.2 (Berkeley) 1/2/94";
+ */
+
+/*
+ * ndp - display, set, delete and flush neighbor cache
+ */
+
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+
+#include <netinet/icmp6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <err.h>
+#include "externs.h"
+
+/* packing rule for routing socket */
+#define ROUNDUP(a) \
+	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+static int tflag;
+static int rtsock = -1;
+static int repeat = 0;
+
+char host_buf[NI_MAXHOST];		/* getnameinfo() */
+char ifix_buf[IFNAMSIZ];		/* if_indextoname() */
+
+static int getsocket(void);
+int parse_host(const char *, struct sockaddr_in6 *);
+int ndpset(int, char **);
+void ndpget(const char *);
+int ndpdelete(const char *);
+void ndpdump(struct sockaddr_in6 *, int);
+static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int);
+int ndp_ether_aton(const char *, u_char *);
+int rtmsg_ndp(int);
+int rtget_ndp(struct sockaddr_in6 **, struct sockaddr_dl **);
+
+static int
+getsocket(void)
+{
+	socklen_t len = sizeof(cli_rtable);
+
+	if (rtsock >= 0)
+		return rtsock;
+	rtsock = socket(AF_ROUTE, SOCK_RAW, 0);
+	if (rtsock == -1) {
+		printf("%% routing socket: %s", strerror(errno));
+		return -1;
+	}
+	if (setsockopt(rtsock, AF_ROUTE, ROUTE_TABLEFILTER, &cli_rtable,
+	    len) == -1) {
+		printf("%% ROUTE_TABLEFILTER: %s", strerror(errno));
+		return -1;
+	}
+
+	return rtsock;
+}
+
+int
+parse_host(const char *host, struct sockaddr_in6 *sin6)
+{
+	struct addrinfo hints, *res;
+	int gai_error;
+
+	bzero(&hints, sizeof(hints));
+	hints.ai_family = AF_INET6;
+	hints.ai_flags = AI_NUMERICHOST;
+
+	gai_error = getaddrinfo(host, NULL, &hints, &res);
+	if (gai_error) {
+		warnx("%% %s: %s", host, gai_strerror(gai_error));
+		return 1;
+	}
+	*sin6 = *(struct sockaddr_in6 *)res->ai_addr;
+	freeaddrinfo(res);
+	return 0;
+}
+
+#if 0	/* we don't support ipv6addr/128 type proxying. */
+static struct	sockaddr_in6 so_mask = {sizeof(so_mask), AF_INET6 };
+#endif
+static struct	sockaddr_in6 blank_sin = {sizeof(blank_sin), AF_INET6 }, sin_m;
+static struct	sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
+static struct	sockaddr_dl ifp_m = { sizeof(ifp_m), AF_LINK };
+static time_t	expire_time;
+static int	flags, found_entry;
+
+/*
+ * Set an individual neighbor cache entry
+ */
+int
+ndpset(int argc, char *argv[])
+{
+	struct sockaddr_in6 *sin = &sin_m;
+	struct sockaddr_dl *sdl;
+	struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+	char *eaddr, *host;
+	u_char *ea;
+	int set = 1, i;
+
+	sdl_m = blank_sdl;
+	sin_m = blank_sin;
+
+	if (NO_ARG(argv[0])) {
+		set = 0;
+		argc--;
+		argv++;
+	}
+
+	if ((set && (argc < 3 || argc > 5)) || (!set && argc != 2)) {
+		printf("%% %s <inet6 addr> <ether addr> [temp] [proxy]\n",
+		    argv[0]);
+		printf("%% no %s <inet6 addr>\n", argv[0]);
+		return 1;
+	}
+
+	if (!set)
+		return ndpdelete(argv[1]);
+
+	if (argc >= 3) {
+		host = argv[1];
+		eaddr = argv[2];
+	} else {
+		host = argv[1];
+		eaddr = NULL;
+	}
+
+	if (getsocket() < 0)
+		return 1;
+
+	if (parse_host(host, sin))
+		return 1;
+	ea = (u_char *)LLADDR(&sdl_m);
+	if (ndp_ether_aton(eaddr, ea) == 0)
+		sdl_m.sdl_alen = 6;
+	expire_time = 0;
+	flags = 0;
+	for (i = 3; i < argc; i++) {
+		if (isprefix(argv[i], "temp")) {
+			struct timeval now;
+
+			gettimeofday(&now, 0);
+			expire_time = now.tv_sec + 20 * 60;
+		} else if (isprefix(argv[i], "proxy")) {
+			flags |= RTF_ANNOUNCE;
+		} else {
+			printf("%% invalid parameter: %s\n", argv[i]);
+			return (0);
+		}
+	}
+
+	if (rtget_ndp(&sin, &sdl)) {
+		printf("%% RTM_GET(%s) failed", host);
+		return 1;
+	}
+
+	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr) &&
+	    sin->sin6_scope_id == sin_m.sin6_scope_id) {
+		if (sdl->sdl_family == AF_LINK &&
+		    (rtm->rtm_flags & RTF_LLINFO) &&
+		    !(rtm->rtm_flags & RTF_GATEWAY)) {
+			switch (sdl->sdl_type) {
+			case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
+			case IFT_ISO88024: case IFT_ISO88025:
+				goto overwrite;
+			}
+		}
+		/*
+		 * IPv4 arp command retries with sin_other = SIN_PROXY here.
+		 */
+		warnx("%% ndpset: cannot configure a new entry");
+		return 1;
+	}
+
+overwrite:
+	if (sdl->sdl_family != AF_LINK) {
+		printf("%% cannot intuit interface index and type for %s\n",
+		    host);
+		return (1);
+	}
+	sdl_m.sdl_type = sdl->sdl_type;
+	sdl_m.sdl_index = sdl->sdl_index;
+	return (rtmsg_ndp(RTM_ADD));
+}
+
+/*
+ * Display an individual neighbor cache entry
+ */
+void
+ndpget(const char *host)
+{
+	struct sockaddr_in6 *sin = &sin_m;
+
+	sin_m = blank_sin;
+	if (parse_host(host, sin))
+		return;
+
+	found_entry = 0;
+	ndpdump(sin, 0);
+	if (found_entry == 0)
+		printf("%s -- no entry\n", host);
+}
+
+/*
+ * Delete a neighbor cache entry
+ */
+int
+ndpdelete(const char *host)
+{
+	struct sockaddr_in6 *sin = &sin_m;
+	struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+	struct sockaddr_dl *sdl;
+
+	getsocket();
+	sin_m = blank_sin;
+	if (parse_host(host, sin))
+		return 1;
+	if (rtget_ndp(&sin, &sdl)) {
+		printf("%% RTM_GET(%s) failed", host);
+		return 1;
+	}
+
+	if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr) &&
+	    sin->sin6_scope_id == sin_m.sin6_scope_id) {
+		if (sdl->sdl_family == AF_LINK && rtm->rtm_flags & RTF_LLINFO) {
+			if (rtm->rtm_flags & RTF_LOCAL)
+				return (0);
+			if (!(rtm->rtm_flags & RTF_GATEWAY))
+				goto delete;
+		}
+		/*
+		 * IPv4 arp command retries with sin_other = SIN_PROXY here.
+		 */
+		return 1;
+	}
+
+delete:
+	if (sdl->sdl_family != AF_LINK) {
+		printf("%% cannot locate %s\n", host);
+		return (1);
+	}
+	if (rtmsg_ndp(RTM_DELETE) == 0)
+		printf("%% %s deleted\n", host);
+
+	return 0;
+}
+
+/*
+ * strlen("2001:0db8:3333:4444:5555:6666:7777:8888") == 39
+ */
+#define W_ADDR	39
+#define W_LL	17
+#define W_IF	7
+
+/*
+ * Dump the entire neighbor cache
+ */
+void
+ndpdump(struct sockaddr_in6 *addr, int cflag)
+{
+	int mib[7];
+	size_t needed;
+	char *lim, *buf = NULL, *next;
+	struct rt_msghdr *rtm;
+	struct sockaddr_in6 *sin;
+	struct sockaddr_dl *sdl;
+	struct in6_nbrinfo *nbi;
+	struct timeval now;
+	int addrwidth;
+	int llwidth;
+	int ifwidth;
+	char *ifname;
+
+	/* Print header */
+	if (!tflag && !cflag)
+		printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n",
+		    W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address",
+		    W_IF, W_IF, "Netif", "Expire", "S", "Flags");
+
+again:;
+	lim = NULL;
+	mib[0] = CTL_NET;
+	mib[1] = PF_ROUTE;
+	mib[2] = 0;
+	mib[3] = AF_INET6;
+	mib[4] = NET_RT_FLAGS;
+	mib[5] = RTF_LLINFO;
+	mib[6] = cli_rtable;
+	while (1) {
+		if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) {
+			printf("%% sysctl(PF_ROUTE estimate): %s\n",
+			    strerror(errno));
+			return;
+		}
+		if (needed == 0)
+			break;
+		if ((buf = realloc(buf, needed)) == NULL) {
+			printf("%% realloc: %s\n", strerror(errno));
+			return;
+		}
+		if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) {
+			if (errno == ENOMEM)
+				continue;
+			printf("%% sysctl(PF_ROUTE, NET_RT_FLAGS): %s\n",
+			    strerror(errno));
+			return;
+		}
+		lim = buf + needed;
+		break;
+	}
+
+	for (next = buf; next && lim && next < lim; next += rtm->rtm_msglen) {
+		int isrouter = 0, prbs = 0;
+
+		rtm = (struct rt_msghdr *)next;
+		if (rtm->rtm_version != RTM_VERSION)
+			continue;
+		sin = (struct sockaddr_in6 *)(next + rtm->rtm_hdrlen);
+#ifdef __KAME__
+		{
+			struct in6_addr *in6 = &sin->sin6_addr;
+			if ((IN6_IS_ADDR_LINKLOCAL(in6) ||
+			    IN6_IS_ADDR_MC_LINKLOCAL(in6) ||
+			    IN6_IS_ADDR_MC_INTFACELOCAL(in6)) &&
+			    sin->sin6_scope_id == 0) {
+				sin->sin6_scope_id = (u_int32_t)
+				    ntohs(*(u_short *)&in6->s6_addr[2]);
+				*(u_short *)&in6->s6_addr[2] = 0;
+			}
+		}
+#endif
+		sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len));
+
+		/*
+		 * Some OSes can produce a route that has the LINK flag but
+		 * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
+		 * and BSD/OS, where xx is not the interface identifier on
+		 * lo0).  Such routes entry would annoy getnbrinfo() below,
+		 * so we skip them.
+		 * XXX: such routes should have the GATEWAY flag, not the
+		 * LINK flag.  However, there is rotten routing software
+		 * that advertises all routes that have the GATEWAY flag.
+		 * Thus, KAME kernel intentionally does not set the LINK flag.
+		 * What is to be fixed is not ndp, but such routing software
+		 * (and the kernel workaround)...
+		 */
+		if (sdl->sdl_family != AF_LINK)
+			continue;
+
+		if (!(rtm->rtm_flags & RTF_HOST))
+			continue;
+
+		if (addr) {
+			if (!IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
+			    &sin->sin6_addr) || addr->sin6_scope_id !=
+			    sin->sin6_scope_id)
+				continue;
+			found_entry = 1;
+		} else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
+			continue;
+		getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
+		    sizeof(host_buf), NULL, 0, NI_NUMERICHOST);
+		if (cflag) {
+			if (rtm->rtm_flags & RTF_CLONED)
+				ndpdelete(host_buf);
+			continue;
+		}
+		gettimeofday(&now, 0);
+		if (tflag) {
+			char buf[sizeof("00:00:00")];
+			struct tm *tm;
+
+			tm = localtime(&now.tv_sec);
+			if (tm != NULL) {
+				strftime(buf, sizeof(buf), "%H:%M:%S", tm);
+				printf("%s.%06ld ", buf, now.tv_usec);
+			}
+		}
+
+		addrwidth = strlen(host_buf);
+		if (addrwidth < W_ADDR)
+			addrwidth = W_ADDR;
+		llwidth = strlen(ether_str(sdl));
+		if (W_ADDR + W_LL - addrwidth > llwidth)
+			llwidth = W_ADDR + W_LL - addrwidth;
+		ifname = if_indextoname(sdl->sdl_index, ifix_buf);
+		if (!ifname)
+			ifname = "?";
+		ifwidth = strlen(ifname);
+		if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
+			ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
+
+		printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf,
+		    llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
+
+		/* Print neighbor discovery specific information */
+		nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1);
+		if (nbi) {
+			if (nbi->expire > now.tv_sec) {
+				printf(" %-9.9s",
+				    sec2str(nbi->expire - now.tv_sec));
+			} else if (nbi->expire == 0)
+				printf(" %-9.9s", "permanent");
+			else
+				printf(" %-9.9s", "expired");
+
+			switch (nbi->state) {
+			case ND6_LLINFO_NOSTATE:
+				 printf(" N");
+				 break;
+			case ND6_LLINFO_INCOMPLETE:
+				 printf(" I");
+				 break;
+			case ND6_LLINFO_REACHABLE:
+				 printf(" R");
+				 break;
+			case ND6_LLINFO_STALE:
+				 printf(" S");
+				 break;
+			case ND6_LLINFO_DELAY:
+				 printf(" D");
+				 break;
+			case ND6_LLINFO_PROBE:
+				 printf(" P");
+				 break;
+			default:
+				 printf(" ?");
+				 break;
+			}
+
+			isrouter = nbi->isrouter;
+			prbs = nbi->asked;
+		} else {
+			warnx("%% failed to get neighbor information");
+			printf("  ");
+		}
+
+		printf(" %s%s%s",
+		    (rtm->rtm_flags & RTF_LOCAL) ? "l" : "",
+		    isrouter ? "R" : "",
+		    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
+
+		if (prbs)
+			printf(" %d", prbs);
+
+		printf("\n");
+	}
+
+	if (repeat) {
+		printf("\n");
+		fflush(stdout);
+		sleep(repeat);
+		goto again;
+	}
+
+	free(buf);
+}
+
+static struct in6_nbrinfo *
+getnbrinfo(struct in6_addr *addr, int ifindex, int warning)
+{
+	static struct in6_nbrinfo nbi;
+	int s;
+
+	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
+		printf("%% socket: %s\n", strerror(errno));
+		return NULL;
+	}
+
+	bzero(&nbi, sizeof(nbi));
+	if_indextoname(ifindex, nbi.ifname);
+	nbi.addr = *addr;
+	if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) == -1) {
+		if (warning)
+			warn("%% ioctl(SIOCGNBRINFO_IN6)");
+		close(s);
+		return(NULL);
+	}
+
+	close(s);
+	return(&nbi);
+}
+
+int
+ndp_ether_aton(const char *a, u_char *n)
+{
+	int i, o[6];
+
+	i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
+	    &o[3], &o[4], &o[5]);
+	if (i != 6) {
+		warnx("%% invalid Ethernet address '%s'", a);
+		return (1);
+	}
+	for (i = 0; i < 6; i++)
+		n[i] = o[i];
+	return (0);
+}
+
+int
+rtmsg_ndp(int cmd)
+{
+	static int seq;
+	int rlen;
+	struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+	char *cp = m_rtmsg.m_space;
+	int l;
+
+	errno = 0;
+	if (cmd == RTM_DELETE)
+		goto doit;
+	bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+	rtm->rtm_flags = flags;
+	rtm->rtm_version = RTM_VERSION;
+	rtm->rtm_tableid = cli_rtable;
+
+	switch (cmd) {
+	default:
+		errx(1, "internal wrong cmd");
+	case RTM_ADD:
+		rtm->rtm_addrs |= RTA_GATEWAY;
+		if (expire_time) {
+			rtm->rtm_rmx.rmx_expire = expire_time;
+			rtm->rtm_inits = RTV_EXPIRE;
+		}
+		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
+#if 0	/* we don't support ipv6addr/128 type proxying. */
+		if (rtm->rtm_flags & RTF_ANNOUNCE) {
+			rtm->rtm_flags &= ~RTF_HOST;
+			rtm->rtm_addrs |= RTA_NETMASK;
+		}
+#endif
+		/* FALLTHROUGH */
+	case RTM_GET:
+		rtm->rtm_addrs |= (RTA_DST | RTA_IFP);
+	}
+
+#define NEXTADDR(w, s)							\
+	if (rtm->rtm_addrs & (w)) {					\
+		memcpy(cp, &(s), sizeof(s));				\
+		ADVANCE(cp, (struct sockaddr *)&(s));			\
+	}
+
+#ifdef __KAME__
+	{
+		struct sockaddr_in6 sin6 = sin_m;
+		struct in6_addr *in6 = &sin6.sin6_addr;
+		if (IN6_IS_ADDR_LINKLOCAL(in6) ||
+		    IN6_IS_ADDR_MC_LINKLOCAL(in6) ||
+		    IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
+			*(u_int16_t *)& in6->s6_addr[2] =
+			    htons(sin6.sin6_scope_id);
+			sin6.sin6_scope_id = 0;
+		}
+		NEXTADDR(RTA_DST, sin6);
+	}
+#else
+	NEXTADDR(RTA_DST, sin_m);
+#endif
+	NEXTADDR(RTA_GATEWAY, sdl_m);
+#if 0	/* we don't support ipv6addr/128 type proxying. */
+	memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
+	NEXTADDR(RTA_NETMASK, so_mask);
+#endif
+	NEXTADDR(RTA_IFP, ifp_m);
+
+	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
+doit:
+	l = rtm->rtm_msglen;
+	rtm->rtm_seq = ++seq;
+	rtm->rtm_type = cmd;
+	if ((rlen = write(rtsock, (char *)&m_rtmsg, l)) == -1) {
+		if (errno != ESRCH || cmd != RTM_DELETE) {
+			printf("%% rtmsg_ndp: writing to routing socket: %s\n",
+			    strerror(errno));
+		}
+	}
+	do {
+		l = read(rtsock, (char *)&m_rtmsg, sizeof(m_rtmsg));
+	} while (l > 0 && (rtm->rtm_version != RTM_VERSION ||
+	    rtm->rtm_seq != seq || rtm->rtm_pid != pid));
+	if (l == -1)
+		warn("%% read from routing socket");
+	return (0);
+}
+
+int
+rtget_ndp(struct sockaddr_in6 **sinp, struct sockaddr_dl **sdlp)
+{
+	struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+	struct sockaddr_in6 *sin = NULL;
+	struct sockaddr_dl *sdl = NULL;
+	struct sockaddr *sa;
+	char *cp;
+	unsigned int i;
+
+	if (rtmsg_ndp(RTM_GET) < 0)
+		return (1);
+
+	if (rtm->rtm_addrs) {
+		cp = ((char *)rtm + rtm->rtm_hdrlen);
+		for (i = 1; i; i <<= 1) {
+			if (i & rtm->rtm_addrs) {
+				sa = (struct sockaddr *)cp;
+				switch (i) {
+				case RTA_DST:
+					sin = (struct sockaddr_in6 *)sa;
+					break;
+				case RTA_IFP:
+					sdl = (struct sockaddr_dl *)sa;
+					break;
+				default:
+					break;
+				}
+				ADVANCE(cp, sa);
+			}
+		}
+	}
+
+	if (sin == NULL || sdl == NULL)
+		return (1);
+
+#ifdef __KAME__
+	{
+		static struct sockaddr_in6 ksin;
+		struct in6_addr *in6;
+
+		/* do not damage the route message, we need it for delete */
+		ksin = *sin;
+		sin = &ksin;
+		in6 = &sin->sin6_addr;
+
+		if ((IN6_IS_ADDR_LINKLOCAL(in6) ||
+		    IN6_IS_ADDR_MC_LINKLOCAL(in6) ||
+		    IN6_IS_ADDR_MC_INTFACELOCAL(in6)) &&
+		    sin->sin6_scope_id == 0) {
+			sin->sin6_scope_id = (u_int32_t)ntohs(*(u_short *)
+			    &in6->s6_addr[2]);
+			*(u_short *)&in6->s6_addr[2] = 0;
+		}
+	}
+#endif
+	*sinp = sin;
+	*sdlp = sdl;
+
+	return (0);
+}
blob - 53ea5284bf5b69f7a8b75f78b1db789e91913803
blob + 5f9794d6b92a5e5ef101c1b2fc72a8e8fdb75b2d
--- nsh.8
+++ nsh.8
@@ -261,6 +261,7 @@ nsh/help
   rtable        Routing table switch
   group         Modify group attributes
   arp           Static ARP set
+  ndp           Static NDP set
   bridge        Modify bridge parameters
   show          Show system information
   ip            Set IP networking parameters
@@ -450,6 +451,26 @@ TBC
 Set or remove a static IP address and MAC address binding for Address
 Resolution Protocol.
 TBC
+.Pp
+.Tg ndp
+.Op no
+.Ic ndp
+.Op Ar IPv6-Address
+.Op Ar MAC-Address
+.Op Cm temp
+.Op Cm proxy
+.Pp
+Set or remove a static IPv6 address and MAC address binding for the
+IPv6 Neighbour Discovery Protocol (NDP).
+The entry will be permanent unless the word
+.Cm temp
+is given in the command.
+If the word
+.Cm proxy
+is given, this system will act as an ND Proxy server,
+responding to requests for
+.Ar IPv6-Address
+even though the MAC address is not its own.
 .Pp
 .Tg bridge
 .Op no
@@ -1450,7 +1471,7 @@ nsh/no verbose
 .El
 .Tg show
 .Ic show
-.Op hostname | interface | route | route6 | sadb | arp | kernel | bgp | ospf\
+.Op hostname | interface | route | route6 | sadb | arp | ndp | kernel | bgp | ospf\
  | ospf6 |eigrp | rip | ldp | ike | ipsec | dvmrp | relay | dhcp | smtp\
  | ldap | monitor | version | users | running-config | startup-config |\&? | help
 .Pp
@@ -1470,6 +1491,7 @@ nsh(p)/show
   route6          IPv6 route table or route lookup
   sadb            Security Association Database
   arp             ARP table
+  ndp             NDP table
   kernel          Kernel statistics
   bgp             BGP information
   ospf            OSPF information
@@ -1729,7 +1751,7 @@ firewall rules, and other information compiled by
 TBC
 .Tg flush
 .Ic flush
-.Op routes | arp | line | bridge-dyn | bridge-all | bridge-rule | pf | history |\&? | help
+.Op routes | arp | ndp | line | bridge-dyn | bridge-all | bridge-rule | pf | history |\&? | help
 .Pp
 Clear various system tables.
 .Pp
@@ -1741,6 +1763,10 @@ Clear the system routing table.
 .Pp
 Clear the system arp cache and static arp table.
 .Pp
+.Ic flush ndp
+.Pp
+Clear the system NDP cache and static NDP table.
+.Pp
 .Ic flush bridge-dyn
 .Ar bridge-name
 .Pp