/* * iperf, Copyright (c) 2014, 2017, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. * * If you have questions about your rights to use or distribute this * software, please contact Berkeley Lab's Technology Transfer * Department at TTD@lbl.gov. * * NOTICE. This software is owned by the U.S. Department of Energy. * As such, the U.S. Government has been granted for itself and others * acting on its behalf a paid-up, nonexclusive, irrevocable, * worldwide license in the Software to reproduce, prepare derivative * works, and perform publicly and display publicly. Beginning five * (5) years after the date permission to assert copyright is obtained * from the U.S. Department of Energy, and subject to any subsequent * five (5) year renewals, the U.S. Government is granted for itself * and others acting on its behalf a paid-up, nonexclusive, * irrevocable, worldwide license in the Software to reproduce, * prepare derivative works, distribute copies to the public, perform * publicly and display publicly, and to permit others to do so. * * This code is distributed under a BSD style license, see the LICENSE * file for complete information. */ /* * routines related to collection TCP_INFO using getsockopt() * * Brian Tierney, ESnet (bltierney@es.net) * * Note that this is only really useful on Linux. * XXX: only standard on linux versions 2.4 and later # * FreeBSD has a limited implementation that only includes the following: * tcpi_snd_ssthresh, tcpi_snd_cwnd, tcpi_rcv_space, tcpi_rtt * Based on information on http://wiki.freebsd.org/8.0TODO, I dont think this will be * fixed before v8.1 at the earliest. * * OSX has no support. * * I think MS Windows does support TCP_INFO, but iperf3 does not currently support Windows. */ #include #include #include #include #include #include #include #include #include "iperf.h" #include "iperf_api.h" #include "iperf_locale.h" /*************************************************************/ int has_tcpinfo(void) { #if (defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) \ && defined(TCP_INFO) return 1; #else return 0; #endif } /*************************************************************/ int has_tcpinfo_retransmits(void) { #if defined(linux) && defined(TCP_MD5SIG) /* TCP_MD5SIG doesn't actually have anything to do with TCP ** retransmits, it just showed up in the same rev of the header ** file. If it's present then struct tcp_info has the ** tcpi_total_retrans field that we need; if not, not. */ return 1; #else #if defined(__FreeBSD__) && __FreeBSD_version >= 600000 return 1; /* Should work now */ #elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO) return 1; #else return 0; #endif #endif } /*************************************************************/ void save_tcpinfo(struct iperf_stream *sp, struct iperf_interval_results *irp) { #if (defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) && \ defined(TCP_INFO) socklen_t tcp_info_length = sizeof(struct tcp_info); if (getsockopt(sp->socket, IPPROTO_TCP, TCP_INFO, (void *)&irp->tcpInfo, &tcp_info_length) < 0) iperf_err(sp->test, "getsockopt - %s", strerror(errno)); if (sp->test->debug) { printf("tcpi_snd_cwnd %u tcpi_snd_mss %u tcpi_rtt %u\n", irp->tcpInfo.tcpi_snd_cwnd, irp->tcpInfo.tcpi_snd_mss, irp->tcpInfo.tcpi_rtt); } #endif } /*************************************************************/ long get_total_retransmits(struct iperf_interval_results *irp) { #if defined(linux) && defined(TCP_MD5SIG) return irp->tcpInfo.tcpi_total_retrans; #elif defined(__FreeBSD__) && __FreeBSD_version >= 600000 return irp->tcpInfo.tcpi_snd_rexmitpack; #elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO) return irp->tcpInfo.tcpi_snd_rexmitpack; #else return -1; #endif } /*************************************************************/ /* * Return snd_cwnd in octets. */ long get_snd_cwnd(struct iperf_interval_results *irp) { #if defined(linux) && defined(TCP_MD5SIG) return (long)irp->tcpInfo.tcpi_snd_cwnd * irp->tcpInfo.tcpi_snd_mss; #elif defined(__FreeBSD__) && __FreeBSD_version >= 600000 return irp->tcpInfo.tcpi_snd_cwnd; #elif defined(__NetBSD__) && defined(TCP_INFO) return (long)irp->tcpInfo.tcpi_snd_cwnd * irp->tcpInfo.tcpi_snd_mss; #elif defined(__OpenBSD__) && defined(TCP_INFO) return irp->tcpInfo.tcpi_snd_cwnd; #else return -1; #endif } /*************************************************************/ /* * Return snd_wnd in octets. */ long get_snd_wnd(struct iperf_interval_results *irp) { #if !defined(HAVE_TCP_INFO_SND_WND) return -1; #elif defined(linux) && defined(TCP_MD5SIG) return irp->tcpInfo.tcpi_snd_wnd; #elif defined(__FreeBSD__) && __FreeBSD_version >= 600000 return irp->tcpInfo.tcpi_snd_wnd; #elif defined(__NetBSD__) && defined(TCP_INFO) return (long)irp->tcpInfo.tcpi_snd_wnd * irp->tcpInfo.tcpi_snd_mss; #elif defined(__OpenBSD__) && defined(TCP_INFO) return irp->tcpInfo.tcpi_snd_wnd; #else return -1; #endif } /*************************************************************/ /* * Return rtt in usec. */ long get_rtt(struct iperf_interval_results *irp) { #if defined(linux) && defined(TCP_MD5SIG) return irp->tcpInfo.tcpi_rtt; #elif defined(__FreeBSD__) && __FreeBSD_version >= 600000 return irp->tcpInfo.tcpi_rtt; #elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO) return irp->tcpInfo.tcpi_rtt; #else return -1; #endif } /*************************************************************/ /* * Return rttvar in usec. */ long get_rttvar(struct iperf_interval_results *irp) { #if defined(linux) && defined(TCP_MD5SIG) return irp->tcpInfo.tcpi_rttvar; #elif defined(__FreeBSD__) && __FreeBSD_version >= 600000 return irp->tcpInfo.tcpi_rttvar; #elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO) return irp->tcpInfo.tcpi_rttvar; #else return -1; #endif } /*************************************************************/ /* * Return PMTU in bytes. */ long get_pmtu(struct iperf_interval_results *irp) { #if defined(linux) && defined(TCP_MD5SIG) return irp->tcpInfo.tcpi_pmtu; #else return -1; #endif } /*************************************************************/ void build_tcpinfo_message(struct iperf_interval_results *r, char *message) { #if defined(linux) && defined(TCP_INFO) sprintf(message, report_tcpInfo, r->tcpInfo.tcpi_snd_cwnd, r->tcpInfo.tcpi_snd_ssthresh, r->tcpInfo.tcpi_rcv_ssthresh, r->tcpInfo.tcpi_unacked, r->tcpInfo.tcpi_sacked, r->tcpInfo.tcpi_lost, r->tcpInfo.tcpi_retrans, r->tcpInfo.tcpi_fackets, r->tcpInfo.tcpi_rtt, r->tcpInfo.tcpi_reordering); #endif #if (defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO) sprintf(message, report_tcpInfo, r->tcpInfo.tcpi_snd_cwnd, r->tcpInfo.tcpi_rcv_space, r->tcpInfo.tcpi_snd_ssthresh, r->tcpInfo.tcpi_rtt); #endif }