I've written an extension to systat that allows you to monitor the
    traffic through active network interfaces on the system, akin to
    netstat -I.  I've attached the patch to this e-mail, but it can also be
    found at http://arpa.com/~trent/systat-ifstat-current.patch[.tar.gz].

    All comments on code, style or functionality are welcome.

    Regards,

        Trent.
Common subdirectories: /shared/data/trent/src/src/usr.bin/systat/CVS and 
/usr/src/usr.bin/systat/CVS
diff -uBN /shared/data/trent/src/src/usr.bin/systat/Makefile 
/usr/src/usr.bin/systat/Makefile
--- /shared/data/trent/src/src/usr.bin/systat/Makefile  Fri Feb  8 23:07:35 2002
+++ /usr/src/usr.bin/systat/Makefile    Sat Dec 28 16:23:57 2002
@@ -4,7 +4,7 @@
 PROG=  systat
 SRCS=  cmds.c cmdtab.c devs.c fetch.c iostat.c keyboard.c main.c \
        mbufs.c netcmds.c netstat.c pigs.c swap.c icmp.c mode.c ip.c tcp.c \
-       vmstat.c
+       vmstat.c convtbl.c ifcmds.c ifstat.c
 DPADD= ${LIBCURSES} ${LIBM} ${LIBKVM} ${LIBDEVSTAT}
 LDADD= -lcurses -lm -lkvm -ldevstat
 
diff -uBN /shared/data/trent/src/src/usr.bin/systat/cmds.c 
/usr/src/usr.bin/systat/cmds.c
--- /shared/data/trent/src/src/usr.bin/systat/cmds.c    Wed Dec 12 00:13:37 2001
+++ /usr/src/usr.bin/systat/cmds.c      Sat Jan  4 14:11:32 2003
@@ -119,6 +119,7 @@
                        goto done;
                 alarm(0);
                (*curcmd->c_close)(wnd);
+               curcmd->c_flags &= ~CF_INIT;
                wnd = (*p->c_open)();
                if (wnd == 0) {
                        error("Couldn't open new display");
diff -uBN /shared/data/trent/src/src/usr.bin/systat/cmdtab.c 
/usr/src/usr.bin/systat/cmdtab.c
--- /shared/data/trent/src/src/usr.bin/systat/cmdtab.c  Wed Dec 12 00:13:37 2001
+++ /usr/src/usr.bin/systat/cmdtab.c    Sat Dec 28 16:30:52 2002
@@ -71,6 +71,9 @@
        { "tcp",        showtcp,        fetchtcp,       labeltcp,
          inittcp,      opentcp,        closetcp,       cmdmode,
          resettcp,     0 },
+       { "ifstat",     showifstat,     fetchifstat,    labelifstat,
+         initifstat,   openifstat,     closeifstat,    cmdifstat,
+         0,            CF_LOADAV },
         { NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0 }
 };
 struct  cmdtab *curcmd = &cmdtab[0];
diff -uBN /shared/data/trent/src/src/usr.bin/systat/convtbl.c 
/usr/src/usr.bin/systat/convtbl.c
--- /shared/data/trent/src/src/usr.bin/systat/convtbl.c Thu Jan  1 01:00:00 1970
+++ /usr/src/usr.bin/systat/convtbl.c   Sat Jan  4 00:13:11 2003
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2003, Trent Nelson, <[EMAIL PROTECTED]>.
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include "convtbl.h"
+
+struct convtbl convtbl[] = {
+       /* mul, scale, str */
+       { BYTE, BYTES, "bytes" },       /* SC_BYTE      (0) */
+       { BYTE, KILO, "KB" },           /* SC_KILOBYTE  (1) */
+       { BYTE, MEGA, "MB" },           /* SC_MEGABYTE  (2) */
+       { BYTE, GIGA, "GB" },           /* SC_GIGABYTE  (3) */
+
+       { BIT, BITS, "b" },             /* SC_BITS      (4) */
+       { BIT, KILO, "Kb" },            /* SC_KILOBITS  (5) */
+       { BIT, MEGA, "Mb" },            /* SC_MEGABITS  (6) */
+       { BIT, GIGA, "Gb" },            /* SC_GIGABITS  (7) */
+
+       { 0, 0, "" }                    /* SC_AUTO      (8) */
+
+};
+
+
+static __inline__
+struct convtbl *
+get_tbl_ptr(const u_long size, const u_int scale)
+{
+       struct  convtbl *tbl_ptr = NULL;
+       u_long  tmp = 0;
+       u_int   index = scale;
+
+       /* If our index is out of range, default to auto-scaling. */
+       if (index > SC_AUTO)
+               index = SC_AUTO;
+
+       if (index == SC_AUTO)
+               /*
+                * Simple but elegant algorithm.  Count how many times
+                * we can shift our size value right by a factor of ten,
+                * incrementing an index each time.  We then use the
+                * index as the array index into the conversion table.
+                */
+               for (tmp = size, index = SC_KILOBYTE;
+                    tmp >= MEGA && index <= SC_GIGABYTE;
+                    tmp >>= 10, index++);
+
+       tbl_ptr = &convtbl[index];
+       return tbl_ptr;
+}
+
+double
+convert(const u_long size, const u_int scale)
+{
+       struct  convtbl *tp = NULL;
+
+       tp = get_tbl_ptr(size, scale);
+
+       return ((double)size * tp->mul / tp->scale);
+
+}
+
+char *
+get_string(const u_long size, const u_int scale)
+{
+       struct  convtbl *tp = NULL;
+
+       tp = get_tbl_ptr(size, scale);
+
+       return tp->str;
+}
diff -uBN /shared/data/trent/src/src/usr.bin/systat/convtbl.h 
/usr/src/usr.bin/systat/convtbl.h
--- /shared/data/trent/src/src/usr.bin/systat/convtbl.h Thu Jan  1 01:00:00 1970
+++ /usr/src/usr.bin/systat/convtbl.h   Sat Jan  4 00:13:11 2003
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2003, Trent Nelson, <[EMAIL PROTECTED]>.
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id$
+ */
+
+#ifndef _CONVTBL_H_
+#define _CONVTBL_H_
+
+#include <sys/types.h>
+
+#define BITS   (1)
+#define BYTES  (1)
+#define KILO   (1024)
+#define        MEGA    (KILO * 1024)
+#define GIGA   (MEGA * 1024)
+
+#define SC_BYTE                (0)
+#define SC_KILOBYTE    (1)
+#define SC_MEGABYTE    (2)
+#define SC_GIGABYTE    (3)
+#define SC_BIT         (4)
+#define        SC_KILOBIT      (5)
+#define        SC_MEGABIT      (6)
+#define SC_GIGABIT     (7)
+#define SC_AUTO                (8)
+
+#define BIT    (8)
+#define BYTE   (1)
+
+struct convtbl {
+       u_int    mul;
+       u_int    scale;
+       char    *str;
+};
+
+extern struct convtbl convtbl[];
+
+extern double   convert(const u_long, const u_int);
+extern char    *get_string(const u_long, const u_int);
+
+#endif         /* ! _CONVTBL_H_ */
diff -uBN /shared/data/trent/src/src/usr.bin/systat/extern.h 
/usr/src/usr.bin/systat/extern.h
--- /shared/data/trent/src/src/usr.bin/systat/extern.h  Fri Mar 22 01:42:31 2002
+++ /usr/src/usr.bin/systat/extern.h    Sat Dec 28 16:35:36 2002
@@ -72,8 +72,9 @@
 
 int     checkhost(struct inpcb *);
 int     checkport(struct inpcb *);
-void    closeiostat(WINDOW *);
 void    closeicmp(WINDOW *);
+void    closeifstat(WINDOW *);
+void    closeiostat(WINDOW *);
 void    closeip(WINDOW *);
 void    closekre(WINDOW *);
 void    closembufs(WINDOW *);
@@ -81,6 +82,7 @@
 void    closepigs(WINDOW *);
 void    closeswap(WINDOW *);
 void    closetcp(WINDOW *);
+int     cmdifstat(const char *, const char *);
 int     cmdiostat(const char *, const char *);
 int     cmdkre(const char *, const char *);
 int     cmdnetstat(const char *, const char *);
@@ -92,6 +94,7 @@
 int     dkcmd(char *, char *);
 void    error(const char *fmt, ...) __printflike(1, 2);
 void    fetchicmp(void);
+void    fetchifstat(void);
 void    fetchip(void);
 void    fetchiostat(void);
 void    fetchkre(void);
@@ -102,6 +105,7 @@
 void    fetchtcp(void);
 void    getsysctl(const char *, void *, size_t);
 int     initicmp(void);
+int     initifstat(void);
 int     initip(void);
 int     initiostat(void);
 int     initkre(void);
@@ -113,6 +117,7 @@
 int     keyboard(void);
 int     kvm_ckread(void *, void *, int);
 void    labelicmp(void);
+void    labelifstat(void);
 void    labelip(void);
 void    labeliostat(void);
 void    labelkre(void);
@@ -126,6 +131,7 @@
 int     netcmd(const char *, const char *);
 void    nlisterr(struct nlist []);
 WINDOW *openicmp(void);
+WINDOW *openifstat(void);
 WINDOW *openip(void);
 WINDOW *openiostat(void);
 WINDOW *openkre(void);
@@ -139,6 +145,7 @@
 void    resetip(void);
 void    resettcp(void);
 void    showicmp(void);
+void    showifstat(void);
 void    showip(void);
 void    showiostat(void);
 void    showkre(void);
diff -uBN /shared/data/trent/src/src/usr.bin/systat/ifcmds.c 
/usr/src/usr.bin/systat/ifcmds.c
--- /shared/data/trent/src/src/usr.bin/systat/ifcmds.c  Thu Jan  1 01:00:00 1970
+++ /usr/src/usr.bin/systat/ifcmds.c    Sat Jan  4 00:13:11 2003
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2003, Trent Nelson, <[EMAIL PROTECTED]>.
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <net/if.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>      /* For IFT_ETHER */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <float.h>
+#include <err.h>
+
+#include "systat.h"
+#include "extern.h"
+#include "mode.h"
+#include "convtbl.h"
+
+int    curscale = SC_AUTO;
+
+static int selectscale(const char *);
+
+int
+ifcmd(const char *cmd, const char *args)
+{
+       if (prefix((char *)cmd, (char *)"scale")) {
+               if (*args != '\0' && selectscale(args) != -1)
+                       ;
+               else {
+                       move(CMDLINE, 0);
+                       clrtoeol();
+                       addstr("what scale? kbit, kbyte, mbit, mbyte, " \
+                              "gbit, gbyte, auto");
+               } 
+       }
+       return 1;
+}
+
+static int
+selectscale(const char *args)
+{
+       int     retval = 0;
+
+#define streq(a,b)     (strcmp(a,b) == 0)
+       if (streq(args, "default") || streq(args, "auto"))
+               curscale = SC_AUTO;
+       else if (streq(args, "kbit"))
+               curscale = SC_KILOBIT;
+       else if (streq(args, "kbyte"))
+               curscale = SC_KILOBYTE;
+       else if (streq(args, "mbit"))
+               curscale = SC_MEGABIT;
+       else if (streq(args, "mbyte"))
+               curscale = SC_MEGABYTE;
+       else if (streq(args, "gbit"))
+               curscale = SC_GIGABIT;
+       else if (streq(args, "gbyte"))
+               curscale = SC_GIGABYTE;
+       else
+               retval = -1;
+
+       return retval;
+}
diff -uBN /shared/data/trent/src/src/usr.bin/systat/ifstat.c 
/usr/src/usr.bin/systat/ifstat.c
--- /shared/data/trent/src/src/usr.bin/systat/ifstat.c  Thu Jan  1 01:00:00 1970
+++ /usr/src/usr.bin/systat/ifstat.c    Sat Jan  4 15:57:12 2003
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2003, Trent Nelson, <[EMAIL PROTECTED]>.
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 INTIFSTAT_ERRUPTION)
+ * 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/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <net/if.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>      /* For IFT_ETHER */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <float.h>
+#include <err.h>
+
+#include "systat.h"
+#include "extern.h"
+#include "mode.h"
+#include "convtbl.h"
+
+                                /* Column numbers */
+
+#define C1     0               /*  0-19 */
+#define C2     20              /* 20-39 */ 
+#define C3     40              /* 40-59 */
+#define C4     60              /* 60-80 */
+#define C5     80              /* Used for label positioning. */
+
+static int col0 = 0;
+static int col1 = C1;
+static int col2 = C2;
+static int col3 = C3;
+static int col4 = C4;
+static int col5 = C5;
+
+
+SLIST_HEAD(, if_stat)          curlist;        
+SLIST_HEAD(, if_stat_disp)     displist;
+
+struct if_stat {
+       SLIST_ENTRY(if_stat)     link;
+       char    if_name[IF_NAMESIZE];
+       struct  ifmibdata if_mib;
+       struct  timeval tv;
+       struct  timeval tv_lastchanged;
+       u_long  if_in_curtraffic;
+       u_long  if_out_curtraffic;
+       u_long  if_in_traffic_peak;
+       u_long  if_out_traffic_peak;
+       u_int   if_row;                 /* Index into ifmib sysctl */
+       u_int   if_ypos;                /* 0 if not being displayed */
+       u_int   display;
+};
+
+extern  u_int curscale;
+
+static  void  right_align_string(const struct if_stat *);
+static  void  getifmibdata(const int, struct ifmibdata *);
+static  void  sort_interface_list(void);
+static  u_int getifnum(void);
+
+#define IFSTAT_ERR(n, s)       do {                                    \
+       putchar('');                                                   \
+       closeifstat(wnd);                                               \
+       err((n), (s));                                                  \
+} while (0)
+
+#define STARTING_ROW   (8)
+#define ROW_SPACING    (3)
+
+#define TOPLINE 5
+#define TOPLABEL \
+"      Interface           Traffic               Peak                Total"
+
+#define CLEAR_LINE(y, x)       do {                                    \
+       wmove(wnd, y, x);                                               \
+       wclrtoeol(wnd);                                                 \
+} while (0)
+
+#define IN_col2                (ifp->if_in_curtraffic)
+#define OUT_col2       (ifp->if_out_curtraffic)
+#define IN_col3                (ifp->if_in_traffic_peak)
+#define OUT_col3       (ifp->if_out_traffic_peak)
+#define IN_col4                (ifp->if_mib.ifmd_data.ifi_ibytes)
+#define OUT_col4       (ifp->if_mib.ifmd_data.ifi_obytes)
+
+#define EMPTY_COLUMN   "                    "
+#define CLEAR_COLUMN(y, x)     mvprintw((y), (x), "%20s", EMPTY_COLUMN);
+
+#define DOPUTRATE(c, r, d)     do {                                    \
+       CLEAR_COLUMN(r, c);                                             \
+       mvprintw(r, (c), "%10.3f %s%s  ",                               \
+                convert(d##_##c, curscale),                            \
+                get_string(d##_##c, curscale),                         \
+                "/s");                                                 \
+} while (0)
+
+#define DOPUTTOTAL(c, r, d)    do {                                    \
+       CLEAR_COLUMN((r), (c));                                         \
+       mvprintw((r), (c), "%12.3f %s  ",                               \
+                convert(d##_##c, SC_AUTO),                             \
+                get_string(d##_##c, SC_AUTO));                         \
+} while (0)
+
+#define PUTRATE(c, r)  do {                                            \
+       DOPUTRATE(c, (r), IN);                                          \
+       DOPUTRATE(c, (r)+1, OUT);                                       \
+} while (0)
+
+#define PUTTOTAL(c, r) do {                                            \
+       DOPUTTOTAL(c, (r), IN);                                         \
+       DOPUTTOTAL(c, (r)+1, OUT);                                      \
+} while (0)
+
+#define PUTNAME(p) do {                                                        \
+       mvprintw(p->if_ypos, 0, "%s", p->if_name);                      \
+       mvprintw(p->if_ypos, col2-3, "%s", (const char *)"in");         \
+       mvprintw(p->if_ypos+1, col2-3, "%s", (const char *)"out");      \
+} while (0)
+
+
+WINDOW *
+openifstat(void)
+{
+       return (subwin(stdscr, LINES-1-5, 0, 5, 0));
+}
+
+void
+closeifstat(WINDOW *w)
+{
+       struct if_stat  *node = NULL;
+
+       while (!SLIST_EMPTY(&curlist)) {
+               node = SLIST_FIRST(&curlist);
+               SLIST_REMOVE_HEAD(&curlist, link);
+               free(node);
+       }
+
+       if (w != NULL) {
+               wclear(w);
+               wrefresh(w);
+               delwin(w);
+       }
+
+       return;
+}
+
+
+void
+labelifstat(void)
+{
+
+       wmove(wnd, TOPLINE, 0);
+       wclrtoeol(wnd);
+       mvprintw(TOPLINE, 0, "%s", TOPLABEL);
+
+       return;
+}
+
+void
+showifstat(void)
+{
+       struct  if_stat *ifp = NULL;
+       SLIST_FOREACH(ifp, &curlist, link) {
+               if (ifp->display == 0)
+                       continue;
+               PUTNAME(ifp);
+               PUTRATE(col2, ifp->if_ypos);
+               PUTRATE(col3, ifp->if_ypos);
+               PUTTOTAL(col4, ifp->if_ypos);
+       }
+
+       return;
+}
+
+int 
+initifstat(void)
+{
+       struct   if_stat *p = NULL;
+       u_int    n = 0, i = 0;
+
+       n = getifnum();
+       if (n <= 0)
+               return -1;
+
+       SLIST_INIT(&curlist);
+
+       for (i = 0; i < n; i++) {
+               p = (struct if_stat *)malloc(sizeof(struct if_stat));
+               if (p == NULL)
+                       IFSTAT_ERR(1, "out of memory");
+               memset((void *)p, 0, sizeof(struct if_stat));
+               SLIST_INSERT_HEAD(&curlist, p, link);
+               p->if_row = i+1;
+               getifmibdata(p->if_row, &p->if_mib);
+               right_align_string(p);
+
+               /* 
+                * Initially, we only display interfaces that have
+                * received some traffic.
+                */
+               if (p->if_mib.ifmd_data.ifi_ibytes != 0)
+                       p->display = 1;
+       }
+
+       sort_interface_list();
+
+       return 1;
+}
+
+void
+fetchifstat(void)
+{
+       struct  if_stat *ifp = NULL;
+       struct  timeval tv, new_tv, old_tv;
+       double  elapsed = 0.0;
+       u_int   new_inb, new_outb, old_inb, old_outb = 0;
+       u_int   error = 0;
+       u_int   we_need_to_sort_interface_list = 0;
+
+       SLIST_FOREACH(ifp, &curlist, link) {
+               /* 
+                * Grab a copy of the old input/output values before we
+                * call getifmibdata().
+                */
+               old_inb = ifp->if_mib.ifmd_data.ifi_ibytes;             
+               old_outb = ifp->if_mib.ifmd_data.ifi_obytes;
+               ifp->tv_lastchanged = ifp->if_mib.ifmd_data.ifi_lastchange;
+
+               error = gettimeofday(&new_tv, (struct timezone *)0);
+               if (error) 
+                       IFSTAT_ERR(2, "error getting time of day");
+               (void)getifmibdata(ifp->if_row, &ifp->if_mib);
+
+
+                new_inb = ifp->if_mib.ifmd_data.ifi_ibytes;
+                new_outb = ifp->if_mib.ifmd_data.ifi_obytes;
+
+               /* Display interface if it's received some traffic. */
+               if (new_inb > 0 && old_inb == 0) {
+                       ifp->display = 1;
+                       we_need_to_sort_interface_list++;
+               } 
+
+               /*
+                * The rest is pretty trivial.  Calculate the new values
+                * for our current traffic rates, and while we're there,
+                * see if we have new peak rates.
+                */
+                old_tv = ifp->tv;
+                timersub(&new_tv, &old_tv, &tv);
+                elapsed = tv.tv_sec + (tv.tv_usec * 1e-6);
+
+               ifp->if_in_curtraffic = new_inb - old_inb;
+               ifp->if_out_curtraffic = new_outb - old_outb;
+
+               /*
+                * Rather than divide by the time specified on the comm-
+                * and line, we divide by ``elapsed'' as this is likely
+                * to be more accurate.
+                */
+                ifp->if_in_curtraffic /= elapsed;
+                ifp->if_out_curtraffic /= elapsed;
+
+               if (ifp->if_in_curtraffic > ifp->if_in_traffic_peak)
+                       ifp->if_in_traffic_peak = ifp->if_in_curtraffic;
+
+               if (ifp->if_out_curtraffic > ifp->if_out_traffic_peak)
+                       ifp->if_out_traffic_peak = ifp->if_out_curtraffic;
+
+               ifp->tv.tv_sec = new_tv.tv_sec;
+               ifp->tv.tv_usec = new_tv.tv_usec;
+
+       }
+
+       if (we_need_to_sort_interface_list)
+               sort_interface_list();
+
+       return;
+}
+
+/* 
+ * We want to right justify our interface names against the first column
+ * (first sixteen or so characters), so we need to do some alignment.
+ */
+static void
+right_align_string(const struct if_stat *ifp)
+{
+       int      str_len = 0, pad_len = 0;
+       char    *newstr = NULL, *ptr = NULL;
+
+       if (ifp == NULL || ifp->if_mib.ifmd_name == NULL)
+               return;
+       else {
+               /* string length + '\0' */
+               str_len = strlen(ifp->if_mib.ifmd_name)+1;
+               pad_len = IF_NAMESIZE-(str_len);
+
+               newstr = (char *)ifp->if_name;
+               ptr = newstr + pad_len;
+               (void)memset((void *)newstr, (int)' ', IF_NAMESIZE);
+               (void)strncpy(ptr, (const char *)&ifp->if_mib.ifmd_name,
+                             str_len);
+       }
+
+       return;
+}
+
+/*
+ * This function iterates through our list of interfaces, identifying
+ * those that are to be displayed (ifp->display = 1).  For each interf-
+ * rface that we're displaying, we generate an appropriate position for
+ * it on the screen (ifp->if_ypos).
+ *
+ * This function is called any time a change is made to an interface's
+ * ``display'' state.
+ */
+void
+sort_interface_list(void)
+{
+       struct  if_stat *ifp = NULL;
+       u_int   y = 0;  
+
+       y = STARTING_ROW;
+       SLIST_FOREACH(ifp, &curlist, link) {
+               if (ifp->display) {
+                       ifp->if_ypos = y;
+                       y += ROW_SPACING;
+               }
+       }
+}
+
+static
+unsigned int
+getifnum(void)
+{
+       int     error   = 0;
+       u_int   data    = 0;
+       size_t  datalen = 0;
+       static  int name[] = { CTL_NET,
+                              PF_LINK,
+                              NETLINK_GENERIC,
+                              IFMIB_SYSTEM,
+                              IFMIB_IFCOUNT };
+
+       datalen = sizeof(data);
+       error = sysctl(name, 
+                      5,
+                      (void *)&data,
+                      (size_t *)&datalen,
+                      (void *)NULL,
+                      (size_t)0);
+       if (error)
+               IFSTAT_ERR(1, "sysctl error");
+       return data;
+}
+
+static void 
+getifmibdata(int row, struct ifmibdata *data)
+{
+       int     error   = 0;
+       size_t  datalen = 0;
+       static  int name[] = { CTL_NET,
+                              PF_LINK,
+                              NETLINK_GENERIC,
+                              IFMIB_IFDATA,
+                              0,
+                              IFDATA_GENERAL };
+       datalen = sizeof(*data);
+       name[4] = row;
+
+       error = sysctl(name,
+                      6,
+                      (void *)data,
+                      (size_t *)&datalen,
+                      (void *)NULL,
+                      (size_t)0);
+       if (error)
+               IFSTAT_ERR(2, "sysctl error getting interface data");
+}
+
+int
+cmdifstat(const char *cmd, const char *args)
+{
+       int     retval = 0;
+
+       retval = ifcmd(cmd, args);
+       /* ifcmd() returns 1 on success */
+       if (retval == 1) {
+               showifstat();
+               refresh();
+       }
+
+       return retval;
+}
diff -uBN /shared/data/trent/src/src/usr.bin/systat/systat.1 
/usr/src/usr.bin/systat/systat.1
--- /shared/data/trent/src/src/usr.bin/systat/systat.1  Fri Dec 27 12:15:35 2002
+++ /usr/src/usr.bin/systat/systat.1    Sat Jan  4 14:31:06 2003
@@ -30,7 +30,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\"    @(#)systat.1    8.2 (Berkeley) 12/30/93
-.\" $FreeBSD: src/usr.bin/systat/systat.1,v 1.36 2002/12/27 12:15:35 schweikh Exp $
+.\" $FreeBSD: src/usr.bin/systat/systat.1,v 1.23.2.9 2002/12/29 16:35:40 schweikh Exp 
+$
 .\"
 .Dd September 9, 1997
 .Dt SYSTAT 1
@@ -88,6 +88,7 @@
 .Ar display
 to be one of:
 .Ic icmp ,
+.Ic ifstat ,
 .Ic iostat ,
 .Ic ip ,
 .Ic mbufs ,
@@ -432,6 +433,27 @@
 Reset the port, host, and protocol matching mechanisms to the default
 (any protocol, port, or host).
 .El
+.It Ic ifstat
+Display the network traffic going through active interfaces on the
+system.  Idle interfaces will not be displayed until they receive some
+traffic.
+.Pp
+For each interface being displayed, the current, peak and total 
+statistics are displayed for incoming and outgoing traffic.  By default,
+the 
+.Ic ifstat
+display will automatically scale the units being used so that they are
+in a human-readable format.  The scaling units used for the current and peak
+traffic columns can be altered by the 
+.Ic scale
+command.  
+.Pp
+.Bl -tag -width Ar -compact
+.It Cm scale Op Ar units
+Modify the scale used to display the current and peak traffic over all
+interfaces.  The following units are recognised: kbit, kbyte, mbit,
+mbyte, gbit, gbyte and auto.
+.El
 .El
 .Pp
 Commands to switch between displays may be abbreviated to the
@@ -500,8 +522,8 @@
 .Xr iostat 8 ,
 .Xr vmstat 8
 .Sh FILES
-.Bl -tag -width /boot/kernel/kernel -compact
-.It Pa /boot/kernel/kernel
+.Bl -tag -width /etc/networks -compact
+.It Pa /kernel
 For the namelist.
 .It Pa /dev/kmem
 For information in main memory.

Reply via email to