Skip to content

Commit

Permalink
Add tcp_info.snd_wnd to JSON output.
Browse files Browse the repository at this point in the history
tcp_info.snd_wnd is available on FreeBSD and NetBSD since TCP_INFO was
added.  It was added to Linux 5.4 in late 2019 and becomes available
in Ubuntu 20.04 and Debian 11.

Tested on:
* Debian 11 running on x86-64 with this field.
* Debian 10 armv7 running on Raspberry Pi 2 without this field.
* NetBSD 9.2 armv7 running on Raspberry Pi 3 with this field.
* FreeBSD 13 aarch64 running on Raspberry Pi 4 with this field.
  • Loading branch information
chenshuo authored and hanvari committed Jul 3, 2021
1 parent aae27e7 commit 54d35c9
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 3 deletions.
90 changes: 90 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,63 @@ fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno

} # ac_fn_c_check_header_mongrel

# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
# ----------------------------------------------------
# Tries to find if the field MEMBER exists in type AGGR, after including
# INCLUDES, setting cache variable VAR accordingly.
ac_fn_c_check_member ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
$as_echo_n "checking for $2.$3... " >&6; }
if eval \${$4+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$5
int
main ()
{
static $2 ac_aggr;
if (ac_aggr.$3)
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$4=yes"
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$5
int
main ()
{
static $2 ac_aggr;
if (sizeof ac_aggr.$3)
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$4=yes"
else
eval "$4=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$4
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno

} # ac_fn_c_check_member
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
Expand Down Expand Up @@ -13383,6 +13440,19 @@ else
fi


for ac_header in linux/tcp.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "linux/tcp.h" "ac_cv_header_linux_tcp_h" "$ac_includes_default"
if test "x$ac_cv_header_linux_tcp_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LINUX_TCP_H 1
_ACEOF

fi

done


# Check for SCTP support
if $try_sctp; then
for ac_header in sys/socket.h
Expand Down Expand Up @@ -14020,6 +14090,26 @@ $as_echo "#define HAVE_DONT_FRAGMENT 1" >>confdefs.h

fi

ac_fn_c_check_member "$LINENO" "struct tcp_info" "tcpi_snd_wnd" "ac_cv_member_struct_tcp_info_tcpi_snd_wnd" "#ifdef HAVE_LINUX_TCP_H
#include <linux/tcp.h>
#else
#include <netinet/tcp.h>
#endif
"
if test "x$ac_cv_member_struct_tcp_info_tcpi_snd_wnd" = xyes; then :
iperf3_cv_header_tcp_info_snd_wnd=yes
else
iperf3_cv_header_tcp_info_snd_wnd=no
fi


if test "x$iperf3_cv_header_tcp_info_snd_wnd" = "xyes"; then

$as_echo "#define HAVE_TCP_INFO_SND_WND 1" >>confdefs.h

fi

# Check if we need -lrt for clock_gettime
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
$as_echo_n "checking for library containing clock_gettime... " >&6; }
Expand Down
15 changes: 15 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ AC_ARG_WITH([sctp],
]
)

AC_CHECK_HEADERS([linux/tcp.h])

# Check for SCTP support
if $try_sctp; then
AC_CHECK_HEADERS([sys/socket.h])
Expand Down Expand Up @@ -258,6 +260,19 @@ if test "x$iperf3_cv_header_dontfragment" = "xyes"; then
AC_DEFINE([HAVE_DONT_FRAGMENT], [1], [Have IP_MTU_DISCOVER/IP_DONTFRAG sockopt.])
fi

AC_CHECK_MEMBER([struct tcp_info.tcpi_snd_wnd],
[iperf3_cv_header_tcp_info_snd_wnd=yes], [iperf3_cv_header_tcp_info_snd_wnd=no],
[#ifdef HAVE_LINUX_TCP_H
#include <linux/tcp.h>
#else
#include <netinet/tcp.h>
#endif
])

if test "x$iperf3_cv_header_tcp_info_snd_wnd" = "xyes"; then
AC_DEFINE([HAVE_TCP_INFO_SND_WND], [1], [Have tcpi_snd_wnd field in tcp_info.])
fi

# Check if we need -lrt for clock_gettime
AC_SEARCH_LIBS(clock_gettime, [rt posix4])
# Check for clock_gettime support
Expand Down
6 changes: 6 additions & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#ifdef HAVE_LINUX_TCP_H
#include <linux/tcp.h>
#else
#include <netinet/tcp.h>
#endif
#include <net/if.h> // for IFNAMSIZ

#if defined(HAVE_CPUSET_SETAFFINITY)
Expand Down Expand Up @@ -100,6 +104,7 @@ struct iperf_interval_results
int interval_retrans;
int interval_sacks;
int snd_cwnd;
int snd_wnd;
TAILQ_ENTRY(iperf_interval_results) irlistentries;
void *custom_data;
int rtt;
Expand All @@ -123,6 +128,7 @@ struct iperf_stream_result
int stream_sum_rtt;
int stream_count_rtt;
int stream_max_snd_cwnd;
int stream_max_snd_wnd;
struct iperf_time start_time;
struct iperf_time end_time;
struct iperf_time start_time_fixed;
Expand Down
10 changes: 7 additions & 3 deletions src/iperf_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <netinet/tcp.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/mman.h>
Expand Down Expand Up @@ -3070,6 +3069,11 @@ iperf_stats_callback(struct iperf_test *test)
rp->stream_max_snd_cwnd = temp.snd_cwnd;
}

temp.snd_wnd = get_snd_wnd(&temp);
if (temp.snd_wnd > rp->stream_max_snd_wnd) {
rp->stream_max_snd_wnd = temp.snd_wnd;
}

temp.rtt = get_rtt(&temp);
if (temp.rtt > rp->stream_max_rtt) {
rp->stream_max_rtt = temp.rtt;
Expand Down Expand Up @@ -3501,7 +3505,7 @@ iperf_print_results(struct iperf_test *test)
if (test->sender_has_retransmits) {
/* Sender summary, TCP and SCTP with retransmits. */
if (test->json_output)
cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d max_snd_cwnd: %d max_rtt: %d min_rtt: %d mean_rtt: %d sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (int64_t) sp->result->stream_retrans, (int64_t) sp->result->stream_max_snd_cwnd, (int64_t) sp->result->stream_max_rtt, (int64_t) sp->result->stream_min_rtt, (int64_t) ((sp->result->stream_count_rtt == 0) ? 0 : sp->result->stream_sum_rtt / sp->result->stream_count_rtt), stream_must_be_sender));
cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d max_snd_cwnd: %d max_snd_wnd: %d max_rtt: %d min_rtt: %d mean_rtt: %d sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (int64_t) sp->result->stream_retrans, (int64_t) sp->result->stream_max_snd_cwnd, (int64_t) sp->result->stream_max_snd_wnd, (int64_t) sp->result->stream_max_rtt, (int64_t) sp->result->stream_min_rtt, (int64_t) ((sp->result->stream_count_rtt == 0) ? 0 : sp->result->stream_sum_rtt / sp->result->stream_count_rtt), stream_must_be_sender));
else
if (test->role == 's' && !sp->sender) {
if (test->verbose)
Expand Down Expand Up @@ -3915,7 +3919,7 @@ print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON *
if (test->sender_has_retransmits == 1 && sp->sender) {
/* Interval, TCP with retransmits. */
if (test->json_output)
cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d snd_cwnd: %d rtt: %d rttvar: %d pmtu: %d omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (int64_t) irp->interval_retrans, (int64_t) irp->snd_cwnd, (int64_t) irp->rtt, (int64_t) irp->rttvar, (int64_t) irp->pmtu, irp->omitted, sp->sender));
cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d snd_cwnd: %d snd_wnd: %d rtt: %d rttvar: %d pmtu: %d omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (int64_t) irp->interval_retrans, (int64_t) irp->snd_cwnd, (int64_t) irp->snd_wnd, (int64_t) irp->rtt, (int64_t) irp->rttvar, (int64_t) irp->pmtu, irp->omitted, sp->sender));
else {
unit_snprintf(cbuf, UNIT_LEN, irp->snd_cwnd, 'A');
iperf_printf(test, report_bw_retrans_cwnd_format, sp->socket, mbuf, st, et, ubuf, nbuf, irp->interval_retrans, cbuf, irp->omitted?report_omitted:"");
Expand Down
1 change: 1 addition & 0 deletions src/iperf_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ int has_tcpinfo_retransmits(void);
void save_tcpinfo(struct iperf_stream *sp, struct iperf_interval_results *irp);
long get_total_retransmits(struct iperf_interval_results *irp);
long get_snd_cwnd(struct iperf_interval_results *irp);
long get_snd_wnd(struct iperf_interval_results *irp);
long get_rtt(struct iperf_interval_results *irp);
long get_rttvar(struct iperf_interval_results *irp);
long get_pmtu(struct iperf_interval_results *irp);
Expand Down
6 changes: 6 additions & 0 deletions src/iperf_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H

/* Define to 1 if you have the <linux/tcp.h> header file. */
#undef HAVE_LINUX_TCP_H

/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H

Expand Down Expand Up @@ -90,6 +93,9 @@
/* Have TCP_CONGESTION sockopt. */
#undef HAVE_TCP_CONGESTION

/* Have tcpi_snd_wnd field in tcp_info. */
#undef HAVE_TCP_INFO_SND_WND

/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H

Expand Down
20 changes: 20 additions & 0 deletions src/tcp_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,26 @@ get_snd_cwnd(struct iperf_interval_results *irp)
#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 irp->tcpInfo.tcpi_snd_wnd * irp->tcpInfo.tcpi_snd_mss;
#else
return -1;
#endif
}

/*************************************************************/
/*
* Return rtt in usec.
Expand Down

0 comments on commit 54d35c9

Please sign in to comment.