Merge pull request #1210 from esnet/issue-1157-take-2

Closes #1157.

Incorporates and closes #1180.
This commit is contained in:
Bruce A. Mah 2021-09-30 11:34:11 -07:00 committed by GitHub
commit 9149c64b9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 104 additions and 9 deletions

View File

@ -140,9 +140,12 @@ Note that when using this feature, a process will only be bound
to a single CPU (as opposed to a set containing potentially multiple
CPUs).
.TP
.BR -B ", " --bind " \fIhost\fR"
.BR -B ", " --bind " \fIhost\fR[\fB%\fIdev\fR]"
bind to the specific interface associated with address \fIhost\fR.
.BR --bind-dev " \fIdev\R"
If an optional interface is specified, it is treated as a shortcut
for \fB--bind-dev \fIdev\fR.
Note that a percent sign and interface device name are required for IPv6 link-local address literals.
.BR --bind-dev " \fIdev\fR"
bind to the specified network interface.
This option uses SO_BINDTODEVICE, and may require root permissions.
(Available on Linux and possibly other systems.)
@ -229,10 +232,13 @@ time skew threshold (in seconds) between the server and client
during the authentication process.
.SH "CLIENT SPECIFIC OPTIONS"
.TP
.BR -c ", " --client " \fIhost\fR"
.BR -c ", " --client " \fIhost\fR[\fB%\fIdev\fR]"
run in client mode, connecting to the specified server.
By default, a test consists of sending data from the client to the
server, unless the \-R flag is specified.
If an optional interface is specified, it is treated as a shortcut
for \fB--bind-dev \fIdev\fR.
Note that a percent sign and interface device name are required for IPv6 link-local address literals.
.TP
.BR --sctp
use SCTP rather than TCP (FreeBSD and Linux)

View File

@ -925,6 +925,54 @@ iperf_on_test_finish(struct iperf_test *test)
/******************************************************************************/
/*
* iperf_parse_hostname tries to split apart a string into hostname %
* interface parts, which are returned in **p and **p1, if they
* exist. If the %interface part is detected, and it's not an IPv6
* link local address, then returns 1, else returns 0.
*
* Modifies the string pointed to by spec in-place due to the use of
* strtok(3). The caller should strdup(3) or otherwise copy the string
* if an unmodified copy is needed.
*/
int
iperf_parse_hostname(struct iperf_test *test, char *spec, char **p, char **p1) {
struct in6_addr ipv6_addr;
// Format is <addr>[%<device>]
if ((*p = strtok(spec, "%")) != NULL &&
(*p1 = strtok(NULL, "%")) != NULL) {
/*
* If an IPv6 literal for a link-local address, then
* tell the caller to leave the "%" in the hostname.
*/
if (inet_pton(AF_INET6, *p, &ipv6_addr) == 1 &&
IN6_IS_ADDR_LINKLOCAL(&ipv6_addr)) {
if (test->debug) {
iperf_printf(test, "IPv6 link-local address literal detected\n");
}
return 0;
}
/*
* Other kind of address or FQDN. The interface name after
* "%" is a shorthand for --bind-dev.
*/
else {
if (test->debug) {
iperf_printf(test, "p %s p1 %s\n", *p, *p1);
}
return 1;
}
}
else {
if (test->debug) {
iperf_printf(test, "noparse\n");
}
return 0;
}
}
int
iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
{
@ -1019,6 +1067,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
char* comma;
#endif /* HAVE_CPU_AFFINITY */
char* slash;
char *p, *p1;
struct xbind_entry *xbe;
double farg;
int rcv_timeout_in = 0;
@ -1101,6 +1150,18 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
}
iperf_set_test_role(test, 'c');
iperf_set_test_server_hostname(test, optarg);
if (iperf_parse_hostname(test, optarg, &p, &p1)) {
#if defined(HAVE_SO_BINDTODEVICE)
/* Get rid of the hostname we saved earlier. */
free(iperf_get_test_server_hostname(test));
iperf_set_test_server_hostname(test, p);
iperf_set_test_bind_dev(test, p1);
#else /* HAVE_SO_BINDTODEVICE */
i_errno = IEBINDDEVNOSUPPORT;
return -1;
#endif /* HAVE_SO_BINDTODEVICE */
}
break;
case 'u':
set_protocol(test, Pudp);
@ -1212,12 +1273,25 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
test->settings->socket_bufsize = (int) farg;
client_flag = 1;
break;
case 'B':
test->bind_address = strdup(optarg);
iperf_set_test_bind_address(test, optarg);
if (iperf_parse_hostname(test, optarg, &p, &p1)) {
#if defined(HAVE_SO_BINDTODEVICE)
/* Get rid of the hostname we saved earlier. */
free(iperf_get_test_server_hostname(test));
iperf_set_test_server_hostname(test, p);
iperf_set_test_bind_dev(test, p1);
#else /* HAVE_SO_BINDTODEVICE */
i_errno = IEBINDDEVNOSUPPORT;
return -1;
#endif /* HAVE_SO_BINDTODEVICE */
}
break;
#if defined (HAVE_SO_BINDTODEVICE)
case OPT_BIND_DEV:
test->bind_dev = strdup(optarg);
iperf_set_test_bind_dev(test, optarg);
break;
#endif /* HAVE_SO_BINDTODEVICE */
case OPT_CLIENT_PORT:

View File

@ -427,7 +427,9 @@ enum {
IEAUTHTEST = 142, // Test authorization failed
IEBINDDEV = 143, // Unable to bind-to-device (check perror, maybe permissions?)
IENOMSG = 144, // No message was received for NO_MSG_RCVD_TIMEOUT time period
IESETDONTFRAGMENT = 145, // Unable to set IP Do-Not-Fragment
IESETDONTFRAGMENT = 145, // Unable to set IP Do-Not-Fragment
IEBINDDEVNOSUPPORT = 146, // `ip%%dev` is not supported as system does not support bind to device
IEHOSTDEV = 147, // host device name (ip%%<dev>) is supported (and required) only for IPv6 link-local address
/* Stream errors */
IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror)
IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror)

View File

@ -438,6 +438,15 @@ iperf_strerror(int int_errno)
case IEIDLETIMEOUT:
snprintf(errstr, len, "idle timeout parameter is not positive or larger than allowed limit");
break;
case IEBINDDEV:
snprintf(errstr, len, "Unable to bind-to-device (check perror, maybe permissions?)");
break;
case IEBINDDEVNOSUPPORT:
snprintf(errstr, len, "`<ip>%%<dev>` is not supported as system does not support bind to device");
break;
case IEHOSTDEV:
snprintf(errstr, len, "host device name (ip%%<dev>) is supported (and required) only for IPv6 link-local address");
break;
case IENOMSG:
snprintf(errstr, len, "idle timeout for receiving data");
break;

View File

@ -105,9 +105,12 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
#if defined(HAVE_CPU_AFFINITY)
" -A, --affinity n/n,m set CPU affinity\n"
#endif /* HAVE_CPU_AFFINITY */
" -B, --bind <host> bind to the interface associated with the address <host>\n"
#if defined(HAVE_SO_BINDTODEVICE)
" --bind-dev <dev> bind to the network interface with SO_BINDTODEVICE\n"
" -B, --bind <host>[%<dev>] bind to the interface associated with the address <host>\n"
" (optional <dev> equivalent to `--bind-dev <dev>`)\n"
" --bind-dev <dev> bind to the network interface with SO_BINDTODEVICE\n"
#else /* HAVE_SO_BINDTODEVICE */
" -B, --bind <host> bind to the interface associated with the address <host>\n"
#endif /* HAVE_SO_BINDTODEVICE */
" -V, --verbose more detailed output\n"
" -J, --json output in JSON format\n"
@ -139,7 +142,8 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" and client during the authentication process\n"
#endif //HAVE_SSL
"Client specific:\n"
" -c, --client <host> run in client mode, connecting to <host>\n"
" -c, --client <host>[%<dev>] run in client mode, connecting to <host>\n"
" (option <dev> equivalent to `--bind-dev <dev>`)\n"
#if defined(HAVE_SCTP_H)
" --sctp use SCTP rather than TCP\n"
" -X, --xbind <name> bind SCTP association to links\n"