Awhile back, DragonlyFlyBSD added a netbw option to systat that I've ported
to FreeBSD and found handy at various times:

   netbw      Display aggregate and per-connection TCP receive and transmit
                      rates.  Only active TCP connections are shown.

Leading to output such as:

tcp accepts        connects                 rcv 1.192G snd 15.77K rexmit

  192.168.10.80:22      192.168.10.20:23103 rcv        snd 415.7  [  NTSX ]
  192.168.10.80:22      192.168.10.20:46560 rcv 19.80M snd 14.47K [  NTSX ]
  192.168.10.80:22      192.168.10.20:60699 rcv        snd 886.3  [  NTSX ]
  192.168.10.81:5201    192.168.10.51:60844 rcv 293.2M snd        [R  TSX ]
  192.168.10.81:5201    192.168.10.51:60845 rcv 293.5M snd        [R  TSX ]
  192.168.10.81:5201    192.168.10.51:60846 rcv 293.2M snd        [R  TSX ]
  192.168.10.81:5201    192.168.10.51:60847 rcv 292.9M snd        [R  TSX ]

It uses the sequences number from the 'struct tcpcb' to derive the rates,
which is usually good but certainly not perfect (i.e., don't set the
interval too long).

I'd like to commit this if anybody else thinks they'd find it useful.

http://people.freebsd.org/~bryanv/patches/systat-netbw.patch
From d0a4f282f3e36eb53cb0a50a64aa4597e52b7d42 Mon Sep 17 00:00:00 2001
From: Bryan Venteicher <bry...@daemoninthecloset.org>
Date: Tue, 1 Jul 2014 00:51:29 -0500
Subject: [PATCH] Add 'netbw' display to systat

---
 usr.bin/systat/Makefile |   2 +-
 usr.bin/systat/cmdtab.c |   3 +
 usr.bin/systat/extern.h |   7 +
 usr.bin/systat/netbw.c  | 476 ++++++++++++++++++++++++++++++++++++++++++++++++
 usr.bin/systat/systat.1 |   4 +
 5 files changed, 491 insertions(+), 1 deletion(-)
 create mode 100644 usr.bin/systat/netbw.c

diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile
index 1bb2da0..a17e4dd 100644
--- a/usr.bin/systat/Makefile
+++ b/usr.bin/systat/Makefile
@@ -7,7 +7,7 @@ PROG=	systat
 SRCS=	cmds.c cmdtab.c devs.c fetch.c iostat.c keyboard.c main.c \
 	netcmds.c netstat.c pigs.c swap.c icmp.c \
 	mode.c ip.c tcp.c \
-	vmstat.c convtbl.c ifcmds.c ifstat.c
+	vmstat.c convtbl.c ifcmds.c ifstat.c netbw.c
 
 .if ${MK_INET6_SUPPORT} != "no"
 SRCS+=	icmp6.c ip6.c
diff --git a/usr.bin/systat/cmdtab.c b/usr.bin/systat/cmdtab.c
index c9c9e7d..0e225ec 100644
--- a/usr.bin/systat/cmdtab.c
+++ b/usr.bin/systat/cmdtab.c
@@ -55,6 +55,9 @@ struct	cmdtab cmdtab[] = {
 	{ "netstat",	shownetstat,	fetchnetstat,	labelnetstat,
 	  initnetstat,	opennetstat,	closenetstat,	cmdnetstat,
 	  0,		CF_LOADAV },
+	{ "netbw",	shownetbw,	fetchnetbw,	labelnetbw,
+	  initnetbw,	opennetbw,	closenetbw,	NULL,
+	  0,		0 },
 	{ "icmp",	showicmp,	fetchicmp,	labelicmp,
 	  initicmp,	openicmp,	closeicmp,	cmdmode,
 	  reseticmp,	CF_LOADAV },
diff --git a/usr.bin/systat/extern.h b/usr.bin/systat/extern.h
index 17fffc1..38d4084 100644
--- a/usr.bin/systat/extern.h
+++ b/usr.bin/systat/extern.h
@@ -76,6 +76,7 @@ void	 closeiostat(WINDOW *);
 void	 closeip(WINDOW *);
 void	 closeip6(WINDOW *);
 void	 closekre(WINDOW *);
+void	 closenetbw(WINDOW *);
 void	 closenetstat(WINDOW *);
 void	 closepigs(WINDOW *);
 void	 closeswap(WINDOW *);
@@ -83,6 +84,7 @@ void	 closetcp(WINDOW *);
 int	 cmdifstat(const char *, const char *);
 int	 cmdiostat(const char *, const char *);
 int	 cmdkre(const char *, const char *);
+int	 cmdnetbw(const char *, const char *);
 int	 cmdnetstat(const char *, const char *);
 struct	 cmdtab *lookup(const char *);
 void	 command(const char *);
@@ -98,6 +100,7 @@ void	 fetchip(void);
 void	 fetchip6(void);
 void	 fetchiostat(void);
 void	 fetchkre(void);
+void	 fetchnetbw(void);
 void	 fetchnetstat(void);
 void	 fetchpigs(void);
 void	 fetchswap(void);
@@ -111,6 +114,7 @@ int	 initip(void);
 int	 initip6(void);
 int	 initiostat(void);
 int	 initkre(void);
+int	 initnetbw(void);
 int	 initnetstat(void);
 int	 initpigs(void);
 int	 initswap(void);
@@ -124,6 +128,7 @@ void	 labelip(void);
 void	 labelip6(void);
 void	 labeliostat(void);
 void	 labelkre(void);
+void	 labelnetbw(void);
 void	 labelnetstat(void);
 void	 labelpigs(void);
 void	 labels(void);
@@ -139,6 +144,7 @@ WINDOW	*openip(void);
 WINDOW	*openip6(void);
 WINDOW	*openiostat(void);
 WINDOW	*openkre(void);
+WINDOW	*opennetbw(void);
 WINDOW	*opennetstat(void);
 WINDOW	*openpigs(void);
 WINDOW	*openswap(void);
@@ -156,6 +162,7 @@ void	 showip(void);
 void	 showip6(void);
 void	 showiostat(void);
 void	 showkre(void);
+void	 shownetbw(void);
 void	 shownetstat(void);
 void	 showpigs(void);
 void	 showswap(void);
diff --git a/usr.bin/systat/netbw.c b/usr.bin/systat/netbw.c
new file mode 100644
index 0000000..785af5f
--- /dev/null
+++ b/usr.bin/systat/netbw.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2013 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dil...@backplane.com>
+ *
+ * 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 DragonFly Project 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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/param.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#endif
+#include <netinet/in_pcb.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcp_debug.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <nlist.h>
+#include <paths.h>
+#include "systat.h"
+#include "extern.h"
+
+/* Reduce diff from DragonflyBSD. */
+union in_dependaddr {
+	struct in_addr_4in6 id46_addr;
+	struct in6_addr id6_addr;
+};
+typedef int32_t tcp_seq_diff_t;
+#define s6_addr16 __u6_addr.__u6_addr16
+
+struct mytcpcb {
+	RB_ENTRY(mytcpcb)	rb_node;
+	int			seq;
+	struct xtcpcb		xtcp;
+	struct xtcpcb		last_xtcp;
+};
+
+static int
+mytcpcb_cmp(struct mytcpcb *tcp1, struct mytcpcb *tcp2)
+{
+	int r;
+
+	/*
+	 * Low local or foreign port comes first (local has priority).
+	 */
+	if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_lport) >= 1024 &&
+	    ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_lport) >= 1024) {
+		if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_fport) <
+		    ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_fport))
+			return(-1);
+		if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_fport) >
+		    ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_fport))
+			return(1);
+	}
+
+	if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_lport) <
+	    ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_lport))
+		return(-1);
+	if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_lport) >
+	    ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_lport))
+		return(1);
+	if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_fport) <
+	    ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_fport))
+		return(-1);
+	if (ntohs(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_fport) >
+	    ntohs(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_fport))
+		return(1);
+
+	/*
+	 * Sort IPV4 vs IPV6 addresses
+	 */
+	if ((tcp1->xtcp.xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)) <
+	    (tcp2->xtcp.xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)))
+		return(-1);
+	if ((tcp1->xtcp.xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)) >
+	    (tcp2->xtcp.xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)))
+		return(1);
+
+	/*
+	 * Local and foreign addresses
+	 */
+	if (tcp1->xtcp.xt_inp.inp_vflag & INP_IPV4) {
+		if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr) <
+		    ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr))
+			return(-1);
+		if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr) >
+		    ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_laddr.s_addr))
+			return(1);
+		if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr) <
+		    ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr))
+			return(-1);
+		if (ntohl(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr) >
+		    ntohl(tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie_faddr.s_addr))
+			return(1);
+	} else if (tcp1->xtcp.xt_inp.inp_vflag & INP_IPV6) {
+		r = bcmp(&tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr,
+			 &tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr,
+			 sizeof(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr));
+		if (r)
+			return(r);
+	} else {
+		r = bcmp(&tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr,
+			 &tcp2->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr,
+			 sizeof(tcp1->xtcp.xt_inp.inp_inc.inc_ie.ie6_faddr));
+		if (r)
+			return(r);
+	}
+	return(0);
+}
+
+struct mytcpcb_tree;
+static RB_HEAD(mytcpcb_tree, mytcpcb);
+RB_PROTOTYPE_STATIC(mytcpcb_tree, mytcpcb, rb_node, mytcpcb_cmp);
+RB_GENERATE_STATIC(mytcpcb_tree, mytcpcb, rb_node, mytcpcb_cmp);
+
+static struct mytcpcb_tree mytcp_tree;
+static struct timeval tv_curr;
+static struct timeval tv_last;
+static struct tcpstat tcp_curr;
+static struct tcpstat tcp_last;
+static int tcp_pcb_seq;
+
+static const char *numtok(double value);
+static void netbwline(int row, struct mytcpcb *elm, double delta_time);
+static const char *netaddrstr(u_char vflags, union in_dependaddr *depaddr,
+		       u_int16_t port);
+static void updatepcb(struct xtcpcb *xtcp);
+
+#define DELTARATE(field)	\
+	((double)(tcp_curr.field - tcp_last.field) / delta_time)
+
+#define DELTAELM(field)		\
+	((double)(tcp_seq_diff_t)(elm->xtcp.field -		\
+				  elm->last_xtcp.field) /	\
+	 delta_time)
+
+#define DELTAELMSCALE(field, scale)		\
+	((double)((tcp_seq_diff_t)(elm->xtcp.field -		\
+				   elm->last_xtcp.field) << scale) / \
+	 delta_time)
+
+WINDOW *
+opennetbw(void)
+{
+	RB_INIT(&mytcp_tree);
+	return (subwin(stdscr, LINES-0-1, 0, 0, 0));
+}
+
+void
+closenetbw(WINDOW *w)
+{
+	struct mytcpcb *mytcp;
+
+	while ((mytcp = RB_ROOT(&mytcp_tree)) != NULL) {
+		RB_REMOVE(mytcpcb_tree, &mytcp_tree, mytcp);
+		free(mytcp);
+	}
+
+	if (w != NULL) {
+		wclear(w);
+		wrefresh(w);
+		delwin(w);
+	}
+}
+
+int
+initnetbw(void)
+{
+	return(1);
+}
+
+void
+fetchnetbw(void)
+{
+	struct xinpgen *inpg;
+	struct xtcpcb *xtp;
+	char *cur, *end;
+	size_t len;
+
+	/*
+	 * Extract PCB list
+	 */
+	inpg = (struct xinpgen *)sysctl_dynread("net.inet.tcp.pcblist", &len);
+	if (inpg == NULL)
+		return;
+	/*
+	 * We currently do no require a consistent pcb list.
+	 * Try to be robust in case of struct size changes
+	 */
+	cur = ((char *)inpg) + inpg->xig_len;
+	/* There is also a trailing struct xinpgen */
+	end = ((char *)inpg) + len - inpg->xig_len;
+	if (end <= cur) {
+		free(inpg);
+		return;
+	}
+	++tcp_pcb_seq;
+	xtp = (struct xtcpcb *)cur;
+	while (cur + xtp->xt_len <= end) {
+		updatepcb(xtp);
+		cur += xtp->xt_len;
+		xtp = (struct xtcpcb *)cur;
+	}
+	free(inpg);
+
+	/*
+	 * General stats
+	 */
+	tcp_last = tcp_curr;
+	len = sizeof(tcp_curr);
+	if (sysctlbyname("net.inet.tcp.stats", &tcp_curr, &len, NULL, 0) < 0)
+		return;
+	tv_last = tv_curr;
+	gettimeofday(&tv_curr, NULL);
+}
+
+void
+labelnetbw(void)
+{
+	wmove(wnd, 0, 0);
+	wclrtobot(wnd);
+#if 0
+	mvwaddstr(wnd, 0, LADDR, "Local Address");
+	mvwaddstr(wnd, 0, FADDR, "Foreign Address");
+	mvwaddstr(wnd, 0, PROTO, "Proto");
+	mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
+	mvwaddstr(wnd, 0, SNDCC, "Send-Q");
+	mvwaddstr(wnd, 0, STATE, "(state)");
+#endif
+}
+
+void
+shownetbw(void)
+{
+	double delta_time;
+	struct mytcpcb *elm, *telm;
+	int row;
+
+	delta_time = (double)(tv_curr.tv_sec - tv_last.tv_sec) - 1.0 +
+		     (tv_curr.tv_usec + 1000000 - tv_last.tv_usec) / 1e6;
+	if (delta_time < 0.1)
+		return;
+
+	mvwprintw(wnd, 0, 0,
+		  "tcp accepts %s connects %s "
+		  "         rcv %s snd %s rexmit %s",
+		  numtok(DELTARATE(tcps_accepts)),
+		  numtok(DELTARATE(tcps_connects) - DELTARATE(tcps_accepts)),
+		  numtok(DELTARATE(tcps_rcvbyte)),
+		  numtok(DELTARATE(tcps_sndbyte)),
+		  numtok(DELTARATE(tcps_sndrexmitbyte)));
+
+	row = 2;
+	RB_FOREACH_SAFE(elm, mytcpcb_tree, &mytcp_tree, telm) {
+		if (elm->seq == tcp_pcb_seq &&
+		    (elm->xtcp.xt_socket.so_rcv.sb_cc ||
+		     elm->xtcp.xt_socket.so_snd.sb_cc ||
+		     DELTAELM(xt_tp.snd_max) ||
+		     DELTAELM(xt_tp.rcv_nxt)
+		    )) {
+			if (row < LINES - 3)
+				netbwline(row, elm, delta_time);
+			++row;
+		} else if (elm->seq != tcp_pcb_seq) {
+			RB_REMOVE(mytcpcb_tree, &mytcp_tree, elm);
+			free(elm);
+		}
+	}
+	wmove(wnd, row, 0);
+	wclrtobot(wnd);
+	mvwprintw(wnd, LINES-2, 0,
+		  "Rate/sec, "
+		  "R=rxpend T=txpend N=nodelay T=tstmp "
+		  "S=sack X=winscale F=fastrec");
+}
+
+static void
+netbwline(int row, struct mytcpcb *elm, double delta_time)
+{
+	mvwprintw(wnd, row, 0,
+		  "%s %s "
+		  /*"rxb %s txb %s "*/
+		  "rcv %s snd %s "
+		  "[%c%c%c%c%c%c%c]",
+		  netaddrstr(
+		    elm->xtcp.xt_inp.inp_vflag,
+		    (union in_dependaddr *)&elm->xtcp.xt_inp.inp_inc.inc_ie.
+			ie_dependladdr,
+		    ntohs(elm->xtcp.xt_inp.inp_inc.inc_ie.ie_lport)),
+		  netaddrstr(
+		    elm->xtcp.xt_inp.inp_vflag,
+		    (union in_dependaddr*)&elm->xtcp.xt_inp.inp_inc.inc_ie.
+			ie_dependfaddr,
+		    ntohs(elm->xtcp.xt_inp.inp_inc.inc_ie.ie_fport)),
+		/*
+		  numtok(elm->xtcp.xt_socket.so_rcv.sb_cc),
+		  numtok(elm->xtcp.xt_socket.so_snd.sb_cc),
+		*/
+		  numtok(DELTAELM(xt_tp.rcv_nxt)),
+		  numtok(DELTAELM(xt_tp.snd_max)),
+		  (elm->xtcp.xt_socket.so_rcv.sb_cc > 15000 ?
+		   'R' : ' '),
+		  (elm->xtcp.xt_socket.so_snd.sb_cc > 15000 ?
+		   'T' : ' '),
+		  ((elm->xtcp.xt_tp.t_flags & TF_NODELAY) ?
+		   'N' : ' '),
+		  ((elm->xtcp.xt_tp.t_flags & TF_RCVD_TSTMP) ?
+		   'T' : ' '),
+		  ((elm->xtcp.xt_tp.t_flags & TF_SACK_PERMIT) ?
+		   'S' : ' '),
+		  ((elm->xtcp.xt_tp.t_flags & TF_RCVD_SCALE) ?
+		   'X' : ' '),
+		  ((elm->xtcp.xt_tp.t_flags & TF_FASTRECOVERY) ?
+		   'F' : ' ')
+	);
+	wclrtoeol(wnd);
+}
+
+#if 0
+int
+cmdnetbw(const char *cmd __unused, char *args __unused)
+{
+	fetchnetbw();
+	shownetbw();
+	refresh();
+
+	return (0);
+}
+#endif
+
+#define MAXINDEXES 8
+
+static const char *
+numtok(double value)
+{
+	static char buf[MAXINDEXES][32];
+	static int nexti;
+	static const char *suffixes[] = { " ", "K", "M", "G", "T", NULL };
+	int suffix = 0;
+	const char *fmt;
+
+	while (value >= 1000.0 && suffixes[suffix+1]) {
+		value /= 1000.0;
+		++suffix;
+	}
+	nexti = (nexti + 1) % MAXINDEXES;
+	if (value < 0.001) {
+		fmt = "      ";
+	} else if (value < 1.0) {
+		fmt = "%5.3f%s";
+	} else if (value < 10.0) {
+		fmt = "%5.3f%s";
+	} else if (value < 100.0) {
+		fmt = "%5.2f%s";
+	} else {
+		fmt = "%5.1f%s";
+	}
+	snprintf(buf[nexti], sizeof(buf[nexti]),
+		 fmt, value, suffixes[suffix]);
+	return (buf[nexti]);
+}
+
+static const char *
+netaddrstr(u_char vflags, union in_dependaddr *depaddr, u_int16_t port)
+{
+	static char buf[MAXINDEXES][64];
+	static int nexta;
+	char bufip[64];
+
+	nexta = (nexta + 1) % MAXINDEXES;
+
+	if (vflags & INP_IPV4) {
+		snprintf(bufip, sizeof(bufip),
+			 "%d.%d.%d.%d",
+			 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 24) &
+			  255,
+			 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 16) &
+			  255,
+			 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 8) &
+			  255,
+			 (ntohl(depaddr->id46_addr.ia46_addr4.s_addr) >> 0) &
+			  255);
+		snprintf(buf[nexta], sizeof(buf[nexta]),
+			 "%15s:%-5d", bufip, port);
+	} else if (vflags & INP_IPV6) {
+		snprintf(bufip, sizeof(bufip),
+			 "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+			 ntohs(depaddr->id6_addr.s6_addr16[0]),
+			 ntohs(depaddr->id6_addr.s6_addr16[1]),
+			 ntohs(depaddr->id6_addr.s6_addr16[2]),
+			 ntohs(depaddr->id6_addr.s6_addr16[3]),
+			 ntohs(depaddr->id6_addr.s6_addr16[4]),
+			 ntohs(depaddr->id6_addr.s6_addr16[5]),
+			 ntohs(depaddr->id6_addr.s6_addr16[6]),
+			 ntohs(depaddr->id6_addr.s6_addr16[7]));
+		snprintf(buf[nexta], sizeof(buf[nexta]),
+			 "%39s:%-5d", bufip, port);
+	} else {
+		snprintf(bufip, sizeof(bufip), "<unknown>");
+		snprintf(buf[nexta], sizeof(buf[nexta]),
+			 "%15s:%-5d", bufip, port);
+	}
+	return (buf[nexta]);
+}
+
+static void
+updatepcb(struct xtcpcb *xtcp)
+{
+	struct mytcpcb dummy;
+	struct mytcpcb *elm;
+
+	dummy.xtcp = *xtcp;
+	if ((elm = RB_FIND(mytcpcb_tree, &mytcp_tree, &dummy)) == NULL) {
+		elm = malloc(sizeof(*elm));
+		bzero(elm, sizeof(*elm));
+		elm->xtcp = *xtcp;
+		elm->last_xtcp = *xtcp;
+		RB_INSERT(mytcpcb_tree, &mytcp_tree, elm);
+	} else {
+		elm->last_xtcp = elm->xtcp;
+		elm->xtcp = *xtcp;
+	}
+	elm->seq = tcp_pcb_seq;
+}
diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1
index 5fc3257..0ab1738 100644
--- a/usr.bin/systat/systat.1
+++ b/usr.bin/systat/systat.1
@@ -94,6 +94,7 @@ to be one of:
 .Ic iostat ,
 .Ic ip ,
 .Ic ip6 ,
+.Ic netbw ,
 .Ic netstat ,
 .Ic pigs ,
 .Ic swap ,
@@ -441,6 +442,9 @@ Display statistics averaged over the refresh interval (the default).
 .It Cm zero
 Reset running statistics to zero.
 .El
+.It Ic netbw
+Display aggregate and per-connection TCP receive and transmit rates.
+Only active TCP connections are shown.
 .It Ic netstat
 Display, in the lower window, network connections.
 By default,
-- 
1.8.5.4

_______________________________________________
freebsd-net@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "freebsd-net-unsubscr...@freebsd.org"

Reply via email to