commit 1b67eebf6758eab11b454ef83f99448d3e38a961 from: Stefan Sperling date: Mon Feb 27 18:50:09 2023 UTC add nsh commands for the IPv6 Neighbour Discovery Protocol Add four new nsh commands to manage the NDP cache, similar to the commands which exist for the IPv4 ARP cache: 1. show ndp 2. ndp [temp] [proxy] 3. no ndp 4. flush ndp Based on code from OpenBSD usr.sbin/ndp/ndp.c. Some code is shared between nsh's arp.c and the new ndp.c file. 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 [temp] [proxy]\n", + argv[0]); + printf("%% no %s \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