svn commit: r355750 - in head: contrib/netcat usr.bin/nc

Edward Tomasz Napierala trasz at FreeBSD.org
Sat Dec 14 10:53:53 UTC 2019


Author: trasz
Date: Sat Dec 14 10:53:52 2019
New Revision: 355750
URL: https://svnweb.freebsd.org/changeset/base/355750

Log:
  Add -M option to nc(1), which makes it print the TCP connection
  statistics obtained with stats(3) in JSON format to standard error.
  
  Reviewed by:	allanjude, thj, cem (earlier version)
  Tested by:	thj
  MFC after:	2 weeks
  Relnotes:	yes
  Sponsored by:	Klara Inc.
  Differential Revision:	https://reviews.freebsd.org/D21324

Modified:
  head/contrib/netcat/nc.1
  head/contrib/netcat/netcat.c
  head/usr.bin/nc/Makefile

Modified: head/contrib/netcat/nc.1
==============================================================================
--- head/contrib/netcat/nc.1	Sat Dec 14 09:54:30 2019	(r355749)
+++ head/contrib/netcat/nc.1	Sat Dec 14 10:53:52 2019	(r355750)
@@ -27,7 +27,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd September 26, 2015
+.Dd August 20, 2019
 .Dt NC 1
 .Os
 .Sh NAME
@@ -36,7 +36,7 @@
 .Sh SYNOPSIS
 .Nm nc
 .Bk -words
-.Op Fl 46DdEFhklNnrStUuvz
+.Op Fl 46DdEFhklMNnrStUuvz
 .Op Fl e Ar IPsec_policy
 .Op Fl I Ar length
 .Op Fl i Ar interval
@@ -170,6 +170,12 @@ options.
 Additionally, any timeouts specified with the
 .Fl w
 option are ignored.
+.It Fl M
+Collect per-connection TCP statistics using the
+.Xr stats 3
+framework and print them in JSON format to
+.Xr stderr 4
+after the connection is closed.
 .It Fl N
 .Xr shutdown 2
 the network socket after EOF on the input.

Modified: head/contrib/netcat/netcat.c
==============================================================================
--- head/contrib/netcat/netcat.c	Sat Dec 14 09:54:30 2019	(r355749)
+++ head/contrib/netcat/netcat.c	Sat Dec 14 10:53:52 2019	(r355750)
@@ -33,10 +33,16 @@
  * *Hobbit* <hobbit at avian.org>.
  */
 
+#include <errno.h>
+#include <stdio.h>
+#include <sys/arb.h>
 #include <sys/limits.h>
 #include <sys/types.h>
+#include <sys/sbuf.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
+#include <sys/qmath.h>
+#include <sys/stats.h>
 #include <sys/time.h>
 #include <sys/uio.h>
 #include <sys/un.h>
@@ -50,7 +56,6 @@
 #include <arpa/telnet.h>
 
 #include <err.h>
-#include <errno.h>
 #include <getopt.h>
 #include <fcntl.h>
 #include <limits.h>
@@ -58,7 +63,6 @@
 #include <poll.h>
 #include <signal.h>
 #include <stdarg.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -85,6 +89,7 @@ int	Fflag;					/* fdpass sock to stdout */
 unsigned int iflag;				/* Interval Flag */
 int	kflag;					/* More than one connect */
 int	lflag;					/* Bind to local port */
+int	FreeBSD_Mflag;				/* Measure using stats(3) */
 int	Nflag;					/* shutdown() network socket */
 int	nflag;					/* Don't do name look up */
 int	FreeBSD_Oflag;				/* Do not use TCP options */
@@ -123,6 +128,8 @@ int	udptest(int);
 int	unix_bind(char *);
 int	unix_connect(char *);
 int	unix_listen(char *);
+void	FreeBSD_stats_setup(int);
+void	FreeBSD_stats_print(int);
 void	set_common_sockopts(int, int);
 int	map_tos(char *, int *);
 void	report_connect(const struct sockaddr *, socklen_t);
@@ -167,7 +174,7 @@ main(int argc, char *argv[])
 	signal(SIGPIPE, SIG_IGN);
 
 	while ((ch = getopt_long(argc, argv,
-	    "46DdEe:FhI:i:klNnoO:P:p:rSs:tT:UuV:vw:X:x:z",
+	    "46DdEe:FhI:i:klMNnoO:P:p:rSs:tT:UuV:vw:X:x:z",
 	    longopts, NULL)) != -1) {
 		switch (ch) {
 		case '4':
@@ -224,6 +231,13 @@ main(int argc, char *argv[])
 		case 'l':
 			lflag = 1;
 			break;
+		case 'M':
+#ifndef WITH_STATS
+			errx(1, "-M requires stats(3) support");
+#else
+			FreeBSD_Mflag = 1;
+#endif
+			break;
 		case 'N':
 			Nflag = 1;
 			break;
@@ -451,6 +465,8 @@ main(int argc, char *argv[])
 				if (vflag)
 					report_connect((struct sockaddr *)&cliaddr, len);
 
+				if (FreeBSD_Mflag)
+					FreeBSD_stats_setup(connfd);
 				readwrite(connfd);
 				close(connfd);
 			}
@@ -801,6 +817,7 @@ readwrite(int net_fd)
 	unsigned char stdinbuf[BUFSIZE];
 	size_t stdinbufpos = 0;
 	int n, num_fds;
+	int stats_printed = 0;
 	ssize_t ret;
 
 	/* don't read from stdin if requested */
@@ -827,17 +844,23 @@ readwrite(int net_fd)
 		/* both inputs are gone, buffers are empty, we are done */
 		if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1
 		    && stdinbufpos == 0 && netinbufpos == 0) {
+			if (FreeBSD_Mflag && !stats_printed)
+				FreeBSD_stats_print(net_fd);
 			close(net_fd);
 			return;
 		}
 		/* both outputs are gone, we can't continue */
 		if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) {
+			if (FreeBSD_Mflag && !stats_printed)
+				FreeBSD_stats_print(net_fd);
 			close(net_fd);
 			return;
 		}
 		/* listen and net in gone, queues empty, done */
 		if (lflag && pfd[POLL_NETIN].fd == -1
 		    && stdinbufpos == 0 && netinbufpos == 0) {
+			if (FreeBSD_Mflag && !stats_printed)
+				FreeBSD_stats_print(net_fd);
 			close(net_fd);
 			return;
 		}
@@ -858,8 +881,11 @@ readwrite(int net_fd)
 		}
 
 		/* timeout happened */
-		if (num_fds == 0)
+		if (num_fds == 0) {
+			if (FreeBSD_Mflag)
+				FreeBSD_stats_print(net_fd);
 			return;
+		}
 
 		/* treat socket error conditions */
 		for (n = 0; n < 4; n++) {
@@ -961,8 +987,13 @@ readwrite(int net_fd)
 
 		/* stdin gone and queue empty? */
 		if (pfd[POLL_STDIN].fd == -1 && stdinbufpos == 0) {
-			if (pfd[POLL_NETOUT].fd != -1 && Nflag)
+			if (pfd[POLL_NETOUT].fd != -1 && Nflag) {
+				if (FreeBSD_Mflag) {
+					FreeBSD_stats_print(net_fd);
+					stats_printed = 1;
+				}
 				shutdown(pfd[POLL_NETOUT].fd, SHUT_WR);
+			}
 			pfd[POLL_NETOUT].fd = -1;
 		}
 		/* net in gone and queue empty? */
@@ -1181,6 +1212,67 @@ udptest(int s)
 }
 
 void
+FreeBSD_stats_setup(int s)
+{
+
+	if (setsockopt(s, IPPROTO_TCP, TCP_STATS,
+	    &FreeBSD_Mflag, sizeof(FreeBSD_Mflag)) == -1) {
+		if (errno == EOPNOTSUPP) {
+			warnx("getsockopt(TCP_STATS) failed; "
+			    "kernel built without \"options STATS\"?");
+		}
+		err(1, "enable TCP_STATS gathering");
+	}
+}
+
+void
+FreeBSD_stats_print(int s)
+{
+#ifdef WITH_STATS
+	struct statsblob *statsb;
+	struct sbuf *sb;
+	socklen_t sockoptlen;
+	int error;
+
+	/*
+	 * This usleep is a workaround for TCP_STATS reporting
+	 * incorrect values for TXPB.
+	 */
+	usleep(100000);
+
+	sockoptlen = 2048;
+	statsb = malloc(sockoptlen);
+	if (statsb == NULL)
+		err(1, "malloc");
+	error = getsockopt(s, IPPROTO_TCP, TCP_STATS, statsb, &sockoptlen);
+	if (error != 0) {
+		if (errno == EOVERFLOW && statsb->cursz > sockoptlen) {
+			/* Retry with a larger size. */
+			sockoptlen = statsb->cursz;
+			statsb = realloc(statsb, sockoptlen);
+			if (statsb == NULL)
+				err(1, "realloc");
+			error = getsockopt(s, IPPROTO_TCP, TCP_STATS,
+			    statsb, &sockoptlen);
+		}
+		if (error != 0)
+			err(1, "getsockopt");
+	}
+
+	sb = sbuf_new_auto();
+	error = stats_blob_tostr(statsb, sb, SB_STRFMT_JSON, SB_TOSTR_META);
+	if (error != 0)
+		errc(1, error, "stats_blob_tostr");
+
+	error = sbuf_finish(sb);
+	if (error != 0)
+		err(1, "sbuf_finish");
+
+	fprintf(stderr, "%s\n", sbuf_data(sb));
+#endif
+}
+
+void
 set_common_sockopts(int s, int af)
 {
 	int x = 1;
@@ -1224,6 +1316,8 @@ set_common_sockopts(int s, int af)
 		    &FreeBSD_Oflag, sizeof(FreeBSD_Oflag)) == -1)
 			err(1, "disable TCP options");
 	}
+	if (FreeBSD_Mflag)
+		FreeBSD_stats_setup(s);
 #ifdef IPSEC
 	if (ipsec_policy[0] != NULL)
 		add_ipsec_policy(s, af, ipsec_policy[0]);

Modified: head/usr.bin/nc/Makefile
==============================================================================
--- head/usr.bin/nc/Makefile	Sat Dec 14 09:54:30 2019	(r355749)
+++ head/usr.bin/nc/Makefile	Sat Dec 14 10:53:52 2019	(r355750)
@@ -1,5 +1,7 @@
 # $FreeBSD$
 
+.include <src.opts.mk>
+
 .PATH: ${SRCTOP}/contrib/netcat
 
 PROG=	nc
@@ -7,6 +9,11 @@ SRCS=	netcat.c atomicio.c socks.c
 
 CFLAGS+=-DIPSEC
 LIBADD=	ipsec
+
+.if ${MK_STATS} != "no" && !defined(RESCUE)
+LIBADD+=	sbuf stats
+CFLAGS+=	-DWITH_STATS
+.endif
 
 WARNS?=	2
 


More information about the svn-src-all mailing list