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