From nobody Tue Apr 25 12:32:36 2023 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4Q5LxJ6dPtz477wc; Tue, 25 Apr 2023 12:32:36 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Q5LxJ6PR1z4L0p; Tue, 25 Apr 2023 12:32:36 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1682425956; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=FndA5h0W4U53mt9NalBcP/ZzjVyHJgwFKnnR61Bj08A=; b=fvGf8vsTEJSLOWP4F+EOSfUQWSNs8/qv/h1/u3ziqolg4YFuA2VXgOVt080x5h/frEJPhu djVCWD0H5IGYv8wzx5bcbCUSK14OjjA31IrjpROmY/pWVYS2LSUoBtvkc+AEsIKHjQiCUS 96L3W4mchRcxNN7b0S5ShAOF3e3SM7YwvlvfFTEk4toYZ4vPC+Fc0q3+gIPprSOjzroKbm puAxT87ZOttJyO06xQH+++IsdUfexp1f9Esc6guSkdLm+cUJlfPB6VO6muui1ELfSDoZd1 3KuMLHBuTbBZxU9CI9UvC+HQN8oQQqSrCKQiS3J5jCBGaCyK6E5VyXpt0U6+Rg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1682425956; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=FndA5h0W4U53mt9NalBcP/ZzjVyHJgwFKnnR61Bj08A=; b=SyAaMVZebHOrPH5njOH9aHE9IjC4Cxhzpxhe5YNenw9vrDOF4ZIWlrFxQOPU80RV6Rgdkr CLBfOyHZ3kAmhdmYn1kqormX1eGicKgym8x4lnfUXhKTn10OV8QeWFKxTpoOFuQzFcibu/ MZyFqXndna+vQamB1Wkf5tR8+b4H/SQ7KOIlpJ4vPqZvBBgPqHpHa/bfb0rXnnNKo+M/VJ Ih/hyYQjwTB59sjEWqlg6ohD8v6nyTLqjRZmHbcCa0yraA6RKyUI+ITohRcbOvFlMfnxqp NOBG6SfiSTc84soiRXRSeuzJlIwD9HNA7B4oIVeoZuxwNKnCDA5uF68Oq2OpvQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1682425956; a=rsa-sha256; cv=none; b=xCrBynY5XZ8J8yI2F+wpSAldijn+1Uq+f15/IUmG2RWRvMfu3MwSGLaFGUbeSuKUZ/GOmn LyrQ0ooH6oj/TIMBw5JqDk5MKZ97vK0l4L/whtWxvwGA5pnavR0sZaR8d/I6K0Uck+gT9i 5Aj7UMbnXFDx7LqCLkRlydVdjGGxT/Kojk1mMyZwgz1gmJ0j4fGw1OtPe440zjxGooBsSH mmtH7mo3MIS1g7FVqudLmvdam2jdvnJZIYfQqM1Q0p99CfCZxEEqMIBOkAWUlGnpwIEpny yGscyIAUYk/Gh1Mk+Lhp5CvEAyVglKWdsFlOf+DGAjQdBkOh/yGY2IWuvPXoRA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Q5LxJ5SjZz16h1; Tue, 25 Apr 2023 12:32:36 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 33PCWawq044055; Tue, 25 Apr 2023 12:32:36 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 33PCWaGC044054; Tue, 25 Apr 2023 12:32:36 GMT (envelope-from git) Date: Tue, 25 Apr 2023 12:32:36 GMT Message-Id: <202304251232.33PCWaGC044054@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: "Alexander V. Chernikov" Subject: git: 91fbe0819bb9 - main - ndp: convert ndp(8) to netlink. List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: melifaro X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 91fbe0819bb9c6e1a5e5b854075deb51742eb41f Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by melifaro: URL: https://cgit.FreeBSD.org/src/commit/?id=91fbe0819bb9c6e1a5e5b854075deb51742eb41f commit 91fbe0819bb9c6e1a5e5b854075deb51742eb41f Author: Alexander V. Chernikov AuthorDate: 2023-04-25 12:30:39 +0000 Commit: Alexander V. Chernikov CommitDate: 2023-04-25 12:30:39 +0000 ndp: convert ndp(8) to netlink. The change is intended to be fully transparent to the users. Similarly to route(8) and netstat(8), ndp can be build without netlink by defining WITHOUT_NETLINK in make.conf. Differential Revision: https://reviews.freebsd.org/D39720 --- usr.sbin/ndp/Makefile | 7 + usr.sbin/ndp/ndp.c | 185 ++++++++++------ usr.sbin/ndp/ndp.h | 27 +++ usr.sbin/ndp/ndp_netlink.c | 511 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 661 insertions(+), 69 deletions(-) diff --git a/usr.sbin/ndp/Makefile b/usr.sbin/ndp/Makefile index 84eb586083b3..1722f4a5a2ef 100644 --- a/usr.sbin/ndp/Makefile +++ b/usr.sbin/ndp/Makefile @@ -31,6 +31,13 @@ CFLAGS+= -DEXPERIMENTAL CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG .endif +.if ${MK_NETLINK_SUPPORT} != "no" +SRCS+= ndp_netlink.c +.else +CFLAGS+=-DWITHOUT_NETLINK +.endif + + WARNS?= 3 .include diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c index eaf652507e09..23e186466c22 100644 --- a/usr.sbin/ndp/ndp.c +++ b/usr.sbin/ndp/ndp.c @@ -102,6 +102,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,8 @@ #include #include "gmt2local.h" +#include "ndp.h" + #define NEXTADDR(w, s) \ if (rtm->rtm_addrs & (w)) { \ bcopy((char *)&s, cp, sizeof(s)); \ @@ -119,8 +122,6 @@ } static pid_t pid; -static int nflag; -static int tflag; static int32_t thiszone; /* time difference with gmt */ static int s = -1; static int repeat = 0; @@ -129,16 +130,13 @@ static char host_buf[NI_MAXHOST]; /* getnameinfo() */ static char ifix_buf[IFNAMSIZ]; /* if_indextoname() */ static int file(char *); -static void getsocket(void); static int set(int, char **); static void get(char *); static int delete(char *); -static void dump(struct sockaddr_in6 *, int); +static int dump(struct sockaddr_in6 *, int); static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int); -static char *ether_str(struct sockaddr_dl *); static int ndp_ether_aton(char *, u_char *); static void usage(void); -static int rtmsg(int); static void ifinfo(char *, int, char **); static void rtrlist(void); static void plist(void); @@ -149,8 +147,11 @@ static void harmonize_rtr(void); static void getdefif(void); static void setdefif(char *); #endif -static char *sec2str(time_t); -static void ts_print(const struct timeval *); + +#ifdef WITHOUT_NETLINK +static void getsocket(void); +static int rtmsg(int); +#endif static const char *rtpref_str[] = { "medium", /* 00 */ @@ -159,8 +160,27 @@ static const char *rtpref_str[] = { "low" /* 11 */ }; +struct ndp_opts opts = {}; + #define NDP_XO_VERSION "1" +bool +valid_type(int if_type) +{ + switch (if_type) { + case IFT_ETHER: + case IFT_FDDI: + case IFT_ISO88023: + case IFT_ISO88024: + case IFT_ISO88025: + case IFT_L2VLAN: + case IFT_BRIDGE: + return (true); + break; + } + return (false); +} + int main(int argc, char **argv) { @@ -206,10 +226,10 @@ main(int argc, char **argv) arg = optarg; break; case 'n': - nflag = 1; + opts.nflag = true; break; case 't': - tflag = 1; + opts.tflag = true; break; case 'A': if (mode) { @@ -385,12 +405,12 @@ static struct sockaddr_dl blank_sdl = { .sdl_family = AF_LINK }; static struct sockaddr_dl sdl_m; -static time_t expire_time; -static int flags, found_entry; +#ifdef WITHOUT_NETLINK static struct { struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; +#endif /* * Set an individual neighbor cache entry @@ -398,44 +418,44 @@ static struct { static int set(int argc, char **argv) { - register struct sockaddr_in6 *sin = &sin_m; - register struct sockaddr_dl *sdl; - register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); - struct addrinfo hints, *res; + struct sockaddr_in6 *sin = &sin_m; int gai_error; u_char *ea; char *host = argv[0], *eaddr = argv[1]; - getsocket(); argc -= 2; argv += 2; sdl_m = blank_sdl; sin_m = blank_sin; - bzero(&hints, sizeof(hints)); - hints.ai_family = AF_INET6; - gai_error = getaddrinfo(host, NULL, &hints, &res); + gai_error = getaddr(host, sin); if (gai_error) { xo_warnx("%s: %s", host, gai_strerror(gai_error)); return 1; } - sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - sin->sin6_scope_id = - ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; + ea = (u_char *)LLADDR(&sdl_m); if (ndp_ether_aton(eaddr, ea) == 0) sdl_m.sdl_alen = 6; - flags = expire_time = 0; while (argc-- > 0) { if (strncmp(argv[0], "temp", 4) == 0) { struct timeval now; gettimeofday(&now, 0); - expire_time = now.tv_sec + 20 * 60; + opts.expire_time = now.tv_sec + 20 * 60; } else if (strncmp(argv[0], "proxy", 5) == 0) - flags |= RTF_ANNOUNCE; + opts.flags |= RTF_ANNOUNCE; argv++; } + +#ifndef WITHOUT_NETLINK + return (set_nl(0, sin, &sdl_m, host)); +#else + struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); + struct sockaddr_dl *sdl; + + getsocket(); + if (rtmsg(RTM_GET) < 0) { xo_errx(1, "RTM_GET(%s) failed", host); /* NOTREACHED */ @@ -445,12 +465,8 @@ set(int argc, char **argv) if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) { if (sdl->sdl_family == AF_LINK && !(rtm->rtm_flags & RTF_GATEWAY)) { - switch (sdl->sdl_type) { - case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: - case IFT_ISO88024: case IFT_ISO88025: - case IFT_L2VLAN: case IFT_BRIDGE: + if (valid_type(sdl->sdl_type)) goto overwrite; - } } xo_warnx("cannot configure a new entry"); return 1; @@ -464,6 +480,24 @@ overwrite: sdl_m.sdl_type = sdl->sdl_type; sdl_m.sdl_index = sdl->sdl_index; return (rtmsg(RTM_ADD)); +#endif +} + +int +getaddr(char *host, struct sockaddr_in6 *sin6) +{ + struct addrinfo hints = { .ai_family = AF_INET6 }; + struct addrinfo *res; + + int gai_error = getaddrinfo(host, NULL, &hints, &res); + if (gai_error != 0) + return (gai_error); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + sin6->sin6_scope_id = + ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; + return (0); } /* @@ -473,55 +507,45 @@ static void get(char *host) { struct sockaddr_in6 *sin = &sin_m; - struct addrinfo hints, *res; int gai_error; sin_m = blank_sin; - bzero(&hints, sizeof(hints)); - hints.ai_family = AF_INET6; - gai_error = getaddrinfo(host, NULL, &hints, &res); + + gai_error = getaddr(host, sin); if (gai_error) { xo_warnx("%s: %s", host, gai_strerror(gai_error)); return; } - sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - sin->sin6_scope_id = - ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; - dump(sin, 0); - if (found_entry == 0) { + if (dump(sin, 0) == 0) { getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL ,0, - (nflag ? NI_NUMERICHOST : 0)); + (opts.nflag ? NI_NUMERICHOST : 0)); xo_errx(1, "%s (%s) -- no entry", host, host_buf); } } +#ifdef WITHOUT_NETLINK /* * Delete a neighbor cache entry */ static int -delete(char *host) +delete_rtsock(char *host) { struct sockaddr_in6 *sin = &sin_m; register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; register char *cp = m_rtmsg.m_space; struct sockaddr_dl *sdl; - struct addrinfo hints, *res; int gai_error; getsocket(); sin_m = blank_sin; - bzero(&hints, sizeof(hints)); - hints.ai_family = AF_INET6; - gai_error = getaddrinfo(host, NULL, &hints, &res); + gai_error = getaddr(host, sin); if (gai_error) { xo_warnx("%s: %s", host, gai_strerror(gai_error)); return 1; } - sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - sin->sin6_scope_id = - ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; + if (rtmsg(RTM_GET) < 0) { xo_errx(1, "RTM_GET(%s) failed", host); /* NOTREACHED */ @@ -552,7 +576,7 @@ delete: getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, sizeof(host_buf), NULL, 0, - (nflag ? NI_NUMERICHOST : 0)); + (opts.nflag ? NI_NUMERICHOST : 0)); xo_open_instance("neighbor-cache"); char *ifname = if_indextoname(sdl->sdl_index, ifix_buf); @@ -571,15 +595,11 @@ delete: return 0; } -#define W_ADDR 36 -#define W_LL 17 -#define W_IF 6 - /* * Dump the entire neighbor cache */ -static void -dump(struct sockaddr_in6 *addr, int cflag) +static int +dump_rtsock(struct sockaddr_in6 *addr, int cflag) { int mib[6]; size_t needed; @@ -596,7 +616,7 @@ dump(struct sockaddr_in6 *addr, int cflag) char *ifname; /* Print header */ - if (!tflag && !cflag) { + if (!opts.tflag && !cflag) { char xobuf[200]; snprintf(xobuf, sizeof(xobuf), "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n", @@ -626,6 +646,7 @@ again:; } else buf = lim = NULL; + int count = 0; for (next = buf; next && next < lim; next += rtm->rtm_msglen) { int isrouter = 0, prbs = 0; @@ -658,9 +679,9 @@ again:; &sin->sin6_addr) == 0 || addr->sin6_scope_id != sin->sin6_scope_id) continue; - found_entry = 1; } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)) continue; + count++; if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) { /* XXX: should scope id be filled in the kernel? */ @@ -668,7 +689,7 @@ again:; sin->sin6_scope_id = sdl->sdl_index; } getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, - sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0)); + sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)); if (cflag) { #ifdef RTF_WASCLONED if (rtm->rtm_flags & RTF_WASCLONED) @@ -684,7 +705,7 @@ again:; continue; } gettimeofday(&now, 0); - if (tflag) + if (opts.tflag) ts_print(&now); addrwidth = strlen(host_buf); @@ -795,6 +816,30 @@ again:; } xo_close_list("neighbor-cache"); + + return (count); +} +#endif + + +static int +delete(char *host) +{ +#ifndef WITHOUT_NETLINK + return (delete_nl(0, host)); +#else + return (delete_rtsock(host)); +#endif +} + +static int +dump(struct sockaddr_in6 *addr, int cflag) +{ +#ifndef WITHOUT_NETLINK + return (print_entries_nl(0, addr, cflag)); +#else + return (dump_rtsock(addr, cflag)); +#endif } static struct in6_nbrinfo * @@ -820,7 +865,7 @@ getnbrinfo(struct in6_addr *addr, int ifindex, int warning) return(&nbi); } -static char * +char * ether_str(struct sockaddr_dl *sdl) { static char hbuf[NI_MAXHOST]; @@ -869,6 +914,7 @@ usage(void) exit(1); } +#ifdef WITHOUT_NETLINK static int rtmsg(int cmd) { @@ -882,7 +928,7 @@ rtmsg(int cmd) if (cmd == RTM_DELETE) goto doit; bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); - rtm->rtm_flags = flags; + rtm->rtm_flags = opts.flags; rtm->rtm_version = RTM_VERSION; switch (cmd) { @@ -890,8 +936,8 @@ rtmsg(int cmd) xo_errx(1, "internal wrong cmd"); case RTM_ADD: rtm->rtm_addrs |= RTA_GATEWAY; - if (expire_time) { - rtm->rtm_rmx.rmx_expire = expire_time; + if (opts.expire_time) { + rtm->rtm_rmx.rmx_expire = opts.expire_time; rtm->rtm_inits = RTV_EXPIRE; } rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); @@ -922,6 +968,7 @@ doit: xo_warn("read from routing socket"); return (0); } +#endif static void ifinfo(char *ifname, int argc, char **argv) @@ -1129,9 +1176,9 @@ rtrlist(void) if (getnameinfo((struct sockaddr *)&p->rtaddr, p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0, - (nflag ? NI_NUMERICHOST : 0)) != 0) + (opts.nflag ? NI_NUMERICHOST : 0)) != 0) strlcpy(host_buf, "?", sizeof(host_buf)); - if (nflag) + if (opts.nflag) paddr = host_buf; else { inet_ntop(AF_INET6, &p->rtaddr.sin6_addr, abuf, sizeof(abuf)); @@ -1187,7 +1234,7 @@ plist(void) size_t l; struct timeval now; const int niflags = NI_NUMERICHOST; - int ninflags = nflag ? NI_NUMERICHOST : 0; + int ninflags = opts.nflag ? NI_NUMERICHOST : 0; char namebuf[NI_MAXHOST]; if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { @@ -1430,7 +1477,7 @@ getdefif(void) } #endif /* SIOCSDEFIFACE_IN6 */ -static char * +char * sec2str(time_t total) { static char result[256]; @@ -1475,7 +1522,7 @@ sec2str(time_t total) * Print the timestamp * from tcpdump/util.c */ -static void +void ts_print(const struct timeval *tvp) { int sec; diff --git a/usr.sbin/ndp/ndp.h b/usr.sbin/ndp/ndp.h new file mode 100644 index 000000000000..5b2558982e86 --- /dev/null +++ b/usr.sbin/ndp/ndp.h @@ -0,0 +1,27 @@ +#ifndef _USR_SBIN_NDP_NDP_H_ +#define _USR_SBIN_NDP_NDP_H_ + +#define W_ADDR 36 +#define W_LL 17 +#define W_IF 6 + +struct ndp_opts { + bool nflag; + bool tflag; + int flags; + time_t expire_time; +}; + +extern struct ndp_opts opts; + +bool valid_type(int if_type); +void ts_print(const struct timeval *tvp); +char *ether_str(struct sockaddr_dl *sdl); +char *sec2str(time_t total); +int getaddr(char *host, struct sockaddr_in6 *sin6); +int print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag); +int delete_nl(uint32_t ifindex, char *host); +int set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, + char *host); + +#endif diff --git a/usr.sbin/ndp/ndp_netlink.c b/usr.sbin/ndp/ndp_netlink.c new file mode 100644 index 000000000000..927cbf9ddcb9 --- /dev/null +++ b/usr.sbin/ndp/ndp_netlink.c @@ -0,0 +1,511 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gmt2local.h" + + +#include +#include +#include +#include +#include +#include + +#include +#include "ndp.h" + +#define RTF_ANNOUNCE RTF_PROTO2 + +static void +nl_init_socket(struct snl_state *ss) +{ + if (snl_init(ss, NETLINK_ROUTE)) + return; + + if (modfind("netlink") == -1 && errno == ENOENT) { + /* Try to load */ + if (kldload("netlink") == -1) + err(1, "netlink is not loaded and load attempt failed"); + if (snl_init(ss, NETLINK_ROUTE)) + return; + } + + err(1, "unable to open netlink socket"); +} + +static bool +get_link_info(struct snl_state *ss, uint32_t ifindex, + struct snl_parsed_link_simple *link) +{ + struct snl_writer nw; + + snl_init_writer(ss, &nw); + + struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); + struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg); + if (ifmsg != NULL) + ifmsg->ifi_index = ifindex; + if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + return (false); + + hdr = snl_read_reply(ss, hdr->nlmsg_seq); + + if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK) + return (false); + + if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link)) + return (false); + + return (true); +} + + + +static bool +has_l2(struct snl_state *ss, uint32_t ifindex) +{ + struct snl_parsed_link_simple link = {}; + + if (!get_link_info(ss, ifindex, &link)) + return (false); + + return (valid_type(link.ifi_type) != 0); +} + +static uint32_t +get_myfib() +{ + uint32_t fibnum = 0; + size_t len = sizeof(fibnum); + + sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0); + + return (fibnum); +} + +static void +ip6_writemask(struct in6_addr *addr6, uint8_t mask) +{ + uint32_t *cp; + + for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) + *cp++ = 0xFFFFFFFF; + if (mask > 0) + *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); +} +#define s6_addr32 __u6_addr.__u6_addr32 +#define IN6_MASK_ADDR(a, m) do { \ + (a)->s6_addr32[0] &= (m)->s6_addr32[0]; \ + (a)->s6_addr32[1] &= (m)->s6_addr32[1]; \ + (a)->s6_addr32[2] &= (m)->s6_addr32[2]; \ + (a)->s6_addr32[3] &= (m)->s6_addr32[3]; \ +} while (0) + +static int +guess_ifindex(struct snl_state *ss, uint32_t fibnum, const struct sockaddr_in6 *dst) +{ + struct snl_writer nw; + + if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) + return (dst->sin6_scope_id); + else if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) + return (0); + + + snl_init_writer(ss, &nw); + + struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE); + struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg); + rtm->rtm_family = AF_INET6; + + snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)dst); + snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum); + + if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + return (0); + + hdr = snl_read_reply(ss, hdr->nlmsg_seq); + + if (hdr->nlmsg_type != NL_RTM_NEWROUTE) { + /* No route found, unable to guess ifindex */ + return (0); + } + + struct snl_parsed_route r = {}; + if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) + return (0); + + if (r.rta_multipath || (r.rta_rtflags & RTF_GATEWAY)) + return (0); + + /* Check if the interface is of supported type */ + if (has_l2(ss, r.rta_oif)) + return (r.rta_oif); + + /* Check the case when we matched the loopback route for P2P */ + snl_init_writer(ss, &nw); + hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP); + snl_reserve_msg_object(&nw, struct nhmsg); + + int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD); + snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id); + snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET); + snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum); + snl_end_attr_nested(&nw, off); + + if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + return (0); + + hdr = snl_read_reply(ss, hdr->nlmsg_seq); + + if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) { + /* No nexthop found, unable to guess ifindex */ + return (0); + } + + struct snl_parsed_nhop nh = {}; + if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh)) + return (0); + + return (nh.nhaf_aif); +} + +static uint32_t +fix_ifindex(struct snl_state *ss, uint32_t ifindex, const struct sockaddr_in6 *sa) +{ + if (ifindex == 0) + ifindex = guess_ifindex(ss, get_myfib(), sa); + return (ifindex); +} + +static void +print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link) +{ + struct timeval now; + char host_buf[NI_MAXHOST]; + int addrwidth; + int llwidth; + int ifwidth; + char *ifname; + + getnameinfo(neigh->nda_dst, sizeof(struct sockaddr_in6), host_buf, + sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)); + + gettimeofday(&now, 0); + if (opts.tflag) + ts_print(&now); + + struct sockaddr_dl sdl = { + .sdl_family = AF_LINK, + .sdl_type = link->ifi_type, + .sdl_len = sizeof(struct sockaddr_dl), + .sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr), + }; + memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen); + + 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 = link->ifla_ifname; + ifwidth = strlen(ifname); + if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) + ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; + + xo_open_instance("neighbor-cache"); + /* Compose format string for libxo, as it doesn't support *.* */ + char xobuf[200]; + snprintf(xobuf, sizeof(xobuf), + "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}", + addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth); + xo_emit(xobuf, host_buf, ether_str(&sdl), ifname); + + /* Print neighbor discovery specific information */ + uint32_t expire = neigh->ndaf_next_ts; + int expire_in = expire - now.tv_sec; + if (expire > now.tv_sec) + xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in); + else if (expire == 0) + xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent"); + else + xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in); + + const char *lle_state = ""; + switch (neigh->ndm_state) { + case NUD_INCOMPLETE: + lle_state = "I"; + break; + case NUD_REACHABLE: + lle_state = "R"; + break; + case NUD_STALE: + lle_state = "S"; + break; + case NUD_DELAY: + lle_state = "D"; + break; + case NUD_PROBE: + lle_state = "P"; + break; + case NUD_FAILED: + lle_state = "F"; + break; + default: + lle_state = "N"; + break; + } + xo_emit(" {:neighbor-state/%s}", lle_state); + + bool isrouter = neigh->ndm_flags & NTF_ROUTER; + + /* + * other flags. R: router, P: proxy, W: ?? + */ + char flgbuf[8]; + snprintf(flgbuf, sizeof(flgbuf), "%s%s", + isrouter ? "R" : "", + (neigh->ndm_flags & NTF_PROXY) ? "p" : ""); + xo_emit(" {:nd-flags/%s}", flgbuf); + + if (neigh->nda_probes != 0) + xo_emit("{u:/ %d}", neigh->nda_probes); + + xo_emit("\n"); + xo_close_instance("neighbor-cache"); +} + +int +print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag) +{ + struct snl_state ss_req = {}, ss_cmd = {}; + struct snl_parsed_link_simple link = {}; + struct snl_writer nw; + + nl_init_socket(&ss_req); + snl_init_writer(&ss_req, &nw); + + struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH); + struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); + if (ndmsg != NULL) { + ndmsg->ndm_family = AF_INET6; + ndmsg->ndm_ifindex = ifindex; + } + + if (!snl_finalize_msg(&nw) || !snl_send_message(&ss_req, hdr)) { + snl_free(&ss_req); + return (0); + } + + uint32_t nlmsg_seq = hdr->nlmsg_seq; + struct snl_errmsg_data e = {}; + int count = 0; + nl_init_socket(&ss_cmd); + + /* Print header */ + if (!opts.tflag && !cflag) { + char xobuf[200]; + snprintf(xobuf, sizeof(xobuf), + "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n", + W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF); + xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags"); + } + xo_open_list("neighbor-cache"); + + while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) { + struct snl_parsed_neigh neigh = {}; + + if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh)) + continue; + + if (neigh.nda_ifindex != link.ifi_index) { + snl_clear_lb(&ss_cmd); + memset(&link, 0, sizeof(link)); + if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link)) + continue; + } + + /* TODO: embed LL in the parser */ + struct sockaddr_in6 *dst = (struct sockaddr_in6 *)neigh.nda_dst; + if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) + dst->sin6_scope_id = neigh.nda_ifindex; + + if (addr != NULL) { + if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, + &dst->sin6_addr) == 0 || + addr->sin6_scope_id != dst->sin6_scope_id) + continue; + } + + print_entry(&neigh, &link); + if (cflag) { + char dst_str[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, &dst->sin6_addr, dst_str, sizeof(dst_str)); + delete_nl(neigh.nda_ifindex, dst_str); + } + count++; + snl_clear_lb(&ss_req); + } + xo_close_list("neighbor-cache"); + + snl_free(&ss_req); + snl_free(&ss_cmd); + + return (count); +} + +int +delete_nl(uint32_t ifindex, char *host) +{ + struct snl_state ss = {}; + struct snl_writer nw; + struct sockaddr_in6 dst; + + int gai_error = getaddr(host, &dst); + if (gai_error) { + xo_warnx("%s: %s", host, gai_strerror(gai_error)); + return 1; + } + + nl_init_socket(&ss); + + ifindex = fix_ifindex(&ss, ifindex, &dst); + if (ifindex == 0) { + xo_warnx("delete: cannot locate %s", host); + snl_free(&ss); + return (0); + } + + snl_init_writer(&ss, &nw); + struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH); + struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); + if (ndmsg != NULL) { + ndmsg->ndm_family = AF_INET6; + ndmsg->ndm_ifindex = ifindex; + } + snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst); + + if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) { + snl_free(&ss); + return (1); + } + + struct snl_errmsg_data e = {}; + snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); + if (e.error != 0) { + if (e.error_str != NULL) + xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str); + else + xo_warnx("delete %s: %s", host, strerror(e.error)); + } else { + char host_buf[NI_MAXHOST]; + char ifix_buf[IFNAMSIZ]; + + getnameinfo((struct sockaddr *)&dst, *** 73 LINES SKIPPED ***