enh: Support SO_BINDTODEVICE (#1097)
This lets iperf work better with multi-homed machines and VRF. Fixes #1089. Based on a patch by Ben Greear <greearb@candelatech.com> via PR #817. Co-authored-by: Ben Greear <greearb@candelatech.com>
Этот коммит содержится в:
родитель
d1260e6f94
Коммит
21581a7216
@ -1,4 +1,4 @@
|
|||||||
# Makefile.in generated by automake 1.16.2 from Makefile.am.
|
# Makefile.in generated by automake 1.16.3 from Makefile.am.
|
||||||
# @configure_input@
|
# @configure_input@
|
||||||
|
|
||||||
# Copyright (C) 1994-2020 Free Software Foundation, Inc.
|
# Copyright (C) 1994-2020 Free Software Foundation, Inc.
|
||||||
@ -204,6 +204,8 @@ am__relativize = \
|
|||||||
DIST_ARCHIVES = $(distdir).tar.gz
|
DIST_ARCHIVES = $(distdir).tar.gz
|
||||||
GZIP_ENV = --best
|
GZIP_ENV = --best
|
||||||
DIST_TARGETS = dist-gzip
|
DIST_TARGETS = dist-gzip
|
||||||
|
# Exists only to be overridden by the user if desired.
|
||||||
|
AM_DISTCHECK_DVI_TARGET = dvi
|
||||||
distuninstallcheck_listfiles = find . -type f -print
|
distuninstallcheck_listfiles = find . -type f -print
|
||||||
am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
|
am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
|
||||||
| sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
|
| sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
|
||||||
@ -629,7 +631,7 @@ distcheck: dist
|
|||||||
$(DISTCHECK_CONFIGURE_FLAGS) \
|
$(DISTCHECK_CONFIGURE_FLAGS) \
|
||||||
--srcdir=../.. --prefix="$$dc_install_base" \
|
--srcdir=../.. --prefix="$$dc_install_base" \
|
||||||
&& $(MAKE) $(AM_MAKEFLAGS) \
|
&& $(MAKE) $(AM_MAKEFLAGS) \
|
||||||
&& $(MAKE) $(AM_MAKEFLAGS) dvi \
|
&& $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \
|
||||||
&& $(MAKE) $(AM_MAKEFLAGS) check \
|
&& $(MAKE) $(AM_MAKEFLAGS) check \
|
||||||
&& $(MAKE) $(AM_MAKEFLAGS) install \
|
&& $(MAKE) $(AM_MAKEFLAGS) install \
|
||||||
&& $(MAKE) $(AM_MAKEFLAGS) installcheck \
|
&& $(MAKE) $(AM_MAKEFLAGS) installcheck \
|
||||||
|
13
aclocal.m4
поставляемый
13
aclocal.m4
поставляемый
@ -1,4 +1,4 @@
|
|||||||
# generated automatically by aclocal 1.16.2 -*- Autoconf -*-
|
# generated automatically by aclocal 1.16.3 -*- Autoconf -*-
|
||||||
|
|
||||||
# Copyright (C) 1996-2020 Free Software Foundation, Inc.
|
# Copyright (C) 1996-2020 Free Software Foundation, Inc.
|
||||||
|
|
||||||
@ -9059,7 +9059,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
|
|||||||
[am__api_version='1.16'
|
[am__api_version='1.16'
|
||||||
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
|
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
|
||||||
dnl require some minimum version. Point them to the right macro.
|
dnl require some minimum version. Point them to the right macro.
|
||||||
m4_if([$1], [1.16.2], [],
|
m4_if([$1], [1.16.3], [],
|
||||||
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
|
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -9075,7 +9075,7 @@ m4_define([_AM_AUTOCONF_VERSION], [])
|
|||||||
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
|
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
|
||||||
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
|
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
|
||||||
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
|
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
|
||||||
[AM_AUTOMAKE_VERSION([1.16.2])dnl
|
[AM_AUTOMAKE_VERSION([1.16.3])dnl
|
||||||
m4_ifndef([AC_AUTOCONF_VERSION],
|
m4_ifndef([AC_AUTOCONF_VERSION],
|
||||||
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
|
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
|
||||||
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
|
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
|
||||||
@ -9763,12 +9763,7 @@ AC_DEFUN([AM_MISSING_HAS_RUN],
|
|||||||
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
|
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
|
||||||
AC_REQUIRE_AUX_FILE([missing])dnl
|
AC_REQUIRE_AUX_FILE([missing])dnl
|
||||||
if test x"${MISSING+set}" != xset; then
|
if test x"${MISSING+set}" != xset; then
|
||||||
case $am_aux_dir in
|
MISSING="\${SHELL} '$am_aux_dir/missing'"
|
||||||
*\ * | *\ *)
|
|
||||||
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
|
|
||||||
*)
|
|
||||||
MISSING="\${SHELL} $am_aux_dir/missing" ;;
|
|
||||||
esac
|
|
||||||
fi
|
fi
|
||||||
# Use eval to expand $SHELL
|
# Use eval to expand $SHELL
|
||||||
if eval "$MISSING --is-lightweight"; then
|
if eval "$MISSING --is-lightweight"; then
|
||||||
|
38
configure
поставляемый
38
configure
поставляемый
@ -2531,12 +2531,7 @@ program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
|
|||||||
am_aux_dir=`cd "$ac_aux_dir" && pwd`
|
am_aux_dir=`cd "$ac_aux_dir" && pwd`
|
||||||
|
|
||||||
if test x"${MISSING+set}" != xset; then
|
if test x"${MISSING+set}" != xset; then
|
||||||
case $am_aux_dir in
|
MISSING="\${SHELL} '$am_aux_dir/missing'"
|
||||||
*\ * | *\ *)
|
|
||||||
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
|
|
||||||
*)
|
|
||||||
MISSING="\${SHELL} $am_aux_dir/missing" ;;
|
|
||||||
esac
|
|
||||||
fi
|
fi
|
||||||
# Use eval to expand $SHELL
|
# Use eval to expand $SHELL
|
||||||
if eval "$MISSING --is-lightweight"; then
|
if eval "$MISSING --is-lightweight"; then
|
||||||
@ -13950,6 +13945,37 @@ $as_echo "#define HAVE_SO_MAX_PACING_RATE 1" >>confdefs.h
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check for SO_BINDTODEVICE sockopt (believed to be Linux only)
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking SO_BINDTODEVICE socket option" >&5
|
||||||
|
$as_echo_n "checking SO_BINDTODEVICE socket option... " >&6; }
|
||||||
|
if ${iperf3_cv_header_so_bindtodevice+:} false; then :
|
||||||
|
$as_echo_n "(cached) " >&6
|
||||||
|
else
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#ifdef SO_BINDTODEVICE
|
||||||
|
yes
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_ACEOF
|
||||||
|
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
|
||||||
|
$EGREP "yes" >/dev/null 2>&1; then :
|
||||||
|
iperf3_cv_header_so_bindtodevice=yes
|
||||||
|
else
|
||||||
|
iperf3_cv_header_so_bindtodevice=no
|
||||||
|
fi
|
||||||
|
rm -f conftest*
|
||||||
|
|
||||||
|
fi
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $iperf3_cv_header_so_bindtodevice" >&5
|
||||||
|
$as_echo "$iperf3_cv_header_so_bindtodevice" >&6; }
|
||||||
|
if test "x$iperf3_cv_header_so_bindtodevice" = "xyes"; then
|
||||||
|
|
||||||
|
$as_echo "#define HAVE_SO_BINDTODEVICE 1" >>confdefs.h
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if we need -lrt for clock_gettime
|
# Check if we need -lrt for clock_gettime
|
||||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
|
||||||
$as_echo_n "checking for library containing clock_gettime... " >&6; }
|
$as_echo_n "checking for library containing clock_gettime... " >&6; }
|
||||||
|
13
configure.ac
13
configure.ac
@ -224,6 +224,19 @@ if test "x$iperf3_cv_header_so_max_pacing_rate" = "xyes"; then
|
|||||||
AC_DEFINE([HAVE_SO_MAX_PACING_RATE], [1], [Have SO_MAX_PACING_RATE sockopt.])
|
AC_DEFINE([HAVE_SO_MAX_PACING_RATE], [1], [Have SO_MAX_PACING_RATE sockopt.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check for SO_BINDTODEVICE sockopt (believed to be Linux only)
|
||||||
|
AC_CACHE_CHECK([SO_BINDTODEVICE socket option],
|
||||||
|
[iperf3_cv_header_so_bindtodevice],
|
||||||
|
AC_EGREP_CPP(yes,
|
||||||
|
[#include <sys/socket.h>
|
||||||
|
#ifdef SO_BINDTODEVICE
|
||||||
|
yes
|
||||||
|
#endif
|
||||||
|
],iperf3_cv_header_so_bindtodevice=yes,iperf3_cv_header_so_bindtodevice=no))
|
||||||
|
if test "x$iperf3_cv_header_so_bindtodevice" = "xyes"; then
|
||||||
|
AC_DEFINE([HAVE_SO_BINDTODEVICE], [1], [Have SO_BINDTODEVICE sockopt.])
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if we need -lrt for clock_gettime
|
# Check if we need -lrt for clock_gettime
|
||||||
AC_SEARCH_LIBS(clock_gettime, [rt posix4])
|
AC_SEARCH_LIBS(clock_gettime, [rt posix4])
|
||||||
# Check for clock_gettime support
|
# Check for clock_gettime support
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Makefile.in generated by automake 1.16.2 from Makefile.am.
|
# Makefile.in generated by automake 1.16.3 from Makefile.am.
|
||||||
# @configure_input@
|
# @configure_input@
|
||||||
|
|
||||||
# Copyright (C) 1994-2020 Free Software Foundation, Inc.
|
# Copyright (C) 1994-2020 Free Software Foundation, Inc.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Makefile.in generated by automake 1.16.2 from Makefile.am.
|
# Makefile.in generated by automake 1.16.3 from Makefile.am.
|
||||||
# @configure_input@
|
# @configure_input@
|
||||||
|
|
||||||
# Copyright (C) 1994-2020 Free Software Foundation, Inc.
|
# Copyright (C) 1994-2020 Free Software Foundation, Inc.
|
||||||
@ -479,6 +479,7 @@ am__set_TESTS_bases = \
|
|||||||
bases='$(TEST_LOGS)'; \
|
bases='$(TEST_LOGS)'; \
|
||||||
bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
|
bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
|
||||||
bases=`echo $$bases`
|
bases=`echo $$bases`
|
||||||
|
AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
|
||||||
RECHECK_LOGS = $(TEST_LOGS)
|
RECHECK_LOGS = $(TEST_LOGS)
|
||||||
AM_RECURSIVE_TARGETS = check recheck
|
AM_RECURSIVE_TARGETS = check recheck
|
||||||
TEST_SUITE_LOG = test-suite.log
|
TEST_SUITE_LOG = test-suite.log
|
||||||
@ -1565,7 +1566,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
|
|||||||
test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
|
test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
|
||||||
fi; \
|
fi; \
|
||||||
echo "$${col}$$br$${std}"; \
|
echo "$${col}$$br$${std}"; \
|
||||||
echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
|
echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \
|
||||||
echo "$${col}$$br$${std}"; \
|
echo "$${col}$$br$${std}"; \
|
||||||
create_testsuite_report --maybe-color; \
|
create_testsuite_report --maybe-color; \
|
||||||
echo "$$col$$br$$std"; \
|
echo "$$col$$br$$std"; \
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
# define _GNU_SOURCE
|
# define _GNU_SOURCE
|
||||||
#endif
|
#endif
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
#include <net/if.h> // for IFNAMSIZ
|
||||||
|
|
||||||
#if defined(HAVE_CPUSET_SETAFFINITY)
|
#if defined(HAVE_CPUSET_SETAFFINITY)
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
@ -258,6 +259,7 @@ struct iperf_test
|
|||||||
char *server_hostname; /* -c option */
|
char *server_hostname; /* -c option */
|
||||||
char *tmp_template;
|
char *tmp_template;
|
||||||
char *bind_address; /* first -B option */
|
char *bind_address; /* first -B option */
|
||||||
|
char *bind_dev; /* bind to network device */
|
||||||
TAILQ_HEAD(xbind_addrhead, xbind_entry) xbind_addrs; /* all -X opts */
|
TAILQ_HEAD(xbind_addrhead, xbind_entry) xbind_addrs; /* all -X opts */
|
||||||
int bind_port; /* --cport option */
|
int bind_port; /* --cport option */
|
||||||
int server_port;
|
int server_port;
|
||||||
|
@ -139,6 +139,10 @@ CPUs).
|
|||||||
.TP
|
.TP
|
||||||
.BR -B ", " --bind " \fIhost\fR"
|
.BR -B ", " --bind " \fIhost\fR"
|
||||||
bind to the specific interface associated with address \fIhost\fR.
|
bind to the specific interface associated with address \fIhost\fR.
|
||||||
|
.BR --bind-dev " \fIdev\R"
|
||||||
|
bind to the specified network interface.
|
||||||
|
This option uses SO_BINDTODEVICE, and may require root permissions.
|
||||||
|
(Available on Linux and possibly other systems.)
|
||||||
.TP
|
.TP
|
||||||
.BR -V ", " --verbose " "
|
.BR -V ", " --verbose " "
|
||||||
give more detailed output
|
give more detailed output
|
||||||
|
@ -339,6 +339,12 @@ iperf_get_test_bind_address(struct iperf_test *ipt)
|
|||||||
return ipt->bind_address;
|
return ipt->bind_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
iperf_get_test_bind_dev(struct iperf_test *ipt)
|
||||||
|
{
|
||||||
|
return ipt->bind_dev;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
iperf_get_test_udp_counters_64bit(struct iperf_test *ipt)
|
iperf_get_test_udp_counters_64bit(struct iperf_test *ipt)
|
||||||
{
|
{
|
||||||
@ -661,6 +667,12 @@ iperf_set_test_bind_address(struct iperf_test *ipt, const char *bnd_address)
|
|||||||
ipt->bind_address = strdup(bnd_address);
|
ipt->bind_address = strdup(bnd_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
iperf_set_test_bind_dev(struct iperf_test *ipt, char *bnd_dev)
|
||||||
|
{
|
||||||
|
ipt->bind_dev = strdup(bnd_dev);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
iperf_set_test_udp_counters_64bit(struct iperf_test *ipt, int udp_counters_64bit)
|
iperf_set_test_udp_counters_64bit(struct iperf_test *ipt, int udp_counters_64bit)
|
||||||
{
|
{
|
||||||
@ -894,6 +906,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
|
|||||||
{"bidir", no_argument, NULL, OPT_BIDIRECTIONAL},
|
{"bidir", no_argument, NULL, OPT_BIDIRECTIONAL},
|
||||||
{"window", required_argument, NULL, 'w'},
|
{"window", required_argument, NULL, 'w'},
|
||||||
{"bind", required_argument, NULL, 'B'},
|
{"bind", required_argument, NULL, 'B'},
|
||||||
|
#if defined(HAVE_SO_BINDTODEVICE)
|
||||||
|
{"bind-dev", required_argument, NULL, OPT_BIND_DEV},
|
||||||
|
#endif /* HAVE_SO_BINDTODEVICE */
|
||||||
{"cport", required_argument, NULL, OPT_CLIENT_PORT},
|
{"cport", required_argument, NULL, OPT_CLIENT_PORT},
|
||||||
{"set-mss", required_argument, NULL, 'M'},
|
{"set-mss", required_argument, NULL, 'M'},
|
||||||
{"no-delay", no_argument, NULL, 'N'},
|
{"no-delay", no_argument, NULL, 'N'},
|
||||||
@ -1147,6 +1162,11 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
|
|||||||
case 'B':
|
case 'B':
|
||||||
test->bind_address = strdup(optarg);
|
test->bind_address = strdup(optarg);
|
||||||
break;
|
break;
|
||||||
|
#if defined (HAVE_SO_BINDTODEVICE)
|
||||||
|
case OPT_BIND_DEV:
|
||||||
|
test->bind_dev = strdup(optarg);
|
||||||
|
break;
|
||||||
|
#endif /* HAVE_SO_BINDTODEVICE */
|
||||||
case OPT_CLIENT_PORT:
|
case OPT_CLIENT_PORT:
|
||||||
portno = atoi(optarg);
|
portno = atoi(optarg);
|
||||||
if (portno < 1 || portno > 65535) {
|
if (portno < 1 || portno > 65535) {
|
||||||
@ -2635,6 +2655,8 @@ iperf_free_test(struct iperf_test *test)
|
|||||||
free(test->tmp_template);
|
free(test->tmp_template);
|
||||||
if (test->bind_address)
|
if (test->bind_address)
|
||||||
free(test->bind_address);
|
free(test->bind_address);
|
||||||
|
if (test->bind_dev)
|
||||||
|
free(test->bind_dev);
|
||||||
if (!TAILQ_EMPTY(&test->xbind_addrs)) {
|
if (!TAILQ_EMPTY(&test->xbind_addrs)) {
|
||||||
struct xbind_entry *xbe;
|
struct xbind_entry *xbe;
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ typedef uint64_t iperf_size_t;
|
|||||||
#define OPT_SERVER_BITRATE_LIMIT 21
|
#define OPT_SERVER_BITRATE_LIMIT 21
|
||||||
#define OPT_TIMESTAMPS 22
|
#define OPT_TIMESTAMPS 22
|
||||||
#define OPT_SERVER_SKEW_THRESHOLD 23
|
#define OPT_SERVER_SKEW_THRESHOLD 23
|
||||||
|
#define OPT_BIND_DEV 24
|
||||||
|
|
||||||
/* states */
|
/* states */
|
||||||
#define TEST_START 1
|
#define TEST_START 1
|
||||||
@ -408,6 +409,7 @@ enum {
|
|||||||
IESETPACING= 140, // Unable to set socket pacing rate
|
IESETPACING= 140, // Unable to set socket pacing rate
|
||||||
IESETBUF2= 141, // Socket buffer size incorrect (written value != read value)
|
IESETBUF2= 141, // Socket buffer size incorrect (written value != read value)
|
||||||
IEAUTHTEST = 142, // Test authorization failed
|
IEAUTHTEST = 142, // Test authorization failed
|
||||||
|
IEBINDDEV = 143, // Unable to bind-to-device (check perror, maybe permissions?)
|
||||||
/* Stream errors */
|
/* Stream errors */
|
||||||
IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror)
|
IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror)
|
||||||
IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror)
|
IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror)
|
||||||
|
@ -336,7 +336,7 @@ iperf_connect(struct iperf_test *test)
|
|||||||
/* Create and connect the control channel */
|
/* Create and connect the control channel */
|
||||||
if (test->ctrl_sck < 0)
|
if (test->ctrl_sck < 0)
|
||||||
// Create the control channel using an ephemeral port
|
// Create the control channel using an ephemeral port
|
||||||
test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, 0, test->server_hostname, test->server_port, test->settings->connect_timeout);
|
test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, 0, test->server_hostname, test->server_port, test->settings->connect_timeout);
|
||||||
if (test->ctrl_sck < 0) {
|
if (test->ctrl_sck < 0) {
|
||||||
i_errno = IECONNECT;
|
i_errno = IECONNECT;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -48,6 +48,9 @@
|
|||||||
/* Define to 1 if you have the `SetProcessAffinityMask' function. */
|
/* Define to 1 if you have the `SetProcessAffinityMask' function. */
|
||||||
#undef HAVE_SETPROCESSAFFINITYMASK
|
#undef HAVE_SETPROCESSAFFINITYMASK
|
||||||
|
|
||||||
|
/* Have SO_BINDTODEVICE sockopt. */
|
||||||
|
#undef HAVE_SO_BINDTODEVICE
|
||||||
|
|
||||||
/* Have SO_MAX_PACING_RATE sockopt. */
|
/* Have SO_MAX_PACING_RATE sockopt. */
|
||||||
#undef HAVE_SO_MAX_PACING_RATE
|
#undef HAVE_SO_MAX_PACING_RATE
|
||||||
|
|
||||||
|
@ -105,6 +105,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
|
|||||||
" -A, --affinity n/n,m set CPU affinity\n"
|
" -A, --affinity n/n,m set CPU affinity\n"
|
||||||
#endif /* HAVE_CPU_AFFINITY */
|
#endif /* HAVE_CPU_AFFINITY */
|
||||||
" -B, --bind <host> bind to the interface associated with the address <host>\n"
|
" -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"
|
||||||
|
#endif /* HAVE_SO_BINDTODEVICE */
|
||||||
" -V, --verbose more detailed output\n"
|
" -V, --verbose more detailed output\n"
|
||||||
" -J, --json output in JSON format\n"
|
" -J, --json output in JSON format\n"
|
||||||
" --logfile f send output to a log file\n"
|
" --logfile f send output to a log file\n"
|
||||||
@ -233,6 +236,9 @@ const char client_port[] =
|
|||||||
const char bind_address[] =
|
const char bind_address[] =
|
||||||
"Binding to local address %s\n";
|
"Binding to local address %s\n";
|
||||||
|
|
||||||
|
const char bind_dev[] =
|
||||||
|
"Binding to local network device %s\n";
|
||||||
|
|
||||||
const char bind_port[] =
|
const char bind_port[] =
|
||||||
"Binding to local port %s\n";
|
"Binding to local port %s\n";
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ extern const char seperator_line[];
|
|||||||
extern const char server_port[] ;
|
extern const char server_port[] ;
|
||||||
extern const char client_port[] ;
|
extern const char client_port[] ;
|
||||||
extern const char bind_address[] ;
|
extern const char bind_address[] ;
|
||||||
|
extern const char bind_dev[] ;
|
||||||
extern const char multicast_ttl[] ;
|
extern const char multicast_ttl[] ;
|
||||||
extern const char join_multicast[] ;
|
extern const char join_multicast[] ;
|
||||||
extern const char client_datagram_size[] ;
|
extern const char client_datagram_size[] ;
|
||||||
|
@ -211,6 +211,21 @@ iperf_sctp_listen(struct iperf_test *test)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (test->bind_dev) {
|
||||||
|
#if defined(SO_BINDTODEVICE)
|
||||||
|
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
|
||||||
|
test->bind_dev, IFNAMSIZ) < 0)
|
||||||
|
#endif // SO_BINDTODEVICE
|
||||||
|
{
|
||||||
|
saved_errno = errno;
|
||||||
|
close(s);
|
||||||
|
freeaddrinfo(res);
|
||||||
|
i_errno = IEBINDDEV;
|
||||||
|
errno = saved_errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
|
#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
|
||||||
if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC ||
|
if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC ||
|
||||||
test->settings->domain == AF_INET6)) {
|
test->settings->domain == AF_INET6)) {
|
||||||
@ -284,7 +299,7 @@ iperf_sctp_connect(struct iperf_test *test)
|
|||||||
#if defined(HAVE_SCTP_H)
|
#if defined(HAVE_SCTP_H)
|
||||||
int s, opt, saved_errno;
|
int s, opt, saved_errno;
|
||||||
char portstr[6];
|
char portstr[6];
|
||||||
struct addrinfo hints, *local_res, *server_res;
|
struct addrinfo hints, *local_res = NULL, *server_res = NULL;
|
||||||
|
|
||||||
if (test->bind_address) {
|
if (test->bind_address) {
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
@ -309,8 +324,7 @@ iperf_sctp_connect(struct iperf_test *test)
|
|||||||
|
|
||||||
s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP);
|
s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP);
|
||||||
if (s < 0) {
|
if (s < 0) {
|
||||||
if (test->bind_address)
|
freeaddrinfo(local_res);
|
||||||
freeaddrinfo(local_res);
|
|
||||||
freeaddrinfo(server_res);
|
freeaddrinfo(server_res);
|
||||||
i_errno = IESTREAMCONNECT;
|
i_errno = IESTREAMCONNECT;
|
||||||
return -1;
|
return -1;
|
||||||
@ -336,6 +350,22 @@ iperf_sctp_connect(struct iperf_test *test)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (test->bind_dev) {
|
||||||
|
#if defined(SO_BINDTODEVICE)
|
||||||
|
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
|
||||||
|
test->bind_dev, IFNAMSIZ) < 0)
|
||||||
|
#endif // SO_BINDTODEVICE
|
||||||
|
{
|
||||||
|
saved_errno = errno;
|
||||||
|
close(s);
|
||||||
|
freeaddrinfo(local_res);
|
||||||
|
freeaddrinfo(server_res);
|
||||||
|
i_errno = IEBINDDEV;
|
||||||
|
errno = saved_errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Various ways to bind the local end of the connection.
|
* Various ways to bind the local end of the connection.
|
||||||
* 1. --bind (with or without --cport).
|
* 1. --bind (with or without --cport).
|
||||||
|
@ -70,7 +70,7 @@ int
|
|||||||
iperf_server_listen(struct iperf_test *test)
|
iperf_server_listen(struct iperf_test *test)
|
||||||
{
|
{
|
||||||
retry:
|
retry:
|
||||||
if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) {
|
if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, test->server_port)) < 0) {
|
||||||
if (errno == EAFNOSUPPORT && (test->settings->domain == AF_INET6 || test->settings->domain == AF_UNSPEC)) {
|
if (errno == EAFNOSUPPORT && (test->settings->domain == AF_INET6 || test->settings->domain == AF_UNSPEC)) {
|
||||||
/* If we get "Address family not supported by protocol", that
|
/* If we get "Address family not supported by protocol", that
|
||||||
** probably means we were compiled with IPv6 but the running
|
** probably means we were compiled with IPv6 but the running
|
||||||
@ -609,7 +609,7 @@ iperf_run_server(struct iperf_test *test)
|
|||||||
FD_CLR(test->listener, &test->read_set);
|
FD_CLR(test->listener, &test->read_set);
|
||||||
close(test->listener);
|
close(test->listener);
|
||||||
test->listener = 0;
|
test->listener = 0;
|
||||||
if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) {
|
if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, test->server_port)) < 0) {
|
||||||
cleanup_server(test);
|
cleanup_server(test);
|
||||||
i_errno = IELISTEN;
|
i_errno = IELISTEN;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -440,7 +440,7 @@ iperf_udp_accept(struct iperf_test *test)
|
|||||||
/*
|
/*
|
||||||
* Create a new "listening" socket to replace the one we were using before.
|
* Create a new "listening" socket to replace the one we were using before.
|
||||||
*/
|
*/
|
||||||
test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->server_port);
|
test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port);
|
||||||
if (test->prot_listener < 0) {
|
if (test->prot_listener < 0) {
|
||||||
i_errno = IESTREAMLISTEN;
|
i_errno = IESTREAMLISTEN;
|
||||||
return -1;
|
return -1;
|
||||||
@ -472,7 +472,7 @@ iperf_udp_listen(struct iperf_test *test)
|
|||||||
{
|
{
|
||||||
int s;
|
int s;
|
||||||
|
|
||||||
if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->server_port)) < 0) {
|
if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port)) < 0) {
|
||||||
i_errno = IESTREAMLISTEN;
|
i_errno = IESTREAMLISTEN;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -499,7 +499,7 @@ iperf_udp_connect(struct iperf_test *test)
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Create and bind our local socket. */
|
/* Create and bind our local socket. */
|
||||||
if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_port, test->server_hostname, test->server_port, -1)) < 0) {
|
if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, -1)) < 0) {
|
||||||
i_errno = IESTREAMCONNECT;
|
i_errno = IESTREAMCONNECT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -317,6 +317,16 @@ get_optional_features(void)
|
|||||||
numfeatures++;
|
numfeatures++;
|
||||||
#endif /* HAVE_SSL */
|
#endif /* HAVE_SSL */
|
||||||
|
|
||||||
|
#if defined(HAVE_SO_BINDTODEVICE)
|
||||||
|
if (numfeatures > 0) {
|
||||||
|
strncat(features, ", ",
|
||||||
|
sizeof(features) - strlen(features) - 1);
|
||||||
|
}
|
||||||
|
strncat(features, "bind to device",
|
||||||
|
sizeof(features) - strlen(features) - 1);
|
||||||
|
numfeatures++;
|
||||||
|
#endif /* HAVE_SO_BINDTODEVICE */
|
||||||
|
|
||||||
if (numfeatures == 0) {
|
if (numfeatures == 0) {
|
||||||
strncat(features, "None",
|
strncat(features, "None",
|
||||||
sizeof(features) - strlen(features) - 1);
|
sizeof(features) - strlen(features) - 1);
|
||||||
|
@ -25,6 +25,7 @@ Setting test parameters:
|
|||||||
.nf
|
.nf
|
||||||
void iperf_set_test_role( struct iperf_test *pt, char role );
|
void iperf_set_test_role( struct iperf_test *pt, char role );
|
||||||
void iperf_set_test_bind_address( struct iperf_test *t, char *bind_address );
|
void iperf_set_test_bind_address( struct iperf_test *t, char *bind_address );
|
||||||
|
void iperf_set_test_bind_dev( struct iperf_test *t, char *bind_dev );
|
||||||
void iperf_set_test_server_hostname( struct iperf_test *t, char *server_host );
|
void iperf_set_test_server_hostname( struct iperf_test *t, char *server_host );
|
||||||
void iperf_set_test_server_port( struct iperf_test *t, int server_port );
|
void iperf_set_test_server_port( struct iperf_test *t, int server_port );
|
||||||
void iperf_set_test_duration( struct iperf_test *t, int duration );
|
void iperf_set_test_duration( struct iperf_test *t, int duration );
|
||||||
|
36
src/net.c
36
src/net.c
@ -61,6 +61,7 @@
|
|||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#endif /* HAVE_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"
|
||||||
@ -121,9 +122,9 @@ timeout_connect(int s, const struct sockaddr *name, socklen_t namelen,
|
|||||||
|
|
||||||
/* make connection to server */
|
/* make connection to server */
|
||||||
int
|
int
|
||||||
netdial(int domain, int proto, const char *local, int local_port, const char *server, int port, int timeout)
|
netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout)
|
||||||
{
|
{
|
||||||
struct addrinfo hints, *local_res, *server_res;
|
struct addrinfo hints, *local_res = NULL, *server_res = NULL;
|
||||||
int s, saved_errno;
|
int s, saved_errno;
|
||||||
|
|
||||||
if (local) {
|
if (local) {
|
||||||
@ -148,6 +149,21 @@ netdial(int domain, int proto, const char *local, int local_port, const char *se
|
|||||||
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) */
|
/* Bind the local address if given a name (with or without --cport) */
|
||||||
if (local) {
|
if (local) {
|
||||||
if (local_port) {
|
if (local_port) {
|
||||||
@ -218,7 +234,7 @@ netdial(int domain, int proto, const char *local, int local_port, const char *se
|
|||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
|
|
||||||
int
|
int
|
||||||
netannounce(int domain, int proto, const 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];
|
||||||
@ -255,6 +271,20 @@ netannounce(int domain, int proto, const char *local, int port)
|
|||||||
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) {
|
||||||
|
@ -28,8 +28,8 @@
|
|||||||
#define __NET_H
|
#define __NET_H
|
||||||
|
|
||||||
int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout);
|
int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout);
|
||||||
int netdial(int domain, int proto, const char *local, int local_port, const char *server, int port, int timeout);
|
int netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout);
|
||||||
int netannounce(int domain, int proto, const char *local, int port);
|
int netannounce(int domain, int proto, const char *local, const char *bind_dev, int port);
|
||||||
int Nread(int fd, char *buf, size_t count, int prot);
|
int Nread(int fd, char *buf, size_t count, int prot);
|
||||||
int Nwrite(int fd, const char *buf, size_t count, int prot) /* __attribute__((hot)) */;
|
int Nwrite(int fd, const char *buf, size_t count, int prot) /* __attribute__((hot)) */;
|
||||||
int has_sendfile(void);
|
int has_sendfile(void);
|
||||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user