version 1.1.1.1, 2016/10/18 13:28:18
|
version 1.1.1.3, 2023/09/27 11:14:54
|
Line 1
|
Line 1
|
/* |
/* |
* iperf, Copyright (c) 2014, 2015, The Regents of the University of | * iperf, Copyright (c) 2014-2019, The Regents of the University of |
* California, through Lawrence Berkeley National Laboratory (subject |
* California, through Lawrence Berkeley National Laboratory (subject |
* to receipt of any required approvals from the U.S. Dept. of |
* to receipt of any required approvals from the U.S. Dept. of |
* Energy). All rights reserved. |
* Energy). All rights reserved. |
Line 29
|
Line 29
|
#include <stdio.h> |
#include <stdio.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <errno.h> |
#include <errno.h> |
|
#include <arpa/inet.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/errno.h> |
|
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <netinet/tcp.h> |
|
#include <assert.h> |
#include <assert.h> |
#include <netdb.h> |
#include <netdb.h> |
#include <string.h> |
#include <string.h> |
#include <sys/fcntl.h> | #include <fcntl.h> |
| #include <limits.h> |
|
|
#ifdef HAVE_SENDFILE |
#ifdef HAVE_SENDFILE |
#ifdef linux |
#ifdef linux |
Line 56
|
Line 56
|
#endif |
#endif |
#endif /* HAVE_SENDFILE */ |
#endif /* HAVE_SENDFILE */ |
|
|
|
#ifdef HAVE_POLL_H |
|
#include <poll.h> |
|
#endif /* HAVE_POLL_H */ |
|
|
|
#include "iperf.h" |
#include "iperf_util.h" |
#include "iperf_util.h" |
#include "net.h" |
#include "net.h" |
#include "timer.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 |
|
* presence of this declaration is a sign we need to revisit this layering. |
|
*/ |
|
extern int gerror; |
|
|
|
/* |
|
* timeout_connect adapted from netcat, via OpenBSD and FreeBSD |
|
* Copyright (c) 2001 Eric Jackson <ericj@monkey.org> |
|
*/ |
|
int |
|
timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, |
|
int timeout) |
|
{ |
|
struct pollfd pfd; |
|
socklen_t optlen; |
|
int flags, optval; |
|
int ret; |
|
|
|
flags = 0; |
|
if (timeout != -1) { |
|
flags = fcntl(s, F_GETFL, 0); |
|
if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) |
|
return -1; |
|
} |
|
|
|
if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) { |
|
pfd.fd = s; |
|
pfd.events = POLLOUT; |
|
if ((ret = poll(&pfd, 1, timeout)) == 1) { |
|
optlen = sizeof(optval); |
|
if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR, |
|
&optval, &optlen)) == 0) { |
|
errno = optval; |
|
ret = optval == 0 ? 0 : -1; |
|
} |
|
} else if (ret == 0) { |
|
errno = ETIMEDOUT; |
|
ret = -1; |
|
} else |
|
ret = -1; |
|
} |
|
|
|
if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1) |
|
ret = -1; |
|
|
|
return (ret); |
|
} |
|
|
/* netdial and netannouce code comes from libtask: http://swtch.com/libtask/ |
/* netdial and netannouce code comes from libtask: http://swtch.com/libtask/ |
* Copyright: http://swtch.com/libtask/COPYRIGHT |
* Copyright: http://swtch.com/libtask/COPYRIGHT |
*/ |
*/ |
|
|
/* make connection to server */ | /* create a socket */ |
int |
int |
netdial(int domain, int proto, char *local, int local_port, char *server, int port) | 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; | int s, saved_errno; |
| char portstr[6]; |
|
|
if (local) { |
if (local) { |
memset(&hints, 0, sizeof(hints)); |
memset(&hints, 0, sizeof(hints)); |
hints.ai_family = domain; |
hints.ai_family = domain; |
hints.ai_socktype = proto; |
hints.ai_socktype = proto; |
if (getaddrinfo(local, NULL, &hints, &local_res) != 0) | if ((gerror = getaddrinfo(local, NULL, &hints, &local_res)) != 0) |
return -1; |
return -1; |
} |
} |
|
|
memset(&hints, 0, sizeof(hints)); |
memset(&hints, 0, sizeof(hints)); |
hints.ai_family = domain; |
hints.ai_family = domain; |
hints.ai_socktype = proto; |
hints.ai_socktype = proto; |
if (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; |
return -1; |
|
} |
|
|
s = socket(server_res->ai_family, proto, 0); |
s = socket(server_res->ai_family, proto, 0); |
if (s < 0) { |
if (s < 0) { |
Line 93 netdial(int domain, int proto, char *local, int local_
|
Line 156 netdial(int domain, int proto, char *local, int local_
|
return -1; |
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) { |
if (local_port) { |
if (local_port) { |
struct sockaddr_in *lcladdr; |
struct sockaddr_in *lcladdr; |
lcladdr = (struct sockaddr_in *)local_res->ai_addr; |
lcladdr = (struct sockaddr_in *)local_res->ai_addr; |
lcladdr->sin_port = htons(local_port); |
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) { |
if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { |
|
saved_errno = errno; |
close(s); |
close(s); |
freeaddrinfo(local_res); |
freeaddrinfo(local_res); |
freeaddrinfo(server_res); |
freeaddrinfo(server_res); |
|
errno = saved_errno; |
return -1; |
return -1; |
} |
} |
freeaddrinfo(local_res); |
freeaddrinfo(local_res); |
} |
} |
|
/* No local name, but --cport given */ |
|
else if (local_port) { |
|
size_t addrlen; |
|
struct sockaddr_storage lcl; |
|
|
((struct sockaddr_in *) server_res->ai_addr)->sin_port = htons(port); | /* IPv4 */ |
if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) { | 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 { |
| close(s); |
| freeaddrinfo(server_res); |
| 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; |
| } |
| } |
| |
| *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); |
close(s); |
freeaddrinfo(server_res); |
freeaddrinfo(server_res); |
|
errno = saved_errno; |
return -1; |
return -1; |
} |
} |
|
|
Line 124 netdial(int domain, int proto, char *local, int local_
|
Line 258 netdial(int domain, int proto, char *local, int local_
|
/***************************************************************/ |
/***************************************************************/ |
|
|
int |
int |
netannounce(int domain, int proto, char *local, int port) | netannounce(int domain, int proto, const char *local, const char *bind_dev, int port) |
{ |
{ |
struct addrinfo hints, *res; |
struct addrinfo hints, *res; |
char portstr[6]; |
char portstr[6]; |
int s, opt; | int s, opt, saved_errno; |
|
|
snprintf(portstr, 6, "%d", port); |
snprintf(portstr, 6, "%d", port); |
memset(&hints, 0, sizeof(hints)); |
memset(&hints, 0, sizeof(hints)); |
/* | /* |
* If binding to the wildcard address with no explicit address |
* If binding to the wildcard address with no explicit address |
* family specified, then force us to get an AF_INET6 socket. On |
* family specified, then force us to get an AF_INET6 socket. On |
* CentOS 6 and MacOS, getaddrinfo(3) with AF_UNSPEC in ai_family, |
* CentOS 6 and MacOS, getaddrinfo(3) with AF_UNSPEC in ai_family, |
Line 152 netannounce(int domain, int proto, char *local, int po
|
Line 286 netannounce(int domain, int proto, char *local, int po
|
} |
} |
hints.ai_socktype = proto; |
hints.ai_socktype = proto; |
hints.ai_flags = AI_PASSIVE; |
hints.ai_flags = AI_PASSIVE; |
if (getaddrinfo(local, portstr, &hints, &res) != 0) | if ((gerror = getaddrinfo(local, portstr, &hints, &res)) != 0) |
return -1; | return -1; |
|
|
s = socket(res->ai_family, proto, 0); |
s = socket(res->ai_family, proto, 0); |
if (s < 0) { |
if (s < 0) { |
Line 161 netannounce(int domain, int proto, char *local, int po
|
Line 295 netannounce(int domain, int proto, char *local, int po
|
return -1; |
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; |
opt = 1; |
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, |
(char *) &opt, sizeof(opt)) < 0) { |
(char *) &opt, sizeof(opt)) < 0) { |
|
saved_errno = errno; |
close(s); |
close(s); |
freeaddrinfo(res); |
freeaddrinfo(res); |
|
errno = saved_errno; |
return -1; |
return -1; |
} |
} |
/* |
/* |
Line 182 netannounce(int domain, int proto, char *local, int po
|
Line 332 netannounce(int domain, int proto, char *local, int po
|
opt = 0; |
opt = 0; |
else |
else |
opt = 1; |
opt = 1; |
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, | if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, |
(char *) &opt, sizeof(opt)) < 0) { |
(char *) &opt, sizeof(opt)) < 0) { |
|
saved_errno = errno; |
close(s); |
close(s); |
freeaddrinfo(res); |
freeaddrinfo(res); |
|
errno = saved_errno; |
return -1; |
return -1; |
} |
} |
} |
} |
#endif /* IPV6_V6ONLY */ |
#endif /* IPV6_V6ONLY */ |
|
|
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) { |
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) { |
|
saved_errno = errno; |
close(s); |
close(s); |
freeaddrinfo(res); |
freeaddrinfo(res); |
|
errno = saved_errno; |
return -1; |
return -1; |
} |
} |
|
|
freeaddrinfo(res); |
freeaddrinfo(res); |
| |
if (proto == SOCK_STREAM) { |
if (proto == SOCK_STREAM) { |
if (listen(s, 5) < 0) { | if (listen(s, INT_MAX) < 0) { |
| saved_errno = errno; |
close(s); |
close(s); |
|
errno = saved_errno; |
return -1; |
return -1; |
} |
} |
} |
} |
Line 219 Nread(int fd, char *buf, size_t count, int prot)
|
Line 375 Nread(int fd, char *buf, size_t count, int prot)
|
{ |
{ |
register ssize_t r; |
register ssize_t r; |
register size_t nleft = count; |
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) { |
while (nleft > 0) { |
r = read(fd, buf, nleft); |
r = read(fd, buf, nleft); |
if (r < 0) { |
if (r < 0) { |
Line 232 Nread(int fd, char *buf, size_t count, int prot)
|
Line 414 Nread(int fd, char *buf, size_t count, int prot)
|
|
|
nleft -= r; |
nleft -= r; |
buf += 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; |
return count - nleft; |
} |
} |
Line 292 has_sendfile(void)
|
Line 507 has_sendfile(void)
|
int |
int |
Nsendfile(int fromfd, int tofd, const char *buf, size_t count) |
Nsendfile(int fromfd, int tofd, const char *buf, size_t count) |
{ |
{ |
off_t offset; |
|
#if defined(HAVE_SENDFILE) |
#if defined(HAVE_SENDFILE) |
|
off_t offset; |
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6)) |
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6)) |
off_t sent; |
off_t sent; |
#endif |
#endif |
Line 351 Nsendfile(int fromfd, int tofd, const char *buf, size_
|
Line 566 Nsendfile(int fromfd, int tofd, const char *buf, size_
|
} |
} |
|
|
/*************************************************************************/ |
/*************************************************************************/ |
|
|
/** |
|
* getsock_tcp_mss - Returns the MSS size for TCP |
|
* |
|
*/ |
|
|
|
int |
|
getsock_tcp_mss(int inSock) |
|
{ |
|
int mss = 0; |
|
|
|
int rc; |
|
socklen_t len; |
|
|
|
assert(inSock >= 0); /* print error and exit if this is not true */ |
|
|
|
/* query for mss */ |
|
len = sizeof(mss); |
|
rc = getsockopt(inSock, IPPROTO_TCP, TCP_MAXSEG, (char *)&mss, &len); |
|
if (rc == -1) { |
|
perror("getsockopt TCP_MAXSEG"); |
|
return -1; |
|
} |
|
|
|
return mss; |
|
} |
|
|
|
|
|
|
|
/*************************************************************/ |
|
|
|
/* sets TCP_NODELAY and TCP_MAXSEG if requested */ |
|
// XXX: This function is not being used. |
|
|
|
int |
|
set_tcp_options(int sock, int no_delay, int mss) |
|
{ |
|
socklen_t len; |
|
int rc; |
|
int new_mss; |
|
|
|
if (no_delay == 1) { |
|
len = sizeof(no_delay); |
|
rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&no_delay, len); |
|
if (rc == -1) { |
|
perror("setsockopt TCP_NODELAY"); |
|
return -1; |
|
} |
|
} |
|
#ifdef TCP_MAXSEG |
|
if (mss > 0) { |
|
len = sizeof(new_mss); |
|
assert(sock != -1); |
|
|
|
/* set */ |
|
new_mss = mss; |
|
len = sizeof(new_mss); |
|
rc = setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, (char *)&new_mss, len); |
|
if (rc == -1) { |
|
perror("setsockopt TCP_MAXSEG"); |
|
return -1; |
|
} |
|
/* verify results */ |
|
rc = getsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, (char *)&new_mss, &len); |
|
if (rc == -1) { |
|
perror("getsockopt TCP_MAXSEG"); |
|
return -1; |
|
} |
|
if (new_mss != mss) { |
|
perror("setsockopt value mismatch"); |
|
return -1; |
|
} |
|
} |
|
#endif |
|
return 0; |
|
} |
|
|
|
/****************************************************************************/ |
|
|
|
int |
int |
setnonblocking(int fd, int nonblocking) |
setnonblocking(int fd, int nonblocking) |