diff -ruN old/config.h.in new/config.h.in --- old/config.h.in 2020-10-01 07:31:13.000000000 +0800 +++ new/config.h.in 2020-10-03 05:29:30.000000000 +0800 @@ -69,6 +69,10 @@ to 0 if you don't. */ #undef HAVE_DECL_MCAST_JOIN_SOURCE_GROUP +/* Define to 1 if you have the declaration of `MSG_DONTWAIT', and to 0 if you + don't. */ +#undef HAVE_DECL_MSG_DONTWAIT + /* Define to 1 if you have the declaration of `pthread_cancel', and to 0 if you don't. */ #undef HAVE_DECL_PTHREAD_CANCEL diff -ruN old/configure new/configure --- old/configure 2020-10-01 07:31:13.000000000 +0800 +++ new/configure 2020-10-03 05:29:30.000000000 +0800 @@ -8053,6 +8053,17 @@ cat >>confdefs.h <<_ACEOF #define HAVE_DECL_SO_BINDTODEVICE $ac_have_decl _ACEOF +ac_fn_c_check_decl "$LINENO" "MSG_DONTWAIT" "ac_cv_have_decl_MSG_DONTWAIT" "$in_h +" +if test "x$ac_cv_have_decl_MSG_DONTWAIT" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_MSG_DONTWAIT $ac_have_decl +_ACEOF ac_fn_c_check_decl "$LINENO" "SO_MAX_PACING_RATE" "ac_cv_have_decl_SO_MAX_PACING_RATE" "$in_h " if test "x$ac_cv_have_decl_SO_MAX_PACING_RATE" = xyes; then : diff -ruN old/configure.ac new/configure.ac --- old/configure.ac 2020-10-01 07:30:58.000000000 +0800 +++ new/configure.ac 2020-10-03 05:29:22.000000000 +0800 @@ -184,7 +184,7 @@ #endif ' -AC_CHECK_DECLS([AF_INET6, SO_TIMESTAMP, SO_SNDTIMEO, SO_BINDTODEVICE, +AC_CHECK_DECLS([AF_INET6, SO_TIMESTAMP, SO_SNDTIMEO, SO_BINDTODEVICE, MSG_DONTWAIT, SO_MAX_PACING_RATE, IPV6_TCLASS, IP_MULTICAST_ALL, MCAST_JOIN_GROUP, MCAST_JOIN_SOURCE_GROUP, IPV6_ADD_MEMBERSHIP, IPV6_MULTICAST_HOPS, diff -ruN old/include/Client.hpp new/include/Client.hpp --- old/include/Client.hpp 2020-09-29 07:40:17.000000000 +0800 +++ new/include/Client.hpp 2020-10-03 02:42:49.000000000 +0800 @@ -123,7 +123,7 @@ // UDP plain void RunUDP(void); // client connect - void PeerXchange(int len); + void PeerXchange(void); thread_Settings *mSettings; #if WIN32 diff -ruN old/include/payloads.h new/include/payloads.h --- old/include/payloads.h 2020-09-29 07:40:17.000000000 +0800 +++ new/include/payloads.h 2020-10-03 05:12:16.000000000 +0800 @@ -61,6 +61,7 @@ #define HEADER_UDPTESTS 0x20000000 #define HEADER_SEQNO64B 0x08000000 #define HEADER_VERSION2 0x04000000 +#define HEADER_V2PEERDETECT 0x02000000 #define HEADER_LEN_BIT 0x00010000 #define SERVER_HEADER_EXTEND 0x40000000 #define RUN_NOW 0x00000001 @@ -169,6 +170,8 @@ }; struct client_hdrext { + int32_t type; + int32_t length; int16_t upperflags; int16_t lowerflags; uint32_t version_u; diff -ruN old/include/version.h new/include/version.h --- old/include/version.h 2020-10-01 02:21:03.000000000 +0800 +++ new/include/version.h 2020-10-03 05:17:37.000000000 +0800 @@ -1,4 +1,4 @@ #define IPERF_VERSION "2.0.14a" -#define IPERF_VERSION_DATE "30 September 2020" +#define IPERF_VERSION_DATE "2 October 2020" #define IPERF_VERSION_MAJORHEX 0x00020000 #define IPERF_VERSION_MINORHEX 0x000E0000 diff -ruN old/man/iperf.1 new/man/iperf.1 --- old/man/iperf.1 2020-10-01 14:51:10.000000000 +0800 +++ new/man/iperf.1 2020-10-03 02:42:49.000000000 +0800 @@ -186,7 +186,7 @@ time in seconds to hold back or delay after the TCP connect and prior to the socket writes. For UDP it's the delay between the traffic thread starting and the first write. .TP .BR " --txstart-time "\fIn\fR.\fIn\fR -set the txstart-time to \fIn\fR.\fIn\fR using unix or epoch time format (supports microsecond resolution, e.g 1536014418.123456) +set the txstart-time to \fIn\fR.\fIn\fR using unix or epoch time format (supports microsecond resolution, e.g 1536014418.123456) An example to delay one second using command subsitution is iperf -c 192.168.1.10 --txstart-time $(expr $(date +%s) + 1).$(date +%N) .TP .BR -B ", " --bind " \fIip\fR | \fIip\fR:\fIport\fR | \fIipv6 -V\fR | \fI[ipv6]\fR:\fIport -V\fR" bind src ip addr and optional port as the source of traffic (see notes) diff -ruN old/src/Client.cpp new/src/Client.cpp --- old/src/Client.cpp 2020-09-30 12:51:23.000000000 +0800 +++ new/src/Client.cpp 2020-10-03 05:30:52.000000000 +0800 @@ -200,7 +200,7 @@ PostReport(tmp); } // Post the connect report unless peer version exchange is set - if (connected && isConnectionReport(mSettings) && !isSumOnly(mSettings)) + if (connected && isConnectionReport(mSettings) && !isSumOnly(mSettings) && !isPeerVerDetect(mSettings)) PostReport(InitConnectionReport(mSettings, connecttime)); } // end Connect @@ -1202,7 +1202,7 @@ void Client::SendFirstPayload (void) { if (!isCompat(mSettings) && !isConnectOnly(mSettings)) { - int len = 0; + int pktlen = 0; if (myReport && !TimeZero(myReport->info.ts.startTime)) { reportstruct->packetTime = myReport->info.ts.startTime; } else { @@ -1210,63 +1210,46 @@ reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); } - if (isUDP(mSettings)) { - len = Settings_GenerateClientHdr(mSettings, (void *) mBuf, reportstruct->packetTime); - if (len > 0) { + pktlen = Settings_GenerateClientHdr(mSettings, (void *) mBuf, reportstruct->packetTime); + if (pktlen > 0) { + if (isUDP(mSettings)) { struct client_udp_testhdr *tmphdr = (struct client_udp_testhdr *) mBuf; WritePacketID(reportstruct->packetID++); tmphdr->seqno_ts.tv_sec = htonl(reportstruct->packetTime.tv_sec); tmphdr->seqno_ts.tv_usec = htonl(reportstruct->packetTime.tv_usec); - udp_payload_minimum = len; + udp_payload_minimum = pktlen; } - } else { - len = Settings_GenerateClientHdr(mSettings, (void *) mBuf, reportstruct->packetTime); - if (len > 0) { - if (isPeerVerDetect(mSettings) && !isServerReverse(mSettings)) { - PeerXchange(len); +#if HAVE_DECL_MSG_DONTWAIT + reportstruct->packetLen = send(mySocket, mBuf, pktlen, MSG_DONTWAIT); +#else + reportstruct->packetLen = send(mySocket, mBuf, pktlen, 0); +#endif + WARN_errno(reportstruct->packetLen < 0, "send_hdr"); + if (reportstruct->packetLen > 0) { + ReportPacket(myReport, reportstruct); + if (!isUDP(mSettings) && isPeerVerDetect(mSettings) && !isServerReverse(mSettings)) { + PeerXchange(); } } + } else { + WARN_errno(1, "send first fail"); } - reportstruct->packetLen = send(mySocket, mBuf, len, 0); - WARN_errno(reportstruct->packetLen < 0, "send_hdr"); - ReportPacket(myReport, reportstruct); } } -void Client::PeerXchange (int len) { - int currLen = 0; - // Run compatability detection and test info exchange for tests that require it - int optflag; -#ifdef TCP_NODELAY - // Disable Nagle to reduce latency of this intial message - optflag=1; - if (setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY, (char *)&optflag, sizeof(int)) < 0) - WARN_errno(0, "tcpnodelay"); -#endif - currLen = send(mySocket, mBuf, len, 0); - if (currLen < 0) { - WARN_errno(currLen < 0, "send_hdr_v2"); - } else { - int n; - client_hdr_ack ack; - int sotimer = 2000000; // 2 seconds - SetSocketOptionsReceiveTimeout(mSettings, sotimer); - /* - * Hang read and see if this is a header ack message - */ - if ((n = recvn(mySocket, (char *)&ack, sizeof(client_hdr_ack), 0)) == sizeof(client_hdr_ack)) { - if (ntohl(ack.typelen.type) == CLIENTHDRACK && ntohl(ack.typelen.length) == sizeof(client_hdr_ack)) { - mSettings->peer_version_u = ntohl(ack.version_u); - mSettings->peer_version_l = ntohl(ack.version_l); - } - } else { - WARN_errno(1, "recvack"); +void Client::PeerXchange (void) { + int n; + client_hdr_ack ack; + /* + * Hang read and see if this is a header ack message + */ + if ((n = recvn(mySocket, (char *)&ack, sizeof(client_hdr_ack), 0)) == sizeof(client_hdr_ack)) { + if (ntohl(ack.typelen.type) == CLIENTHDRACK && ntohl(ack.typelen.length) == sizeof(client_hdr_ack)) { + mSettings->peer_version_u = ntohl(ack.version_u); + mSettings->peer_version_l = ntohl(ack.version_l); } - } - optflag = 0; - // Re-enable Nagle - if (setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY, (char *)&optflag, sizeof(int)) < 0) { - WARN_errno(0, "tcpnodelay"); + } else { + WARN_errno(1, "recvack"); } } diff -ruN old/src/Listener.cpp new/src/Listener.cpp --- old/src/Listener.cpp 2020-10-01 15:18:50.000000000 +0800 +++ new/src/Listener.cpp 2020-10-03 04:28:03.000000000 +0800 @@ -990,7 +990,6 @@ flags = ntohl(hdr->base.flags); // figure out the length of the test header if ((peeklen = Settings_ClientHdrPeekLen(flags)) > 0) { - peeklen = Settings_ClientHdrPeekLen(flags); // read the test settings passed to the server by the client n = recvn(server->mSock, mBuf, peeklen, MSG_PEEK); FAIL_errno((n < peeklen), "read tcp test info", server); @@ -1027,7 +1026,12 @@ } } // Handle case that requires an ack back to the client - if (!isUDP(server) && (flags & HEADER_EXTEND)) { + // Signaled by not UDP (only supported by TCP) + // and either 2.0.13 flags or the newer 2.0.14 flag of + // V2PEERDETECT + if (!isUDP(server) && \ + ((!(flags & HEADER_VERSION2) && (flags & HEADER_EXTEND)) || \ + (flags & HEADER_V2PEERDETECT))) { client_test_ack(server); } return rc; diff -ruN old/src/ReportOutputs.c new/src/ReportOutputs.c --- old/src/ReportOutputs.c 2020-10-01 02:27:19.000000000 +0800 +++ new/src/ReportOutputs.c 2020-10-03 04:32:33.000000000 +0800 @@ -426,7 +426,7 @@ } // Sum reports -void udp_output_sum_read(struct TransferInfo *stats) { +void udp_output_sum_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); printf(report_sum_bw_jitter_loss_format, @@ -440,7 +440,7 @@ stats->ts.iEnd, stats->cntOutofOrder); } } -void udp_output_sumcnt_read(struct TransferInfo *stats) { +void udp_output_sumcnt_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_sumcnt); _print_stats_common(stats); printf(report_sumcnt_bw_jitter_loss_format, stats->threadcnt, @@ -454,21 +454,21 @@ stats->ts.iEnd, stats->cntOutofOrder); } } -void udp_output_sum_write(struct TransferInfo *stats) { +void udp_output_sum_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } -void udp_output_sumcnt_write(struct TransferInfo *stats) { +void udp_output_sumcnt_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_sumcnt); _print_stats_common(stats); printf(report_sumcnt_bw_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } -void udp_output_sum_read_enhanced(struct TransferInfo *stats) { +void udp_output_sum_read_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_pps_enhanced); _print_stats_common(stats); printf(report_sum_bw_pps_enhanced_format, @@ -477,7 +477,7 @@ stats->cntError, stats->cntDatagrams, (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0)); } -void udp_output_sum_write_enhanced(struct TransferInfo *stats) { +void udp_output_sum_write_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_pps_enhanced); _print_stats_common(stats); printf(report_sum_bw_pps_enhanced_format, @@ -487,14 +487,14 @@ stats->sock_callstats.write.WriteErr, ((stats->cntIPG && (stats->IPGsum > 0.0)) ? (stats->cntIPG / stats->IPGsum) : 0.0)); } -void tcp_output_sum_read(struct TransferInfo *stats) { +void tcp_output_sum_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } -void tcp_output_sum_read_enhanced(struct TransferInfo *stats) { +void tcp_output_sum_read_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_read_enhanced); _print_stats_common(stats); printf(report_sum_bw_read_enhanced_format, @@ -510,7 +510,7 @@ stats->sock_callstats.read.bins[6], stats->sock_callstats.read.bins[7]); } -void tcp_output_sumcnt_read(struct TransferInfo *stats) { +void tcp_output_sumcnt_read (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_sumcnt); _print_stats_common(stats); printf(report_sumcnt_bw_format, stats->threadcnt, @@ -528,21 +528,21 @@ stats->sock_callstats.write.TCPretry); } -void tcp_output_sum_write(struct TransferInfo *stats) { +void tcp_output_sum_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw); _print_stats_common(stats); printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } -void tcp_output_sumcnt_write(struct TransferInfo *stats) { +void tcp_output_sumcnt_write (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_sumcnt); _print_stats_common(stats); printf(report_sumcnt_bw_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext); } -void tcp_output_sum_write_enhanced(struct TransferInfo *stats) { +void tcp_output_sum_write_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_write_enhanced); _print_stats_common(stats); printf(report_sum_bw_write_enhanced_format, @@ -793,7 +793,7 @@ } } -void reporter_print_connection_report(struct ConnectionInfo *report) { +void reporter_print_connection_report (struct ConnectionInfo *report) { assert(report->common); // copy the inet_ntop into temp buffers, to avoid overwriting char local_addr[REPORT_ADDRLEN]; @@ -920,7 +920,7 @@ } } -void reporter_print_settings_report(struct ReportSettings *report) { +void reporter_print_settings_report (struct ReportSettings *report) { assert(report != NULL); report->pid = (int) getpid(); printf("%s", separator_line); diff -ruN old/src/Server.cpp new/src/Server.cpp --- old/src/Server.cpp 2020-09-30 13:49:20.000000000 +0800 +++ new/src/Server.cpp 2020-10-03 02:42:49.000000000 +0800 @@ -416,6 +416,7 @@ } else { n = recvn(mSettings->mSock, mBuf, sizeof(uint32_t), MSG_PEEK); if (n == 0) { + fprintf(stderr, "WARN: zero read on header flags\n"); //peer closed the socket, with no writes e.g. a connect-only test return -1; } @@ -424,7 +425,6 @@ flags = ntohl(tcp_pkt->base.flags); // figure out the length of the test header if ((peeklen = Settings_ClientHdrPeekLen(flags)) > 0) { - peeklen = Settings_ClientHdrPeekLen(flags); // read the test settings passed to the mSettings by the client n = recvn(mSettings->mSock, mBuf, peeklen, 0); FAIL_errno((n < peeklen), "read tcp test info", mSettings); diff -ruN old/src/Settings.cpp new/src/Settings.cpp --- old/src/Settings.cpp 2020-10-01 14:51:25.000000000 +0800 +++ new/src/Settings.cpp 2020-10-03 04:55:18.000000000 +0800 @@ -1045,6 +1045,10 @@ bail = true; } if (isUDP(mExtSettings)) { + if (isPeerVerDetect(mExtSettings)) { + fprintf(stderr, "ERROR: option of -X or --peer-detect not supported with -u UDP\n"); + bail = true; + } if (isConnectOnly(mExtSettings)) { fprintf(stderr, "ERROR: option of --connect-only not supported with -u UDP\n"); bail = true; @@ -1181,6 +1185,11 @@ fprintf(stderr, "ERROR: option of --no-udp-fin is not suppported on the server\n"); bail = true; } + if (isPeerVerDetect(mExtSettings)) { + fprintf(stderr, "ERROR: option of -X or --peer-detect not supported on the server\n"); + bail = true; + } + } if (bail) exit(1); @@ -1611,15 +1620,15 @@ uint16_t len = 0; uint32_t flags = 0; - flags = (HEADER_SEQNO64B | HEADER_LEN_BIT); // use 64 bit by default + flags = (HEADER_SEQNO64B | HEADER_VERSION2); // use 64 bit by default // flags common to both TCP and UDP if (isReverse(client)) { - flags |= (isUDP(client) ? (HEADER_VERSION2 | HEADER_UDPTESTS) : HEADER_VERSION2); + flags |= HEADER_UDPTESTS; upperflags |= HEADER_REVERSE; } if (isFullDuplex(client)) { - flags |= (isUDP(client) ? (HEADER_VERSION2 | HEADER_UDPTESTS) : HEADER_VERSION2); + flags |= HEADER_UDPTESTS; upperflags |= HEADER_FULLDUPLEX; } // Now setup UDP and TCP specific passed settings from client to server @@ -1708,7 +1717,10 @@ if (isTripTime(client) || isFQPacing(client) || isIsochronous(client)) len += sizeof (struct isoch_payload); - len += sizeof(struct UDP_datagram); + if (len > 0) { + len += sizeof(struct UDP_datagram); + flags |= HEADER_LEN_BIT; + } hdr->base.flags = htonl(flags | ((len << 1) & 0xFFFE)); } else { // TCP first write with test information struct client_tcp_testhdr *hdr = (struct client_tcp_testhdr *) testhdr; @@ -1748,13 +1760,13 @@ } if (isBWSet(client)) { flags |= (HEADER_EXTEND | HEADER_VERSION2); - hdr->extend.uRate = htonl((uint32_t)client->mUDPRate); + hdr->extend.lRate = htonl((uint32_t)client->mUDPRate); #ifdef HAVE_INT64_T hdr->extend.uRate = htonl(((uint32_t)(client->mUDPRate >> 32)) << 8); #endif } if (isPeerVerDetect(client)) { - flags |= HEADER_EXTEND; + flags |= (HEADER_EXTEND | HEADER_V2PEERDETECT); } if (flags & (HEADER_EXTEND | HEADER_VERSION2)) { // Write flags to header so the listener can determine the tests requested @@ -1772,22 +1784,27 @@ if (flags & (HEADER_VERSION1 | HEADER_VERSION2)) { len += Settings_GenerateClientHdrV1(client, &hdr->base); } + if (len > 0) { + flags |= HEADER_LEN_BIT; + } hdr->base.flags = htonl((flags | ((len << 1) & 0xFFFE))); } return (len); } -int Settings_ClientHdrPeekLen(uint32_t flags) { +int Settings_ClientHdrPeekLen (uint32_t flags) { //* determine peek length int peeklen = 0; if (flags & HEADER_LEN_BIT) { peeklen = (flags & 0xFFFE) >> 1; + if (peeklen <= 0) + fprintf(stderr, "WARN: header length bit set and length invalid\n"); } else { peeklen = 0; - if (flags & HEADER_VERSION1) { + if (flags & (HEADER_VERSION1 | HEADER_EXTEND)) { peeklen = sizeof(struct client_hdr_v1); } - if (flags & HEADER_VERSION2) { + if (flags & (HEADER_VERSION2 | HEADER_EXTEND)) { peeklen += sizeof(struct client_hdrext); } }