diff --git a/configure b/configure index 66a9c077f..bc60a8919 100755 --- a/configure +++ b/configure @@ -16895,6 +16895,16 @@ then : printf '%s\n' "#define HAVE_STRUCT_SCTP_ASSOC_VALUE 1" >>confdefs.h +fi + +ac_fn_c_check_type "$LINENO" "struct sctp_sack_info" "ac_cv_type_struct_sctp_sack_info" "#include +" +if test "x$ac_cv_type_struct_sctp_sack_info" = xyes +then : + +printf '%s\n' "#define HAVE_STRUCT_SCTP_SACK_INFO 1" >>confdefs.h + + fi fi diff --git a/configure.ac b/configure.ac index b04f37a78..782843515 100644 --- a/configure.ac +++ b/configure.ac @@ -152,6 +152,8 @@ AC_CHECK_HEADERS([netinet/sctp.h], AC_DEFINE([HAVE_SCTP_H], [1], [Have SCTP support.]) AC_SEARCH_LIBS(sctp_bindx, [sctp]) AC_CHECK_TYPES([struct sctp_assoc_value], [], [], + [[#include ]]) + AC_CHECK_TYPES([struct sctp_sack_info], [], [], [[#include ]]), [], [#ifdef HAVE_SYS_SOCKET_H diff --git a/src/iperf_api.h b/src/iperf_api.h index 25ac951e0..02c6369f1 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -506,6 +506,7 @@ enum { IESETCNTLKACOUNT = 158, // Unable to set/get socket keepalive TCP number of retries (TCP_KEEPCNT) option IEPTHREADSIGMASK=159, // Unable to initialize sub thread signal mask (check perror) IESERVERTESTDURATIONEXPIRED = 160, // Server test duration expired + IESETSCTPDELAYEDSACK = 161, // Unable to set SCTP_DELAYED_SACK (check perror) /* Stream errors */ IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror) IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror) diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in index 28a6c4e82..c868a8508 100644 --- a/src/iperf_config.h.in +++ b/src/iperf_config.h.in @@ -117,6 +117,9 @@ /* Define to 1 if the system has the type 'struct sctp_assoc_value'. */ #undef HAVE_STRUCT_SCTP_ASSOC_VALUE +/* Define to 1 if the system has the type 'struct sctp_sack_info'. */ +#undef HAVE_STRUCT_SCTP_SACK_INFO + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_ENDIAN_H diff --git a/src/iperf_error.c b/src/iperf_error.c index 40ca492ea..4846cdca4 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -469,6 +469,10 @@ iperf_strerror(int int_errno) snprintf(errstr, len, "unable to set SCTP_DISABLE_FRAGMENTS"); perr = 1; break; + case IESETSCTPDELAYEDSACK: + snprintf(errstr, len, "unable to set SCTP_DELAYED_SACK"); + perr = 1; + break; case IESETSCTPNSTREAM: snprintf(errstr, len, "unable to set SCTP_INIT num of SCTP streams\n"); perr = 1; diff --git a/src/iperf_sctp.c b/src/iperf_sctp.c index 1e02a8d50..83585b732 100644 --- a/src/iperf_sctp.c +++ b/src/iperf_sctp.c @@ -125,6 +125,38 @@ iperf_sctp_accept(struct iperf_test * test) return -1; } + if (test->no_delay) { + int opt = 1; + if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) { + i_errno = IESETNODELAY; + close(s); + return -1; + } + } + +#ifdef HAVE_STRUCT_SCTP_SACK_INFO + /* + * Apply SCTP_DELAYED_SACK on the accepted association. On Linux the + * SCTP_DELAYED_SACK setting on the listener does NOT propagate to + * accepted associations, so the per-association setting must be applied + * here for the receiver-side fix to take effect. ENOPROTOOPT is + * non-fatal so this is safe on platforms without support. + */ + { + struct sctp_sack_info sack; + memset(&sack, 0, sizeof(sack)); + sack.sack_assoc_id = 0; + sack.sack_delay = 0; + sack.sack_freq = 1; + if (setsockopt(s, IPPROTO_SCTP, SCTP_DELAYED_SACK, &sack, sizeof(sack)) < 0 && + errno != ENOPROTOOPT) { + i_errno = IESETSCTPDELAYEDSACK; + close(s); + return -1; + } + } +#endif /* HAVE_STRUCT_SCTP_SACK_INFO */ + if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) { i_errno = IERECVCOOKIE; close(s); @@ -248,6 +280,52 @@ iperf_sctp_listen(struct iperf_test *test) return -1; } + if (test->no_delay) { + opt = 1; + if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(res); + errno = saved_errno; + i_errno = IESETNODELAY; + return -1; + } + } + +#ifdef HAVE_STRUCT_SCTP_SACK_INFO + /* + * Disable SCTP delayed-SACK on future accepted associations. With the + * default sack_freq=2/sack_delay=200ms, a sender that writes blocks + * producing exactly one DATA chunk stalls for 200 ms waiting for a SACK + * that the receiver is in turn delaying. iperf3 is a throughput + * benchmark, so request an immediate SACK on every packet. On Linux + * the endpoint default set here does not propagate to accepted + * associations -- iperf_sctp_accept() repeats the call on the per- + * association socket. ENOPROTOOPT (platform without support) is + * non-fatal. + */ + { + struct sctp_sack_info sack; + memset(&sack, 0, sizeof(sack)); +#ifdef SCTP_FUTURE_ASSOC + sack.sack_assoc_id = SCTP_FUTURE_ASSOC; +#else + sack.sack_assoc_id = 0; +#endif + sack.sack_delay = 0; + sack.sack_freq = 1; + if (setsockopt(s, IPPROTO_SCTP, SCTP_DELAYED_SACK, &sack, sizeof(sack)) < 0 && + errno != ENOPROTOOPT) { + saved_errno = errno; + close(s); + freeaddrinfo(res); + errno = saved_errno; + i_errno = IESETSCTPDELAYEDSACK; + return -1; + } + } +#endif /* HAVE_STRUCT_SCTP_SACK_INFO */ + /* servers must call sctp_bindx() _instead_ of bind() */ if (!TAILQ_EMPTY(&test->xbind_addrs)) { if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER)) { @@ -541,6 +619,33 @@ iperf_sctp_connect(struct iperf_test *test) return -1; } +#ifdef HAVE_STRUCT_SCTP_SACK_INFO + /* + * Disable SCTP delayed-SACK on this association (see iperf_sctp_listen() + * for rationale). Applied here as well so that --reverse, which makes + * the client the receiver, still gets immediate SACKs. The socket is + * already connected and 1:1-style, so sack_assoc_id=0 unambiguously + * targets the current association on every platform (RFC 6458 ยง8.1). + * ENOPROTOOPT is treated as non-fatal. + */ + { + struct sctp_sack_info sack; + memset(&sack, 0, sizeof(sack)); + sack.sack_assoc_id = 0; + sack.sack_delay = 0; + sack.sack_freq = 1; + if (setsockopt(s, IPPROTO_SCTP, SCTP_DELAYED_SACK, &sack, sizeof(sack)) < 0 && + errno != ENOPROTOOPT) { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IESETSCTPDELAYEDSACK; + return -1; + } + } +#endif /* HAVE_STRUCT_SCTP_SACK_INFO */ + freeaddrinfo(server_res); return s; #else