diff options
Diffstat (limited to '')
-rw-r--r-- | src/PerfSocket.cpp | 234 |
1 files changed, 186 insertions, 48 deletions
diff --git a/src/PerfSocket.cpp b/src/PerfSocket.cpp index bad4074..363e8b1 100644 --- a/src/PerfSocket.cpp +++ b/src/PerfSocket.cpp @@ -74,6 +74,7 @@ #include "PerfSocket.hpp" #include "SocketAddr.h" #include "util.h" +#include "iperf_multicast_api.h" /* ------------------------------------------------------------------- * Set socket options before the listen() or connect() calls. @@ -85,9 +86,8 @@ void SetSocketOptions (struct thread_Settings *inSettings) { // must occur before call to accept() for large window sizes setsock_tcp_windowsize(inSettings->mSock, inSettings->mTCPWin, (inSettings->mThreadMode == kMode_Client ? 1 : 0)); - +#if HAVE_DECL_TCP_CONGESTION if (isCongestionControl(inSettings)) { -#ifdef TCP_CONGESTION Socklen_t len = strlen(inSettings->mCongestion) + 1; int rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION, inSettings->mCongestion, len); @@ -95,12 +95,56 @@ void SetSocketOptions (struct thread_Settings *inSettings) { fprintf(stderr, "Attempt to set '%s' congestion control failed: %s\n", inSettings->mCongestion, strerror(errno)); unsetCongestionControl(inSettings); + thread_stop(inSettings); + } + char cca[TCP_CCA_NAME_MAX] = ""; + len = sizeof(cca); + if (getsockopt(inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION, &cca, &len) == 0) { + cca[TCP_CCA_NAME_MAX-1]='\0'; + if (strcmp(cca, inSettings->mCongestion) != 0) { + fprintf(stderr, "Failed to set '%s' congestion control got '%s'\n", inSettings->mCongestion, cca); + thread_stop(inSettings); + } } + } else if (isLoadCCA(inSettings)) { + Socklen_t len = strlen(inSettings->mLoadCCA) + 1; + int rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION, + inSettings->mLoadCCA, len); + if (rc == SOCKET_ERROR) { + fprintf(stderr, "Attempt to set '%s' load congestion control failed: %s\n", + inSettings->mLoadCCA, strerror(errno)); + unsetLoadCCA(inSettings); + thread_stop(inSettings); + } + char cca[TCP_CCA_NAME_MAX] = ""; + len = sizeof(cca); + if (getsockopt(inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION, &cca, &len) == 0) { + cca[TCP_CCA_NAME_MAX-1]='\0'; + if (strcmp(cca, inSettings->mLoadCCA) != 0) { + fprintf(stderr, "Failed to set '%s' load congestion control got '%s'\n", inSettings->mLoadCCA, cca); + thread_stop(inSettings); + } + } + } #else - fprintf(stderr, "The -Z option is not available on this operating system\n"); -#endif + if (isCongestionControl(inSettings) || isLoadCCA(inSettings)) { + fprintf(stderr, "TCP congestion control not supported\n"); + thread_stop(inSettings); } +#endif + int boolean = 1; + int rc; + Socklen_t len = sizeof(boolean); +#if HAVE_DECL_SO_REUSEADDR + // reuse the address, so we can run if a former server was killed off + rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&boolean), len); + WARN_errno(rc == SOCKET_ERROR, "SO_REUSEADDR"); +#endif +#if HAVE_DECL_SO_REUSEPORT + setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEPORT, (char*) &boolean, len); + WARN_errno(rc == SOCKET_ERROR, "SO_REUSEADDR"); +#endif #if ((HAVE_TUNTAP_TAP) && (HAVE_TUNTAP_TUN)) if (isTunDev(inSettings) || isTapDev(inSettings)) { @@ -140,28 +184,6 @@ void SetSocketOptions (struct thread_Settings *inSettings) { #endif } else #endif -#if (HAVE_DECL_SO_BINDTODEVICE) - { - char **device = (inSettings->mThreadMode == kMode_Client) ? &inSettings->mIfrnametx : &inSettings->mIfrname; - if (*device) { - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", *device); - if (setsockopt(inSettings->mSock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) { - char *buf; - int len = snprintf(NULL, 0, "%s %s", "bind to device", *device); - len++; // Trailing null byte + extra - buf = static_cast<char *>(malloc(len)); - len = snprintf(buf, len, "%s %s", "bind to device", *device); - WARN_errno(1, buf); - free(buf); - free(*device); - *device = NULL; - FAIL(1, "setsockopt() SO_BINDTODEVICE", inSettings); - } - } - } -#endif // check if we're sending multicast if (isMulticast(inSettings)) { @@ -201,9 +223,7 @@ void SetSocketOptions (struct thread_Settings *inSettings) { WARN_errno(rc == SOCKET_ERROR, "v4 ttl"); } -#if HAVE_DECL_IP_TOS SetSocketOptionsIPTos(inSettings, inSettings->mTOS); -#endif if (!isUDP(inSettings)) { if (isTCPMSS(inSettings)) { @@ -255,12 +275,18 @@ void SetSocketOptions (struct thread_Settings *inSettings) { WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_NOTSENT_LOWAT"); } #endif +#if HAVE_DECL_TCP_TX_DELAY + if (isTcpTxDelay(inSettings) && (inSettings->mTcpTxDelayProb >= 1.0)) { + SetSocketTcpTxDelay(inSettings, inSettings->mTcpTxDelayMean * 1000); + } +#endif } #if HAVE_DECL_SO_MAX_PACING_RATE /* If socket pacing is specified try to enable it. */ if (isFQPacing(inSettings) && inSettings->mFQPacingRate > 0) { int rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, &inSettings->mFQPacingRate, sizeof(inSettings->mFQPacingRate)); + inSettings->mFQPacingRateCurrent = inSettings->mFQPacingRate; WARN_errno(rc == SOCKET_ERROR, "setsockopt SO_MAX_PACING_RATE"); } #endif /* HAVE_SO_MAX_PACING_RATE */ @@ -310,37 +336,149 @@ void SetSocketOptionsReceiveTimeout (struct thread_Settings *mSettings, int time void SetSocketOptionsIPTos (struct thread_Settings *mSettings, int tos) { -#if HAVE_DECL_IP_TOS -#ifdef HAVE_THREAD_DEBUG - thread_debug("Set socket IP_TOS to 0x%x", tos); + bool supported = true; + // set IP TOS (type-of-service) field + if (isOverrideTOS(mSettings) || isSetTOS(mSettings) || (tos > 0)) { + // IPV6_TCLASS is defined on Windows but not implemented. +#if !HAVE_DECL_IPV6_TCLASS || HAVE_WINSOCK2_H + if (isIPV6(mSettings)) { + WARN(1, "WARN: IPV6_TCLASS not supported, setting --tos"); + mSettings->mTOS = 0; + supported = false; + } #endif -#if HAVE_DECL_IPV6_TCLASS && ! defined HAVE_WINSOCK2_H - // IPV6_TCLASS is defined on Windows but not implemented. - if (isIPV6(mSettings)) { - const int dscp = tos; - int rc = setsockopt(mSettings->mSock, IPPROTO_IPV6, IPV6_TCLASS, (char*) &dscp, sizeof(dscp)); - WARN_errno(rc == SOCKET_ERROR, "setsockopt IPV6_TCLASS"); - } else +#if !HAVE_DECL_IP_TOS + if (!isIPV6(mSettings)) { + WARN(1, "WARN: IP_TOS not supported, setting --tos"); + mSettings->mTOS = 0; + supported = false; + } #endif - // set IP TOS (type-of-service) field - if (isOverrideTOS(mSettings) || (tos > 0)) { + if (supported) { int reqtos = tos; Socklen_t len = sizeof(reqtos); - int rc = setsockopt(mSettings->mSock, IPPROTO_IP, IP_TOS, - reinterpret_cast<char*>(&reqtos), len); - WARN_errno(rc == SOCKET_ERROR, "setsockopt IP_TOS"); - rc = getsockopt(mSettings->mSock, IPPROTO_IP, IP_TOS, - reinterpret_cast<char*>(&reqtos), &len); - WARN_errno(rc == SOCKET_ERROR, "getsockopt IP_TOS"); - WARN((reqtos != tos), "IP_TOS setting failed"); + int rc = setsockopt(mSettings->mSock, (isIPV6(mSettings) ? IPPROTO_IPV6 : IPPROTO_IP), \ + (isIPV6(mSettings) ? IPV6_TCLASS : IP_TOS), reinterpret_cast<char*>(&reqtos), len); + WARN_errno(rc == SOCKET_ERROR, (isIPV6(mSettings) ? "setsockopt IPV6_TCLASS" : "setsockopt IP_TOS")); + rc = getsockopt(mSettings->mSock, (isIPV6(mSettings) ? IPPROTO_IPV6 : IPPROTO_IP), \ + (isIPV6(mSettings) ? IPV6_TCLASS : IP_TOS), reinterpret_cast<char*>(&reqtos), &len); + WARN_errno(rc == SOCKET_ERROR, (isIPV6(mSettings) ? "getsockopt IPV6_TCLASS" : "getsockopt IP_TOS")); + if (reqtos != tos) { + char warnbuf[256]; + snprintf(warnbuf, sizeof(warnbuf), "Warning: IP_TOS set to 0x%x, request for setting to 0x%x", reqtos, tos); + warnbuf[sizeof(warnbuf)-1] = '\0'; + WARN(1, warnbuf); + mSettings->mTOS = reqtos; + } } + } +} + +/* + * Networking tools can now establish thousands of flows, each of them + * with a different delay, simulating real world conditions. + * + * This requires FQ packet scheduler or a EDT-enabled NIC. + * + * TCP_TX_DELAY socket option, to set a delay in usec units. + * Note that FQ packet scheduler limits might need some tweaking : + * + * man tc-fq + * + * PARAMETERS + * limit + * Hard limit on the real queue size. When this limit is + * reached, new packets are dropped. If the value is lowered, + * packets are dropped so that the new limit is met. Default + * is 10000 packets. + * + * flow_limit + * Hard limit on the maximum number of packets queued per + * flow. Default value is 100. + * + * Use of TCP_TX_DELAY option will increase number of skbs in FQ qdisc, + * so packets would be dropped if any of the previous limit is hit. + * Using big delays might very well trigger + * old bugs in TSO auto defer logic and/or sndbuf limited detection. + ^ + * [root@rjm-nas rjmcmahon]# tc qdisc replace dev enp4s0 root fq + * [root@rjm-nas rjmcmahon]# tc qdisc replace dev enp2s0 root fq + */ +void SetSocketTcpTxDelay(struct thread_Settings *mSettings, int delay) { +#ifdef TCP_TX_DELAY +#if HAVE_DECL_TCP_TX_DELAY + int rc = setsockopt(mSettings->mSock, IPPROTO_TCP, TCP_TX_DELAY, &delay, sizeof(delay)); + if (rc == SOCKET_ERROR) { + fprintf(stderr, "Fail on TCP_TX_DELAY for sock %d\n", mSettings->mSock); + } +#ifdef HAVE_THREAD_DEBUG + else { + Socklen_t len = sizeof(delay); + rc = getsockopt(mSettings->mSock, IPPROTO_TCP, TCP_TX_DELAY, reinterpret_cast<char*>(&delay), &len); + thread_debug("TCP_TX_DELAY set to %d for sock %d", (int) delay, mSettings->mSock); + } +#endif +#endif #endif } +void sol_bindtodevice (struct thread_Settings *inSettings) { + char *device = (inSettings->mThreadMode == kMode_Client) ? inSettings->mIfrnametx : inSettings->mIfrname; + if (device) { +#if (HAVE_DECL_SO_BINDTODEVICE) + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", device); + if (setsockopt(inSettings->mSock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) { + char *buf; + int len = snprintf(NULL, 0, "%s %s", "bind to device", device); + len++; // Trailing null byte + extra + buf = static_cast<char *>(malloc(len)); + len = snprintf(buf, len, "%s %s", "bind to device", device); + WARN_errno(1, buf); + free(buf); + } +#else + fprintf(stderr, "bind to device not supported\n"); +#endif + + } +} + +void SetSocketBindToDeviceIfNeeded (struct thread_Settings *inSettings) { + if (!isMulticast(inSettings)) { + // typically requires root privileges for unicast bind to device + sol_bindtodevice(inSettings); + } +#ifndef WIN32 + else { // multicast bind below + if (inSettings->mThreadMode != kMode_Client) { + // multicast on the server uses iperf_multicast_join for device binding + // found in listener code, do nothing and return + return; + } + // Handle client side bind to device for multicast + if (!isIPV6(inSettings)) { + // v4 tries with the -B ip first, then legacy socket bind + if (!((inSettings->mLocalhost != NULL) && iperf_multicast_sendif_v4(inSettings))) { + if (inSettings->mIfrnametx != NULL) { + sol_bindtodevice(inSettings); + } + } + } else { + if (!((inSettings->mIfrnametx != NULL) && iperf_multicast_sendif_v6(inSettings))) { + if (inSettings->mIfrnametx != NULL) { + sol_bindtodevice(inSettings); + } + } + } + } +#endif +} /* * Set a socket to blocking or non-blocking -* + * * Returns true on success, or false if there was an error */ bool setsock_blocking (int fd, bool blocking) { |