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* <hob...@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
 
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to