The branch main has been updated by melifaro:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=68636dcb6fded1058318052454eabe35d1f65cb2

commit 68636dcb6fded1058318052454eabe35d1f65cb2
Author:     Alexander V. Chernikov <melif...@freebsd.org>
AuthorDate: 2023-01-27 15:14:39 +0000
Commit:     Alexander V. Chernikov <melif...@freebsd.org>
CommitDate: 2023-01-27 15:36:10 +0000

    netstat: make netstat -rn use netlink instead of rtsock
    
    This change switches route listing in netstat to netlink, with fallback to 
rtsock.
    The outputs are mostly identical, with an exception of not showing kernel
     nexthop indexes for multipath routes.
    
    Differential Revision: https://reviews.freebsd.org/D36529
---
 usr.bin/netstat/Makefile        |   1 +
 usr.bin/netstat/common.h        |  21 +-
 usr.bin/netstat/route.c         |  79 ++++---
 usr.bin/netstat/route_netlink.c | 491 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 550 insertions(+), 42 deletions(-)

diff --git a/usr.bin/netstat/Makefile b/usr.bin/netstat/Makefile
index febab940be4a..e2c9ca09eea5 100644
--- a/usr.bin/netstat/Makefile
+++ b/usr.bin/netstat/Makefile
@@ -6,6 +6,7 @@
 PROG=  netstat
 SRCS=  if.c inet.c main.c mbuf.c mroute.c netisr.c nl_symbols.c route.c \
        unix.c mroute6.c ipsec.c bpf.c pfkey.c sctp.c common.c nhops.c nhgrp.c \
+       route_netlink.c \
        nl_defs.h
 
 nl_symbols.c: nlist_symbols
diff --git a/usr.bin/netstat/common.h b/usr.bin/netstat/common.h
index da058c97d910..d5aadd50b34e 100644
--- a/usr.bin/netstat/common.h
+++ b/usr.bin/netstat/common.h
@@ -45,11 +45,28 @@ extern struct bits rt_bits[];
 const char *fmt_flags(const struct bits *p, int f);
 void print_flags_generic(int flags, const struct bits *pbits,
     const char *format, const char *tag_name);
-int print_sockaddr(const char *name, struct sockaddr *sa,
-    struct sockaddr *mask, int flags, int width);
+int p_sockaddr(const char *name, struct sockaddr *sa, struct sockaddr *mask,
+    int flags, int width);
+
+struct _wid {
+       int dst;
+       int gw;
+       int flags;
+       int pksent;
+       int mtu;
+       int iface;
+       int expire;
+};
+void set_wid(int fam);
+void pr_rthdr(int af1 __unused);
+extern struct _wid wid;
+void p_flags(int f, const char *format);
+
+bool p_rtable_netlink(int fibnum, int af);
 
 struct ifmap_entry {
        char ifname[IFNAMSIZ];
+       uint32_t mtu;
 };
 
 struct ifmap_entry *prepare_ifmap(size_t *ifmap_size);
diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c
index c2a68cae87e8..0026ee924854 100644
--- a/usr.bin/netstat/route.c
+++ b/usr.bin/netstat/route.c
@@ -106,7 +106,6 @@ static const char *netname6(struct sockaddr_in6 *, struct 
sockaddr_in6 *);
 #endif
 static void p_rtable_sysctl(int, int);
 static void p_rtentry_sysctl(const char *name, struct rt_msghdr *);
-static void p_flags(int, const char *);
 static void domask(char *, size_t, u_long);
 
 
@@ -143,7 +142,8 @@ routepr(int fibnum, int af)
        if (fibnum)
                xo_emit(" ({L:fib}: {:fib/%d})", fibnum);
        xo_emit("\n");
-       p_rtable_sysctl(fibnum, af);
+       if (!p_rtable_netlink(fibnum, af))
+               p_rtable_sysctl(fibnum, af);
        xo_close_container("route-information");
 }
 
@@ -197,42 +197,48 @@ pr_family(int af1)
 #define        WID_IF_DEFAULT(af)      ((af) == AF_INET6 ? 8 : (Wflag ? 10 : 
8))
 #endif /*INET6*/
 
-static int wid_dst;
-static int wid_gw;
-static int wid_flags;
-static int wid_pksent;
-static int wid_mtu;
-static int wid_if;
-static int wid_expire;
+struct _wid wid;
 
 /*
  * Print header for routing table columns.
  */
-static void
+void
 pr_rthdr(int af1 __unused)
 {
 
        if (Wflag) {
                xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} "
                    "{T:/%*.*s} {T:/%*.*s} {T:/%*s}\n",
-                       wid_dst,        wid_dst,        "Destination",
-                       wid_gw,         wid_gw,         "Gateway",
-                       wid_flags,      wid_flags,      "Flags",
-                       wid_mtu,        wid_mtu,        "Nhop#",
-                       wid_mtu,        wid_mtu,        "Mtu",
-                       wid_if,         wid_if,         "Netif",
-                       wid_expire,                     "Expire");
+                       wid.dst,        wid.dst,        "Destination",
+                       wid.gw,         wid.gw,         "Gateway",
+                       wid.flags,      wid.flags,      "Flags",
+                       wid.mtu,        wid.mtu,        "Nhop#",
+                       wid.mtu,        wid.mtu,        "Mtu",
+                       wid.iface,      wid.iface,      "Netif",
+                       wid.expire,                     "Expire");
        } else {
                xo_emit("{T:/%-*.*s} {T:/%-*.*s} {T:/%-*.*s} {T:/%*.*s} "
                    "{T:/%*s}\n",
-                       wid_dst,        wid_dst,        "Destination",
-                       wid_gw,         wid_gw,         "Gateway",
-                       wid_flags,      wid_flags,      "Flags",
-                       wid_if,         wid_if,         "Netif",
-                       wid_expire,                     "Expire");
+                       wid.dst,        wid.dst,        "Destination",
+                       wid.gw,         wid.gw,         "Gateway",
+                       wid.flags,      wid.flags,      "Flags",
+                       wid.iface,      wid.iface,      "Netif",
+                       wid.expire,                     "Expire");
        }
 }
 
+void
+set_wid(int fam)
+{
+       wid.dst = WID_DST_DEFAULT(fam);
+       wid.gw = WID_GW_DEFAULT(fam);
+       wid.flags = 6;
+       wid.pksent = 8;
+       wid.mtu = 6;
+       wid.iface = WID_IF_DEFAULT(fam);
+       wid.expire = 6;
+}
+
 static void
 p_rtable_sysctl(int fibnum, int af)
 {
@@ -278,15 +284,8 @@ p_rtable_sysctl(int fibnum, int af)
                                xo_close_instance("rt-family");
                        }
                        need_table_close = true;
-
                        fam = sa->sa_family;
-                       wid_dst = WID_DST_DEFAULT(fam);
-                       wid_gw = WID_GW_DEFAULT(fam);
-                       wid_flags = 6;
-                       wid_pksent = 8;
-                       wid_mtu = 6;
-                       wid_if = WID_IF_DEFAULT(fam);
-                       wid_expire = 6;
+                       set_wid(fam);
                        xo_open_instance("rt-family");
                        pr_family(fam);
                        xo_open_list("rt-entry");
@@ -323,22 +322,22 @@ p_rtentry_sysctl(const char *name, struct rt_msghdr *rtm)
 
        protrusion = p_sockaddr("destination", addr[RTAX_DST],
            addr[RTAX_NETMASK],
-           rtm->rtm_flags, wid_dst);
+           rtm->rtm_flags, wid.dst);
        protrusion = p_sockaddr("gateway", addr[RTAX_GATEWAY], NULL, RTF_HOST,
-           wid_gw - protrusion);
+           wid.gw - protrusion);
        snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ",
-           wid_flags - protrusion);
+           wid.flags - protrusion);
        p_flags(rtm->rtm_flags, buffer);
        /* Output path weight as non-visual property */
        xo_emit("{e:weight/%u}", rtm->rtm_rmx.rmx_weight);
        if (Wflag) {
                /* XXX: use=0? */
-               xo_emit("{t:nhop/%*lu} ", wid_mtu, rtm->rtm_rmx.rmx_nhidx);
+               xo_emit("{t:nhop/%*lu} ", wid.mtu, rtm->rtm_rmx.rmx_nhidx);
 
                if (rtm->rtm_rmx.rmx_mtu != 0)
-                       xo_emit("{t:mtu/%*lu} ", wid_mtu, rtm->rtm_rmx.rmx_mtu);
+                       xo_emit("{t:mtu/%*lu} ", wid.mtu, rtm->rtm_rmx.rmx_mtu);
                else
-                       xo_emit("{P:/%*s} ", wid_mtu, "");
+                       xo_emit("{P:/%*s} ", wid.mtu, "");
        }
 
        memset(prettyname, 0, sizeof(prettyname));
@@ -350,15 +349,15 @@ p_rtentry_sysctl(const char *name, struct rt_msghdr *rtm)
        }
 
        if (Wflag)
-               xo_emit("{t:interface-name/%*s}", wid_if, prettyname);
+               xo_emit("{t:interface-name/%*s}", wid.iface, prettyname);
        else
-               xo_emit("{t:interface-name/%*.*s}", wid_if, wid_if,
+               xo_emit("{t:interface-name/%*.*s}", wid.iface, wid.iface,
                    prettyname);
        if (rtm->rtm_rmx.rmx_expire) {
                time_t expire_time;
 
                if ((expire_time = rtm->rtm_rmx.rmx_expire - uptime.tv_sec) > 0)
-                       xo_emit(" {:expire-time/%*d}", wid_expire,
+                       xo_emit(" {:expire-time/%*d}", wid.expire,
                            (int)expire_time);
        }
 
@@ -472,7 +471,7 @@ fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask, 
int flags)
        return (cp);
 }
 
-static void
+void
 p_flags(int f, const char *format)
 {
 
diff --git a/usr.bin/netstat/route_netlink.c b/usr.bin/netstat/route_netlink.c
new file mode 100644
index 000000000000..7c0ac8fea30c
--- /dev/null
+++ b/usr.bin/netstat/route_netlink.c
@@ -0,0 +1,491 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1988, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/time.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 <netinet/in.h>
+#include <netgraph/ng_socket.h>
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <err.h>
+#include <libxo/xo.h>
+#include "netstat.h"
+#include "common.h"
+#include "nl_defs.h"
+
+
+static void p_rtentry_netlink(struct snl_state *ss, const char *name, struct 
nlmsghdr *hdr);
+
+static struct ifmap_entry *ifmap;
+static size_t ifmap_size;
+
+struct nl_parsed_link {
+       uint32_t                ifi_index;
+       uint32_t                ifla_mtu;
+       char                    *ifla_ifname;
+};
+
+#define        _IN(_field)     offsetof(struct ifinfomsg, _field)
+#define        _OUT(_field)    offsetof(struct nl_parsed_link, _field)
+static struct snl_attr_parser ap_link[] = {
+       { .type = IFLA_IFNAME, .off = _OUT(ifla_ifname), .cb = 
snl_attr_get_string },
+       { .type = IFLA_MTU, .off = _OUT(ifla_mtu), .cb = snl_attr_get_uint32 },
+};
+static struct snl_field_parser fp_link[] = {
+       {.off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = 
snl_field_get_uint32 },
+};
+#undef _IN
+#undef _OUT
+SNL_DECLARE_PARSER(link_parser, struct ifinfomsg, fp_link, ap_link);
+
+/* Generate ifmap using netlink */
+static struct ifmap_entry *
+prepare_ifmap_netlink(struct snl_state *ss, size_t *pifmap_size)
+{
+       struct {
+               struct nlmsghdr hdr;
+               struct ifinfomsg ifmsg;
+       } msg = {
+               .hdr.nlmsg_type = RTM_GETLINK,
+               .hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+               .hdr.nlmsg_seq = snl_get_seq(ss),
+       };
+       msg.hdr.nlmsg_len = sizeof(msg);
+
+       if (!snl_send(ss, &msg, sizeof(msg))) {
+               snl_free(ss);
+               return (NULL);
+       }
+
+       struct ifmap_entry *ifmap = NULL;
+       uint32_t ifmap_size = 0;
+       struct nlmsghdr *hdr;
+       while ((hdr = snl_read_message(ss)) != NULL && hdr->nlmsg_type != 
NLMSG_DONE) {
+               if (hdr->nlmsg_seq != msg.hdr.nlmsg_seq)
+                       continue;
+/*
+               if (hdr->nlmsg_type == NLMSG_ERROR)
+                       break;
+*/
+               struct nl_parsed_link link = {};
+               if (!snl_parse_nlmsg(ss, hdr, &link_parser, &link))
+                       continue;
+               if (link.ifi_index >= ifmap_size) {
+                       size_t size = roundup2(link.ifi_index + 1, 32) * 
sizeof(struct ifmap_entry);
+                       if ((ifmap = realloc(ifmap, size)) == NULL)
+                               errx(2, "realloc(%lu) failed", size);
+                       memset(&ifmap[ifmap_size], 0,
+                           size - ifmap_size *
+                           sizeof(struct ifmap_entry));
+                       ifmap_size = roundup2(link.ifi_index + 1, 32);
+               }
+               if (*ifmap[link.ifi_index].ifname != '\0')
+                       continue;
+               strlcpy(ifmap[link.ifi_index].ifname, link.ifla_ifname, 
IFNAMSIZ);
+               ifmap[link.ifi_index].mtu = link.ifla_mtu;
+       }
+       *pifmap_size = ifmap_size;
+       return (ifmap);
+}
+
+struct rta_mpath_nh {
+       struct sockaddr *gw;
+       uint32_t        ifindex;
+       uint8_t         rtnh_flags;
+       uint8_t         rtnh_weight;
+       uint32_t        rtax_mtu;
+       uint32_t        rta_knh_id;
+       uint32_t        rta_rtflags;
+};
+
+#define        _IN(_field)     offsetof(struct rtnexthop, _field)
+#define        _OUT(_field)    offsetof(struct rta_mpath_nh, _field)
+static const struct snl_attr_parser nla_p_mp_rtmetrics[] = {
+       { .type = NL_RTAX_MTU, .off = _OUT(rtax_mtu), .cb = snl_attr_get_uint32 
},
+};
+SNL_DECLARE_ATTR_PARSER(metrics_mp_parser, nla_p_mp_rtmetrics);
+
+static const struct snl_attr_parser psnh[] = {
+       { .type = NL_RTA_GATEWAY, .off = _OUT(gw), .cb = snl_attr_get_ip },
+       { .type = NL_RTA_METRICS, .arg = &metrics_mp_parser, .cb = 
snl_attr_get_nested },
+       { .type = NL_RTA_KNH_ID, .off = _OUT(rta_knh_id), .cb = 
snl_attr_get_uint32 },
+       { .type = NL_RTA_RTFLAGS, .off = _OUT(gw), .cb = snl_attr_get_uint32 },
+       { .type = NL_RTA_VIA, .off = _OUT(gw), .cb = snl_attr_get_ipvia },
+};
+
+static const struct snl_field_parser fpnh[] = {
+       { .off_in = _IN(rtnh_flags), .off_out = _OUT(rtnh_flags), .cb = 
snl_field_get_uint8 },
+       { .off_in = _IN(rtnh_hops), .off_out = _OUT(rtnh_weight), .cb = 
snl_field_get_uint8 },
+       { .off_in = _IN(rtnh_ifindex), .off_out = _OUT(ifindex), .cb = 
snl_field_get_uint32 },
+};
+#undef _IN
+#undef _OUT
+
+SNL_DECLARE_PARSER(mpath_parser, struct rtnexthop, fpnh, psnh);
+
+struct rta_mpath {
+       int num_nhops;
+       struct rta_mpath_nh nhops[0];
+};
+
+static bool
+nlattr_get_multipath(struct snl_state *ss, struct nlattr *nla, const void 
*arg, void *target)
+{
+       int data_len = nla->nla_len - sizeof(struct nlattr);
+       struct rtnexthop *rtnh;
+
+       int max_nhops = data_len / sizeof(struct rtnexthop);
+       size_t sz = (max_nhops + 2) * sizeof(struct rta_mpath_nh);
+
+       struct rta_mpath *mp = snl_allocz(ss, sz);
+       mp->num_nhops = 0;
+
+       for (rtnh = (struct rtnexthop *)(nla + 1); data_len > 0; ) {
+               struct rta_mpath_nh *mpnh = &mp->nhops[mp->num_nhops++];
+
+               if (!snl_parse_header(ss, rtnh, rtnh->rtnh_len, &mpath_parser, 
mpnh))
+                       return (false);
+
+               int len = NL_ITEM_ALIGN(rtnh->rtnh_len);
+               data_len -= len;
+               rtnh = (struct rtnexthop *)((char *)rtnh + len);
+       }
+       if (data_len != 0 || mp->num_nhops == 0) {
+               return (false);
+       }
+
+       *((struct rta_mpath **)target) = mp;
+       return (true);
+}
+
+
+struct nl_parsed_route {
+       struct sockaddr         *rta_dst;
+       struct sockaddr         *rta_gw;
+       struct nlattr           *rta_metrics;
+       struct rta_mpath        *rta_multipath;
+       uint32_t                rta_expires;
+       uint32_t                rta_oif;
+       uint32_t                rta_expire;
+       uint32_t                rta_table;
+       uint32_t                rta_knh_id;
+       uint32_t                rta_rtflags;
+       uint32_t                rtax_mtu;
+       uint32_t                rtax_weight;
+       uint8_t                 rtm_family;
+       uint8_t                 rtm_type;
+       uint8_t                 rtm_protocol;
+       uint8_t                 rtm_dst_len;
+};
+
+#define        _IN(_field)     offsetof(struct rtmsg, _field)
+#define        _OUT(_field)    offsetof(struct nl_parsed_route, _field)
+static const struct snl_attr_parser nla_p_rtmetrics[] = {
+       { .type = NL_RTAX_MTU, .off = _OUT(rtax_mtu), .cb = snl_attr_get_uint32 
},
+};
+SNL_DECLARE_ATTR_PARSER(metrics_parser, nla_p_rtmetrics);
+
+static const struct snl_attr_parser ps[] = {
+       { .type = NL_RTA_DST, .off = _OUT(rta_dst), .cb = snl_attr_get_ip },
+       { .type = NL_RTA_OIF, .off = _OUT(rta_oif), .cb = snl_attr_get_uint32 },
+       { .type = NL_RTA_GATEWAY, .off = _OUT(rta_gw), .cb = snl_attr_get_ip },
+       { .type = NL_RTA_METRICS, .arg = &metrics_parser, .cb = 
snl_attr_get_nested },
+       { .type = NL_RTA_MULTIPATH, .off = _OUT(rta_multipath), .cb = 
nlattr_get_multipath },
+       { .type = NL_RTA_KNH_ID, .off = _OUT(rta_knh_id), .cb = 
snl_attr_get_uint32 },
+       { .type = NL_RTA_RTFLAGS, .off = _OUT(rta_rtflags), .cb = 
snl_attr_get_uint32 },
+       { .type = NL_RTA_TABLE, .off = _OUT(rta_table), .cb = 
snl_attr_get_uint32 },
+       { .type = NL_RTA_VIA, .off = _OUT(rta_gw), .cb = snl_attr_get_ipvia },
+       { .type = NL_RTA_EXPIRES, .off = _OUT(rta_expire), .cb = 
snl_attr_get_uint32 },
+};
+
+static const struct snl_field_parser fprt[] = {
+       {.off_in = _IN(rtm_family), .off_out = _OUT(rtm_family), .cb = 
snl_field_get_uint8 },
+       {.off_in = _IN(rtm_type), .off_out = _OUT(rtm_type), .cb = 
snl_field_get_uint8 },
+       {.off_in = _IN(rtm_protocol), .off_out = _OUT(rtm_protocol), .cb = 
snl_field_get_uint8 },
+       {.off_in = _IN(rtm_dst_len), .off_out = _OUT(rtm_dst_len), .cb = 
snl_field_get_uint8 },
+};
+#undef _IN
+#undef _OUT
+SNL_DECLARE_PARSER(rtm_parser, struct rtmsg, fprt, ps);
+
+#define        RTF_UP          0x1
+#define        RTF_GATEWAY     0x2
+#define        RTF_HOST        0x4
+#define        RTF_REJECT      0x8
+#define        RTF_DYNAMIC     0x10
+#define RTF_STATIC     0x800
+#define RTF_BLACKHOLE  0x1000
+#define RTF_PROTO2     0x4000
+#define RTF_PROTO1     0x8000
+#define RTF_PROTO3     0x40000
+#define        RTF_FIXEDMTU    0x80000
+#define RTF_PINNED     0x100000
+
+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);
+}
+
+static void
+gen_mask(int family, int plen, struct sockaddr *sa)
+{
+       if (family == AF_INET6) {
+               struct sockaddr_in6 sin6 = {
+                       .sin6_family = AF_INET6,
+                       .sin6_len = sizeof(struct sockaddr_in6),
+               };
+               ip6_writemask(&sin6.sin6_addr, plen);
+               *((struct sockaddr_in6 *)sa) = sin6;
+       } else if (family == AF_INET) {
+               struct sockaddr_in sin = {
+                       .sin_family = AF_INET,
+                       .sin_len = sizeof(struct sockaddr_in),
+                       .sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 
1) : 0),
+               };
+               *((struct sockaddr_in *)sa) = sin;
+       }
+}
+
+struct sockaddr_dl_short {
+       u_char  sdl_len;        /* Total length of sockaddr */
+       u_char  sdl_family;     /* AF_LINK */
+       u_short sdl_index;      /* if != 0, system given index for interface */
+       u_char  sdl_type;       /* interface type */
+       u_char  sdl_nlen;       /* interface name length, no trailing 0 reqd. */
+       u_char  sdl_alen;       /* link level address length */
+       u_char  sdl_slen;       /* link layer selector length */
+       char    sdl_data[8];    /* unused */
+};
+
+static void
+p_path(struct nl_parsed_route *rt)
+{
+       struct sockaddr_in6 mask6;
+       struct sockaddr *pmask = (struct sockaddr *)&mask6;
+       char buffer[128];
+       char prettyname[128];
+       int protrusion;
+
+       gen_mask(rt->rtm_family, rt->rtm_dst_len, pmask);
+       protrusion = p_sockaddr("destination", rt->rta_dst, pmask, 
rt->rta_rtflags, wid.dst);
+       protrusion = p_sockaddr("gateway", rt->rta_gw, NULL, RTF_HOST,
+           wid.gw - protrusion);
+       snprintf(buffer, sizeof(buffer), "{[:-%d}{:flags/%%s}{]:} ",
+           wid.flags - protrusion);
+       p_flags(rt->rta_rtflags | RTF_UP, buffer);
+       /* Output path weight as non-visual property */
+       xo_emit("{e:weight/%u}", rt->rtax_weight);
+
+       memset(prettyname, 0, sizeof(prettyname));
+       if (rt->rta_oif < ifmap_size) {
+               strlcpy(prettyname, ifmap[rt->rta_oif].ifname,
+                   sizeof(prettyname));
+               if (*prettyname == '\0')
+                       strlcpy(prettyname, "---", sizeof(prettyname));
+               if (rt->rtax_mtu == 0)
+                       rt->rtax_mtu = ifmap[rt->rta_oif].mtu;
+       }
+
+       if (Wflag) {
+               /* XXX: use=0? */
+               xo_emit("{t:nhop/%*lu} ", wid.mtu, rt->rta_knh_id);
+
+               if (rt->rtax_mtu != 0)
+                       xo_emit("{t:mtu/%*lu} ", wid.mtu, rt->rtax_mtu);
+               else {
+                       /* use interface mtu */
+                       xo_emit("{P:/%*s} ", wid.mtu, "");
+               }
+
+       }
+
+       if (Wflag)
+               xo_emit("{t:interface-name/%*s}", wid.iface, prettyname);
+       else
+               xo_emit("{t:interface-name/%*.*s}", wid.iface, wid.iface,
+                   prettyname);
+       if (rt->rta_expires > 0) {
+               xo_emit(" {:expire-time/%*u}", wid.expire, rt->rta_expires);
+       }
+}
+
+static void
+p_rtentry_netlink(struct snl_state *ss, const char *name, struct nlmsghdr *hdr)
+{
+
+       struct nl_parsed_route rt = {};
+       if (!snl_parse_nlmsg(ss, hdr, &rtm_parser, &rt))
+               return;
+
+       if (rt.rta_multipath != NULL) {
+               uint32_t orig_rtflags = rt.rta_rtflags;
+               uint32_t orig_mtu = rt.rtax_mtu;
+               for (int i = 0; i < rt.rta_multipath->num_nhops; i++) {
+                       struct rta_mpath_nh *nhop = &rt.rta_multipath->nhops[i];
+
+                       rt.rta_gw = nhop->gw;
+                       rt.rta_oif = nhop->ifindex;
+                       rt.rtax_weight = nhop->rtnh_weight;
+                       rt.rta_rtflags = nhop->rta_rtflags ? nhop->rta_rtflags 
: orig_rtflags;
+                       rt.rta_knh_id = nhop->rta_knh_id;
+                       rt.rtax_mtu = nhop->rtax_mtu ? nhop->rtax_mtu : 
orig_mtu;
+
+                       xo_open_instance(name);
+                       p_path(&rt);
+                       xo_emit("\n");
+                       xo_close_instance(name);
+               }
+               return;
+       }
+
+       struct sockaddr_dl_short sdl_gw = {
+               .sdl_family = AF_LINK,
+               .sdl_len = sizeof(struct sockaddr_dl_short),
+               .sdl_index = rt.rta_oif,
+       };
+       if (rt.rta_gw == NULL)
+               rt.rta_gw = (struct sockaddr *)&sdl_gw;
+
+       xo_open_instance(name);
+       p_path(&rt);
+       xo_emit("\n");
+       xo_close_instance(name);
+}
+
+static const struct snl_hdr_parser *all_parsers[] = {
+       &link_parser, &metrics_mp_parser, &mpath_parser, &metrics_parser, 
&rtm_parser
+};
+
+bool
+p_rtable_netlink(int fibnum, int af)
+{
+       int fam = AF_UNSPEC;
+       int need_table_close = false;
+       struct nlmsghdr *hdr;
+
+       struct snl_state ss = {};
+
+       SNL_VERIFY_PARSERS(all_parsers);
+
+       if (!snl_init(&ss, NETLINK_ROUTE))
+               return (false);
+
+       ifmap = prepare_ifmap_netlink(&ss, &ifmap_size);
+
+       struct {
+               struct nlmsghdr hdr;
+               struct rtmsg rtmsg;
+               struct nlattr nla_fibnum;
+               uint32_t fibnum;
+       } msg = {
+               .hdr.nlmsg_type = RTM_GETROUTE,
+               .hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+               .hdr.nlmsg_seq = snl_get_seq(&ss),
+               .rtmsg.rtm_family = af,
+               .nla_fibnum.nla_len = sizeof(struct nlattr) + sizeof(uint32_t),
+               .nla_fibnum.nla_type = RTA_TABLE,
+               .fibnum = fibnum,
+       };
+       msg.hdr.nlmsg_len = sizeof(msg);
+
+       if (!snl_send(&ss, &msg, sizeof(msg))) {
+               snl_free(&ss);
+               return (false);
+       }
+
+       xo_open_container("route-table");
+       xo_open_list("rt-family");
+       while ((hdr = snl_read_message(&ss)) != NULL && hdr->nlmsg_type != 
NLMSG_DONE) {
+               if (hdr->nlmsg_seq != msg.hdr.nlmsg_seq)
+                       continue;
+               struct rtmsg *rtm = (struct rtmsg *)(hdr + 1);
+               /* Only print family first time. */
+               if (fam != rtm->rtm_family) {
+                       if (need_table_close) {
+                               xo_close_list("rt-entry");
+                               xo_close_instance("rt-family");
+                       }
+                       need_table_close = true;
+                       fam = rtm->rtm_family;
+                       set_wid(fam);
+                       xo_open_instance("rt-family");
+                       pr_family(fam);
+                       xo_open_list("rt-entry");
+                       pr_rthdr(fam);
+               }
+               p_rtentry_netlink(&ss, "rt-entry", hdr);
+               snl_clear_lb(&ss);
+       }
+       if (need_table_close) {
+               xo_close_list("rt-entry");
+               xo_close_instance("rt-family");
+       }
+       xo_close_list("rt-family");
+       xo_close_container("route-table");
+       snl_free(&ss);
+       return (true);
+}
+
+

Reply via email to