From a6b3f26be128cbeaccb736151e2d864a1a63ee61 Mon Sep 17 00:00:00 2001 From: Jef Poskanzer Date: Fri, 29 Mar 2013 17:49:27 -0700 Subject: [PATCH] Dual-stack operation - IPv4 and IP46 in the same server. Also added -4 flag to force IPv4. --- src/iperf.h | 1 - src/iperf3.1 | 5 ++- src/iperf_api.c | 30 +++++++++--------- src/iperf_tcp.c | 82 +++++++++++++++++++++++++++---------------------- src/locale.c | 3 +- src/net.c | 74 +++++++++++++++++++++++++------------------- src/net.h | 1 + 7 files changed, 111 insertions(+), 85 deletions(-) diff --git a/src/iperf.h b/src/iperf.h index 751ded1..5b0437d 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -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 */ diff --git a/src/iperf3.1 b/src/iperf3.1 index 6b825a4..b3beab2 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -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' diff --git a/src/iperf_api.c b/src/iperf_api.c index e10ac34..540b9fb 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -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; diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c index 5c72ddc..00d3843 100644 --- a/src/iperf_tcp.c +++ b/src/iperf_tcp.c @@ -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) { diff --git a/src/locale.c b/src/locale.c index d94daf3..40e4fc0 100644 --- a/src/locale.c +++ b/src/locale.c @@ -94,7 +94,8 @@ const char usage_longstr[] = "Usage: iperf [-s|-c host] [options]\n" " -B, --bind 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" diff --git a/src/net.c b/src/net.c index 665430b..7b69a60 100644 --- a/src/net.c +++ b/src/net.c @@ -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; +} diff --git a/src/net.h b/src/net.h index 48e9275..e4070c4 100644 --- a/src/net.h +++ b/src/net.h @@ -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