--- embedaddon/iperf/src/net.c 2021/03/17 00:36:46 1.1.1.2 +++ embedaddon/iperf/src/net.c 2023/09/27 11:14:54 1.1.1.3 @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -61,10 +60,14 @@ #include #endif /* HAVE_POLL_H */ +#include "iperf.h" #include "iperf_util.h" #include "net.h" #include "timer.h" +static int nread_read_timeout = 10; +static int nread_overall_timeout = 30; + /* * Declaration of gerror in iperf_error.c. Most other files in iperf3 can get this * by including "iperf.h", but net.c lives "below" this layer. Clearly the @@ -119,12 +122,13 @@ timeout_connect(int s, const struct sockaddr *name, so * Copyright: http://swtch.com/libtask/COPYRIGHT */ -/* make connection to server */ +/* create a socket */ int -netdial(int domain, int proto, const char *local, int local_port, const char *server, int port, int timeout) +create_socket(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out) { - struct addrinfo hints, *local_res, *server_res; + struct addrinfo hints, *local_res = NULL, *server_res = NULL; int s, saved_errno; + char portstr[6]; if (local) { memset(&hints, 0, sizeof(hints)); @@ -137,8 +141,12 @@ netdial(int domain, int proto, const char *local, int memset(&hints, 0, sizeof(hints)); hints.ai_family = domain; hints.ai_socktype = proto; - if ((gerror = getaddrinfo(server, NULL, &hints, &server_res)) != 0) + snprintf(portstr, sizeof(portstr), "%d", port); + if ((gerror = getaddrinfo(server, portstr, &hints, &server_res)) != 0) { + if (local) + freeaddrinfo(local_res); return -1; + } s = socket(server_res->ai_family, proto, 0); if (s < 0) { @@ -148,6 +156,21 @@ netdial(int domain, int proto, const char *local, int return -1; } + if (bind_dev) { +#if defined(HAVE_SO_BINDTODEVICE) + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, + bind_dev, IFNAMSIZ) < 0) +#endif // HAVE_SO_BINDTODEVICE + { + saved_errno = errno; + close(s); + freeaddrinfo(local_res); + freeaddrinfo(server_res); + errno = saved_errno; + return -1; + } + } + /* Bind the local address if given a name (with or without --cport) */ if (local) { if (local_port) { @@ -189,6 +212,8 @@ netdial(int domain, int proto, const char *local, int } /* Unknown protocol */ else { + close(s); + freeaddrinfo(server_res); errno = EAFNOSUPPORT; return -1; } @@ -202,7 +227,22 @@ netdial(int domain, int proto, const char *local, int } } - ((struct sockaddr_in *) server_res->ai_addr)->sin_port = htons(port); + *server_res_out = server_res; + return s; +} + +/* make connection to server */ +int +netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout) +{ + struct addrinfo *server_res = NULL; + int s, saved_errno; + + s = create_socket(domain, proto, local, bind_dev, local_port, server, port, &server_res); + if (s < 0) { + return -1; + } + if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && errno != EINPROGRESS) { saved_errno = errno; close(s); @@ -218,7 +258,7 @@ netdial(int domain, int proto, const char *local, int /***************************************************************/ int -netannounce(int domain, int proto, const char *local, int port) +netannounce(int domain, int proto, const char *local, const char *bind_dev, int port) { struct addrinfo hints, *res; char portstr[6]; @@ -226,7 +266,7 @@ netannounce(int domain, int proto, const char *local, snprintf(portstr, 6, "%d", port); memset(&hints, 0, sizeof(hints)); - /* + /* * If binding to the wildcard address with no explicit address * family specified, then force us to get an AF_INET6 socket. On * CentOS 6 and MacOS, getaddrinfo(3) with AF_UNSPEC in ai_family, @@ -247,7 +287,7 @@ netannounce(int domain, int proto, const char *local, hints.ai_socktype = proto; hints.ai_flags = AI_PASSIVE; if ((gerror = getaddrinfo(local, portstr, &hints, &res)) != 0) - return -1; + return -1; s = socket(res->ai_family, proto, 0); if (s < 0) { @@ -255,8 +295,22 @@ netannounce(int domain, int proto, const char *local, return -1; } + if (bind_dev) { +#if defined(HAVE_SO_BINDTODEVICE) + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, + bind_dev, IFNAMSIZ) < 0) +#endif // HAVE_SO_BINDTODEVICE + { + saved_errno = errno; + close(s); + freeaddrinfo(res); + errno = saved_errno; + return -1; + } + } + opt = 1; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) { saved_errno = errno; close(s); @@ -278,7 +332,7 @@ netannounce(int domain, int proto, const char *local, opt = 0; else opt = 1; - if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt)) < 0) { saved_errno = errno; close(s); @@ -298,7 +352,7 @@ netannounce(int domain, int proto, const char *local, } freeaddrinfo(res); - + if (proto == SOCK_STREAM) { if (listen(s, INT_MAX) < 0) { saved_errno = errno; @@ -321,7 +375,33 @@ Nread(int fd, char *buf, size_t count, int prot) { register ssize_t r; register size_t nleft = count; + struct iperf_time ftimeout = { 0, 0 }; + fd_set rfdset; + struct timeval timeout = { nread_read_timeout, 0 }; + + /* + * fd might not be ready for reading on entry. Check for this + * (with timeout) first. + * + * This check could go inside the while() loop below, except we're + * currently considering whether it might make sense to support a + * codepath that bypassese this check, for situations where we + * already know that fd has data on it (for example if we'd gotten + * to here as the result of a select() call. + */ + { + FD_ZERO(&rfdset); + FD_SET(fd, &rfdset); + r = select(fd + 1, &rfdset, NULL, NULL, &timeout); + if (r < 0) { + return NET_HARDERROR; + } + if (r == 0) { + return 0; + } + } + while (nleft > 0) { r = read(fd, buf, nleft); if (r < 0) { @@ -334,6 +414,39 @@ Nread(int fd, char *buf, size_t count, int prot) nleft -= r; buf += r; + + /* + * We need some more bytes but don't want to wait around + * forever for them. In the case of partial results, we need + * to be able to read some bytes every nread_timeout seconds. + */ + if (nleft > 0) { + struct iperf_time now; + + /* + * Also, we have an approximate upper limit for the total time + * that a Nread call is supposed to take. We trade off accuracy + * of this timeout for a hopefully lower performance impact. + */ + iperf_time_now(&now); + if (ftimeout.secs == 0) { + ftimeout = now; + iperf_time_add_usecs(&ftimeout, nread_overall_timeout * 1000000L); + } + if (iperf_time_compare(&ftimeout, &now) < 0) { + break; + } + + FD_ZERO(&rfdset); + FD_SET(fd, &rfdset); + r = select(fd + 1, &rfdset, NULL, NULL, &timeout); + if (r < 0) { + return NET_HARDERROR; + } + if (r == 0) { + break; + } + } } return count - nleft; } @@ -394,8 +507,8 @@ has_sendfile(void) int Nsendfile(int fromfd, int tofd, const char *buf, size_t count) { - off_t offset; #if defined(HAVE_SENDFILE) + off_t offset; #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6)) off_t sent; #endif