diff --git a/configure b/configure index 468276f..08bd4ee 100755 --- a/configure +++ b/configure @@ -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. @@ -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 @@ -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 +#else +#include +#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; } diff --git a/configure.ac b/configure.ac index 596444a..f671cf9 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) @@ -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 +#else +#include +#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 diff --git a/src/iperf.h b/src/iperf.h index 9853ecb..3fc91d0 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -39,7 +39,11 @@ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif +#ifdef HAVE_LINUX_TCP_H +#include +#else #include +#endif #include // for IFNAMSIZ #if defined(HAVE_CPUSET_SETAFFINITY) @@ -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; @@ -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; diff --git a/src/iperf_api.c b/src/iperf_api.c index 305d5a2..f8f2321 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -49,7 +49,6 @@ #ifdef HAVE_STDINT_H #include #endif -#include #include #include #include @@ -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; @@ -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) @@ -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:""); diff --git a/src/iperf_api.h b/src/iperf_api.h index e6f3e29..cb4a86d 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -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); diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in index d78af66..12a38a2 100644 --- a/src/iperf_config.h.in +++ b/src/iperf_config.h.in @@ -30,6 +30,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_TCP_H + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H @@ -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 header file. */ #undef HAVE_UNISTD_H diff --git a/src/tcp_info.c b/src/tcp_info.c index d63e5b4..6fa1709 100644 --- a/src/tcp_info.c +++ b/src/tcp_info.c @@ -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.