From nobody Tue Mar 28 09:08:53 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 4Pm3lP71WNz41dr5; Tue, 28 Mar 2023 09:09:05 +0000 (UTC) (envelope-from melifaro@freebsd.org) Received: from smtp.freebsd.org (smtp.freebsd.org [96.47.72.83]) (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 "smtp.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Pm3lP5V7kz45f1; Tue, 28 Mar 2023 09:09:05 +0000 (UTC) (envelope-from melifaro@freebsd.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1679994545; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=u0aS1zbuHVDAhIbrl6k3MvxBsLWGW331futebHNN180=; b=SD5h/Q6T5LmXOj5aBYlhU2cEayXvXCntHxWCsgGRZ2XSrJxrNm0dYSlzTZ/9LGcL+9vH5D UFLF7kyvTWaLRFnEluspVZz5xnXYs2UH8LjPpMGnhwRinZJT3NTVAmrVzbDIbz6YBliBR4 pu+gdsfMTwqckFnbpRys77Ad388ATIpa9JpUNol5Is5KjKcq3N54Ddrb/8nAsozWtbUQiJ QYkRVhh4EfIhctE/Wg9G1njPsapcW8xTARirExnBTxB6SNM4ouqts0YSoiNPAXM436H8dY sJQkxYBzSWEQ0XyxJ7KuUhzGSMVaihyNvrxJ7FDAsjPbxrGY0IxT4ovx65IabQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1679994545; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=u0aS1zbuHVDAhIbrl6k3MvxBsLWGW331futebHNN180=; b=eovpuNQIS7rL7HRCRtqt8mD0ntoAVWmUpQl3w7f6nvbV5eOL8eN6Hp1I9Hf62OuR2/5ULd XNLEPD6uSSXoucAvOw46cPh+dRqtP0HmRSQCSx5QrM6e39TZms8YRvg+bbhPhjvtetbeqf 0DunVyPehC1kRVj2UFxJVM31t8gzb8VOZXYDiZUO7Yng6q3YTIVLUDCjydol7EF23ry0QB jsCWHIJRdVn3kIflSs+bTAsmYvDIxoGn63pPW1byqzcaC0O3P3cz0CIOhGgKuvnqGPEnWE 4oBMI5naR5bFj1pQRn3NXj+uQXTF+RlcXtIlJimYfvr35HCMAhRC4I22p5bMYQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1679994545; a=rsa-sha256; cv=none; b=yXkQAax6PwfERi/EYgRP+MUnXuYtJRE2QajLES5Q0zvDCemhOTn8QQZ95insAkKF5KKmHC xWpcxlV1U7xfnzCPyW5exYJdmoYQJFvP9Iz6gkLHFjhIFjGfAr1WFoDzKQGohWoUIHTHFv XuVAeEBVwfWN5gmJmZq0Vs7yRTHxsAymd0v5H+HApZIjR6/wdAzcShaCrpLFQYxIixSvP0 ewFOTcKyfFmdRcYcp6cEtrnK0FKMNxupAY4CH3Seq0uVkRGqS3xSHn5GsdvPHSJFIbI2GY gwXeOXbwGQEB6K+11kg0flZpsx6/89mAK7Hsn4AYqSDqasWds1Uoo3UkEbgMsg== Received: from smtpclient.apple (unknown [IPv6:2a02:8084:d6bb:510:198e:52cc:ae4c:3466]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) (Authenticated sender: melifaro/mail) by smtp.freebsd.org (Postfix) with ESMTPSA id 4Pm3lN5k16zXYN; Tue, 28 Mar 2023 09:09:04 +0000 (UTC) (envelope-from melifaro@freebsd.org) From: Alexander Chernikov Message-Id: Content-Type: multipart/alternative; boundary="Apple-Mail=_AE460F85-A90B-4D1C-9453-E5B4D7CEC4B9" 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 (Mac OS X Mail 16.0 \(3731.400.51.1.1\)) Subject: Re: git: c597432e2297 - main - route(8): convert to netlink Date: Tue, 28 Mar 2023 10:08:53 +0100 In-Reply-To: <81A0DF64-F466-451F-83CB-86A9FC1EB300@freebsd.org> Cc: "src-committers@freebsd.org" , "dev-commits-src-all@freebsd.org" , "dev-commits-src-main@freebsd.org" To: Jessica Clarke References: <202303261107.32QB7UZK058893@gitrepo.freebsd.org> <81A0DF64-F466-451F-83CB-86A9FC1EB300@freebsd.org> X-Mailer: Apple Mail (2.3731.400.51.1.1) X-ThisMailContainsUnwantedMimeParts: N --Apple-Mail=_AE460F85-A90B-4D1C-9453-E5B4D7CEC4B9 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On 27 Mar 2023, at 21:41, Jessica Clarke wrote: >=20 > On 26 Mar 2023, at 12:07, Alexander V. Chernikov > wrote: >>=20 >> The branch main has been updated by melifaro: >>=20 >> URL: = https://cgit.FreeBSD.org/src/commit/?id=3Dc597432e22975f4d409b845377996712= 9c6b57e9 >>=20 >> commit c597432e22975f4d409b8453779967129c6b57e9 >> Author: Alexander V. Chernikov >> AuthorDate: 2023-03-26 09:13:50 +0000 >> Commit: Alexander V. Chernikov >> CommitDate: 2023-03-26 11:06:56 +0000 >>=20 >> route(8): convert to netlink >>=20 >> This change converts all kernel rtsock interactions in route(8) >> to Netlink. >>=20 >> Based on the WITHOUT_NETLINK_SUPPORT src.conf(5) variable, route(8) >> now fully operates either via Netlink or via rtsock/sysctl. >> The default (compile-time) is Netlink. >>=20 >> The output for route delete/add/get/flush is targeted to be exactly >> the same (apart from some error handling cases). >> The output for the route monitor has been changed to improve >> readability and support netlink models. >>=20 >> Other behaviour changes: >> * exact prefix lookup (route -n get a.b.c.d/e) is not yet = supported. >> * route monitor does not show the change originator yet. >=20 > If there are regressions then it should be off by default, = *especially* Hi Jessica, I have a plan to fix both this week. I=E2=80=99d also want to note that = these two are not the documented / often used features. > when we=E2=80=99re just under a month out from the proposed 14 code = slush. >=20 > Jess >=20 >> Differential Revision: https://reviews.freebsd.org/D39007 >> --- >> sbin/route/Makefile | 6 + >> sbin/route/route.c | 117 +++++-- >> sbin/route/route_netlink.c | 835 = +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 930 insertions(+), 28 deletions(-) >>=20 >> diff --git a/sbin/route/Makefile b/sbin/route/Makefile >> index e65030f805bb..aec222310d46 100644 >> --- a/sbin/route/Makefile >> +++ b/sbin/route/Makefile >> @@ -19,6 +19,12 @@ CFLAGS+=3D -DINET6 >> .endif >> CFLAGS+=3D -I. >>=20 >> +.if ${MK_NETLINK_SUPPORT} !=3D "no" >> +SRCS+=3D route_netlink.c >> +.else >> +CFLAGS+=3D-DWITHOUT_NETLINK >> +.endif >> + >> HAS_TESTS=3D >> SUBDIR.${MK_TESTS}+=3D tests >>=20 >> diff --git a/sbin/route/route.c b/sbin/route/route.c >> index 5f33cecb1b20..947c97ce794a 100644 >> --- a/sbin/route/route.c >> +++ b/sbin/route/route.c >> @@ -90,12 +90,11 @@ static struct keytab { >> {0, 0} >> }; >>=20 >> +int verbose, debugonly; >> static struct sockaddr_storage so[RTAX_MAX]; >> static int pid, rtm_addrs; >> -static int s; >> -static int nflag, af, qflag, tflag; >> -static int verbose, aflen; >> -static int locking, lockrest, debugonly; >> +static int nflag, af, aflen, qflag, tflag; >> +static int locking, lockrest; >> static struct rt_metrics rt_metrics; >> static u_long rtm_inits; >> static uid_t uid; >> @@ -103,18 +102,30 @@ static int defaultfib; >> static int numfibs; >> static char domain[MAXHOSTNAMELEN + 1]; >> static bool domain_initialized; >> -static int rtm_seq; >> static char rt_line[NI_MAXHOST]; >> static char net_line[MAXHOSTNAMELEN + 1]; >>=20 >> +#ifdef WITHOUT_NETLINK >> +static int s; >> +static int rtm_seq; >> + >> static struct { >> struct rt_msghdr m_rtm; >> char m_space[512]; >> } m_rtmsg; >>=20 >> +static int rtmsg_rtsock(int, int, int); >> +static int flushroutes_fib_rtsock(int); >> +static void monitor_rtsock(void); >> +#else >> +int rtmsg_nl(int, int, int, struct sockaddr_storage *, = struct rt_metrics *); >> +int flushroutes_fib_nl(int, int); >> +void monitor_nl(int); >> +#endif >> + >> static TAILQ_HEAD(fibl_head_t, fibl) fibl_head; >>=20 >> -static void printb(int, const char *); >> +void printb(int, const char *); >> static void flushroutes(int argc, char *argv[]); >> static int flushroutes_fib(int); >> static int getaddr(int, char *, int); >> @@ -127,7 +138,7 @@ static int inet6_makenetandmask(struct = sockaddr_in6 *, const char *); >> #endif >> static void interfaces(void); >> static void monitor(int, char*[]); >> -static const char *netname(struct sockaddr *); >> +const char *netname(struct sockaddr *); >> static void newroute(int, char **); >> static int newroute_fib(int, char *, int); >> static void pmsg_addrs(char *, int, size_t); >> @@ -135,7 +146,7 @@ static void pmsg_common(struct rt_msghdr *, = size_t); >> static int prefixlen(const char *); >> static void print_getmsg(struct rt_msghdr *, int, int); >> static void print_rtmsg(struct rt_msghdr *, size_t); >> -static const char *routename(struct sockaddr *); >> +const char *routename(struct sockaddr *); >> static int rtmsg(int, int, int); >> static void set_metric(char *, int); >> static int set_sofib(int); >> @@ -216,12 +227,14 @@ main(int argc, char **argv) >>=20 >> pid =3D getpid(); >> uid =3D geteuid(); >> +#ifdef WITHOUT_NETLINK >> if (tflag) >> s =3D open(_PATH_DEVNULL, O_WRONLY, 0); >> else >> s =3D socket(PF_ROUTE, SOCK_RAW, 0); >> if (s < 0) >> err(EX_OSERR, "socket"); >> +#endif >>=20 >> len =3D sizeof(numfibs); >> if (sysctlbyname("net.fibs", (void *)&numfibs, &len, NULL, 0) =3D=3D= -1) >> @@ -264,10 +277,14 @@ static int >> set_sofib(int fib) >> { >>=20 >> +#ifdef WITHOUT_NETLINK >> if (fib < 0) >> return (0); >> return (setsockopt(s, SOL_SOCKET, SO_SETFIB, (void *)&fib, >> sizeof(fib))); >> +#else >> + return (0); >> +#endif >> } >>=20 >> static int >> @@ -395,7 +412,9 @@ flushroutes(int argc, char *argv[]) >>=20 >> if (uid !=3D 0 && !debugonly && !tflag) >> errx(EX_NOPERM, "must be root to alter routing table"); >> +#ifdef WITHOUT_NETLINK >> shutdown(s, SHUT_RD); /* Don't want to read back our messages */ >> +#endif >>=20 >> TAILQ_INIT(&fibl_head); >> while (argc > 1) { >> @@ -441,6 +460,17 @@ flushroutes(int argc, char *argv[]) >>=20 >> static int >> flushroutes_fib(int fib) >> +{ >> +#ifdef WITHOUT_NETLINK >> + return (flushroutes_fib_rtsock(fib)); >> +#else >> + return (flushroutes_fib_nl(fib, af)); >> +#endif >> +} >> + >> +#ifdef WITHOUT_NETLINK >> +static int >> +flushroutes_fib_rtsock(int fib) >> { >> struct rt_msghdr *rtm; >> size_t needed; >> @@ -525,8 +555,9 @@ retry: >> free(buf); >> return (error); >> } >> +#endif >>=20 >> -static const char * >> +const char * >> routename(struct sockaddr *sa) >> { >> struct sockaddr_dl *sdl; >> @@ -645,7 +676,7 @@ routename(struct sockaddr *sa) >> * Return the name of the network whose address is given. >> * The address is assumed to be that of a net, not a host. >> */ >> -static const char * >> +const char * >> netname(struct sockaddr *sa) >> { >> struct sockaddr_dl *sdl; >> @@ -810,8 +841,10 @@ newroute(int argc, char **argv) >> warn("sigaction SIGALRM"); >>=20 >> cmd =3D argv[0]; >> +#ifdef WITHOUT_NETLINK >> if (*cmd !=3D 'g' && *cmd !=3D 's') >> shutdown(s, SHUT_RD); /* Don't want to read back our = messages */ >> +#endif >> while (--argc > 0) { >> if (**(++argv)=3D=3D '-') { >> switch (key =3D keyword(1 + *argv)) { >> @@ -1398,8 +1431,8 @@ retry2: >> static void >> monitor(int argc, char *argv[]) >> { >> - int n, fib, error; >> - char msg[2048], *endptr; >> + int fib, error; >> + char *endptr; >>=20 >> fib =3D defaultfib; >> while (argc > 1) { >> @@ -1435,6 +1468,19 @@ monitor(int argc, char *argv[]) >> interfaces(); >> exit(0); >> } >> +#ifdef WITHOUT_NETLINK >> + monitor_rtsock(); >> +#else >> + monitor_nl(fib); >> +#endif >> +} >> + >> +#ifdef WITHOUT_NETLINK >> +static void >> +monitor_rtsock(void) >> +{ >> + char msg[2048]; >> + int n; >>=20 >> #ifdef SO_RERROR >> n =3D 1; >> @@ -1454,25 +1500,12 @@ monitor(int argc, char *argv[]) >> print_rtmsg((struct rt_msghdr *)(void *)msg, n); >> } >> } >> +#endif >>=20 >> static int >> rtmsg(int cmd, int flags, int fib) >> { >> - int rlen; >> - char *cp =3D m_rtmsg.m_space; >> - int l; >> - >> -#define NEXTADDR(w, u) = \ >> - if (rtm_addrs & (w)) { = \ >> - l =3D SA_SIZE(&(u)); = \ >> - memmove(cp, (char *)&(u), l); = \ >> - cp +=3D l; = \ >> - if (verbose) = \ >> - sodump((struct sockaddr *)&(u), #w); = \ >> - } >> - >> errno =3D 0; >> - memset(&m_rtmsg, 0, sizeof(m_rtmsg)); >> if (cmd =3D=3D 'a') >> cmd =3D RTM_ADD; >> else if (cmd =3D=3D 'c') >> @@ -1488,6 +1521,33 @@ rtmsg(int cmd, int flags, int fib) >> cmd =3D RTM_DELETE; >> flags |=3D RTF_PINNED; >> } >> +#ifdef WITHOUT_NETLINK >> + return (rtmsg_rtsock(cmd, flags, fib)); >> +#else >> + errno =3D rtmsg_nl(cmd, flags, fib, so, &rt_metrics); >> + return (errno =3D=3D 0 ? 0 : -1); >> +#endif >> +} >> + >> +#ifdef WITHOUT_NETLINK >> +static int >> +rtmsg_rtsock(int cmd, int flags, int fib) >> +{ >> + int rlen; >> + char *cp =3D m_rtmsg.m_space; >> + int l; >> + >> + memset(&m_rtmsg, 0, sizeof(m_rtmsg)); >> + >> +#define NEXTADDR(w, u) = \ >> + if (rtm_addrs & (w)) { = \ >> + l =3D SA_SIZE(&(u)); = \ >> + memmove(cp, (char *)&(u), l); = \ >> + cp +=3D l; = \ >> + if (verbose) = \ >> + sodump((struct sockaddr *)&(u), #w); = \ >> + } >> + >> #define rtm m_rtmsg.m_rtm >> rtm.rtm_type =3D cmd; >> rtm.rtm_flags =3D flags; >> @@ -1545,6 +1605,7 @@ rtmsg(int cmd, int flags, int fib) >> #undef rtm >> return (0); >> } >> +#endif >>=20 >> static const char *const msgtypes[] =3D { >> "", >> @@ -1571,7 +1632,7 @@ static const char *const msgtypes[] =3D { >> static const char metricnames[] =3D >> "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire" >> "\1mtu"; >> -static const char routeflags[] =3D >> +const char routeflags[] =3D >> "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE" >> "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE" >> "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3" >> @@ -1812,7 +1873,7 @@ pmsg_addrs(char *cp, int addrs, size_t len) >> (void)fflush(stdout); >> } >>=20 >> -static void >> +void >> printb(int b, const char *str) >> { >> int i; >> diff --git a/sbin/route/route_netlink.c b/sbin/route/route_netlink.c >> new file mode 100644 >> index 000000000000..648d866670fc >> --- /dev/null >> +++ b/sbin/route/route_netlink.c >> @@ -0,0 +1,835 @@ >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include >> +#include >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +const char *routename(struct sockaddr *); >> +const char *netname(struct sockaddr *); >> +void printb(int, const char *); >> +extern const char routeflags[]; >> +extern int verbose, debugonly; >> + >> +int rtmsg_nl(int cmd, int rtm_flags, int fib, struct = sockaddr_storage *so, >> + struct rt_metrics *rt_metrics); >> +int flushroutes_fib_nl(int fib, int af); >> +void monitor_nl(int fib); >> + >> +struct nl_helper; >> +static void print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, = struct sockaddr *dst); >> +static void print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr); >> + >> +#define s6_addr32 __u6_addr.__u6_addr32 >> +#define bitcount32(x) __bitcount32((uint32_t)(x)) >> +static int >> +inet6_get_plen(const struct in6_addr *addr) >> +{ >> + >> + return (bitcount32(addr->s6_addr32[0]) + = bitcount32(addr->s6_addr32[1]) + >> + bitcount32(addr->s6_addr32[2]) + = bitcount32(addr->s6_addr32[3])); >> +} >> + >> +static void >> +ip6_writemask(struct in6_addr *addr6, uint8_t mask) >> +{ >> + uint32_t *cp; >> + >> + for (cp =3D (uint32_t *)addr6; mask >=3D 32; mask -=3D 32) >> + *cp++ =3D 0xFFFFFFFF; >> + if (mask > 0) >> + *cp =3D htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); >> +} >> + >> +static struct sockaddr * >> +get_netmask(struct snl_state *ss, int family, int plen) >> +{ >> + if (family =3D=3D AF_INET) { >> + if (plen =3D=3D 32) >> + return (NULL); >> + >> + struct sockaddr_in *sin =3D snl_allocz(ss, = sizeof(*sin)); >> + >> + sin->sin_len =3D sizeof(*sin); >> + sin->sin_family =3D family; >> + sin->sin_addr.s_addr =3D htonl(plen ? ~((1 << (32 - = plen)) - 1) : 0); >> + >> + return (struct sockaddr *)sin; >> + } else if (family =3D=3D AF_INET6) { >> + if (plen =3D=3D 128) >> + return (NULL); >> + >> + struct sockaddr_in6 *sin6 =3D snl_allocz(ss, = sizeof(*sin6)); >> + >> + sin6->sin6_len =3D sizeof(*sin6); >> + sin6->sin6_family =3D family; >> + ip6_writemask(&sin6->sin6_addr, plen); >> + >> + return (struct sockaddr *)sin6; >> + } >> + return (NULL); >> +} >> + >> +struct nl_helper { >> + struct snl_state ss_cmd; >> +}; >> + >> +static void >> +nl_helper_init(struct nl_helper *h) >> +{ >> + if (!snl_init(&h->ss_cmd, NETLINK_ROUTE)) >> + err(1, "unable to open netlink socket"); >> +} >> + >> +static void >> +nl_helper_free(struct nl_helper *h) >> +{ >> + snl_free(&h->ss_cmd); >> +} >> + >> +static int >> +rtmsg_nl_int(struct nl_helper *h, int cmd, int rtm_flags, int fib, >> + struct sockaddr_storage *so, struct rt_metrics *rt_metrics) >> +{ >> + struct snl_state *ss =3D &h->ss_cmd; >> + struct snl_writer nw; >> + int nl_type =3D 0, nl_flags =3D 0; >> + >> + snl_init_writer(ss, &nw); >> + >> + switch (cmd) { >> + case RTSOCK_RTM_ADD: >> + nl_type =3D RTM_NEWROUTE; >> + nl_flags =3D NLM_F_CREATE | NLM_F_APPEND; /* Do append = by default */ >> + break; >> + case RTSOCK_RTM_CHANGE: >> + nl_type =3D RTM_NEWROUTE; >> + nl_flags =3D NLM_F_REPLACE; >> + break; >> + case RTSOCK_RTM_DELETE: >> + nl_type =3D RTM_DELROUTE; >> + break; >> + case RTSOCK_RTM_GET: >> + nl_type =3D RTM_GETROUTE; >> + break; >> + default: >> + exit(1); >> + } >> + >> + struct sockaddr *dst =3D (struct sockaddr *)&so[RTAX_DST]; >> + struct sockaddr *mask =3D (struct sockaddr *)&so[RTAX_NETMASK]; >> + struct sockaddr *gw =3D (struct sockaddr *)&so[RTAX_GATEWAY]; >> + >> + if (dst =3D=3D NULL) >> + return (EINVAL); >> + >> + struct nlmsghdr *hdr =3D snl_create_msg_request(&nw, nl_type); >> + hdr->nlmsg_flags |=3D nl_flags; >> + >> + int plen =3D 0; >> + int rtm_type =3D RTN_UNICAST; >> + >> + switch (dst->sa_family) { >> + case AF_INET: >> + { >> + struct sockaddr_in *mask4 =3D (struct sockaddr_in = *)mask; >> + >> + plen =3D mask4 ? bitcount32(mask4->sin_addr.s_addr) : = 32; >> + break; >> + } >> + case AF_INET6: >> + { >> + struct sockaddr_in6 *mask6 =3D (struct sockaddr_in6 = *)mask; >> + >> + plen =3D mask6 ? inet6_get_plen(&mask6->sin6_addr) : = 128; >> + break; >> + } >> + default: >> + return (ENOTSUP); >> + } >> + >> + if (rtm_flags & RTF_REJECT) >> + rtm_type =3D RTN_PROHIBIT; >> + else if (rtm_flags & RTF_BLACKHOLE) >> + rtm_type =3D RTN_BLACKHOLE; >> + >> + struct rtmsg *rtm =3D snl_reserve_msg_object(&nw, struct rtmsg); >> + rtm->rtm_family =3D dst->sa_family; >> + rtm->rtm_protocol =3D RTPROT_STATIC; >> + rtm->rtm_type =3D rtm_type; >> + rtm->rtm_dst_len =3D plen; >> + >> + snl_add_msg_attr_ip(&nw, RTA_DST, dst); >> + snl_add_msg_attr_u32(&nw, RTA_TABLE, fib); >> + >> + if (rtm_flags & RTF_GATEWAY) { >> + if (gw->sa_family =3D=3D dst->sa_family) >> + snl_add_msg_attr_ip(&nw, RTA_GATEWAY, gw); >> + else >> + snl_add_msg_attr_ipvia(&nw, RTA_VIA, gw); >> + } else if (gw !=3D NULL) { >> + /* Should be AF_LINK */ >> + struct sockaddr_dl *sdl =3D (struct sockaddr_dl *)gw; >> + if (sdl->sdl_index !=3D 0) >> + snl_add_msg_attr_u32(&nw, RTA_OIF, = sdl->sdl_index); >> + } >> + >> + if (rtm_flags !=3D 0) >> + snl_add_msg_attr_u32(&nw, NL_RTA_RTFLAGS, rtm_flags); >> + >> + if (rt_metrics->rmx_mtu > 0) { >> + int off =3D snl_add_msg_attr_nested(&nw, RTA_METRICS); >> + snl_add_msg_attr_u32(&nw, RTAX_MTU, = rt_metrics->rmx_mtu); >> + snl_end_attr_nested(&nw, off); >> + } >> + >> + if (rt_metrics->rmx_weight > 0) >> + snl_add_msg_attr_u32(&nw, NL_RTA_WEIGHT, = rt_metrics->rmx_weight); >> + >> + if (snl_finalize_msg(&nw) && snl_send_message(ss, hdr)) { >> + struct snl_errmsg_data e =3D {}; >> + >> + hdr =3D snl_read_reply(ss, hdr->nlmsg_seq); >> + if (nl_type =3D=3D NL_RTM_GETROUTE) { >> + if (hdr->nlmsg_type =3D=3D NL_RTM_NEWROUTE) >> + print_getmsg(h, hdr, dst); >> + else { >> + snl_parse_errmsg(ss, hdr, &e); >> + if (e.error =3D=3D ESRCH) >> + warn("route has not been = found"); >> + else >> + warn("message indicates error = %d", e.error); >> + } >> + >> + return (0); >> + } >> + >> + if (snl_parse_errmsg(ss, hdr, &e)) >> + return (e.error); >> + } >> + return (EINVAL); >> +} >> + >> +int >> +rtmsg_nl(int cmd, int rtm_flags, int fib, struct sockaddr_storage = *so, >> + struct rt_metrics *rt_metrics) >> +{ >> + struct nl_helper h =3D {}; >> + >> + nl_helper_init(&h); >> + int error =3D rtmsg_nl_int(&h, cmd, rtm_flags, fib, so, = rt_metrics); >> + nl_helper_free(&h); >> + >> + return (error); >> +} >> + >> +static void >> +get_ifdata(struct nl_helper *h, uint32_t ifindex, struct = snl_parsed_link_simple *link) >> +{ >> + struct snl_state *ss =3D &h->ss_cmd; >> + struct snl_writer nw; >> + >> + snl_init_writer(ss, &nw); >> + struct nlmsghdr *hdr =3D snl_create_msg_request(&nw, = NL_RTM_GETLINK); >> + struct ifinfomsg *ifmsg =3D snl_reserve_msg_object(&nw, struct = ifinfomsg); >> + if (ifmsg !=3D NULL) >> + ifmsg->ifi_index =3D ifindex; >> + if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) >> + return; >> + >> + hdr =3D snl_read_reply(ss, hdr->nlmsg_seq); >> + >> + if (hdr !=3D NULL && hdr->nlmsg_type =3D=3D RTM_NEWLINK) { >> + snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, = link); >> + } >> + >> + if (link->ifla_ifname =3D=3D NULL) { >> + char ifname[16]; >> + >> + snprintf(ifname, sizeof(ifname), "if#%u", ifindex); >> + int len =3D strlen(ifname); >> + char *buf =3D snl_allocz(ss, len + 1); >> + strlcpy(buf, ifname, len + 1); >> + link->ifla_ifname =3D buf; >> + } >> +} >> + >> +static void >> +print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct = sockaddr *dst) >> +{ >> + struct snl_state *ss =3D &h->ss_cmd; >> + struct timespec ts; >> + struct snl_parsed_route r =3D { .rtax_weight =3D = RT_DEFAULT_WEIGHT }; >> + >> + if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) >> + return; >> + >> + struct snl_parsed_link_simple link =3D {}; >> + get_ifdata(h, r.rta_oif, &link); >> + >> + if (r.rtax_mtu =3D=3D 0) >> + r.rtax_mtu =3D link.ifla_mtu; >> + r.rta_rtflags |=3D (RTF_UP | RTF_DONE); >> + >> + (void)printf(" route to: %s\n", routename(dst)); >> + >> + if (r.rta_dst) >> + (void)printf("destination: %s\n", routename(r.rta_dst)); >> + struct sockaddr *mask =3D get_netmask(ss, r.rtm_family, = r.rtm_dst_len); >> + if (mask) >> + (void)printf(" mask: %s\n", routename(mask)); >> + if (r.rta_gw && (r.rta_rtflags & RTF_GATEWAY)) >> + (void)printf(" gateway: %s\n", routename(r.rta_gw)); >> + (void)printf(" fib: %u\n", (unsigned int)r.rta_table); >> + if (link.ifla_ifname) >> + (void)printf(" interface: %s\n", link.ifla_ifname); >> + (void)printf(" flags: "); >> + printb(r.rta_rtflags, routeflags); >> + >> + struct rt_metrics rmx =3D { >> + .rmx_mtu =3D r.rtax_mtu, >> + .rmx_weight =3D r.rtax_weight, >> + .rmx_expire =3D r.rta_expire, >> + }; >> + >> + printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe", >> + "sendpipe", "ssthresh", "rtt,msec", "mtu ", "weight", = "expire"); >> + printf("%8lu ", rmx.rmx_recvpipe); >> + printf("%8lu ", rmx.rmx_sendpipe); >> + printf("%8lu ", rmx.rmx_ssthresh); >> + printf("%8lu ", 0UL); >> + printf("%8lu ", rmx.rmx_mtu); >> + printf("%8lu ", rmx.rmx_weight); >> + if (rmx.rmx_expire > 0) >> + clock_gettime(CLOCK_REALTIME_FAST, &ts); >> + else >> + ts.tv_sec =3D 0; >> + printf("%8ld \n", (long)(rmx.rmx_expire - ts.tv_sec)); >> +} >> + >> +static void >> +print_prefix(struct nl_helper *h, char *buf, int bufsize, struct = sockaddr *sa, int plen) >> +{ >> + int sz =3D 0; >> + >> + if (sa =3D=3D NULL) { >> + snprintf(buf, bufsize, ""); >> + return; >> + } >> + >> + switch (sa->sa_family) { >> + case AF_INET: >> + { >> + struct sockaddr_in *sin =3D (struct sockaddr_in = *)sa; >> + char abuf[INET_ADDRSTRLEN]; >> + >> + inet_ntop(AF_INET, &sin->sin_addr, abuf, = sizeof(abuf)); >> + sz =3D snprintf(buf, bufsize, "%s", abuf); >> + break; >> + } >> + case AF_INET6: >> + { >> + struct sockaddr_in6 *sin6 =3D (struct = sockaddr_in6 *)sa; >> + char abuf[INET6_ADDRSTRLEN]; >> + char *ifname =3D NULL; >> + >> + inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, = sizeof(abuf)); >> + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { >> + struct snl_parsed_link_simple link =3D = {}; >> + >> + if (sin6->sin6_scope_id !=3D 0) { >> + get_ifdata(h, = sin6->sin6_scope_id, &link); >> + ifname =3D link.ifla_ifname; >> + } >> + } >> + if (ifname =3D=3D NULL) >> + sz =3D snprintf(buf, bufsize, "%s", = abuf); >> + else >> + sz =3D snprintf(buf, bufsize, "%s%%%s", = abuf, ifname); >> + break; >> + } >> + default: >> + snprintf(buf, bufsize, "unknown_af#%d", sa->sa_family); >> + plen =3D -1; >> + } >> + >> + if (plen >=3D 0) >> + snprintf(buf + sz, bufsize - sz, "/%d", plen); >> +} >> + >> + >> +static int >> +print_line_prefix(const char *cmd, const char *name) >> +{ >> + struct timespec tp; >> + struct tm tm; >> + char buf[32]; >> + >> + clock_gettime(CLOCK_REALTIME, &tp); >> + localtime_r(&tp.tv_sec, &tm); >> + >> + strftime(buf, sizeof(buf), "%T", &tm); >> + int len =3D printf("%s.%03ld %s %s ", buf, tp.tv_nsec / 1000000, = cmd, name); >> + >> + return (len); >> +} >> + >> +static const char * >> +get_action_name(struct nlmsghdr *hdr, int new_cmd) >> +{ >> + if (hdr->nlmsg_type =3D=3D new_cmd) { >> + //return ((hdr->nlmsg_flags & NLM_F_REPLACE) ? "replace" = : "add"); >> + return ("add/repl"); >> + } else >> + return ("delete"); >> +} >> + >> +static void >> +print_nlmsg_route_nhop(struct nl_helper *h, struct snl_parsed_route = *r, >> + struct rta_mpath_nh *nh, bool first) >> +{ >> + // gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0 >> + if (nh->gw !=3D NULL) { >> + char gwbuf[128]; >> + print_prefix(h, gwbuf, sizeof(gwbuf), nh->gw, -1); >> + printf("gw %s ", gwbuf); >> + } >> + >> + if (nh->ifindex !=3D 0) { >> + struct snl_parsed_link_simple link =3D {}; >> + >> + get_ifdata(h, nh->ifindex, &link); >> + if (nh->rtax_mtu =3D=3D 0) >> + nh->rtax_mtu =3D link.ifla_mtu; >> + printf("iface %s ", link.ifla_ifname); >> + if (nh->rtax_mtu !=3D 0) >> + printf("mtu %d ", nh->rtax_mtu); >> + } >> + >> + if (first) { >> + switch (r->rtm_family) { >> + case AF_INET: >> + printf("table inet.%d", r->rta_table); >> + break; >> + case AF_INET6: >> + printf("table inet6.%d", r->rta_table); >> + break; >> + } >> + } >> + >> + printf("\n"); >> +} >> + >> +static void >> +print_nlmsg_route(struct nl_helper *h, struct nlmsghdr *hdr) >> +{ >> + struct snl_parsed_route r =3D { .rtax_weight =3D = RT_DEFAULT_WEIGHT }; >> + struct snl_state *ss =3D &h->ss_cmd; >> + >> + if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) >> + return; >> + >> + // 20:19:41.333 add route 10.0.0.0/24 gw 10.0.0.1 ifp vtnet0 mtu = 1500 table inet.0 >> + >> + const char *cmd =3D get_action_name(hdr, RTM_NEWROUTE); >> + int len =3D print_line_prefix(cmd, "route"); >> + >> + char buf[128]; >> + print_prefix(h, buf, sizeof(buf), r.rta_dst, r.rtm_dst_len); >> + len +=3D strlen(buf) + 1; >> + printf("%s ", buf); >> + >> + switch (r.rtm_type) { >> + case RTN_BLACKHOLE: >> + printf("blackhole\n"); >> + return; >> + case RTN_UNREACHABLE: >> + printf("unreach(reject)\n"); >> + return; >> + case RTN_PROHIBIT: >> + printf("prohibit(reject)\n"); >> + return; >> + } >> + >> + if (r.rta_multipath !=3D NULL) { >> + bool first =3D true; >> + >> + memset(buf, ' ', sizeof(buf)); >> + buf[len] =3D '\0'; >> + >> + for (int i =3D 0; i < r.rta_multipath->num_nhops; i++) { >> + struct rta_mpath_nh *nh =3D = &r.rta_multipath->nhops[i]; >> + >> + if (!first) >> + printf("%s", buf); >> + print_nlmsg_route_nhop(h, &r, nh, first); >> + first =3D false; >> + } >> + } else { >> + struct rta_mpath_nh nh =3D { >> + .gw =3D r.rta_gw, >> + .ifindex =3D r.rta_oif, >> + .rtax_mtu =3D r.rtax_mtu, >> + }; >> + >> + print_nlmsg_route_nhop(h, &r, &nh, true); >> + } >> +} >> + >> +static const char *operstate[] =3D { >> + "UNKNOWN", /* 0, IF_OPER_UNKNOWN */ >> + "NOTPRESENT", /* 1, IF_OPER_NOTPRESENT */ >> + "DOWN", /* 2, IF_OPER_DOWN */ >> + "LLDOWN", /* 3, IF_OPER_LOWERLAYERDOWN */ >> + "TESTING", /* 4, IF_OPER_TESTING */ >> + "DORMANT", /* 5, IF_OPER_DORMANT */ >> + "UP", /* 6, IF_OPER_UP */ >> +}; >> + >> +static void >> +print_nlmsg_link(struct nl_helper *h, struct nlmsghdr *hdr) >> +{ >> + struct snl_parsed_link l =3D {}; >> + struct snl_state *ss =3D &h->ss_cmd; >> + >> + if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &l)) >> + return; >> + >> + // 20:19:41.333 add iface#3 vtnet0 admin UP oper UP mtu 1500 = table inet.0 >> + const char *cmd =3D get_action_name(hdr, RTM_NEWLINK); >> + print_line_prefix(cmd, "iface"); >> + >> + printf("iface#%u %s ", l.ifi_index, l.ifla_ifname); >> + printf("admin %s ", (l.ifi_flags & IFF_UP) ? "UP" : "DOWN"); >> + if (l.ifla_operstate < NL_ARRAY_LEN(operstate)) >> + printf("oper %s ", operstate[l.ifla_operstate]); >> + if (l.ifla_mtu > 0) >> + printf("mtu %u ", l.ifla_mtu); >> + >> + printf("\n"); >> +} >> + >> +static void >> +print_nlmsg_addr(struct nl_helper *h, struct nlmsghdr *hdr) >> +{ >> + struct snl_parsed_addr attrs =3D {}; >> + struct snl_state *ss =3D &h->ss_cmd; >> + >> + if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs)) >> + return; >> + >> + // add addr 192.168.1.1/24 iface vtnet0 >> + const char *cmd =3D get_action_name(hdr, RTM_NEWADDR); >> + print_line_prefix(cmd, "addr"); >> + >> + char buf[128]; >> + struct sockaddr *addr =3D attrs.ifa_local ? attrs.ifa_local : = attrs.ifa_address; >> + print_prefix(h, buf, sizeof(buf), addr, attrs.ifa_prefixlen); >> + printf("%s ", buf); >> + >> + struct snl_parsed_link_simple link =3D {}; >> + get_ifdata(h, attrs.ifa_index, &link); >> + >> + if (link.ifi_flags & IFF_POINTOPOINT) { >> + char buf[64]; >> + print_prefix(h, buf, sizeof(buf), attrs.ifa_address, = -1); >> + printf("-> %s ", buf); >> + } >> + >> + printf("iface %s ", link.ifla_ifname); >> + >> + printf("\n"); >> +} >> + >> +static const char *nudstate[] =3D { >> + "INCOMPLETE", /* 0x01(0) */ >> + "REACHABLE", /* 0x02(1) */ >> + "STALE", /* 0x04(2) */ >> + "DELAY", /* 0x08(3) */ >> + "PROBE", /* 0x10(4) */ >> + "FAILED", /* 0x20(5) */ >> +}; >> + >> +#define NUD_INCOMPLETE 0x01 /* No lladdr, address = resolution in progress */ >> +#define NUD_REACHABLE 0x02 /* reachable & recently = resolved */ >> +#define NUD_STALE 0x04 /* has lladdr but it's = stale */ >> +#define NUD_DELAY 0x08 /* has lladdr, is stale, = probes delayed */ >> +#define NUD_PROBE 0x10 /* has lladdr, is stale, = probes sent */ >> +#define NUD_FAILED 0x20 /* unused */ >> + >> + >> +static void >> +print_nlmsg_neigh(struct nl_helper *h, struct nlmsghdr *hdr) >> +{ >> + struct snl_parsed_neigh attrs =3D {}; >> + struct snl_state *ss =3D &h->ss_cmd; >> + >> + if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_neigh_parser, &attrs)) >> + return; >> + >> + // add addr 192.168.1.1 state %s lladdr %s iface vtnet0 >> + const char *cmd =3D get_action_name(hdr, RTM_NEWNEIGH); >> + print_line_prefix(cmd, "neigh"); >> + >> + char buf[128]; >> + print_prefix(h, buf, sizeof(buf), attrs.nda_dst, -1); >> + printf("%s ", buf); >> + >> + struct snl_parsed_link_simple link =3D {}; >> + get_ifdata(h, attrs.nda_ifindex, &link); >> + >> + for (unsigned int i =3D 0; i < NL_ARRAY_LEN(nudstate); i++) { >> + if ((1 << i) & attrs.ndm_state) { >> + printf("state %s ", nudstate[i]); >> + break; >> + } >> + } >> + >> + if (attrs.nda_lladdr !=3D NULL) { >> + int if_type =3D link.ifi_type; >> + >> + if ((if_type =3D=3D IFT_ETHER || if_type =3D=3D = IFT_L2VLAN || if_type =3D=3D IFT_BRIDGE) && >> + NLA_DATA_LEN(attrs.nda_lladdr) =3D=3D = ETHER_ADDR_LEN) { >> + struct ether_addr *ll; >> + >> + ll =3D (struct ether_addr = *)NLA_DATA(attrs.nda_lladdr); >> + printf("lladdr %s ", ether_ntoa(ll)); >> + } else { >> + struct sockaddr_dl sdl =3D { >> + .sdl_len =3D sizeof(sdl), >> + .sdl_family =3D AF_LINK, >> + .sdl_index =3D attrs.nda_ifindex, >> + .sdl_type =3D if_type, >> + .sdl_alen =3D = NLA_DATA_LEN(attrs.nda_lladdr), >> + }; >> + if (sdl.sdl_alen < sizeof(sdl.sdl_data)) { >> + void *ll =3D NLA_DATA(attrs.nda_lladdr); >> + >> + memcpy(sdl.sdl_data, ll, sdl.sdl_alen); >> + printf("lladdr %s ", link_ntoa(&sdl)); >> + } >> + } >> + } >> + >> + if (link.ifla_ifname !=3D NULL) >> + printf("iface %s ", link.ifla_ifname); >> + printf("\n"); >> +} >> + >> +static void >> +print_nlmsg_generic(struct nl_helper *h, struct nlmsghdr *hdr) >> +{ >> +} >> + >> +static void >> +print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr) >> +{ >> + switch (hdr->nlmsg_type) { >> + case RTM_NEWLINK: >> + case RTM_DELLINK: >> + print_nlmsg_link(h, hdr); >> + break; >> *** 184 LINES SKIPPED *** --Apple-Mail=_AE460F85-A90B-4D1C-9453-E5B4D7CEC4B9 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8

On 27 = Mar 2023, at 21:41, Jessica Clarke <jrtc27@freebsd.org> = wrote:

On 26 Mar 2023, at = 12:07, Alexander V. Chernikov <melifaro@FreeBSD.org> wrote:

The branch = main has been updated by melifaro:

URL: = https://cgit.FreeBSD.org/src/commit/?id=3Dc597432e22975f4d409b845377996712= 9c6b57e9

commit = c597432e22975f4d409b8453779967129c6b57e9
Author: =     Alexander V. Chernikov = <melifaro@FreeBSD.org>
AuthorDate: 2023-03-26 09:13:50 = +0000
Commit:     Alexander V. Chernikov = <melifaro@FreeBSD.org>
CommitDate: 2023-03-26 11:06:56 = +0000

  route(8): convert to = netlink

  This change converts all kernel rtsock = interactions in route(8)
   to = Netlink.

  Based on the WITHOUT_NETLINK_SUPPORT = src.conf(5) variable, route(8)
   now fully operates = either via Netlink or via rtsock/sysctl.
  The default = (compile-time) is Netlink.

  The output for route = delete/add/get/flush is targeted to be exactly
   the = same (apart from some error handling cases).
  The output = for the route monitor has been changed to = improve
   readability and support netlink = models.

  Other behaviour changes:
  * = exact prefix lookup (route -n get a.b.c.d/e) is not yet = supported.
  * route monitor does not show the change = originator yet.

If = there are regressions then it should be off by default, = *especially*
Hi Jessica,
I have a plan to fix = both this week. I=E2=80=99d also want to note that these two are not the = documented / often used features.
when = we=E2=80=99re just under a month out from the proposed 14 code = slush.

Jess

  Differential Revision: =  https://reviews.freebsd.org/D39007
---
sbin/route/Makefile =        |   6 = +
sbin/route/route.c =         | 117 = +++++--
sbin/route/route_netlink.c | 835 = +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 930 = insertions(+), 28 deletions(-)

diff --git a/sbin/route/Makefile = b/sbin/route/Makefile
index e65030f805bb..aec222310d46 100644
--- = a/sbin/route/Makefile
+++ b/sbin/route/Makefile
@@ -19,6 +19,12 @@ = CFLAGS+=3D -DINET6
.endif
CFLAGS+=3D -I.

+.if = ${MK_NETLINK_SUPPORT} !=3D "no"
+SRCS+=3D = route_netlink.c
+.else
+CFLAGS+=3D-DWITHOUT_NETLINK
+.endi= f
+
HAS_TESTS=3D
SUBDIR.${MK_TESTS}+=3D tests

diff --git = a/sbin/route/route.c b/sbin/route/route.c
index = 5f33cecb1b20..947c97ce794a 100644
--- a/sbin/route/route.c
+++ = b/sbin/route/route.c
@@ -90,12 +90,11 @@ static struct keytab = {
= {0, 0}
};

+int verbose, debugonly;
static = struct sockaddr_storage so[RTAX_MAX];
static int pid, = rtm_addrs;
-static int s;
-static int nflag, = af, qflag, tflag;
-static int verbose, aflen;
-static = int = locking, lockrest, debugonly;
+static int nflag, = af, aflen, qflag, tflag;
+static int locking, lockrest;
static = struct rt_metrics rt_metrics;
static u_long =  rtm_inits;
static uid_t uid;
@@ -103,18 +102,30 @@ = static int = defaultfib;
static int numfibs;
static char = domain[MAXHOSTNAMELEN + 1];
static bool = domain_initialized;
-static int rtm_seq;
static char = rt_line[NI_MAXHOST];
static char net_line[MAXHOSTNAMELEN + = 1];

+#ifdef WITHOUT_NETLINK
+static int = s;
+static int rtm_seq;
+
static struct = {
= struct = rt_msghdr m_rtm;
char m_space[512];
} = m_rtmsg;

+static int rtmsg_rtsock(int, int, = int);
+static int flushroutes_fib_rtsock(int);
+static void = monitor_rtsock(void);
+#else
+int = rtmsg_nl(int, int, int, struct sockaddr_storage *, struct = rt_metrics *);
+int flushroutes_fib_nl(int, int);
+void = monitor_nl(int);
+#endif
+
static = TAILQ_HEAD(fibl_head_t, fibl) fibl_head;

-static void = printb(int, const char *);
+void printb(int, const char = *);
static void flushroutes(int argc, char *argv[]);
static int = flushroutes_fib(int);
static int getaddr(int, char *, int);
@@ = -127,7 +138,7 @@ static int inet6_makenetandmask(struct = sockaddr_in6 *, const char *);
#endif
static void = interfaces(void);
static void monitor(int, char*[]);
-static = const char = *netname(struct sockaddr *);
+const char = *netname(struct sockaddr *);
static void = newroute(int, char **);
static int newroute_fib(int, char *, = int);
static void pmsg_addrs(char *, int, size_t);
@@ -135,7 +146,7 @@ = static void = pmsg_common(struct rt_msghdr *, size_t);
static int = prefixlen(const char *);
static void = print_getmsg(struct rt_msghdr *, int, int);
static void = print_rtmsg(struct rt_msghdr *, size_t);
-static const = char = *routename(struct sockaddr *);
+const char = *routename(struct sockaddr *);
static int = rtmsg(int, int, int);
static void set_metric(char *, = int);
static int set_sofib(int);
@@ -216,12 +227,14 @@ main(int argc, = char **argv)

pid =3D getpid();
uid =3D geteuid();
+#ifdef = WITHOUT_NETLINK
if (tflag)
s =3D open(_PATH_DEVNULL, = O_WRONLY, 0);
else
s =3D socket(PF_ROUTE, SOCK_RAW, = 0);
= if (s < 0)
err(EX_OSERR, = "socket");
+#endif

len =3D sizeof(numfibs);
if = (sysctlbyname("net.fibs", (void *)&numfibs, &len, NULL, 0) =3D=3D = -1)
@@ -264,10 +277,14 @@ static int
set_sofib(int = fib)
{

+#ifdef WITHOUT_NETLINK
if (fib < 0)
return = (0);
= return (setsockopt(s, SOL_SOCKET, SO_SETFIB, (void = *)&fib,
    sizeof(fib)= ));
+#else
+ return (0);
+#endif
}

static int
@@ = -395,7 +412,9 @@ flushroutes(int argc, char *argv[])

if (uid = !=3D 0 && !debugonly && !tflag)
= errx(EX_NOPERM, "must be root to alter routing = table");
+#ifdef WITHOUT_NETLINK
shutdown(s, SHUT_RD); /* Don't = want to read back our messages */
+#endif

= TAILQ_INIT(&fibl_head);
while (argc > 1) {
@@ = -441,6 +460,17 @@ flushroutes(int argc, char *argv[])

static = int
flushroutes_fib(int fib)
+{
+#ifdef = WITHOUT_NETLINK
+ return (flushroutes_fib_rtsock(fib));
+#else
+ return = (flushroutes_fib_nl(fib, af));
+#endif
+}
+
+#ifdef = WITHOUT_NETLINK
+static int
+flushroutes_fib_rtsock(int = fib)
{
= struct rt_msghdr *rtm;
size_t needed;
@@ -525,8 = +555,9 @@ retry:
free(buf);
return = (error);
}
+#endif

-static const char *
+const char = *
routename(struct sockaddr *sa)
{
struct sockaddr_dl *sdl;
@@ = -645,7 +676,7 @@ routename(struct sockaddr *sa)
* Return the name of = the network whose address is given.
* The address is assumed to be = that of a net, not a host.
*/
-static const char *
+const char = *
netname(struct sockaddr *sa)
{
struct sockaddr_dl *sdl;
@@ = -810,8 +841,10 @@ newroute(int argc, char **argv)
= warn("sigaction SIGALRM");

cmd =3D argv[0];
+#ifdef = WITHOUT_NETLINK
if (*cmd !=3D 'g' && *cmd !=3D 's')
= shutdown(s, SHUT_RD); /* Don't want to read back our messages = */
+#endif
while (--argc > 0) {
if (**(++argv)=3D=3D '-') = {
= = = switch (key =3D keyword(1 + *argv)) {
@@ -1398,8 +1431,8 @@ = retry2:
static void
monitor(int argc, char *argv[])
{
- int n, = fib, error;
- char msg[2048], *endptr;
+ int fib, error;
+ char = *endptr;

fib =3D defaultfib;
while (argc > 1) {
@@ = -1435,6 +1468,19 @@ monitor(int argc, char *argv[])
= interfaces();
exit(0);
= }
+#ifdef WITHOUT_NETLINK
+ = monitor_rtsock();
+#else
+ = monitor_nl(fib);
+#endif
+}
+
+#ifdef = WITHOUT_NETLINK
+static void
+monitor_rtsock(void)
+{
+ char = msg[2048];
+ int n;

#ifdef SO_RERROR
n =3D = 1;
@@ -1454,25 +1500,12 @@ monitor(int argc, char *argv[])
= print_rtmsg((struct rt_msghdr *)(void *)msg, n);
= }
}
+#endif

static int
rtmsg(int cmd, int flags, = int fib)
{
- int rlen;
- char *cp =3D = m_rtmsg.m_space;
- int l;
-
-#define NEXTADDR(w, u) = \
- = if (rtm_addrs & (w)) { \
- l =3D = SA_SIZE(&(u)); \
- memmove(cp, (char *)&(u), l); = \
- = = cp +=3D l; \
- if (verbose) \
- = sodump((struct sockaddr *)&(u), #w); = \
- = }
-
errno =3D 0;
- memset(&m_rtmsg, 0, = sizeof(m_rtmsg));
if (cmd =3D=3D 'a')
cmd =3D RTM_ADD;
else if = (cmd =3D=3D 'c')
@@ -1488,6 +1521,33 @@ rtmsg(int cmd, int flags, int = fib)
= = cmd =3D RTM_DELETE;
flags |=3D RTF_PINNED;
= }
+#ifdef WITHOUT_NETLINK
+ return (rtmsg_rtsock(cmd, flags, = fib));
+#else
+ errno =3D rtmsg_nl(cmd, flags, fib, so, = &rt_metrics);
+ return (errno =3D=3D 0 ? 0 : = -1);
+#endif
+}
+
+#ifdef WITHOUT_NETLINK
+static = int
+rtmsg_rtsock(int cmd, int flags, int fib)
+{
+ int = rlen;
+ = char *cp =3D m_rtmsg.m_space;
+ int l;
+
+ = memset(&m_rtmsg, 0, sizeof(m_rtmsg));
+
+#define = NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) { \
+ l =3D = SA_SIZE(&(u)); \
+ memmove(cp, (char *)&(u), l); = \
+ = = cp +=3D l; \
+ if (verbose) \
+ = sodump((struct sockaddr *)&(u), #w); = \
+ = }
+
#define rtm m_rtmsg.m_rtm
= rtm.rtm_type =3D cmd;
rtm.rtm_flags =3D flags;
@@ = -1545,6 +1605,7 @@ rtmsg(int cmd, int flags, int fib)
#undef = rtm
= return (0);
}
+#endif

static const char *const = msgtypes[] =3D {
"",
@@ -1571,7 +1632,7 @@ static const char *const = msgtypes[] =3D {
static const char metricnames[] = =3D
   "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4r= ecvpipe\3expire"
   "\1mtu";
-static const char = routeflags[] =3D
+const char routeflags[] = =3D
   "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7= DONE"
   "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE"=
   "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023P= ROTO3"
@@ -1812,7 +1873,7 @@ pmsg_addrs(char *cp, int addrs, size_t = len)
= (void)fflush(stdout);
}

-static = void
+void
printb(int b, const char *str)
{
int = i;
diff --git a/sbin/route/route_netlink.c = b/sbin/route/route_netlink.c
new file mode 100644
index = 000000000000..648d866670fc
--- /dev/null
+++ = b/sbin/route/route_netlink.c
@@ -0,0 +1,835 @@
+#include = <stdio.h>
+#include <stdlib.h>
+#include = <string.h>
+#include <err.h>
+#include = <errno.h>
+
+#include <sys/bitcount.h>
+#include = <sys/param.h>
+#include <sys/socket.h>
+#include = <sys/sysctl.h>
+#include <sys/time.h>
+#include = <sys/types.h>
+
+#include <netinet/in.h>
+#include = <arpa/inet.h>
+
+#include = <net/ethernet.h>
+#include <net/if.h>
+#include = <net/if_dl.h>
+#include <net/if_types.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>
+
+const char = *routename(struct sockaddr *);
+const char *netname(struct sockaddr = *);
+void printb(int, const char *);
+extern const char = routeflags[];
+extern int verbose, debugonly;
+
+int = rtmsg_nl(int cmd, int rtm_flags, int fib, struct sockaddr_storage = *so,
+    struct rt_metrics *rt_metrics);
+int = flushroutes_fib_nl(int fib, int af);
+void monitor_nl(int = fib);
+
+struct nl_helper;
+static void print_getmsg(struct = nl_helper *h, struct nlmsghdr *hdr, struct sockaddr *dst);
+static = void print_nlmsg(struct nl_helper *h, struct nlmsghdr = *hdr);
+
+#define s6_addr32 __u6_addr.__u6_addr32
+#define = bitcount32(x) __bitcount32((uint32_t)(x))
+static = int
+inet6_get_plen(const struct in6_addr *addr)
+{
+
+ return = (bitcount32(addr->s6_addr32[0]) + bitcount32(addr->s6_addr32[1]) = +
+ =     bitcount32(= addr->s6_addr32[2]) + = bitcount32(addr->s6_addr32[3]));
+}
+
+static = void
+ip6_writemask(struct in6_addr *addr6, uint8_t = mask)
+{
+ uint32_t *cp;
+
+ for (cp =3D (uint32_t *)addr6; = mask >=3D 32; mask -=3D 32)
+ *cp++ =3D 0xFFFFFFFF;
+ if (mask = > 0)
+ = = *cp =3D htonl(mask ? ~((1 << (32 - mask)) - 1) : = 0);
+}
+
+static struct sockaddr *
+get_netmask(struct = snl_state *ss, int family, int plen)
+{
+ if = (family =3D=3D AF_INET) {
+ if (plen =3D=3D 32)
+ return = (NULL);
+
+ struct sockaddr_in *sin =3D snl_allocz(ss, = sizeof(*sin));
+
+ sin->sin_len =3D = sizeof(*sin);
+ sin->sin_family =3D family;
+ = sin->sin_addr.s_addr =3D htonl(plen ? ~((1 << (32 - = plen)) - 1) : 0);
+
+ return (struct sockaddr = *)sin;
+ = } else if (family =3D=3D AF_INET6) {
+ if (plen = =3D=3D 128)
+ return (NULL);
+
+ struct sockaddr_in6 *sin6 =3D = snl_allocz(ss, sizeof(*sin6));
+
+ sin6->sin6_len =3D = sizeof(*sin6);
+ sin6->sin6_family =3D family;
+ = ip6_writemask(&sin6->sin6_addr, plen);
+
+ return = (struct sockaddr *)sin6;
+ }
+ return = (NULL);
+}
+
+struct nl_helper {
+ struct = snl_state ss_cmd;
+};
+
+static void
+nl_helper_init(struct = nl_helper *h)
+{
+ if (!snl_init(&h->ss_cmd, = NETLINK_ROUTE))
+ err(1, "unable to open netlink = socket");
+}
+
+static void
+nl_helper_free(struct nl_helper = *h)
+{
+ snl_free(&h->ss_cmd);
+}
+
+static = int
+rtmsg_nl_int(struct nl_helper *h, int cmd, int rtm_flags, int = fib,
+    struct sockaddr_storage *so, struct = rt_metrics *rt_metrics)
+{
+ struct snl_state *ss =3D = &h->ss_cmd;
+ struct snl_writer nw;
+ int = nl_type =3D 0, nl_flags =3D 0;
+
+ snl_init_writer(ss, = &nw);
+
+ switch (cmd) {
+ case RTSOCK_RTM_ADD:
+ nl_type =3D= RTM_NEWROUTE;
+ nl_flags =3D NLM_F_CREATE | NLM_F_APPEND; /* Do append by = default */
+ break;
+ case RTSOCK_RTM_CHANGE:
+ nl_type =3D= RTM_NEWROUTE;
+ nl_flags =3D NLM_F_REPLACE;
+ = break;
+ case RTSOCK_RTM_DELETE:
+ nl_type =3D = RTM_DELROUTE;
+ break;
+ case RTSOCK_RTM_GET:
+ nl_type =3D= RTM_GETROUTE;
+ break;
+ default:
+ = exit(1);
+ }
+
+ struct sockaddr *dst =3D (struct = sockaddr *)&so[RTAX_DST];
+ struct sockaddr *mask =3D (struct = sockaddr *)&so[RTAX_NETMASK];
+ struct sockaddr *gw =3D (struct = sockaddr *)&so[RTAX_GATEWAY];
+
+ if (dst =3D=3D NULL)
+ return = (EINVAL);
+
+ struct nlmsghdr *hdr =3D snl_create_msg_request(&nw, = nl_type);
+ hdr->nlmsg_flags |=3D nl_flags;
+
+ int plen = =3D 0;
+ = int rtm_type =3D RTN_UNICAST;
+
+ switch = (dst->sa_family) {
+ case AF_INET:
+     {
+ struct = sockaddr_in *mask4 =3D (struct sockaddr_in *)mask;
+
+ plen =3D = mask4 ? bitcount32(mask4->sin_addr.s_addr) : 32;
+ = break;
+     }
+ case = AF_INET6:
+     {
+ struct = sockaddr_in6 *mask6 =3D (struct sockaddr_in6 *)mask;
+
+ plen =3D = mask6 ? inet6_get_plen(&mask6->sin6_addr) : 128;
+ = break;
+     }
+ = default:
+ return (ENOTSUP);
+ }
+
+ if = (rtm_flags & RTF_REJECT)
+ rtm_type =3D = RTN_PROHIBIT;
+ else if (rtm_flags & RTF_BLACKHOLE)
+ rtm_type = =3D RTN_BLACKHOLE;
+
+ struct rtmsg *rtm =3D = snl_reserve_msg_object(&nw, struct rtmsg);
+ = rtm->rtm_family =3D dst->sa_family;
+ = rtm->rtm_protocol =3D RTPROT_STATIC;
+ = rtm->rtm_type =3D rtm_type;
+ rtm->rtm_dst_len =3D = plen;
+
+ snl_add_msg_attr_ip(&nw, RTA_DST, dst);
+ = snl_add_msg_attr_u32(&nw, RTA_TABLE, fib);
+
+ if = (rtm_flags & RTF_GATEWAY) {
+ if (gw->sa_family =3D=3D = dst->sa_family)
+ snl_add_msg_attr_ip(&nw, = RTA_GATEWAY, gw);
+ else
+ snl_add_msg_attr_ipvia(&nw, = RTA_VIA, gw);
+ } else if (gw !=3D NULL) {
+ /* Should = be AF_LINK */
+ struct sockaddr_dl *sdl =3D (struct sockaddr_dl = *)gw;
+ = = if (sdl->sdl_index !=3D 0)
+ snl_add_msg_attr_u32(&nw, = RTA_OIF, sdl->sdl_index);
+ }
+
+ if = (rtm_flags !=3D 0)
+ snl_add_msg_attr_u32(&nw, = NL_RTA_RTFLAGS, rtm_flags);
+
+ if (rt_metrics->rmx_mtu > = 0) {
+ = = int off =3D snl_add_msg_attr_nested(&nw, = RTA_METRICS);
+ snl_add_msg_attr_u32(&nw, RTAX_MTU, = rt_metrics->rmx_mtu);
+ snl_end_attr_nested(&nw, = off);
+ = }
+
+ if (rt_metrics->rmx_weight > 0)
+ = snl_add_msg_attr_u32(&nw, NL_RTA_WEIGHT, = rt_metrics->rmx_weight);
+
+ if (snl_finalize_msg(&nw) = && snl_send_message(ss, hdr)) {
+ struct snl_errmsg_data e =3D = {};
+
+ = = hdr =3D snl_read_reply(ss, hdr->nlmsg_seq);
+ if = (nl_type =3D=3D NL_RTM_GETROUTE) {
+ if (hdr->nlmsg_type =3D=3D = NL_RTM_NEWROUTE)
+ print_getmsg(h, hdr, dst);
+ else = {
+ = = = = snl_parse_errmsg(ss, hdr, &e);
+ if = (e.error =3D=3D ESRCH)
+ warn("route has not been = found");
+ = = = = else
+ warn("message indicates error %d", e.error);
+ = }
+
+ return (0);
+ }
+
+ if = (snl_parse_errmsg(ss, hdr, &e))
+ return (e.error);
+ = }
+ = return (EINVAL);
+}
+
+int
+rtmsg_nl(int cmd, int = rtm_flags, int fib, struct sockaddr_storage *so,
+ =    struct rt_metrics *rt_metrics)
+{
+ struct = nl_helper h =3D {};
+
+ nl_helper_init(&h);
+ int error = =3D rtmsg_nl_int(&h, cmd, rtm_flags, fib, so, rt_metrics);
+ = nl_helper_free(&h);
+
+ return = (error);
+}
+
+static void
+get_ifdata(struct nl_helper *h, = uint32_t ifindex, struct snl_parsed_link_simple *link)
+{
+ struct = snl_state *ss =3D &h->ss_cmd;
+ struct snl_writer = nw;
+
+ = snl_init_writer(ss, &nw);
+ struct nlmsghdr *hdr =3D = snl_create_msg_request(&nw, NL_RTM_GETLINK);
+ struct = ifinfomsg *ifmsg =3D snl_reserve_msg_object(&nw, struct = ifinfomsg);
+ if (ifmsg !=3D NULL)
+ ifmsg->ifi_index =3D = ifindex;
+ = if (!snl_finalize_msg(&nw) || !snl_send_message(ss, = hdr))
+ = = return;
+
+ hdr =3D snl_read_reply(ss, = hdr->nlmsg_seq);
+
+ if (hdr !=3D NULL && = hdr->nlmsg_type =3D=3D RTM_NEWLINK) {
+ = snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, = link);
+ = }
+
+ if (link->ifla_ifname =3D=3D NULL) {
+ char = ifname[16];
+
+ snprintf(ifname, sizeof(ifname), "if#%u", = ifindex);
+ int len =3D strlen(ifname);
+ char *buf = =3D snl_allocz(ss, len + 1);
+ strlcpy(buf, ifname, len + = 1);
+ = = link->ifla_ifname =3D buf;
+ }
+}
+
+static = void
+print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct = sockaddr *dst)
+{
+ struct snl_state *ss =3D = &h->ss_cmd;
+ struct timespec ts;
+ struct = snl_parsed_route r =3D { .rtax_weight =3D RT_DEFAULT_WEIGHT = };
+
+ = if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, = &r))
+ = = return;
+
+ struct snl_parsed_link_simple = link =3D {};
+ get_ifdata(h, r.rta_oif, &link);
+
+ if = (r.rtax_mtu =3D=3D 0)
+ r.rtax_mtu =3D = link.ifla_mtu;
+ r.rta_rtflags |=3D (RTF_UP | RTF_DONE);
+
+ = (void)printf("   route to: %s\n", = routename(dst));
+
+ if (r.rta_dst)
+ = (void)printf("destination: %s\n", = routename(r.rta_dst));
+ struct sockaddr *mask =3D = get_netmask(ss, r.rtm_family, r.rtm_dst_len);
+ if = (mask)
+ = = (void)printf("       mask: %s\n", = routename(mask));
+ if (r.rta_gw && (r.rta_rtflags & = RTF_GATEWAY))
+ (void)printf("    gateway: %s\n", = routename(r.rta_gw));
+ (void)printf(" =        fib: %u\n", (unsigned = int)r.rta_table);
+ if (link.ifla_ifname)
+ (void)printf("  interface: = %s\n", link.ifla_ifname);
+ (void)printf(" =      flags: ");
+ = printb(r.rta_rtflags, routeflags);
+
+ struct = rt_metrics rmx =3D {
+ .rmx_mtu =3D = r.rtax_mtu,
+ .rmx_weight =3D r.rtax_weight,
+ = .rmx_expire =3D r.rta_expire,
+ };
+
+ = printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe",
+     "sendpipe",= "ssthresh", "rtt,msec", "mtu   ", "weight", = "expire");
+ printf("%8lu  ", rmx.rmx_recvpipe);
+ = printf("%8lu  ", rmx.rmx_sendpipe);
+ = printf("%8lu  ", rmx.rmx_ssthresh);
+ = printf("%8lu  ", 0UL);
+ printf("%8lu  ", = rmx.rmx_mtu);
+ printf("%8lu  ", rmx.rmx_weight);
+ if = (rmx.rmx_expire > 0)
+ = clock_gettime(CLOCK_REALTIME_FAST, &ts);
+ = else
+ ts.tv_sec =3D 0;
+ printf("%8ld \n", = (long)(rmx.rmx_expire - ts.tv_sec));
+}
+
+static = void
+print_prefix(struct nl_helper *h, char *buf, int bufsize, = struct sockaddr *sa, int plen)
+{
+ int sz =3D 0;
+
+ if (sa =3D=3D= NULL) {
+ = = snprintf(buf, bufsize, "<NULL>");
+ = return;
+ }
+
+ switch (sa->sa_family) = {
+ = case AF_INET:
+ {
+ struct = sockaddr_in *sin =3D (struct sockaddr_in *)sa;
+ char = abuf[INET_ADDRSTRLEN];
+
+ inet_ntop(AF_INET, = &sin->sin_addr, abuf, sizeof(abuf));
+ sz =3D = snprintf(buf, bufsize, "%s", abuf);
+ break;
+ = }
+ = case AF_INET6:
+ {
+ struct = sockaddr_in6 *sin6 =3D (struct sockaddr_in6 *)sa;
+ char = abuf[INET6_ADDRSTRLEN];
+ char *ifname =3D = NULL;
+
+ inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, = sizeof(abuf));
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) = {
+ = = = = struct snl_parsed_link_simple link =3D {};
+
+ if = (sin6->sin6_scope_id !=3D 0) {
+ get_ifdata(h, = sin6->sin6_scope_id, &link);
+ ifname =3D = link.ifla_ifname;
+ }
+ }
+ if (ifname =3D=3D NULL)
+ sz =3D snprintf(buf, bufsize, = "%s", abuf);
+ else
+ sz =3D snprintf(buf, bufsize, = "%s%%%s", abuf, ifname);
+ break;
+ = }
+ = default:
+ snprintf(buf, bufsize, "unknown_af#%d", = sa->sa_family);
+ plen =3D -1;
+ = }
+
+ if (plen >=3D 0)
+ snprintf(buf + sz, bufsize - sz, = "/%d", plen);
+}
+
+
+static int
+print_line_prefix(const = char *cmd, const char *name)
+{
+ struct timespec tp;
+ struct tm = tm;
+ = char buf[32];
+
+ clock_gettime(CLOCK_REALTIME, = &tp);
+ localtime_r(&tp.tv_sec, &tm);
+
+ = strftime(buf, sizeof(buf), "%T", &tm);
+ int len =3D= printf("%s.%03ld %s %s ", buf, tp.tv_nsec / 1000000, cmd, = name);
+
+ return (len);
+}
+
+static const char = *
+get_action_name(struct nlmsghdr *hdr, int new_cmd)
+{
+ if = (hdr->nlmsg_type =3D=3D new_cmd) {
+ //return ((hdr->nlmsg_flags = & NLM_F_REPLACE) ? "replace" : "add");
+ return = ("add/repl");
+ } else
+ return = ("delete");
+}
+
+static void
+print_nlmsg_route_nhop(struct = nl_helper *h, struct snl_parsed_route *r,
+    struct = rta_mpath_nh *nh, bool first)
+{
+ // gw 10.0.0.1 ifp vtnet0 mtu = 1500 table inet.0
+ if (nh->gw !=3D NULL) {
+ char = gwbuf[128];
+ print_prefix(h, gwbuf, sizeof(gwbuf), nh->gw, = -1);
+ = = printf("gw %s ", gwbuf);
+ }
+
+ if = (nh->ifindex !=3D 0) {
+ struct snl_parsed_link_simple = link =3D {};
+
+ get_ifdata(h, nh->ifindex, &link);
+ if = (nh->rtax_mtu =3D=3D 0)
+ nh->rtax_mtu =3D = link.ifla_mtu;
+ printf("iface %s ", link.ifla_ifname);
+ if = (nh->rtax_mtu !=3D 0)
+ printf("mtu %d ", = nh->rtax_mtu);
+ }
+
+ if (first) {
+ switch = (r->rtm_family) {
+ case AF_INET:
+ = printf("table inet.%d", r->rta_table);
+ = break;
+ case AF_INET6:
+ printf("table inet6.%d", = r->rta_table);
+ break;
+ }
+ = }
+
+ printf("\n");
+}
+
+static = void
+print_nlmsg_route(struct nl_helper *h, struct nlmsghdr = *hdr)
+{
+ struct snl_parsed_route r =3D { .rtax_weight =3D = RT_DEFAULT_WEIGHT };
+ struct snl_state *ss =3D = &h->ss_cmd;
+
+ if (!snl_parse_nlmsg(ss, hdr, = &snl_rtm_route_parser, &r))
+ return;
+
+ // = 20:19:41.333 add route 10.0.0.0/24 gw 10.0.0.1 ifp vtnet0 mtu 1500 table = inet.0
+
+ const char *cmd =3D get_action_name(hdr, = RTM_NEWROUTE);
+ int len =3D print_line_prefix(cmd, = "route");
+
+ char buf[128];
+ print_prefix(h, buf, sizeof(buf), = r.rta_dst, r.rtm_dst_len);
+ len +=3D strlen(buf) + = 1;
+ = printf("%s ", buf);
+
+ switch (r.rtm_type) {
+ case = RTN_BLACKHOLE:
+ printf("blackhole\n");
+ return;
+ case = RTN_UNREACHABLE:
+ printf("unreach(reject)\n");
+ = return;
+ case RTN_PROHIBIT:
+ = printf("prohibit(reject)\n");
+ return;
+ = }
+
+ if (r.rta_multipath !=3D NULL) {
+ bool = first =3D true;
+
+ memset(buf, ' ', = sizeof(buf));
+ buf[len] =3D '\0';
+
+ for (int i =3D 0; i < = r.rta_multipath->num_nhops; i++) {
+ struct rta_mpath_nh *nh =3D = &r.rta_multipath->nhops[i];
+
+ if (!first)
+ = printf("%s", buf);
+ print_nlmsg_route_nhop(h, &r, = nh, first);
+ first =3D false;
+ }
+ } else = {
+ = = struct rta_mpath_nh nh =3D {
+ .gw =3D r.rta_gw,
+ .ifindex = =3D r.rta_oif,
+ .rtax_mtu =3D r.rtax_mtu,
+ = };
+
+ print_nlmsg_route_nhop(h, &r, &nh, = true);
+ = }
+}
+
+static const char *operstate[] =3D {
+ = "UNKNOWN", /* 0, IF_OPER_UNKNOWN */
+ "NOTPRESENT", /* 1, = IF_OPER_NOTPRESENT */
+ "DOWN", /* 2, = IF_OPER_DOWN */
+ "LLDOWN", /* 3, IF_OPER_LOWERLAYERDOWN = */
+ = "TESTING", /* 4, IF_OPER_TESTING */
+ "DORMANT", /* 5, = IF_OPER_DORMANT */
+ "UP", /* 6, IF_OPER_UP = */
+};
+
+static void
+print_nlmsg_link(struct nl_helper *h, = struct nlmsghdr *hdr)
+{
+ struct snl_parsed_link l =3D = {};
+ = struct snl_state *ss =3D &h->ss_cmd;
+
+ if = (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &l))
+ = return;
+
+ // 20:19:41.333 add iface#3 = vtnet0 admin UP oper UP mtu 1500 table inet.0
+ const = char *cmd =3D get_action_name(hdr, RTM_NEWLINK);
+ = print_line_prefix(cmd, "iface");
+
+ = printf("iface#%u %s ", l.ifi_index, l.ifla_ifname);
+ = printf("admin %s ", (l.ifi_flags & IFF_UP) ? "UP" : = "DOWN");
+ = if (l.ifla_operstate < NL_ARRAY_LEN(operstate))
+ = printf("oper %s ", operstate[l.ifla_operstate]);
+ if = (l.ifla_mtu > 0)
+ printf("mtu %u ", = l.ifla_mtu);
+
+ printf("\n");
+}
+
+static = void
+print_nlmsg_addr(struct nl_helper *h, struct nlmsghdr = *hdr)
+{
+ struct snl_parsed_addr attrs =3D {};
+ struct = snl_state *ss =3D &h->ss_cmd;
+
+ if = (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, = &attrs))
+ return;
+
+ // add addr 192.168.1.1/24 iface = vtnet0
+ = const char *cmd =3D get_action_name(hdr, RTM_NEWADDR);
+ = print_line_prefix(cmd, "addr");
+
+ char = buf[128];
+ struct sockaddr *addr =3D attrs.ifa_local ? = attrs.ifa_local : attrs.ifa_address;
+ print_prefix(h, buf, sizeof(buf), = addr, attrs.ifa_prefixlen);
+ printf("%s ", = buf);
+
+ struct snl_parsed_link_simple link =3D {};
+ = get_ifdata(h, attrs.ifa_index, &link);
+
+ if = (link.ifi_flags & IFF_POINTOPOINT) {
+ char = buf[64];
+ = = print_prefix(h, buf, sizeof(buf), attrs.ifa_address, = -1);
+ = = printf("-> %s ", buf);
+ }
+
+ = printf("iface %s ", link.ifla_ifname);
+
+ = printf("\n");
+}
+
+static const char *nudstate[] =3D = {
+ = "INCOMPLETE", /* 0x01(0) */
+ "REACHABLE", /* = 0x02(1) */
+ "STALE", /* 0x04(2) */
+ = "DELAY", /* 0x08(3) */
+ "PROBE", /* = 0x10(4) */
+ "FAILED", /* 0x20(5) = */
+};
+
+#define NUD_INCOMPLETE 0x01 /* No = lladdr, address resolution in progress */
+#define = NUD_REACHABLE 0x02 /* reachable & recently resolved */
+#define = NUD_STALE 0x04 /* has lladdr but it's stale */
+#define = NUD_DELAY 0x08 /* has lladdr, is stale, probes delayed = */
+#define NUD_PROBE 0x10 /* has lladdr, is stale, probes = sent */
+#define NUD_FAILED 0x20 /* unused */
+
+
+static = void
+print_nlmsg_neigh(struct nl_helper *h, struct nlmsghdr = *hdr)
+{
+ struct snl_parsed_neigh attrs =3D {};
+ struct = snl_state *ss =3D &h->ss_cmd;
+
+ if = (!snl_parse_nlmsg(ss, hdr, &snl_rtm_neigh_parser, = &attrs))
+ return;
+
+ // add addr 192.168.1.1 state %s = lladdr %s iface vtnet0
+ const char *cmd =3D = get_action_name(hdr, RTM_NEWNEIGH);
+ print_line_prefix(cmd, = "neigh");
+
+ char buf[128];
+ print_prefix(h, buf, sizeof(buf), = attrs.nda_dst, -1);
+ printf("%s ", = buf);
+
+ struct snl_parsed_link_simple link =3D {};
+ = get_ifdata(h, attrs.nda_ifindex, &link);
+
+ for = (unsigned int i =3D 0; i < NL_ARRAY_LEN(nudstate); i++) {
+ if ((1 = << i) & attrs.ndm_state) {
+ printf("state %s ", = nudstate[i]);
+ break;
+ }
+ = }
+
+ if (attrs.nda_lladdr !=3D NULL) {
+ int = if_type =3D link.ifi_type;
+
+ if ((if_type =3D=3D IFT_ETHER || = if_type =3D=3D IFT_L2VLAN || if_type =3D=3D IFT_BRIDGE) = &&
+     NLA_DATA_LE= N(attrs.nda_lladdr) =3D=3D ETHER_ADDR_LEN) {
+ struct = ether_addr *ll;
+
+ ll =3D (struct ether_addr = *)NLA_DATA(attrs.nda_lladdr);
+ printf("lladdr %s ", = ether_ntoa(ll));
+ } else {
+ struct sockaddr_dl sdl =3D = {
+ = = = = .sdl_len =3D sizeof(sdl),
+ .sdl_family =3D = AF_LINK,
+ = = = = .sdl_index =3D attrs.nda_ifindex,
+ .sdl_type = =3D if_type,
+ .sdl_alen =3D NLA_DATA_LEN(attrs.nda_lladdr),
+ = };
+ if (sdl.sdl_alen < sizeof(sdl.sdl_data)) {
+ void *ll = =3D NLA_DATA(attrs.nda_lladdr);
+
+ memcpy(sdl.sdl_data, ll, = sdl.sdl_alen);
+ printf("lladdr %s ", link_ntoa(&sdl));
+ = }
+ = = }
+ = }
+
+ if (link.ifla_ifname !=3D NULL)
+ = printf("iface %s ", link.ifla_ifname);
+ = printf("\n");
+}
+
+static = void
+print_nlmsg_generic(struct nl_helper *h, struct nlmsghdr = *hdr)
+{
+}
+
+static void
+print_nlmsg(struct nl_helper = *h, struct nlmsghdr *hdr)
+{
+ switch (hdr->nlmsg_type) = {
+ = case RTM_NEWLINK:
+ case RTM_DELLINK:
+ = print_nlmsg_link(h, hdr);
+ break;
*** 184 LINES SKIPPED = ***

= --Apple-Mail=_AE460F85-A90B-4D1C-9453-E5B4D7CEC4B9--