Author: melifaro
Date: Wed Dec 18 18:25:27 2013
New Revision: 259562
URL: http://svnweb.freebsd.org/changeset/base/259562

Log:
  Switch netstat -rn to use standard API for retrieving list of routes
  instead of peeking inside in-kernel radix via kget.
  This permits us to change kernel structures without breaking userland.
  Additionally, this change provide more reliable and faster output.
  
  `Refs` and `Use` fields available in IPv4 by default (and via -W
  for other families) were removed. `Refs` is radix-specific thing
  which is not informative for users. `Use` field value is handy sometimes,
  but a) current API does not support it and b) I'm not sure we will
  support per-rte pcpu counters in near future.
  
  Old method of retrieving data is still supported (either by defining
  NewTree=0 or running netstat with -A). However, Refs/Use fields are
  hidden.
  
  Sponsored by: Yandex LLC
  MFC after:    4 weeks
  PR:           kern/167204

Modified:
  head/usr.bin/netstat/route.c

Modified: head/usr.bin/netstat/route.c
==============================================================================
--- head/usr.bin/netstat/route.c        Wed Dec 18 17:03:43 2013        
(r259561)
+++ head/usr.bin/netstat/route.c        Wed Dec 18 18:25:27 2013        
(r259562)
@@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/sysctl.h>
 
 #include <arpa/inet.h>
+#include <ifaddrs.h>
 #include <libutil.h>
 #include <netdb.h>
 #include <stdint.h>
@@ -113,13 +114,20 @@ typedef union {
 
 static sa_u pt_u;
 
+struct ifmap_entry {
+       char ifname[IFNAMSIZ];
+};
+
+static struct ifmap_entry *ifmap;
+static int ifmap_size;
+
 int    do_rtent = 0;
 struct rtentry rtentry;
 struct radix_node rnode;
 struct radix_mask rmask;
 struct radix_node_head **rt_tables;
 
-int    NewTree = 0;
+int    NewTree = 1;
 
 struct timespec uptime;
 
@@ -129,7 +137,7 @@ static void size_cols_tree(struct radix_
 static void size_cols_rtentry(struct rtentry *rt);
 static void p_tree(struct radix_node *);
 static void p_rtnode(void);
-static void ntreestuff(void);
+static void ntreestuff(int fibnum, int af);
 static void np_rtentry(struct rt_msghdr *);
 static void p_sockaddr(struct sockaddr *, struct sockaddr *, int, int);
 static const char *fmt_sockaddr(struct sockaddr *sa, struct sockaddr *mask,
@@ -175,7 +183,7 @@ routepr(u_long rtree, int fibnum)
        printf("\n");
 
        if (Aflag == 0 && NewTree)
-               ntreestuff();
+               ntreestuff(fibnum, af);
        else {
                if (rtree == 0) {
                        printf("rt_tables: symbol not in namelist\n");
@@ -288,7 +296,7 @@ static int wid_if;
 static int wid_expire;
 
 static void
-size_cols(int ef __unused, struct radix_node *rn)
+size_cols(int ef, struct radix_node *rn)
 {
        wid_dst = WID_DST_DEFAULT(ef);
        wid_gw = WID_GW_DEFAULT(ef);
@@ -299,7 +307,7 @@ size_cols(int ef __unused, struct radix_
        wid_if = WID_IF_DEFAULT(ef);
        wid_expire = 6;
 
-       if (Wflag)
+       if (Wflag && rn != NULL)
                size_cols_tree(rn);
 }
 
@@ -397,27 +405,14 @@ pr_rthdr(int af1)
 
        if (Aflag)
                printf("%-8.8s ","Address");
-       if (af1 == AF_INET || Wflag) {
-               if (Wflag) {
-                       printf("%-*.*s %-*.*s %-*.*s %*.*s %*.*s %*.*s %*.*s 
%*s\n",
-                               wid_dst,        wid_dst,        "Destination",
-                               wid_gw,         wid_gw,         "Gateway",
-                               wid_flags,      wid_flags,      "Flags",
-                               wid_refs,       wid_refs,       "Refs",
-                               wid_use,        wid_use,        "Use",
-                               wid_mtu,        wid_mtu,        "Mtu",
-                               wid_if,         wid_if,         "Netif",
-                               wid_expire,                     "Expire");
-               } else {
-                       printf("%-*.*s %-*.*s %-*.*s %*.*s %*.*s %*.*s %*s\n",
-                               wid_dst,        wid_dst,        "Destination",
-                               wid_gw,         wid_gw,         "Gateway",
-                               wid_flags,      wid_flags,      "Flags",
-                               wid_refs,       wid_refs,       "Refs",
-                               wid_use,        wid_use,        "Use",
-                               wid_if,         wid_if,         "Netif",
-                               wid_expire,                     "Expire");
-               }
+       if (Wflag) {
+               printf("%-*.*s %-*.*s %-*.*s %*.*s %*.*s %*s\n",
+                       wid_dst,        wid_dst,        "Destination",
+                       wid_gw,         wid_gw,         "Gateway",
+                       wid_flags,      wid_flags,      "Flags",
+                       wid_mtu,        wid_mtu,        "Mtu",
+                       wid_if,         wid_if,         "Netif",
+                       wid_expire,                     "Expire");
        } else {
                printf("%-*.*s %-*.*s %-*.*s  %*.*s %*s\n",
                        wid_dst,        wid_dst,        "Destination",
@@ -522,20 +517,61 @@ p_rtnode(void)
 }
 
 static void
-ntreestuff(void)
+ntreestuff(int fibnum, int af)
 {
        size_t needed;
-       int mib[6];
+       int mib[7];
        char *buf, *next, *lim;
        struct rt_msghdr *rtm;
+       struct sockaddr *sa;
+       int fam = 0, ifindex = 0, size;
+
+       struct ifaddrs *ifap, *ifa;
+       struct sockaddr_dl *sdl;
+
+       /*
+        * Retrieve interface list at first
+        * since we need #ifindex -> if_xname match
+        */
+       if (getifaddrs(&ifap) != 0)
+               err(EX_OSERR, "getifaddrs");
+
+       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+               
+               if (ifa->ifa_addr->sa_family != AF_LINK)
+                       continue;
+
+               sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+               ifindex = sdl->sdl_index;
+
+               if (ifindex >= ifmap_size) {
+                       size = roundup(ifindex + 1, 32) *
+                           sizeof(struct ifmap_entry);
+                       if ((ifmap = realloc(ifmap, size)) == NULL)
+                               errx(2, "realloc(%d) failed", size);
+                       memset(&ifmap[ifmap_size], 0,
+                           size - ifmap_size *
+                            sizeof(struct ifmap_entry));
+
+                       ifmap_size = roundup(ifindex + 1, 32);
+               }
+
+               if (*ifmap[ifindex].ifname != '\0')
+                       continue;
+
+               strlcpy(ifmap[ifindex].ifname, ifa->ifa_name, IFNAMSIZ);
+       }
+
+       freeifaddrs(ifap);
 
        mib[0] = CTL_NET;
        mib[1] = PF_ROUTE;
        mib[2] = 0;
-       mib[3] = 0;
+       mib[3] = af;
        mib[4] = NET_RT_DUMP;
        mib[5] = 0;
-       if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+       mib[6] = fibnum;
+       if (sysctl(mib, 7, NULL, &needed, NULL, 0) < 0) {
                err(1, "sysctl: net.route.0.0.dump estimate");
        }
 
@@ -548,6 +584,16 @@ ntreestuff(void)
        lim  = buf + needed;
        for (next = buf; next < lim; next += rtm->rtm_msglen) {
                rtm = (struct rt_msghdr *)next;
+               /*
+                * Peek inside header to determine AF
+                */
+               sa = (struct sockaddr *)(rtm + 1);
+               if (fam != sa->sa_family) {
+                       fam = sa->sa_family;
+                       size_cols(fam, NULL);
+                       pr_family(fam);
+                       pr_rthdr(fam);
+               }
                np_rtentry(rtm);
        }
 }
@@ -556,38 +602,52 @@ static void
 np_rtentry(struct rt_msghdr *rtm)
 {
        struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
-#ifdef notdef
-       static int masks_done, banner_printed;
-#endif
-       static int old_af;
-       int af1 = 0, interesting = RTF_UP | RTF_GATEWAY | RTF_HOST;
+       char buffer[128];
+       char prettyname[128];
+       sa_u addr, mask, gw;
+       unsigned int l;
+
+#define        GETSA(_s, _f)   { \
+       bzero(&(_s), sizeof(_s)); \
+       if (rtm->rtm_addrs & _f) { \
+               l = roundup(sa->sa_len, sizeof(long)); \
+               memcpy(&(_s), sa, (l > sizeof(_s)) ? sizeof(_s) : l); \
+               sa = (struct sockaddr *)((char *)sa + l); \
+       } \
+}
+
+       GETSA(addr, RTA_DST);
+       GETSA(gw, RTA_GATEWAY);
+       GETSA(mask, RTA_NETMASK);
+       p_sockaddr(&addr.u_sa, &mask.u_sa, rtm->rtm_flags, wid_dst);
+       p_sockaddr(&gw.u_sa, NULL, RTF_HOST, wid_gw);
 
-#ifdef notdef
-       /* for the moment, netmasks are skipped over */
-       if (!banner_printed) {
-               printf("Netmasks:\n");
-               banner_printed = 1;
-       }
-       if (masks_done == 0) {
-               if (rtm->rtm_addrs != RTA_DST ) {
-                       masks_done = 1;
-                       af1 = sa->sa_family;
-               }
-       } else
-#endif
-               af1 = sa->sa_family;
-       if (af1 != old_af) {
-               pr_family(af1);
-               old_af = af1;
+       snprintf(buffer, sizeof(buffer), "%%-%d.%ds ", wid_flags, wid_flags);
+       p_flags(rtm->rtm_flags, buffer);
+       if (Wflag) {
+               if (rtm->rtm_rmx.rmx_mtu != 0)
+                       printf("%*lu ", wid_mtu, rtm->rtm_rmx.rmx_mtu);
+               else
+                       printf("%*s ", wid_mtu, "");
        }
-       if (rtm->rtm_addrs == RTA_DST)
-               p_sockaddr(sa, NULL, 0, 36);
-       else {
-               p_sockaddr(sa, NULL, rtm->rtm_flags, 16);
-               sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa);
-               p_sockaddr(sa, NULL, 0, 18);
+
+       memset(prettyname, 0, sizeof(prettyname));
+       if (rtm->rtm_index < ifmap_size) {
+               strlcpy(prettyname, ifmap[rtm->rtm_index].ifname,
+                   sizeof(prettyname));
+               if (*prettyname == '\0')
+                       strlcpy(prettyname, "---", sizeof(prettyname));
        }
-       p_flags(rtm->rtm_flags & interesting, "%-6.6s ");
+
+       printf("%*.*s", wid_if, wid_if, prettyname);
+       if (rtm->rtm_rmx.rmx_expire) {
+               time_t expire_time;
+
+               if ((expire_time =
+                   rtm->rtm_rmx.rmx_expire - uptime.tv_sec) > 0)
+                       printf(" %*d", wid_expire, (int)expire_time);
+       }
+
        putchar('\n');
 }
 
@@ -775,8 +835,10 @@ p_rtentry(struct rtentry *rt)
        snprintf(buffer, sizeof(buffer), "%%-%d.%ds ", wid_flags, wid_flags);
        p_flags(rt->rt_flags, buffer);
        if (addr.u_sa.sa_family == AF_INET || Wflag) {
+#if 0
                printf("%*d %*lu ", wid_refs, rt->rt_refcnt,
                                     wid_use, rt->rt_use);
+#endif
                if (Wflag) {
                        if (rt->rt_rmx.rmx_mtu != 0)
                                printf("%*lu ", wid_mtu, rt->rt_rmx.rmx_mtu);
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to