The branch main has been updated by melifaro:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=91fbe0819bb9c6e1a5e5b854075deb51742eb41f

commit 91fbe0819bb9c6e1a5e5b854075deb51742eb41f
Author:     Alexander V. Chernikov <[email protected]>
AuthorDate: 2023-04-25 12:30:39 +0000
Commit:     Alexander V. Chernikov <[email protected]>
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 <bsd.prog.mk>
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 <netdb.h>
 #include <errno.h>
 #include <nlist.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
 #include <paths.h>
@@ -112,6 +113,8 @@
 #include <libxo/xo.h>
 #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 <sys/param.h>
+#include <sys/module.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/linker.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 <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <netinet/icmp6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <errno.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <string.h>
+#include <paths.h>
+#include <err.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libxo/xo.h>
+#include "gmt2local.h"
+
+
+#include <netlink/netlink.h>
+#include <netlink/netlink_route.h>
+#include <netlink/netlink_snl.h>
+#include <netlink/netlink_snl_route.h>
+#include <netlink/netlink_snl_route_compat.h>
+#include <netlink/netlink_snl_route_parsers.h>
+
+#include <libxo/xo.h>
+#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 ***

Reply via email to