diff --git a/aclocal.m4 b/aclocal.m4 index 0b6f3d443..8f8528efe 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1081,11 +1081,16 @@ _LT_EOF _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - darwin*) - case ${MACOSX_DEPLOYMENT_TARGET},$host in - 10.[[012]],*|,*powerpc*) + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[[012]][[,.]]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - *) + 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; diff --git a/configure b/configure index 098bce198..468276f36 100755 --- a/configure +++ b/configure @@ -7431,11 +7431,16 @@ $as_echo "$lt_cv_ld_force_load" >&6; } _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - darwin*) - case ${MACOSX_DEPLOYMENT_TARGET},$host in - 10.[012],*|,*powerpc*) + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[012][,.]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; - *) + 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; @@ -13976,6 +13981,45 @@ $as_echo "#define HAVE_SO_BINDTODEVICE 1" >>confdefs.h fi +# Check for IP DF support +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking IP_MTU_DISCOVER or IP_DONTFRAG socket option" >&5 +$as_echo_n "checking IP_MTU_DISCOVER or IP_DONTFRAG socket option... " >&6; } +if ${iperf3_cv_header_dontfragment+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#ifdef IP_MTU_DISCOVER + yes +#endif +#ifdef IP_DONTFRAG + yes +#endif +#ifdef IP_DONTFRAGMENT + yes +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then : + iperf3_cv_header_dontfragment=yes +else + iperf3_cv_header_dontfragment=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $iperf3_cv_header_dontfragment" >&5 +$as_echo "$iperf3_cv_header_dontfragment" >&6; } +if test "x$iperf3_cv_header_dontfragment" = "xyes"; then + +$as_echo "#define HAVE_DONT_FRAGMENT 1" >>confdefs.h + +fi + # Check if we need -lrt for clock_gettime { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 $as_echo_n "checking for library containing clock_gettime... " >&6; } diff --git a/configure.ac b/configure.ac index 7e2b62fbb..596444aa7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -# iperf, Copyright (c) 2014-2020, The Regents of the University of +# iperf, Copyright (c) 2014-2021, The Regents of the University of # California, through Lawrence Berkeley National Laboratory (subject # to receipt of any required approvals from the U.S. Dept. of # Energy). All rights reserved. @@ -237,6 +237,27 @@ if test "x$iperf3_cv_header_so_bindtodevice" = "xyes"; then AC_DEFINE([HAVE_SO_BINDTODEVICE], [1], [Have SO_BINDTODEVICE sockopt.]) fi +# Check for IP DF support +AC_CACHE_CHECK([IP_MTU_DISCOVER or IP_DONTFRAG socket option], +[iperf3_cv_header_dontfragment], +AC_EGREP_CPP(yes, +[#include +#include +#include +#ifdef IP_MTU_DISCOVER + yes +#endif +#ifdef IP_DONTFRAG + yes +#endif +#ifdef IP_DONTFRAGMENT + yes +#endif +],iperf3_cv_header_dontfragment=yes,iperf3_cv_header_dontfragment=no)) +if test "x$iperf3_cv_header_dontfragment" = "xyes"; then + AC_DEFINE([HAVE_DONT_FRAGMENT], [1], [Have IP_MTU_DISCOVER/IP_DONTFRAG sockopt.]) +fi + # Check if we need -lrt for clock_gettime AC_SEARCH_LIBS(clock_gettime, [rt posix4]) # Check for clock_gettime support diff --git a/src/iperf.h b/src/iperf.h index c46001def..8a5427306 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -153,6 +153,7 @@ struct iperf_settings iperf_size_t blocks; /* number of blocks (packets) to send */ char unit_format; /* -f */ int num_ostreams; /* SCTP initmsg settings */ + int dont_fragment; /* Whether to set IP flag Do-Not_Fragment */ #if defined(HAVE_SSL) char *authtoken; /* Authentication token */ char *client_username; diff --git a/src/iperf3.1 b/src/iperf3.1 index aec3c0754..15b7640c7 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -1,4 +1,4 @@ -.TH IPERF3 1 "September 2020" ESnet "User Manuals" +.TH IPERF3 1 "February 2021" ESnet "User Manuals" .SH NAME iperf3 \- perform network throughput tests .SH SYNOPSIS @@ -397,6 +397,10 @@ It might help to test and reveal problems in networking gear with hardware compression (including some WiFi access points), where iperf2 and iperf3 perform differently, just based on payload entropy. .TP +.BR --dont-fragment +Set the IPv4 Don't Fragment (DF) bit on outgoing packets. +Only applicable to tests doing UDP over IPv4. +.TP .BR --username " \fIusername\fR" username to use for authentication to the iperf server (if built with OpenSSL support). diff --git a/src/iperf_api.c b/src/iperf_api.c index f9fca90ca..9bb0de3ae 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -394,6 +394,12 @@ iperf_get_test_idle_timeout(struct iperf_test *ipt) return ipt->settings->idle_timeout; } +int +iperf_get_dont_fragment(struct iperf_test *ipt) +{ + return ipt->settings->dont_fragment; +} + char* iperf_get_test_congestion_control(struct iperf_test* ipt) { @@ -737,6 +743,12 @@ iperf_set_test_idle_timeout(struct iperf_test* ipt, int to) ipt->settings->idle_timeout = to; } +void +iperf_set_dont_fragment(struct iperf_test* ipt, int dnf) +{ + ipt->settings->dont_fragment = dnf; +} + void iperf_set_test_congestion_control(struct iperf_test* ipt, char* cc) { @@ -968,6 +980,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"get-server-output", no_argument, NULL, OPT_GET_SERVER_OUTPUT}, {"udp-counters-64bit", no_argument, NULL, OPT_UDP_COUNTERS_64BIT}, {"no-fq-socket-pacing", no_argument, NULL, OPT_NO_FQ_SOCKET_PACING}, +#if defined(HAVE_DONT_FRAGMENT) + {"dont-fragment", no_argument, NULL, OPT_DONT_FRAGMENT}, +#endif /* HAVE_DONT_FRAGMENT */ #if defined(HAVE_SSL) {"username", required_argument, NULL, OPT_CLIENT_USERNAME}, {"rsa-public-key-path", required_argument, NULL, OPT_CLIENT_RSA_PUBLIC_KEY}, @@ -1381,6 +1396,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) return -1; #endif break; +#if defined(HAVE_DONT_FRAGMENT) + case OPT_DONT_FRAGMENT: + test->settings->dont_fragment = 1; + client_flag = 1; + break; +#endif /* HAVE_DONT_FRAGMENT */ #if defined(HAVE_SSL) case OPT_CLIENT_USERNAME: client_username = strdup(optarg); @@ -1980,6 +2001,10 @@ send_parameters(struct iperf_test *test) cJSON_AddNumberToObject(j, "udp_counters_64bit", iperf_get_test_udp_counters_64bit(test)); if (test->repeating_payload) cJSON_AddNumberToObject(j, "repeating_payload", test->repeating_payload); +#if defined(HAVE_DONT_FRAGMENT) + if (test->settings->dont_fragment) + cJSON_AddNumberToObject(j, "dont_fragment", test->settings->dont_fragment); +#endif /* HAVE_DONT_FRAGMENT */ #if defined(HAVE_SSL) /* Send authentication parameters */ if (test->settings->client_username && test->settings->client_password && test->settings->client_rsa_pubkey){ @@ -2088,6 +2113,10 @@ get_parameters(struct iperf_test *test) iperf_set_test_udp_counters_64bit(test, 1); if ((j_p = cJSON_GetObjectItem(j, "repeating_payload")) != NULL) test->repeating_payload = 1; +#if defined(HAVE_DONT_FRAGMENT) + if ((j_p = cJSON_GetObjectItem(j, "dont_fragment")) != NULL) + test->settings->dont_fragment = j_p->valueint; +#endif /* HAVE_DONT_FRAGMENT */ #if defined(HAVE_SSL) if ((j_p = cJSON_GetObjectItem(j, "authtoken")) != NULL) test->settings->authtoken = strdup(j_p->valuestring); @@ -2883,6 +2912,7 @@ iperf_reset_test(struct iperf_test *test) test->settings->burst = 0; test->settings->mss = 0; test->settings->tos = 0; + test->settings->dont_fragment = 0; #if defined(HAVE_SSL) if (test->settings->authtoken) { @@ -4063,6 +4093,45 @@ iperf_init_stream(struct iperf_stream *sp, struct iperf_test *test) } } +#if defined(HAVE_DONT_FRAGMENT) + /* Set Don't Fragment (DF). Only applicable to IPv4/UDP tests. */ + if (iperf_get_test_protocol_id(test) == Pudp && + getsockdomain(sp->socket) == AF_INET && + iperf_get_dont_fragment(test)) { + + /* + * There are multiple implementations of this feature depending on the OS. + * We need to handle separately Linux, UNIX, and Windows, as well as + * the case that DF isn't supported at all (such as on macOS). + */ +#if defined(IP_MTU_DISCOVER) /* Linux version of IP_DONTFRAG */ + opt = IP_PMTUDISC_DO; + if (setsockopt(sp->socket, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof(opt)) < 0) { + i_errno = IESETDONTFRAGMENT; + return -1; + } +#else +#if defined(IP_DONTFRAG) /* UNIX does IP_DONTFRAG */ + opt = 1; + if (setsockopt(sp->socket, IPPROTO_IP, IP_DONTFRAG, &opt, sizeof(opt)) < 0) { + i_errno = IESETDONTFRAGMENT; + return -1; + } +#else +#if defined(IP_DONTFRAGMENT) /* Windows does IP_DONTFRAGMENT */ + opt = 1; + if (setsockopt(sp->socket, IPPROTO_IP, IP_DONTFRAGMENT, &opt, sizeof(opt)) < 0) { + i_errno = IESETDONTFRAGMENT; + return -1; + } +#else + i_errno = IESETDONTFRAGMENT; + return -1; +#endif /* IP_DONTFRAGMENT */ +#endif /* IP_DONTFRAG */ +#endif /* IP_MTU_DISCOVER */ + } +#endif /* HAVE_DONT_FRAGMENT */ return 0; } diff --git a/src/iperf_api.h b/src/iperf_api.h index c35f6eecd..432599183 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -82,6 +82,7 @@ typedef uint64_t iperf_size_t; #define OPT_SERVER_SKEW_THRESHOLD 23 #define OPT_BIND_DEV 24 #define OPT_IDLE_TIMEOUT 25 +#define OPT_DONT_FRAGMENT 26 /* states */ #define TEST_START 1 @@ -140,6 +141,7 @@ char* iperf_get_extra_data( struct iperf_test* ipt ); char* iperf_get_iperf_version(void); int iperf_get_test_no_delay( struct iperf_test* ipt ); int iperf_get_test_connect_timeout( struct iperf_test* ipt ); +int iperf_get_dont_fragment( struct iperf_test* ipt ); char* iperf_get_test_congestion_control(struct iperf_test* ipt); /* Setter routines for some fields inside iperf_test. */ @@ -178,6 +180,7 @@ void iperf_set_test_tos( struct iperf_test* ipt, int tos ); void iperf_set_test_extra_data( struct iperf_test* ipt, const char *dat ); void iperf_set_test_bidirectional( struct iperf_test* ipt, int bidirectional); void iperf_set_test_no_delay( struct iperf_test* ipt, int no_delay); +void iperf_set_dont_fragment( struct iperf_test* ipt, int dont_fragment ); void iperf_set_test_congestion_control(struct iperf_test* ipt, char* cc); #if defined(HAVE_SSL) @@ -415,6 +418,7 @@ 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 /* Stream errors */ IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror) IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror) diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in index a8aec642a..d78af669a 100644 --- a/src/iperf_config.h.in +++ b/src/iperf_config.h.in @@ -15,6 +15,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H +/* Have IP_MTU_DISCOVER/IP_DONTFRAG sockopt. */ +#undef HAVE_DONT_FRAGMENT + /* Define to 1 if you have the header file. */ #undef HAVE_ENDIAN_H diff --git a/src/iperf_error.c b/src/iperf_error.c index 1769a7f54..0775ba2c3 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -433,6 +433,9 @@ iperf_strerror(int int_errno) case IENOMSG: snprintf(errstr, len, "no message was received for %d seconds", NO_MSG_RCVD_TIMEOUT); break; + case IESETDONTFRAGMENT: + snprintf(errstr, len, "unable to set IP Do-Not-Fragment flag"); + break; default: snprintf(errstr, len, "int_errno=%d", int_errno); perr = 1; diff --git a/src/iperf_locale.c b/src/iperf_locale.c index dce0f0b1c..9a74fc498 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -189,6 +189,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " --udp-counters-64bit use 64-bit counters in UDP test packets\n" " --repeating-payload use repeating pattern in payload, instead of\n" " randomized payload (like in iperf2)\n" +#if defined(HAVE_DONT_FRAGMENT) + " --dont-fragment set IPv4 Don't Fragment flag\n" +#endif /* HAVE_DONT_FRAGMENT */ #if defined(HAVE_SSL) " --username username for authentication\n" " --rsa-public-key-path path to the RSA public key used to encrypt\n" diff --git a/src/iperf_util.c b/src/iperf_util.c index d54cdf284..8155270ba 100644 --- a/src/iperf_util.c +++ b/src/iperf_util.c @@ -327,6 +327,16 @@ get_optional_features(void) numfeatures++; #endif /* HAVE_SO_BINDTODEVICE */ +#if defined(HAVE_DONT_FRAGMENT) + if (numfeatures > 0) { + strncat(features, ", ", + sizeof(features) - strlen(features) - 1); + } + strncat(features, "support IPv4 don't fragment", + sizeof(features) - strlen(features) - 1); + numfeatures++; +#endif /* HAVE_DONT_FRAGMENT */ + if (numfeatures == 0) { strncat(features, "None", sizeof(features) - strlen(features) - 1);