From owner-svn-src-all@freebsd.org Sat Dec 14 10:53:53 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id D32AC1C95F7; Sat, 14 Dec 2019 10:53:53 +0000 (UTC) (envelope-from trasz@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 47Zkt95GWzz4FTZ; Sat, 14 Dec 2019 10:53:53 +0000 (UTC) (envelope-from trasz@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id B00122396C; Sat, 14 Dec 2019 10:53:53 +0000 (UTC) (envelope-from trasz@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id xBEArrsr015755; Sat, 14 Dec 2019 10:53:53 GMT (envelope-from trasz@FreeBSD.org) Received: (from trasz@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id xBEArrUq015753; Sat, 14 Dec 2019 10:53:53 GMT (envelope-from trasz@FreeBSD.org) Message-Id: <201912141053.xBEArrUq015753@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: trasz set sender to trasz@FreeBSD.org using -f From: Edward Tomasz Napierala Date: Sat, 14 Dec 2019 10:53:53 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r355750 - in head: contrib/netcat usr.bin/nc X-SVN-Group: head X-SVN-Commit-Author: trasz X-SVN-Commit-Paths: in head: contrib/netcat usr.bin/nc X-SVN-Commit-Revision: 355750 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 14 Dec 2019 10:53:53 -0000 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* . */ +#include +#include +#include #include #include +#include #include #include +#include +#include #include #include #include @@ -50,7 +56,6 @@ #include #include -#include #include #include #include @@ -58,7 +63,6 @@ #include #include #include -#include #include #include #include @@ -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 + .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