diff --git a/src/iperf_api.c b/src/iperf_api.c index 84cd151..878c9c4 100755 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1115,10 +1115,6 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) return -1; } #endif //HAVE_SSL - if (!test->bind_address && test->bind_port) { - i_errno = IEBIND; - return -1; - } if (blksize == 0) { if (test->protocol->id == Pudp) blksize = 0; /* try to dynamically determine from MSS */ diff --git a/src/iperf_api.h b/src/iperf_api.h index 692db0e..f1b4a14 100755 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2017, The Regents of the University of + * iperf, Copyright (c) 2014-2018, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -306,7 +306,7 @@ enum { IEENDCONDITIONS = 16, // Only one test end condition (-t, -n, -k) may be specified IELOGFILE = 17, // Can't open log file IENOSCTP = 18, // No SCTP support available - IEBIND = 19, // Local port specified with no local bind option + IEBIND = 19, // UNUSED: Local port specified with no local bind option IEUDPBLOCKSIZE = 20, // Block size invalid IEBADTOS = 21, // Bad TOS value IESETCLIENTAUTH = 22, // Bad configuration of client authentication diff --git a/src/iperf_error.c b/src/iperf_error.c index d38333a..945984e 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -122,7 +122,7 @@ iperf_strerror(int int_errno) case IEINTERVAL: snprintf(errstr, len, "invalid report interval (min = %g, max = %g seconds)", MIN_INTERVAL, MAX_INTERVAL); break; - case IEBIND: + case IEBIND: /* UNUSED */ snprintf(errstr, len, "--bind must be specified to use --cport"); break; case IEUDPBLOCKSIZE: diff --git a/src/iperf_sctp.c b/src/iperf_sctp.c index 28b98a7..1487742 100644 --- a/src/iperf_sctp.c +++ b/src/iperf_sctp.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014, 2015, The Regents of the University of + * iperf, Copyright (c) 2014-2018, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -274,6 +274,67 @@ iperf_sctp_connect(struct iperf_test *test) return -1; } + /* + * Various ways to bind the local end of the connection. + * 1. --bind (with or without --cport). + */ + if (test->bind_address) { + struct sockaddr_in *lcladdr; + lcladdr = (struct sockaddr_in *)local_res->ai_addr; + lcladdr->sin_port = htons(test->bind_port); + + if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(local_res); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IESTREAMCONNECT; + return -1; + } + freeaddrinfo(local_res); + } + /* --cport, no --bind */ + else if (test->bind_port) { + size_t addrlen; + struct sockaddr_storage lcl; + + /* IPv4 */ + if (server_res->ai_family == AF_INET) { + struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl; + lcladdr->sin_family = AF_INET; + lcladdr->sin_port = htons(test->bind_port); + lcladdr->sin_addr.s_addr = INADDR_ANY; + addrlen = sizeof(struct sockaddr_in); + } + /* IPv6 */ + else if (server_res->ai_family == AF_INET6) { + struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl; + lcladdr->sin6_family = AF_INET6; + lcladdr->sin6_port = htons(test->bind_port); + lcladdr->sin6_addr = in6addr_any; + addrlen = sizeof(struct sockaddr_in6); + } + /* Unknown protocol */ + else { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IEPROTOCOL; + return -1; + } + + if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IESTREAMCONNECT; + return -1; + } + } + if (test->no_delay != 0) { opt = 1; if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) { diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c index dc3bf07..3d000b7 100644 --- a/src/iperf_tcp.c +++ b/src/iperf_tcp.c @@ -349,6 +349,9 @@ iperf_tcp_listen(struct iperf_test *test) /* iperf_tcp_connect * * connect to a TCP stream listener + * This function is roughly similar to netdial(), and may indeed have + * been derived from it at some point, but it sets many TCP-specific + * options between socket creation and connection. */ int iperf_tcp_connect(struct iperf_test *test) @@ -389,11 +392,14 @@ iperf_tcp_connect(struct iperf_test *test) return -1; } + /* + * Various ways to bind the local end of the connection. + * 1. --bind (with or without --cport). + */ if (test->bind_address) { struct sockaddr_in *lcladdr; lcladdr = (struct sockaddr_in *)local_res->ai_addr; lcladdr->sin_port = htons(test->bind_port); - local_res->ai_addr = (struct sockaddr *)lcladdr; if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { saved_errno = errno; @@ -406,6 +412,46 @@ iperf_tcp_connect(struct iperf_test *test) } freeaddrinfo(local_res); } + /* --cport, no --bind */ + else if (test->bind_port) { + size_t addrlen; + struct sockaddr_storage lcl; + + /* IPv4 */ + if (server_res->ai_family == AF_INET) { + struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl; + lcladdr->sin_family = AF_INET; + lcladdr->sin_port = htons(test->bind_port); + lcladdr->sin_addr.s_addr = INADDR_ANY; + addrlen = sizeof(struct sockaddr_in); + } + /* IPv6 */ + else if (server_res->ai_family == AF_INET6) { + struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl; + lcladdr->sin6_family = AF_INET6; + lcladdr->sin6_port = htons(test->bind_port); + lcladdr->sin6_addr = in6addr_any; + addrlen = sizeof(struct sockaddr_in6); + } + /* Unknown protocol */ + else { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IEPROTOCOL; + return -1; + } + + if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + i_errno = IESTREAMCONNECT; + return -1; + } + } /* Set socket options */ if (test->no_delay) { diff --git a/src/net.c b/src/net.c index f06d4c4..fd525ee 100644 --- a/src/net.c +++ b/src/net.c @@ -140,12 +140,12 @@ netdial(int domain, int proto, char *local, int local_port, char *server, int po return -1; } + /* Bind the local address if given a name (with or without --cport) */ if (local) { if (local_port) { struct sockaddr_in *lcladdr; lcladdr = (struct sockaddr_in *)local_res->ai_addr; lcladdr->sin_port = htons(local_port); - local_res->ai_addr = (struct sockaddr *)lcladdr; } if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { @@ -158,6 +158,41 @@ netdial(int domain, int proto, char *local, int local_port, char *server, int po } freeaddrinfo(local_res); } + /* No local name, but --cport given */ + else if (local_port) { + size_t addrlen; + struct sockaddr_storage lcl; + + /* IPv4 */ + if (server_res->ai_family == AF_INET) { + struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl; + lcladdr->sin_family = AF_INET; + lcladdr->sin_port = htons(local_port); + lcladdr->sin_addr.s_addr = INADDR_ANY; + addrlen = sizeof(struct sockaddr_in); + } + /* IPv6 */ + else if (server_res->ai_family == AF_INET6) { + struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl; + lcladdr->sin6_family = AF_INET6; + lcladdr->sin6_port = htons(local_port); + lcladdr->sin6_addr = in6addr_any; + addrlen = sizeof(struct sockaddr_in6); + } + /* Unknown protocol */ + else { + errno = EAFNOSUPPORT; + return -1; + } + + if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + return -1; + } + } ((struct sockaddr_in *) server_res->ai_addr)->sin_port = htons(port); if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && errno != EINPROGRESS) {