Index: net/Makefile =================================================================== --- net/Makefile +++ net/Makefile @@ -1406,6 +1406,7 @@ SUBDIR += tcpmssd SUBDIR += tcpproxy SUBDIR += tcpreen + SUBDIR += tcprtt SUBDIR += tcpsg SUBDIR += tcpshow SUBDIR += tcpslice Index: net/tcprtt/Makefile =================================================================== --- /dev/null +++ net/tcprtt/Makefile @@ -0,0 +1,23 @@ +# $FreeBSD$ + +PORTNAME= tcprtt +PORTVERSION= 1.0 +CATEGORIES= net +DISTFILES= #empty + +MAINTAINER= rs@netflix.com +COMMENT= Measures the TCP handshake RTT using the stats(9) statistics framework + +LICENSE= BSD2CLAUSE + +PLIST_FILES= bin/tcprtt man/man8/${PORTNAME}.8.gz + +do-extract: + ${MKDIR} ${WRKSRC} + (cd ${FILESDIR} && ${CP} -R . ${WRKSRC}) + +do-install: + ${INSTALL_PROGRAM} ${WRKSRC}/tcprtt ${STAGEDIR}${PREFIX}/bin + ${INSTALL_MAN} ${WRKSRC}/tcprtt.8 ${STAGEDIR}${PREFIX}/man/man8 + +.include Index: net/tcprtt/files/Makefile =================================================================== --- /dev/null +++ net/tcprtt/files/Makefile @@ -0,0 +1,6 @@ +PROG= tcprtt +MAN= tcprtt.8 +DPADD= ${LIBSTATS} ${LIBSBUF} +LDADD= -lstats -lsbuf + +.include Index: net/tcprtt/files/tcprtt.8 =================================================================== --- /dev/null +++ net/tcprtt/files/tcprtt.8 @@ -0,0 +1,70 @@ +.\" Copyright (C) 2016 Olivier Poitrey +.\" All rights reserved. +.\" +.\" 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(s), this list of conditions and the following disclaimer as +.\" the first lines of this file unmodified other than the possible +.\" addition of one or more copyright notices. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice(s), this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 HOLDER(S) 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. +.\" +.\" $FreeBSD$ +.Dd June 20, 2019 +.Dt TCPRTT 8 +.Os +.Sh NAME +.Nm tcprtt +.Nd measure TCP handshake RTT using the stats(3) framework +.Sh SYNOPSIS +.Nm +.Op Fl f +.Ar host +.Ar port +.Sh DESCRIPTION +The +.Nm +utility reliably measures the TCP handshake round trip time +using the +.Xr stats 3 +statistics framework. +.Pp +Both IPv4 and IPv6 are supported. +If the +.Ar host +argument is a hostname containing several A and AAAA records, +one line per record will be printed. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl f +Stop after the first address of each family +.El +.Pp +For each address measured, a line is printed with 3 columns: address +family (4 or 6), the numerical address and the measured RTT in +milliseconds. +.Pp +When an error occurs for one of the measurement, the -errno(2) is +printed in place of the RTT. +.Pp +If at least one entry returns an error, the command exits with the value +of the last encountered error. +.Sh SEE ALSO +.Xr stats 3 Index: net/tcprtt/files/tcprtt.c =================================================================== --- /dev/null +++ net/tcprtt/files/tcprtt.c @@ -0,0 +1,186 @@ +/*- + * Copyright (C) 2016 Olivier Poitrey + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* Must come after qmath.h and tree.h */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +static int +get_rtt(int domain, const struct sockaddr *addr, socklen_t addrlen); + +static int +usage(void); + +/* + * The tcprtt utility reliably measures the TCP handshake round trip time + * using the stats(9) statistics framework. + */ +int +main(int argc, char *argv[]) +{ + struct addrinfo hints, *res, *res0; + int c, family, first, lasterr, rtt; + char host[NI_MAXHOST], serv[NI_MAXSERV]; + + /* Stop at first value for each given address family */ + first = 0; + + while ((c = getopt (argc, argv, "f")) != -1) { + if (c == 'f') + first = 1; + else + return usage(); + } + + /* Parse options: */ + if (argc != optind + 2) { + return usage(); + } + strncpy(host, argv[optind], sizeof(host)); + strncpy(serv, argv[optind+1], sizeof(serv)); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if (getaddrinfo(host, serv, &hints, &res0)) { + err(EX_OSERR, "getaddrinfo"); + } + lasterr = 0; + for (res = res0; res; res = res->ai_next) { + family = 4; + if (res->ai_family == AF_INET6) { + family = 6; + } + /* if -f option is provided, check if this address family already got + a result */ + if (first > 0 && ((first >> family) & 1) == 1) { + continue; + } + rtt = get_rtt(res->ai_family, res->ai_addr, res->ai_addrlen); + if (rtt < 0) { + lasterr = rtt; + } + /* store the fact we got a response for this address family */ + if (first > 0) { + first |= 1 << family; + } + getnameinfo(res->ai_addr, res->ai_addrlen, + host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST); + printf("%d %s %d\n", family, host, rtt); + } + freeaddrinfo(res0); + + return(-lasterr); +} + +int +usage() +{ + printf("Syntax: tcprtt [-f] %d\n", optopt); + return(EX_USAGE); +} + +int +get_rtt(int domain, const struct sockaddr *addr, socklen_t addrlen) +{ + struct statsblob *sb; + socklen_t sockoptlen; + uint32_t rtt; + int enable_stats, error, ret, s; + + /* Create a TCP socket */ + s = socket(domain, SOCK_STREAM, IPPROTO_TCP); + if (s < 0) { + return(-errno); + } + + /* Enable stats gathering on the socket */ + enable_stats = 1; + if (setsockopt(s, IPPROTO_TCP, TCP_STATS, &enable_stats, sizeof(enable_stats)) != 0) { + close(s); + return(-errno); + } + + /* Connect to specified addr */ + if (connect(s, addr, addrlen) < 0) { + close(s); + return(-errno); + } + + /* Use Lawrence's kernel RTT stats feature to get precise measurement of the RTT */ + sockoptlen = 2048; + sb = (struct statsblob *)malloc(sockoptlen); + if (sb == NULL) { + close(s); + free(sb); + return(-errno); + } + if ((ret = getsockopt(s, IPPROTO_TCP, TCP_STATS, sb, &sockoptlen))) { + if (errno == EOVERFLOW && sb->cursz > sockoptlen) { + /* Retry with a larger size. */ + sockoptlen = sb->cursz; + sb = realloc(sb, sockoptlen); + if (sb == NULL) + errno = ENOMEM; + else + ret = getsockopt(s, IPPROTO_TCP, TCP_STATS, sb, &sockoptlen); + } + if (ret != 0) { + close(s); + free(sb); + return (-errno); + } + } + + error = stats_voistat_fetch_u32(sb, VOI_TCP_RTT, VS_STYPE_MAX, &rtt); + close(s); + free(sb); + if (error != 0) + return (-error); + + return (rtt); +} Index: net/tcprtt/pkg-descr =================================================================== --- /dev/null +++ net/tcprtt/pkg-descr @@ -0,0 +1,2 @@ +The tcprtt utility reliably measures the TCP handshake round trip time +using the stats(9) statistics framework.