From nobody Mon Mar 27 20:41:44 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 4Pll985F4Xz422pG for ; Mon, 27 Mar 2023 20:41:48 +0000 (UTC) (envelope-from jrtc27@jrtc27.com) Received: from mail-wr1-f41.google.com (mail-wr1-f41.google.com [209.85.221.41]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "smtp.gmail.com", Issuer "GTS CA 1D4" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Pll9752N6z3NdH for ; Mon, 27 Mar 2023 20:41:47 +0000 (UTC) (envelope-from jrtc27@jrtc27.com) Authentication-Results: mx1.freebsd.org; dkim=none; spf=pass (mx1.freebsd.org: domain of jrtc27@jrtc27.com designates 209.85.221.41 as permitted sender) smtp.mailfrom=jrtc27@jrtc27.com; dmarc=none Received: by mail-wr1-f41.google.com with SMTP id l12so10069038wrm.10 for ; Mon, 27 Mar 2023 13:41:47 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679949706; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=wrj2GoCQBZFL18b6PiFwOeqWbUvwRVZB0C/WTMjD84s=; b=0kk/SB3RXxXg4vtOau/zMf1ARFwuGa5CiU5PPjJ7qoQSQp/RzUNu2LnqzCmX0qpQOT 4aleEvZq0hfKd/GSFQ5/LRoGAPgUuAEJmXzDRULo3lhzfxBoTWRtNA6UXlKJmV2W1mva JuVlka00Qz8Jv2k//E71V6FZAIdJLY7wqbVHkpEQRYaXE/pLYGne3+OKwsU2R6U10Y/4 u7aAX8P8wOpo+Qi2DTyhGsmrS4gOIHwAS3fqjYxKg0LAI/oQhiWfbLgVo3tCOQfSBtr1 84TqmTdI62SucMxb7Z1uJSES75EYber/HsoxIW9DBCP+zqgsgQv0jQDg7cKPH/njXBTh UJVA== X-Gm-Message-State: AAQBX9eZ4N6Z8Sq/RbEJICAYiWORLEMhzY7aIH7Dd+Tl+g8F5FlGaaFo H+lpgPkKw0HfOtJmDzcPUDPi5Q== X-Google-Smtp-Source: AKy350YMkjt8LT3jz+8Q6QiYEStIvwRXDyom1d+EOzmuwvBzhU0d2EMBWU0DmX0PDvNsDTRj5MrJ4g== X-Received: by 2002:adf:fa50:0:b0:2c7:d7ca:4c89 with SMTP id y16-20020adffa50000000b002c7d7ca4c89mr9877780wrr.58.1679949705808; Mon, 27 Mar 2023 13:41:45 -0700 (PDT) Received: from smtpclient.apple (global-5-144.n-2.net.cam.ac.uk. [131.111.5.144]) by smtp.gmail.com with ESMTPSA id o6-20020adfeac6000000b002c71a32394dsm25937964wrn.64.2023.03.27.13.41.45 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 27 Mar 2023 13:41:45 -0700 (PDT) Content-Type: text/plain; charset=utf-8 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 \(3696.120.41.1.1\)) Subject: Re: git: c597432e2297 - main - route(8): convert to netlink From: Jessica Clarke In-Reply-To: <202303261107.32QB7UZK058893@gitrepo.freebsd.org> Date: Mon, 27 Mar 2023 21:41:44 +0100 Cc: "src-committers@freebsd.org" , "dev-commits-src-all@freebsd.org" , "dev-commits-src-main@freebsd.org" Content-Transfer-Encoding: quoted-printable Message-Id: <81A0DF64-F466-451F-83CB-86A9FC1EB300@freebsd.org> References: <202303261107.32QB7UZK058893@gitrepo.freebsd.org> To: "Alexander V. Chernikov" X-Mailer: Apple Mail (2.3696.120.41.1.1) X-Spamd-Result: default: False [-2.50 / 15.00]; NEURAL_HAM_LONG(-1.00)[-1.000]; NEURAL_HAM_MEDIUM(-1.00)[-1.000]; NEURAL_HAM_SHORT(-1.00)[-0.998]; MV_CASE(0.50)[]; FORGED_SENDER(0.30)[jrtc27@freebsd.org,jrtc27@jrtc27.com]; R_SPF_ALLOW(-0.20)[+ip4:209.85.128.0/17]; MIME_GOOD(-0.10)[text/plain]; RCPT_COUNT_THREE(0.00)[4]; PREVIOUSLY_DELIVERED(0.00)[dev-commits-src-all@freebsd.org]; TO_DN_EQ_ADDR_SOME(0.00)[]; DMARC_NA(0.00)[freebsd.org]; FROM_HAS_DN(0.00)[]; RCVD_IN_DNSWL_NONE(0.00)[209.85.221.41:from]; TO_MATCH_ENVRCPT_SOME(0.00)[]; FREEFALL_USER(0.00)[jrtc27]; MIME_TRACE(0.00)[0:+]; MLMMJ_DEST(0.00)[dev-commits-src-all@freebsd.org]; RCVD_COUNT_THREE(0.00)[3]; TO_DN_SOME(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; ARC_NA(0.00)[]; RCVD_TLS_LAST(0.00)[]; ASN(0.00)[asn:15169, ipnet:209.85.128.0/17, country:US]; R_DKIM_NA(0.00)[]; FROM_NEQ_ENVFROM(0.00)[jrtc27@freebsd.org,jrtc27@jrtc27.com]; MID_RHS_MATCH_FROM(0.00)[]; RWL_MAILSPIKE_POSSIBLE(0.00)[209.85.221.41:from] X-Rspamd-Queue-Id: 4Pll9752N6z3NdH X-Spamd-Bar: -- X-ThisMailContainsUnwantedMimeParts: N 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. If there are regressions then it should be off by default, *especially* 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(-) >=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 ***