Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ struct iperf_test
int sender_has_retransmits;
int other_side_has_retransmits; /* used if mode == BIDIRECTIONAL */
struct protocol *protocol;
int udp_ctrl_sck; /* --udp-cntl-sck - true if control socket is UDP */
signed char state;
char *server_hostname; /* -c option */
char *tmp_template;
Expand Down
11 changes: 11 additions & 0 deletions src/iperf_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
#if defined(HAVE_IPPROTO_MPTCP)
{"mptcp", no_argument, NULL, 'm'},
#endif
{"udp-control", no_argument, NULL, OPT_UDP_CONTROL},
{"debug", optional_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
Expand Down Expand Up @@ -1662,6 +1663,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
test->mptcp = 1;
break;
#endif
case OPT_UDP_CONTROL:
test->udp_ctrl_sck = 1;
break;
case 'h':
usage_long(stdout);
exit(0);
Expand Down Expand Up @@ -1784,6 +1788,11 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
}
test->settings->blksize = blksize;

if (test->role == 'c' && test->udp_ctrl_sck && test->protocol->id != Pudp) {
i_errno = IEUDPCNTLONLYUDP;
return -1;
}

if (!rate_flag)
test->settings->rate = test->protocol->id == Pudp ? UDP_RATE : 0;

Expand Down Expand Up @@ -3112,6 +3121,8 @@ iperf_defaults(struct iperf_test *testp)

set_protocol(testp, Ptcp);

testp->udp_ctrl_sck = 0;

#if defined(HAVE_SCTP_H)
sctp = protocol_new();
if (!sctp) {
Expand Down
2 changes: 2 additions & 0 deletions src/iperf_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t;
#define OPT_JSON_STREAM 28
#define OPT_SND_TIMEOUT 29
#define OPT_USE_PKCS1_PADDING 30
#define OPT_UDP_CONTROL 31

/* states */
#define TEST_START 1
Expand Down Expand Up @@ -423,6 +424,7 @@ enum {
IESNDTIMEOUT = 33, // Illegal message send timeout
IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP
IESERVERAUTHUSERS = 35, // Cannot access authorized users file
IEUDPCNTLONLYUDP = 36, // UDP Control Socket is supported only in UDP test
/* Test errors */
IENEWTEST = 100, // Unable to create a new test (check perror)
IEINITTEST = 101, // Test initialization failed (check perror)
Expand Down
76 changes: 42 additions & 34 deletions src/iperf_client_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

#include "iperf.h"
#include "iperf_api.h"
#include "iperf_udp.h"
#include "iperf_util.h"
#include "iperf_locale.h"
#include "iperf_time.h"
Expand Down Expand Up @@ -316,8 +317,7 @@ iperf_handle_message_client(struct iperf_test *test)
iperf_printf(test, "Reading new State from the Server - current state is %d-%s\n", test->state, state_to_text(test->state));
}

/*!!! Why is this read() and not Nread()? */
if ((rval = read(test->ctrl_sck, (char*) &test->state, sizeof(signed char))) <= 0) {
if ((rval = Nread(test->ctrl_sck, (char*) &test->state, sizeof(signed char), Ptcp)) <= 0) {
if (rval == 0) {
i_errno = IECTRLCLOSE;
return -1;
Expand Down Expand Up @@ -429,29 +429,36 @@ iperf_connect(struct iperf_test *test)
make_cookie(test->cookie);

/* Create and connect the control channel */
if (test->ctrl_sck < 0)
// Create the control channel using an ephemeral port
test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, 0, test->server_hostname, test->server_port, test->settings->connect_timeout);
if (test->ctrl_sck < 0) {
i_errno = IECONNECT;
return -1;
}

// set TCP_NODELAY for lower latency on control messages
int flag = 1;
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) {
i_errno = IESETNODELAY;
return -1;
}

// Create the control channel using an ephemeral port
if (test->udp_ctrl_sck) { // UDP Control Socket
test->ctrl_sck = iperf_udp_connect(test);
if (test->ctrl_sck < 0) {
i_errno = IECONNECT;
return -1;
}
} else { // TCP Control Socket
test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, 0, test->server_hostname, test->server_port, test->settings->connect_timeout);
if (test->ctrl_sck < 0) {
i_errno = IECONNECT;
return -1;
}
// set TCP_NODELAY for lower latency on control messages
int flag = 1;
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) {
i_errno = IESETNODELAY;
return -1;
}
#if defined(HAVE_TCP_USER_TIMEOUT)
if ((opt = test->settings->snd_timeout)) {
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
i_errno = IESETUSERTIMEOUT;
return -1;
if ((opt = test->settings->snd_timeout)) {
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
i_errno = IESETUSERTIMEOUT;
return -1;
}
}
#endif /* HAVE_TCP_USER_TIMEOUT */
}
}
#endif /* HAVE_TCP_USER_TIMEOUT */

if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
i_errno = IESENDCOOKIE;
Expand All @@ -461,21 +468,22 @@ iperf_connect(struct iperf_test *test)
FD_SET(test->ctrl_sck, &test->read_set);
if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;

len = sizeof(opt);
if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) {
test->ctrl_sck_mss = 0;
}
else {
if (opt > 0 && opt <= MAX_UDP_BLOCKSIZE) {
test->ctrl_sck_mss = opt;
if (!test->udp_ctrl_sck) { // TCP Control Socket
len = sizeof(opt);
if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) {
test->ctrl_sck_mss = 0;
}
else {
char str[WARN_STR_LEN];
snprintf(str, sizeof(str),
"Ignoring nonsense TCP MSS %d", opt);
warning(str);

test->ctrl_sck_mss = 0;
if (opt > 0 && opt <= MAX_UDP_BLOCKSIZE) {
test->ctrl_sck_mss = opt;
} else {
char str[WARN_STR_LEN];
snprintf(str, sizeof(str),
"Ignoring nonsense TCP MSS %d", opt);
warning(str);

test->ctrl_sck_mss = 0;
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/iperf_error.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ iperf_strerror(int int_errno)
case IEUDPBLOCKSIZE:
snprintf(errstr, len, "block size invalid (minimum = %d bytes, maximum = %d bytes)", MIN_UDP_BLOCKSIZE, MAX_UDP_BLOCKSIZE);
break;
case IEUDPCNTLONLYUDP:
snprintf(errstr, len, "UDP Control Socket is supported only in UDP test");
break;
case IEBADTOS:
snprintf(errstr, len, "bad TOS value (must be between 0 and 255 inclusive)");
break;
Expand Down
1 change: 1 addition & 0 deletions src/iperf_locale.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
#if defined(HAVE_IPPROTO_MPTCP)
" -m, --mptcp use MPTCP rather than plain TCP\n"
#endif
" --udp-control use UDP for the control socket (instead of default TCP)\n"
" -d, --debug[=#] emit debugging output\n"
" (optional optional \"=\" and debug level: 1-4. Default is 4 - all messages)\n"
" -v, --version show version information and quit\n"
Expand Down
83 changes: 63 additions & 20 deletions src/iperf_server_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,10 @@ iperf_server_worker_run(void *s) {
int
iperf_server_listen(struct iperf_test *test)
{
int proto = (test->udp_ctrl_sck) ? Pudp : Ptcp;

retry:
if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, test->server_port)) < 0) {
if((test->listener = netannounce(test->settings->domain, proto, test->bind_address, test->bind_dev, test->server_port)) < 0) {
if (errno == EAFNOSUPPORT && (test->settings->domain == AF_INET6 || test->settings->domain == AF_UNSPEC)) {
/* If we get "Address family not supported by protocol", that
** probably means we were compiled with IPv6 but the running
Expand Down Expand Up @@ -157,32 +159,50 @@ iperf_accept(struct iperf_test *test)
signed char rbuf = ACCESS_DENIED;
socklen_t len;
struct sockaddr_storage addr;
int rc;

len = sizeof(addr);
if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
i_errno = IEACCEPT;
return ret;
if (test->udp_ctrl_sck) { // UDP Control Socket
s = test->listener;
}
else { // TCP Control Socket
len = sizeof(addr);
if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
i_errno = IEACCEPT;
return ret;
}
}

/* UDP listener socket should be accepted to allow communication (including for sending Deny) */
if (test->udp_ctrl_sck) { // UDP Control Socket
if (test->debug_level >= DEBUG_LEVEL_INFO) {
printf("Accepting new Connection Request over UDP socket %d\n", s);
}
if ((rc = iperf_udp_accept_socket(test, s)) < 0) {
return rc;
}
}

if (test->ctrl_sck == -1) {
/* Server free, accept new client */
test->ctrl_sck = s;
// set TCP_NODELAY for lower latency on control messages
int flag = 1;
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) {
i_errno = IESETNODELAY;
goto error_handling;
}
if (!test->udp_ctrl_sck) { // TCP Control Socket
// set TCP_NODELAY for lower latency on control messages
int flag = 1;
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) {
i_errno = IESETNODELAY;
goto error_handling;
}

#if defined(HAVE_TCP_USER_TIMEOUT)
int opt;
if ((opt = test->settings->snd_timeout)) {
if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
i_errno = IESETUSERTIMEOUT;
goto error_handling;
int opt;
if ((opt = test->settings->snd_timeout)) {
if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
i_errno = IESETUSERTIMEOUT;
goto error_handling;
}
}
}
#endif /* HAVE_TCP_USER_TIMEOUT */
}

if (Nread(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) != COOKIE_SIZE) {
/*
Expand Down Expand Up @@ -220,6 +240,24 @@ iperf_accept(struct iperf_test *test)
if (test->debug)
printf("successfully sent ACCESS_DENIED to an unsolicited connection request during active test\n");
}

// Create new UDP listener socket. Done before closing the previous listener socket to allow the
// ACCESS_DENIED message to be sent out of the server before the socket is closed.
if (test->udp_ctrl_sck) {
FD_CLR(s, &test->read_set);
if (test->debug_level >= DEBUG_LEVEL_INFO) {
printf("Creating new UDP Listener socket\n");
}
if ((test->listener = iperf_udp_listen(test)) < 0) {
return -1;
}
if (test->debug_level >= DEBUG_LEVEL_INFO) {
printf("Created new UDP Listener socket=%d\n", test->listener);
}
FD_SET(test->listener, &test->read_set);
test->max_fd = (test->max_fd < test->listener) ? test->listener : test->max_fd;
}

close(s);
}
return 0;
Expand Down Expand Up @@ -839,9 +877,14 @@ iperf_run_server(struct iperf_test *test)

if (rec_streams_accepted == streams_to_rec && send_streams_accepted == streams_to_send) {
if (test->protocol->id != Ptcp) {
FD_CLR(test->prot_listener, &test->read_set);
close(test->prot_listener);
test->prot_listener = -1;
if (test->udp_ctrl_sck) {
test->listener = test->prot_listener;
test->prot_listener = -1;
} else {
FD_CLR(test->prot_listener, &test->read_set);
close(test->prot_listener);
test->prot_listener = -1;
}
} else {
if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) {
FD_CLR(test->listener, &test->read_set);
Expand Down
Loading