Dual-stack operation - IPv4 and IP46 in the same server.

Also added -4 flag to force IPv4.
This commit is contained in:
Jef Poskanzer 2013-03-29 17:49:27 -07:00
parent 5bafe6e6cf
commit a6b3f26be1
7 changed files with 111 additions and 85 deletions

View File

@ -142,7 +142,6 @@ struct iperf_test
int no_delay; /* -N option */
int output_format; /* -O option */
int reverse; /* -R option */
int v6domain; /* -6 option */
int verbose; /* -V option - verbose mode */
int json_output; /* -J option - JSON output */
int zerocopy; /* -Z option - use sendfile */

View File

@ -86,8 +86,11 @@ set TCP maximum segment size (MTU - 40 bytes)
.BR -N ", " --no-delay " "
set TCP no delay, disabling Nagle's Algorithm
.TP
.BR -4 ", " --version4 " "
only use IPv4
.TP
.BR -6 ", " --version6 " "
use IPv6
only use IPv6
.TP
.BR -S ", " --tos " \fIn\fR"
set the IP 'type of service'

View File

@ -327,7 +327,7 @@ iperf_on_connect(struct iperf_test *test)
struct sockaddr_in *sa_inP;
struct sockaddr_in6 *sa_in6P;
socklen_t len;
int domain, opt;
int opt;
if (test->role == 'c') {
if (test->json_output)
@ -335,12 +335,11 @@ iperf_on_connect(struct iperf_test *test)
else
printf("Connecting to host %s, port %d\n", test->server_hostname, test->server_port);
} else {
domain = test->settings->domain;
len = sizeof(sa);
getpeername(test->ctrl_sck, (struct sockaddr *) &sa, &len);
if (domain == AF_INET) {
if (getsockdomain(test->ctrl_sck) == AF_INET) {
sa_inP = (struct sockaddr_in *) &sa;
inet_ntop(domain, &sa_inP->sin_addr, ipr, sizeof(ipr));
inet_ntop(AF_INET, &sa_inP->sin_addr, ipr, sizeof(ipr));
port = ntohs(sa_inP->sin_port);
if (test->json_output)
cJSON_AddItemToObject(test->json_start, "accepted_connection", iperf_json_printf("host: %s port: %d", ipr, (int64_t) port));
@ -348,7 +347,7 @@ iperf_on_connect(struct iperf_test *test)
printf("Accepted connection from %s, port %d\n", ipr, port);
} else {
sa_in6P = (struct sockaddr_in6 *) &sa;
inet_ntop(domain, &sa_in6P->sin6_addr, ipr, sizeof(ipr));
inet_ntop(AF_INET6, &sa_in6P->sin6_addr, ipr, sizeof(ipr));
port = ntohs(sa_in6P->sin6_port);
if (test->json_output)
cJSON_AddItemToObject(test->json_start, "accepted_connection", iperf_json_printf("host: %s port: %d", ipr, (int64_t) port));
@ -434,7 +433,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
blksize = 0;
server_flag = client_flag = 0;
while ((ch = getopt_long(argc, argv, "p:f:i:DVJdvsc:ub:t:n:l:P:Rw:B:M:N6S:Zh", longopts, NULL)) != -1) {
while ((ch = getopt_long(argc, argv, "p:f:i:DVJdvsc:ub:t:n:l:P:Rw:B:M:N46S:Zh", longopts, NULL)) != -1) {
switch (ch) {
case 'p':
test->server_port = atoi(optarg);
@ -546,6 +545,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
test->no_delay = 1;
client_flag = 1;
break;
case '4':
test->settings->domain = AF_INET;
break;
case '6':
test->settings->domain = AF_INET6;
break;
@ -1113,16 +1115,16 @@ void
connect_msg(struct iperf_stream *sp)
{
char ipl[INET6_ADDRSTRLEN], ipr[INET6_ADDRSTRLEN];
int lport, rport, domain = sp->settings->domain;
int lport, rport;
if (domain == AF_INET) {
inet_ntop(domain, (void *) &((struct sockaddr_in *) &sp->local_addr)->sin_addr, ipl, sizeof(ipl));
inet_ntop(domain, (void *) &((struct sockaddr_in *) &sp->remote_addr)->sin_addr, ipr, sizeof(ipr));
if (getsockdomain(sp->socket) == AF_INET) {
inet_ntop(AF_INET, (void *) &((struct sockaddr_in *) &sp->local_addr)->sin_addr, ipl, sizeof(ipl));
inet_ntop(AF_INET, (void *) &((struct sockaddr_in *) &sp->remote_addr)->sin_addr, ipr, sizeof(ipr));
lport = ntohs(((struct sockaddr_in *) &sp->local_addr)->sin_port);
rport = ntohs(((struct sockaddr_in *) &sp->remote_addr)->sin_port);
} else {
inet_ntop(domain, (void *) &((struct sockaddr_in6 *) &sp->local_addr)->sin6_addr, ipl, sizeof(ipl));
inet_ntop(domain, (void *) &((struct sockaddr_in6 *) &sp->remote_addr)->sin6_addr, ipr, sizeof(ipr));
inet_ntop(AF_INET6, (void *) &((struct sockaddr_in6 *) &sp->local_addr)->sin6_addr, ipl, sizeof(ipl));
inet_ntop(AF_INET6, (void *) &((struct sockaddr_in6 *) &sp->remote_addr)->sin6_addr, ipr, sizeof(ipr));
lport = ntohs(((struct sockaddr_in6 *) &sp->local_addr)->sin6_port);
rport = ntohs(((struct sockaddr_in6 *) &sp->remote_addr)->sin6_port);
}
@ -1171,7 +1173,7 @@ iperf_defaults(struct iperf_test *testp)
testp->reporter_interval = 0;
testp->num_streams = 1;
testp->settings->domain = AF_INET;
testp->settings->domain = AF_UNSPEC;
testp->settings->unit_format = 'a';
testp->settings->socket_bufsize = 0; /* use autotuning */
testp->settings->blksize = DEFAULT_TCP_BLKSIZE;
@ -1768,7 +1770,7 @@ iperf_init_stream(struct iperf_stream *sp, struct iperf_test *test)
}
/* Set IP TOS */
if ((opt = test->settings->tos)) {
if (test->settings->domain == AF_INET6) {
if (getsockdomain(sp->socket) == AF_INET6) {
#ifdef IPV6_TCLASS
if (setsockopt(sp->socket, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt)) < 0) {
i_errno = IESETCOS;

View File

@ -114,18 +114,31 @@ iperf_tcp_accept(struct iperf_test * test)
int
iperf_tcp_listen(struct iperf_test *test)
{
int s, opt;
struct addrinfo hints, *res;
char portstr[6];
int s, opt;
s = test->listener;
if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) {
FD_CLR(s, &test->read_set);
close(s);
if ((s = socket(test->settings->domain, SOCK_STREAM, 0)) < 0) {
snprintf(portstr, 6, "%d", test->server_port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = (test->settings->domain == AF_UNSPEC ? AF_INET6 : test->settings->domain);
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(test->bind_address, portstr, &hints, &res) != 0) {
i_errno = IESTREAMLISTEN;
return -1;
}
if ((s = socket(res->ai_family, SOCK_STREAM, 0)) < 0) {
i_errno = IESTREAMLISTEN;
return -1;
}
if (test->no_delay) {
opt = 1;
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
@ -155,17 +168,13 @@ iperf_tcp_listen(struct iperf_test *test)
i_errno = IEREUSEADDR;
return -1;
}
snprintf(portstr, 6, "%d", test->server_port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = test->settings->domain;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
// XXX: Check getaddrinfo for errors!
if (getaddrinfo(test->bind_address, portstr, &hints, &res) != 0) {
i_errno = IESTREAMLISTEN;
return -1;
}
if (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET6) {
if (test->settings->domain == AF_UNSPEC)
opt = 0;
else if (test->settings->domain == AF_INET6)
opt = 1;
setsockopt(s, SOL_SOCKET, IPV6_V6ONLY, (char *) &opt, sizeof(opt));
}
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
close(s);
@ -194,31 +203,40 @@ iperf_tcp_listen(struct iperf_test *test)
int
iperf_tcp_connect(struct iperf_test *test)
{
int s, opt;
struct addrinfo hints, *res;
struct addrinfo hints, *local_res, *server_res;
char portstr[6];
if ((s = socket(test->settings->domain, SOCK_STREAM, 0)) < 0) {
i_errno = IESTREAMCONNECT;
return -1;
}
int s, opt;
if (test->bind_address) {
memset(&hints, 0, sizeof(hints));
hints.ai_family = test->settings->domain;
hints.ai_socktype = SOCK_STREAM;
// XXX: Check getaddrinfo for errors!
if (getaddrinfo(test->bind_address, NULL, &hints, &res) != 0) {
if (getaddrinfo(test->bind_address, NULL, &hints, &local_res) != 0) {
i_errno = IESTREAMCONNECT;
return -1;
}
}
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
memset(&hints, 0, sizeof(hints));
hints.ai_family = test->settings->domain;
hints.ai_socktype = SOCK_STREAM;
snprintf(portstr, sizeof(portstr), "%d", test->server_port);
if (getaddrinfo(test->server_hostname, portstr, &hints, &server_res) != 0) {
i_errno = IESTREAMCONNECT;
return -1;
}
if ((s = socket(server_res->ai_family, SOCK_STREAM, 0)) < 0) {
i_errno = IESTREAMCONNECT;
return -1;
}
if (test->bind_address) {
if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
i_errno = IESTREAMCONNECT;
return -1;
}
freeaddrinfo(res);
freeaddrinfo(local_res);
}
/* Set socket options */
@ -246,22 +264,12 @@ iperf_tcp_connect(struct iperf_test *test)
}
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = test->settings->domain;
hints.ai_socktype = SOCK_STREAM;
snprintf(portstr, 6, "%d", test->server_port);
// XXX: Check getaddrinfo for errors!
if (getaddrinfo(test->server_hostname, portstr, &hints, &res) != 0) {
if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
i_errno = IESTREAMCONNECT;
return -1;
}
if (connect(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0 && errno != EINPROGRESS) {
i_errno = IESTREAMCONNECT;
return -1;
}
freeaddrinfo(res);
freeaddrinfo(server_res);
/* Send cookie for verification */
if (Nwrite(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) {

View File

@ -94,7 +94,8 @@ const char usage_longstr[] = "Usage: iperf [-s|-c host] [options]\n"
" -B, --bind <host> bind to a specific interface or multicast address\n"
" -M, --set-mss # set TCP maximum segment size (MTU - 40 bytes)\n"
" -N, --nodelay set TCP no delay, disabling Nagle's Algorithm\n"
" -6, --version6 use IPv6\n"
" -4, --version4 only use IPv4\n"
" -6, --version6 only use IPv6\n"
" -S, --tos N set the IP 'type of service'\n"
" -Z, --zerocopy use a 'zero copy' method of sending data\n"

View File

@ -44,45 +44,38 @@
int
netdial(int domain, int proto, char *local, char *server, int port)
{
struct addrinfo hints, *local_res, *server_res;
int s;
struct addrinfo hints, *res;
s = socket(domain, proto, 0);
if (s < 0) {
return -1;
}
if (local) {
memset(&hints, 0, sizeof(hints));
hints.ai_family = domain;
hints.ai_socktype = proto;
// XXX: Check getaddrinfo for errors!
if (getaddrinfo(local, NULL, &hints, &res) != 0)
if (getaddrinfo(local, NULL, &hints, &local_res) != 0)
return -1;
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0)
return -1;
freeaddrinfo(res);
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = domain;
hints.ai_socktype = proto;
// XXX: Check getaddrinfo for errors!
if (getaddrinfo(server, NULL, &hints, &res) != 0)
if (getaddrinfo(server, NULL, &hints, &server_res) != 0)
return -1;
((struct sockaddr_in *) res->ai_addr)->sin_port = htons(port);
if (connect(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0 && errno != EINPROGRESS) {
s = socket(server_res->ai_family, proto, 0);
if (s < 0)
return -1;
if (local) {
if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0)
return -1;
freeaddrinfo(local_res);
}
freeaddrinfo(res);
((struct sockaddr_in *) server_res->ai_addr)->sin_port = htons(port);
if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS)
return -1;
freeaddrinfo(server_res);
return s;
}
@ -91,26 +84,32 @@ netdial(int domain, int proto, char *local, char *server, int port)
int
netannounce(int domain, int proto, char *local, int port)
{
int s, opt;
struct addrinfo hints, *res;
char portstr[6];
s = socket(domain, proto, 0);
if (s < 0) {
return -1;
}
opt = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt));
int s, opt;
snprintf(portstr, 6, "%d", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = domain;
hints.ai_family = (domain == AF_UNSPEC ? AF_INET6 : domain);
hints.ai_socktype = proto;
hints.ai_flags = AI_PASSIVE;
// XXX: Check getaddrinfo for errors!
if (getaddrinfo(local, portstr, &hints, &res) != 0)
return -1;
s = socket(res->ai_family, proto, 0);
if (s < 0)
return -1;
opt = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt));
if (domain == AF_UNSPEC || domain == AF_INET6) {
if (domain == AF_UNSPEC)
opt = 0;
else if (domain == AF_INET6)
opt = 1;
setsockopt(s, SOL_SOCKET, IPV6_V6ONLY, (char *) &opt, sizeof(opt));
}
if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
close(s);
return -1;
@ -357,3 +356,16 @@ setnonblocking(int fd, int nonblocking)
}
return 0;
}
/****************************************************************************/
int
getsockdomain(int sock)
{
struct sockaddr sa;
socklen_t len;
if (getsockname(sock, &sa, &len) < 0)
return -1;
return sa.sa_family;
}

View File

@ -19,6 +19,7 @@ int Nsendfile(int fromfd, int tofd, const char *buf, size_t count) /* __attribut
int getsock_tcp_mss(int inSock);
int set_tcp_options(int sock, int no_delay, int mss);
int setnonblocking(int fd, int nonblocking);
int getsockdomain(int sock);
#define NET_SOFTERROR -1
#define NET_HARDERROR -2