#include "iperf_config.h"
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <assert.h>
#include <netdb.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#if defined(HAVE_UDP_SEGMENT) || defined(HAVE_UDP_GRO)
#include <linux/udp.h>
#endif
#ifdef HAVE_SENDFILE
#ifdef linux
#include <sys/sendfile.h>
#else
#ifdef __FreeBSD__
#include <sys/uio.h>
#else
#if defined(__APPLE__) && defined(__MACH__)
#include <AvailabilityMacros.h>
#if defined(MAC_OS_X_VERSION_10_6)
#include <sys/uio.h>
#endif
#endif
#endif
#endif
#endif
#ifdef HAVE_IP_BOUND_IF
#include <netinet/in.h>
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#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;
extern int gerror;
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);
}
int
bind_to_device(int s, int domain, const char *bind_dev)
{
#if defined(HAVE_SO_BINDTODEVICE)
return setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, bind_dev, IFNAMSIZ);
#elif defined(HAVE_IP_BOUND_IF)
int opt;
switch (domain) {
case IPPROTO_IP:
opt = IP_BOUND_IF;
break;
case IPPROTO_IPV6:
opt = IPV6_BOUND_IF;
break;
default:
errno = ENOTSUP;
return -1;
}
int index = if_nametoindex(bind_dev);
if (index == 0) {
return -1;
}
return setsockopt(s, domain, opt, &index, sizeof(index));
#else
errno = ENOTSUP;
return -1;
#endif
}
int
create_socket(int domain, int type, 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 = NULL, *server_res = NULL;
int s, saved_errno;
char portstr[6];
if (local) {
memset(&hints, 0, sizeof(hints));
hints.ai_family = domain;
hints.ai_socktype = type;
if ((gerror = getaddrinfo(local, NULL, &hints, &local_res)) != 0)
return -1;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = domain;
hints.ai_socktype = type;
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, type, proto);
if (s < 0) {
if (local)
freeaddrinfo(local_res);
freeaddrinfo(server_res);
return -1;
}
if (bind_dev) {
if (bind_to_device(s, domain, bind_dev) < 0) {
saved_errno = errno;
close(s);
freeaddrinfo(local_res);
freeaddrinfo(server_res);
errno = saved_errno;
return -1;
}
}
if (local) {
if (local_port) {
struct sockaddr_in *lcladdr;
lcladdr = (struct sockaddr_in *)local_res->ai_addr;
lcladdr->sin_port = htons(local_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;
return -1;
}
freeaddrinfo(local_res);
}
else if (local_port) {
size_t addrlen;
struct sockaddr_storage lcl;
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);
}
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);
}
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;
}
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, 0, 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);
freeaddrinfo(server_res);
errno = saved_errno;
return -1;
}
freeaddrinfo(server_res);
return s;
}
int
netannounce(int domain, int proto, const char *local, const char *bind_dev, int port)
{
struct addrinfo hints, *res;
char portstr[6];
int s, opt, saved_errno;
snprintf(portstr, 6, "%d", port);
memset(&hints, 0, sizeof(hints));
if (domain == AF_UNSPEC && !local) {
hints.ai_family = AF_INET6;
}
else {
hints.ai_family = domain;
}
hints.ai_socktype = proto;
hints.ai_flags = AI_PASSIVE;
if ((gerror = getaddrinfo(local, portstr, &hints, &res)) != 0)
return -1;
s = socket(res->ai_family, proto, 0);
if (s < 0) {
freeaddrinfo(res);
return -1;
}
if (bind_dev) {
#if defined(HAVE_SO_BINDTODEVICE)
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
bind_dev, IFNAMSIZ) < 0)
#endif {
saved_errno = errno;
close(s);
freeaddrinfo(res);
errno = saved_errno;
return -1;
}
}
opt = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *) &opt, sizeof(opt)) < 0) {
saved_errno = errno;
close(s);
freeaddrinfo(res);
errno = saved_errno;
return -1;
}
#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
if (res->ai_family == AF_INET6 && (domain == AF_UNSPEC || domain == AF_INET6)) {
if (domain == AF_UNSPEC)
opt = 0;
else
opt = 1;
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(char *) &opt, sizeof(opt)) < 0) {
saved_errno = errno;
close(s);
freeaddrinfo(res);
errno = saved_errno;
return -1;
}
}
#endif
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
saved_errno = errno;
close(s);
freeaddrinfo(res);
errno = saved_errno;
return -1;
}
freeaddrinfo(res);
if (proto == SOCK_STREAM) {
if (listen(s, INT_MAX) < 0) {
saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
}
return s;
}
int
Nread(int fd, char *buf, size_t count, int prot)
{
return Nrecv(fd, buf, count, prot, 0);
}
int
Nrecv(int fd, char *buf, size_t count, int prot, int sock_opt)
{
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_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) {
if (sock_opt)
r = recv(fd, buf, nleft, sock_opt);
else
r = read(fd, buf, nleft);
if (r < 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
break;
else
return NET_HARDERROR;
} else if (r == 0)
break;
if (sock_opt & MSG_TRUNC) {
size_t bytes_copied = (r > nleft)? nleft: r;
nleft -= bytes_copied;
buf += bytes_copied;
}
else {
nleft -= r;
buf += r;
}
if (nleft > 0) {
struct iperf_time now;
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;
}
int
Nread_no_select(int fd, char *buf, size_t count, int prot)
{
return Nrecv_no_select(fd, buf, count, prot, 0);
}
int
Nrecv_no_select(int fd, char *buf, size_t count, int prot, int sock_opt)
{
register ssize_t r;
register size_t nleft = count;
while (nleft > 0) {
if (sock_opt)
r = recv(fd, buf, nleft, sock_opt);
else
r = read(fd, buf, nleft);
if (r < 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
break;
else
return NET_HARDERROR;
} else if (r == 0)
break;
if (sock_opt & MSG_TRUNC) {
size_t bytes_copied = (r > nleft)? nleft: r;
nleft -= bytes_copied;
buf += bytes_copied;
}
else {
nleft -= r;
buf += r;
}
}
return count - nleft;
}
#ifdef HAVE_UDP_GRO
static int recv_msg_gro(int fd, char *buf, int len, int *gso_size)
{
char control[CMSG_SPACE(sizeof(uint16_t))] = {0};
struct msghdr msg = {0};
struct iovec iov = {0};
struct cmsghdr *cmsg;
uint16_t *gsosizeptr;
int ret;
if (!buf || len <= 0 || !gso_size) {
return -1;
}
iov.iov_base = buf;
iov.iov_len = len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
*gso_size = -1;
ret = recvmsg(fd, &msg, MSG_DONTWAIT);
if (ret > 0) {
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_UDP && cmsg->cmsg_type == UDP_GRO) {
if (cmsg->cmsg_len >= CMSG_LEN(sizeof(uint16_t))) {
gsosizeptr = (uint16_t *) CMSG_DATA(cmsg);
*gso_size = *gsosizeptr;
if (*gso_size <= 0 || *gso_size > len) {
*gso_size = -1;
}
}
break;
}
}
}
return ret;
}
int
Nread_gro(int fd, char *buf, size_t count, int prot, int *dgram_sz)
{
register ssize_t r;
if (!buf || count <= 0 || !dgram_sz) {
return NET_HARDERROR;
}
if (count > MAX_UDP_BLOCKSIZE) {
count = MAX_UDP_BLOCKSIZE;
}
r = recv_msg_gro(fd, buf, count, dgram_sz);
if (r < 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
return 0;
} else {
return NET_HARDERROR;
}
}
if (r > 0 && *dgram_sz > 0 && *dgram_sz > r) {
*dgram_sz = r;
}
return r;
}
#else
int
Nread_gro(int fd, char *buf, size_t count, int prot, int *dgram_sz)
{
return NET_HARDERROR;
}
#endif
int
Nwrite(int fd, const char *buf, size_t count, int prot)
{
register ssize_t r;
register size_t nleft = count;
while (nleft > 0) {
r = write(fd, buf, nleft);
if (r < 0) {
switch (errno) {
case EINTR:
case EAGAIN:
#if (EAGAIN != EWOULDBLOCK)
case EWOULDBLOCK:
#endif
if (count == nleft)
return NET_SOFTERROR;
return count - nleft;
case ENOBUFS :
return NET_SOFTERROR;
default:
return NET_HARDERROR;
}
} else if (r == 0)
return NET_SOFTERROR;
nleft -= r;
buf += r;
}
return count;
}
#ifdef HAVE_UDP_SEGMENT
static void udp_msg_gso(struct cmsghdr *cm, uint16_t gso_size)
{
uint16_t *valp;
cm->cmsg_level = IPPROTO_UDP;
cm->cmsg_type = UDP_SEGMENT;
cm->cmsg_len = CMSG_LEN(sizeof(gso_size));
valp = (void *) CMSG_DATA(cm);
*valp = gso_size;
}
static int udp_sendmsg_gso(int fd, const char *buf, size_t count, uint16_t gso_size)
{
char control[CMSG_SPACE(sizeof(gso_size))] = {0};
struct msghdr msg = {0};
struct iovec iov = {0};
size_t msg_controllen;
struct cmsghdr *cmsg;
int ret;
iov.iov_base = (void *) buf;
iov.iov_len = count;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
cmsg = CMSG_FIRSTHDR(&msg);
udp_msg_gso(cmsg, gso_size);
msg_controllen = CMSG_SPACE(sizeof(gso_size));
msg.msg_controllen = msg_controllen;
ret = sendmsg(fd, &msg, 0);
return ret;
}
int
Nwrite_gso(int fd, const char *buf, size_t count, int prot, uint16_t gso_size)
{
register ssize_t r;
r = udp_sendmsg_gso(fd, buf, count, gso_size);
if (r < 0) {
switch (errno) {
case EINTR:
case EAGAIN:
#if (EAGAIN != EWOULDBLOCK)
case EWOULDBLOCK:
#endif
return 0;
case ENOBUFS:
return NET_SOFTERROR;
default:
return NET_HARDERROR;
}
}
return r;
}
#else
int
Nwrite_gso(int fd, const char *buf, size_t count, int prot, uint16_t gso_size)
{
return NET_HARDERROR;
}
#endif
int
has_sendfile(void)
{
#if defined(HAVE_SENDFILE)
return 1;
#else
return 0;
#endif
}
int
Nsendfile(int fromfd, int tofd, const char *buf, size_t count)
{
#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
register size_t nleft;
register ssize_t r;
nleft = count;
while (nleft > 0) {
offset = count - nleft;
#ifdef linux
r = sendfile(tofd, fromfd, &offset, nleft);
if (r > 0)
nleft -= r;
#elif defined(__FreeBSD__)
r = sendfile(fromfd, tofd, offset, nleft, NULL, &sent, 0);
nleft -= sent;
#elif defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6)
sent = nleft;
r = sendfile(fromfd, tofd, offset, &sent, NULL, 0);
nleft -= sent;
#else
r = -1;
errno = ENOSYS;
#endif
if (r < 0) {
switch (errno) {
case EINTR:
case EAGAIN:
#if (EAGAIN != EWOULDBLOCK)
case EWOULDBLOCK:
#endif
if (count == nleft)
return NET_SOFTERROR;
return count - nleft;
case ENOBUFS:
case ENOMEM:
return NET_SOFTERROR;
default:
return NET_HARDERROR;
}
}
#ifdef linux
else if (r == 0)
return NET_SOFTERROR;
#endif
}
return count;
#else
errno = ENOSYS;
return NET_HARDERROR;
#endif
}
int
setnonblocking(int fd, int nonblocking)
{
int flags, newflags;
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
perror("fcntl(F_GETFL)");
return -1;
}
if (nonblocking)
newflags = flags | (int) O_NONBLOCK;
else
newflags = flags & ~((int) O_NONBLOCK);
if (newflags != flags)
if (fcntl(fd, F_SETFL, newflags) < 0) {
perror("fcntl(F_SETFL)");
return -1;
}
return 0;
}
int
getsockdomain(int sock)
{
struct sockaddr_storage sa;
socklen_t len = sizeof(sa);
if (getsockname(sock, (struct sockaddr *)&sa, &len) < 0) {
return -1;
}
return ((struct sockaddr *) &sa)->sa_family;
}
void
iperf_sync_close_socket(int sock)
{
#ifdef HAVE_SOCKET_SHUTDOWN_SHUT_WR
char buffer[128];
shutdown(sock, SHUT_WR); while (Nread(sock, buffer, sizeof(buffer), 0) > 0); #else
sleep(1); #endif close(sock);
}