summaryrefslogtreecommitdiffstats
path: root/src/PerfSocket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/PerfSocket.cpp')
-rw-r--r--src/PerfSocket.cpp234
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) {