| /* |
| * 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 <stdio.h> |
| #include <stdlib.h> |
| #include <sys/param.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <string.h> |
| #include <netinet/in.h> |
| #include <errno.h> |
| |
| #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 |
| } |