From fff6e52ed419f0d04a0270167e684c4da2d941d5 Mon Sep 17 00:00:00 2001 From: "Ycarus (Yannick Chabanois)" Date: Fri, 31 Jan 2025 09:51:37 +0100 Subject: [PATCH] Update iperf3 with MPTCP and sock5 proxy support --- iperf3/Makefile | 46 +- iperf3/patches/010-big-endian.patch | 21 + iperf3/patches/add-MPTCP-support.patch | 107 ++-- .../patches/add-sock5-proxy-tcp-support.patch | 552 ++++++++++++++++++ openmptcprouter/files/bin/omr-iperf | 3 +- 5 files changed, 671 insertions(+), 58 deletions(-) create mode 100644 iperf3/patches/010-big-endian.patch create mode 100644 iperf3/patches/add-sock5-proxy-tcp-support.patch diff --git a/iperf3/Makefile b/iperf3/Makefile index e165cae70..a5fe60450 100644 --- a/iperf3/Makefile +++ b/iperf3/Makefile @@ -8,15 +8,16 @@ include $(TOPDIR)/rules.mk PKG_NAME:=iperf -PKG_VERSION:=3.17.1 +PKG_VERSION:=3.18 PKG_RELEASE:=1 -PKG_SOURCE:=$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:=https://github.com/esnet/iperf/archive/refs/tags/ -PKG_HASH:=105b4fe7fbce31c9b94a3fec10c46e3b4b298adc076e1e3af52b990e1faf2db9 +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://downloads.es.net/pub/iperf +PKG_HASH:=c0618175514331e766522500e20c94bfb293b4424eb27d7207fb427b88d20bab -PKG_MAINTAINER:=Yannick Chabanois +PKG_MAINTAINER:=Felix Fietkau PKG_LICENSE:=BSD-3-Clause +PKG_CPE_ID:=cpe:/a:es:iperf3 PKG_BUILD_PARALLEL:=1 PKG_INSTALL:=1 @@ -37,24 +38,36 @@ endef define Package/iperf3 $(call Package/iperf3/default) VARIANT:=nossl + DEPENDS:=+libiperf3 endef define Package/iperf3-ssl $(call Package/iperf3/default) TITLE+= with iperf_auth support VARIANT:=ssl - DEPENDS:= +libopenssl + DEPENDS:=+libopenssl +libatomic + CONFLICTS:=iperf3 +endef + +define Package/libiperf3 + SECTION:=libs + CATEGORY:=Libraries + TITLE:=Internet Protocol bandwidth measuring library + URL:=https://github.com/esnet/iperf + DEPENDS+=+libatomic endef TARGET_CFLAGS += -D_GNU_SOURCE -CONFIGURE_ARGS += --disable-shared +TARGET_LDFLAGS += -latomic ifeq ($(BUILD_VARIANT),ssl) - CONFIGURE_ARGS += --with-openssl="$(STAGING_DIR)/usr" + CONFIGURE_ARGS += --with-openssl="$(STAGING_DIR)/usr" --disable-shared else CONFIGURE_ARGS += --without-openssl endif +CONFIGURE_ARGS += --without-sctp + MAKE_FLAGS += noinst_PROGRAMS= define Package/iperf3/description @@ -63,6 +76,17 @@ define Package/iperf3/description characteristics. endef +define Package/libiperf3/description + Libiperf is a library providing an API for iperf3 functionality. +endef + +define Build/InstallDev + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_DIR) $(1)/usr/include + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libiperf.* $(1)/usr/lib/ + $(CP) $(PKG_INSTALL_DIR)/usr/include/* $(1)/usr/include/ +endef + # autoreconf fails if the README file isn't present define Build/Prepare $(call Build/Prepare/Default) @@ -79,5 +103,11 @@ define Package/iperf3-ssl/install $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/iperf3 $(1)/usr/bin/ endef +define Package/libiperf3/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libiperf.so.* $(1)/usr/lib +endef + $(eval $(call BuildPackage,iperf3)) $(eval $(call BuildPackage,iperf3-ssl)) +$(eval $(call BuildPackage,libiperf3)) diff --git a/iperf3/patches/010-big-endian.patch b/iperf3/patches/010-big-endian.patch new file mode 100644 index 000000000..f57ef51a4 --- /dev/null +++ b/iperf3/patches/010-big-endian.patch @@ -0,0 +1,21 @@ +From fe09305eb6f907e4eb637b8edd0c8a986187d1dd Mon Sep 17 00:00:00 2001 +From: Rosen Penev +Date: Sat, 8 Jun 2024 15:23:51 -0700 +Subject: [PATCH] fix crash under big endian musl + +iperf_printf is using an int format here but an int64_t variable. The format only needs the first 3 digits. Cast to int to fix it. +--- + src/iperf_api.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/src/iperf_api.c ++++ b/src/iperf_api.c +@@ -4137,7 +4137,7 @@ iperf_print_results(struct iperf_test *t + iperf_printf(test, report_sender_not_available_summary_format, "SUM"); + } + else { +- iperf_printf(test, report_sum_bw_retrans_format, mbuf, start_time, sender_time, ubuf, nbuf, total_retransmits, report_sender); ++ iperf_printf(test, report_sum_bw_retrans_format, mbuf, start_time, sender_time, ubuf, nbuf, (int)total_retransmits, report_sender); + } + } else { + /* Summary sum, TCP without retransmits. */ diff --git a/iperf3/patches/add-MPTCP-support.patch b/iperf3/patches/add-MPTCP-support.patch index e6251d38b..fce1a388f 100644 --- a/iperf3/patches/add-MPTCP-support.patch +++ b/iperf3/patches/add-MPTCP-support.patch @@ -1,4 +1,4 @@ -From 5f71968be8e8809e4e7b876ff04b4ef3f22eb141 Mon Sep 17 00:00:00 2001 +From cf75cf46785871330717a6d2c889abeb7bbd7bfd Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 6 Mar 2024 11:23:33 +0800 Subject: [PATCH] add MPTCPv1 support @@ -17,10 +17,6 @@ be used like this: > iperf3 -m -s > iperf3 -m -c 127.0.0.1 -There is no need to check for IPPROTO_MPTCP support in configure.ac -at build time, it is at runtime we will see if the kernel being use -supports or not MPTCP. - If IPPROTO_MPTCP is not supported by the kernel being tested, it is normal to fail because the feature is not available and the user explicitly asked to use MPTCP. @@ -29,20 +25,44 @@ Closes: https://github.com/esnet/iperf/pull/1659 Co-developed-by: Paolo Abeni Signed-off-by: Geliang Tang --- + configure.ac | 12 ++++++++++++ src/iperf.h | 1 + src/iperf3.1 | 4 ++++ src/iperf_api.c | 19 ++++++++++++++++++- src/iperf_locale.c | 3 +++ - src/iperf_tcp.c | 22 +++++++++++++++++++--- + src/iperf_tcp.c | 18 +++++++++++++++--- src/net.c | 10 +++++----- src/net.h | 2 +- - 7 files changed, 51 insertions(+), 10 deletions(-) + 8 files changed, 59 insertions(+), 10 deletions(-) +diff --git a/configure.ac b/configure.ac +index 66c1e97a5..22c2a95cf 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -337,6 +337,18 @@ if test "x$iperf3_cv_header_tcp_info_snd_wnd" = "xyes"; then + AC_DEFINE([HAVE_TCP_INFO_SND_WND], [1], [Have tcpi_snd_wnd field in tcp_info.]) + fi + ++# Check for IPPROTO_MPTCP (Linux) ++AC_CACHE_CHECK([MPTCP protocol], ++[iperf3_cv_header_ipproto_mptcp], ++AC_COMPILE_IFELSE( ++ [AC_LANG_PROGRAM([[#include ]], ++ [[int foo = IPPROTO_MPTCP;]])], ++ iperf3_cv_header_ipproto_mptcp=yes, ++ iperf3_cv_header_ipproto_mptcp=no)) ++if test "x$iperf3_cv_header_ipproto_mptcp" = "xyes"; then ++ AC_DEFINE([HAVE_IPPROTO_MPTCP], [1], [Have MPTCP protocol.]) ++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 dc3c0d1df..cb821e1f7 100644 +index 202d3016f..4043031b3 100644 --- a/src/iperf.h +++ b/src/iperf.h -@@ -342,6 +342,7 @@ struct iperf_test +@@ -353,6 +353,7 @@ struct iperf_test int repeating_payload; /* --repeating-payload */ int timestamps; /* --timestamps */ char *timestamp_format; @@ -51,12 +71,12 @@ index dc3c0d1df..cb821e1f7 100644 char *json_output_string; /* rendered JSON output if json_output is set */ /* Select related parameters */ diff --git a/src/iperf3.1 b/src/iperf3.1 -index 2efd53dea..ebc603408 100644 +index f8eff48d2..9e425cabc 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 -@@ -193,6 +193,10 @@ parameter is specified in ms, and defaults to the system settings. - This functionality depends on the TCP_USER_TIMEOUT socket option, and - will not work on systems that do not support it. +@@ -202,6 +202,10 @@ iperf-3.17, OAEP padding is used, however this is a breaking change + that is not compatible with older iperf3 versions. Use this option to + preserve the less secure, but more compatible, behavior. .TP +.BR -m ", " --mptcp " " +use mptcp variant for the current protocol. This only applies to @@ -66,33 +86,33 @@ index 2efd53dea..ebc603408 100644 emit debugging output. Primarily (perhaps exclusively) of use to developers. diff --git a/src/iperf_api.c b/src/iperf_api.c -index 1dcfaabf5..f7f1fbfb8 100644 +index fa06dc830..419b48657 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c -@@ -1144,6 +1144,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) +@@ -1149,6 +1149,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"idle-timeout", required_argument, NULL, OPT_IDLE_TIMEOUT}, {"rcv-timeout", required_argument, NULL, OPT_RCV_TIMEOUT}, {"snd-timeout", required_argument, NULL, OPT_SND_TIMEOUT}, -+#if defined(linux) ++#if defined(HAVE_IPPROTO_MPTCP) + {"mptcp", no_argument, NULL, 'm'}, +#endif {"debug", optional_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} -@@ -1169,7 +1172,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) +@@ -1174,7 +1177,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) FILE *ptr_file; #endif /* HAVE_SSL */ - while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) { -+ while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:mM:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) { ++ while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:mhX:", longopts, NULL)) != -1) { switch (flag) { case 'p': portno = atoi(optarg); -@@ -1639,6 +1642,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) +@@ -1647,6 +1650,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->settings->connect_timeout = unit_atoi(optarg); client_flag = 1; break; -+#if defined(linux) ++#if defined(HAVE_IPPROTO_MPTCP) + case 'm': + set_protocol(test, Ptcp); + test->mptcp = 1; @@ -101,58 +121,47 @@ index 1dcfaabf5..f7f1fbfb8 100644 case 'h': usage_long(stdout); exit(0); -@@ -2216,6 +2225,10 @@ send_parameters(struct iperf_test *test) +@@ -2259,6 +2268,10 @@ send_parameters(struct iperf_test *test) cJSON_AddTrueToObject(j, "reverse"); if (test->bidirectional) cJSON_AddTrueToObject(j, "bidirectional"); -+#if defined(linux) ++#if defined(HAVE_IPPROTO_MPTCP) + if (test->mptcp) + cJSON_AddTrueToObject(j, "mptcp"); +#endif if (test->settings->socket_bufsize) cJSON_AddNumberToObject(j, "window", test->settings->socket_bufsize); if (test->settings->blksize) -@@ -2332,6 +2345,10 @@ get_parameters(struct iperf_test *test) +@@ -2375,6 +2388,10 @@ get_parameters(struct iperf_test *test) iperf_set_test_reverse(test, 1); - if ((j_p = cJSON_GetObjectItem(j, "bidirectional")) != NULL) + if ((j_p = iperf_cJSON_GetObjectItemType(j, "bidirectional", cJSON_True)) != NULL) iperf_set_test_bidirectional(test, 1); -+#if defined(linux) -+ if ((j_p = cJSON_GetObjectItem(j, "mptcp")) != NULL) ++#if defined(HAVE_IPPROTO_MPTCP) ++ if ((j_p = iperf_cJSON_GetObjectItemType(j, "mptcp", cJSON_True)) != NULL) + test->mptcp = 1; +#endif - if ((j_p = cJSON_GetObjectItem(j, "window")) != NULL) + if ((j_p = iperf_cJSON_GetObjectItemType(j, "window", cJSON_Number)) != NULL) test->settings->socket_bufsize = j_p->valueint; - if ((j_p = cJSON_GetObjectItem(j, "len")) != NULL) + if ((j_p = iperf_cJSON_GetObjectItemType(j, "len", cJSON_Number)) != NULL) diff --git a/src/iperf_locale.c b/src/iperf_locale.c -index ae0f63a41..d454af4f0 100644 +index 32883da84..f1d89e298 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -128,6 +128,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " --snd-timeout # timeout for unacknowledged TCP data\n" " (in ms, default is system settings)\n" #endif /* HAVE_TCP_USER_TIMEOUT */ -+#if defined(linux) ++#if defined(HAVE_IPPROTO_MPTCP) + " -m, --mptcp use MPTCP rather than plain TCP\n" +#endif " -d, --debug[=#] emit debugging output\n" " (optional optional \"=\" and debug level: 1-4. Default is 4 - all messages)\n" " -v, --version show version information and quit\n" diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c -index 184a1955e..a10322b75 100644 +index 481c09dc8..2c10d7df5 100644 --- a/src/iperf_tcp.c +++ b/src/iperf_tcp.c -@@ -44,6 +44,10 @@ - #include "net.h" - #include "cjson.h" - -+#ifndef IPPROTO_MPTCP -+#define IPPROTO_MPTCP 262 -+#endif -+ - #if defined(HAVE_FLOWLABEL) - #include "flowlabel.h" - #endif /* HAVE_FLOWLABEL */ -@@ -182,9 +186,10 @@ iperf_tcp_listen(struct iperf_test *test) +@@ -184,9 +184,10 @@ iperf_tcp_listen(struct iperf_test *test) * * It's not clear whether this is a requirement or a convenience. */ @@ -164,12 +173,12 @@ index 184a1955e..a10322b75 100644 FD_CLR(s, &test->read_set); close(s); -@@ -210,7 +215,12 @@ iperf_tcp_listen(struct iperf_test *test) +@@ -212,7 +213,12 @@ iperf_tcp_listen(struct iperf_test *test) return -1; } - if ((s = socket(res->ai_family, SOCK_STREAM, 0)) < 0) { -+#if defined(linux) ++#if defined(HAVE_IPPROTO_MPTCP) + if (test->mptcp) + proto = IPPROTO_MPTCP; +#endif @@ -178,13 +187,13 @@ index 184a1955e..a10322b75 100644 freeaddrinfo(res); i_errno = IESTREAMLISTEN; return -1; -@@ -375,8 +385,14 @@ iperf_tcp_connect(struct iperf_test *test) +@@ -380,8 +386,14 @@ iperf_tcp_connect(struct iperf_test *test) socklen_t optlen; int saved_errno; int rcvbuf_actual, sndbuf_actual; + int proto = 0; + -+#if defined(linux) ++#if defined(HAVE_IPPROTO_MPTCP) + if (test->mptcp) + proto = IPPROTO_MPTCP; +#endif @@ -195,7 +204,7 @@ index 184a1955e..a10322b75 100644 i_errno = IESTREAMCONNECT; return -1; diff --git a/src/net.c b/src/net.c -index c82caff1b..849e919f2 100644 +index b693ea7fb..febf20885 100644 --- a/src/net.c +++ b/src/net.c @@ -124,7 +124,7 @@ timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, @@ -243,7 +252,7 @@ index c82caff1b..849e919f2 100644 return -1; } diff --git a/src/net.h b/src/net.h -index f0e1b4f98..1f5cc4d34 100644 +index 859c52cef..fb78d289b 100644 --- a/src/net.h +++ b/src/net.h @@ -28,7 +28,7 @@ diff --git a/iperf3/patches/add-sock5-proxy-tcp-support.patch b/iperf3/patches/add-sock5-proxy-tcp-support.patch new file mode 100644 index 000000000..4cb90879c --- /dev/null +++ b/iperf3/patches/add-sock5-proxy-tcp-support.patch @@ -0,0 +1,552 @@ +From 197d8ba733f0502985abb5b0a22bf9f71c2596a7 Mon Sep 17 00:00:00 2001 +From: David Bar-On +Date: Mon, 25 Mar 2024 22:11:49 +0200 +Subject: [PATCH] Add SOCKS5 Proxy support for TCP + +--- + src/iperf.h | 8 ++ + src/iperf_api.c | 250 ++++++++++++++++++++++++++++++++++++++++- + src/iperf_api.h | 13 ++- + src/iperf_client_api.c | 27 ++++- + src/iperf_error.c | 10 ++ + src/iperf_locale.c | 2 + + src/iperf_tcp.c | 22 +++- + 7 files changed, 323 insertions(+), 9 deletions(-) + +diff --git a/src/iperf.h b/src/iperf.h +index dc3c0d1df..9823dc180 100644 +--- a/src/iperf.h ++++ b/src/iperf.h +@@ -343,6 +343,14 @@ struct iperf_test + int timestamps; /* --timestamps */ + char *timestamp_format; + ++ char *socks5_host; /* --socks5 option */ ++ uint16_t socks5_port; /* --socks5 option optional value */ ++ char *socks5_username; /* --socks5 option optional value */ ++ char *socks5_password; /* --socks5 option optional value */ ++ char socks5_bind_atyp; /* from socks5 CONNECT response ATYP */ ++ char *socks5_bind_host; /* from socks5 CONNECT response BIND.ADDR*/ ++ uint16_t socks5_bind_port; /* from socks5 CONNECT response BIND.PORT */ ++ + char *json_output_string; /* rendered JSON output if json_output is set */ + /* Select related parameters */ + int max_fd; +diff --git a/src/iperf_api.c b/src/iperf_api.c +index 4765d4e97..ca47f708d 100644 +--- a/src/iperf_api.c ++++ b/src/iperf_api.c +@@ -115,7 +115,7 @@ usage() + void + usage_long(FILE *f) + { +- fprintf(f, usage_longstr, DEFAULT_NO_MSG_RCVD_TIMEOUT, UDP_RATE / (1024*1024), DEFAULT_PACING_TIMER, DURATION, DEFAULT_TCP_BLKSIZE / 1024, DEFAULT_UDP_BLKSIZE); ++ fprintf(f, usage_longstr, DEFAULT_NO_MSG_RCVD_TIMEOUT, UDP_RATE / (1024*1024), DEFAULT_PACING_TIMER, DURATION, DEFAULT_TCP_BLKSIZE / 1024, DEFAULT_UDP_BLKSIZE, SOCKS5_DEFAULT_PORT); + } + + +@@ -1100,6 +1100,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) + {"version6", no_argument, NULL, '6'}, + {"tos", required_argument, NULL, 'S'}, + {"dscp", required_argument, NULL, OPT_DSCP}, ++ {"socks5", required_argument, NULL, OPT_SOCKS5}, + {"extra-data", required_argument, NULL, OPT_EXTRA_DATA}, + #if defined(HAVE_FLOWLABEL) + {"flowlabel", required_argument, NULL, 'L'}, +@@ -1157,7 +1158,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) + char* comma; + #endif /* HAVE_CPU_AFFINITY */ + char* slash; +- char *p, *p1; ++ char *p, *p1, *p2; + struct xbind_entry *xbe; + double farg; + int rcv_timeout_in = 0; +@@ -1433,6 +1434,47 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) + } + client_flag = 1; + break; ++ case OPT_SOCKS5: // Format: "[username:password@][:port]" ++ if (strlen(optarg) <= 0) { ++ i_errno = IESOCKS5HOST; ++ return -1; ++ } ++ p1 = strtok(optarg, "@"); // p1 -> user:password ++ if (p1 == NULL) { ++ i_errno = IESOCKS5HOST; ++ return -1; ++ } ++ p = strtok(NULL, "@"); // p -> host[:port] ++ if (p == NULL) { ++ p = p1; ++ p1 = NULL; ++ } ++ p2 = strtok(p, ":"); // parse host[:port] ++ if (strlen(p2) <= 0) { ++ i_errno = IESOCKS5HOST; ++ return -1; ++ } ++ test->socks5_host = strdup(p2); ++ p2 = strtok(NULL, ":"); ++ if (p2 && strlen(p2) > 0) { ++ test->socks5_port = atoi(p2); ++ } ++ if (p1) { // parse user:password ++ p2 = strtok(p1, ":"); ++ if (strlen(p2) <= 0 || strlen(p2) > 255) { ++ i_errno = IESOCKS5HOST; ++ return -1; ++ } ++ test->socks5_username = strdup(p2); ++ p2 = strtok(NULL, ":"); ++ if (!p2 || strlen(p2) <= 0 || strlen(p2) > 255) { ++ i_errno = IESOCKS5HOST; ++ return -1; ++ } ++ test->socks5_password = strdup(p2); ++ } ++ client_flag = 1; ++ break; + case OPT_EXTRA_DATA: + test->extra_data = strdup(optarg); + client_flag = 1; +@@ -1740,6 +1782,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) + return -1; + } + ++ // SOCKS5 Proxy is supported only for TCP ++ if(test->role == 'c' && test->socks5_host && test->protocol->id != Ptcp) { ++ i_errno = IESOCKS5RTCPONLY; ++ return -1; ++ } ++ + if (blksize == 0) { + if (test->protocol->id == Pudp) + blksize = 0; /* try to dynamically determine from MSS */ +@@ -2943,6 +2991,12 @@ iperf_defaults(struct iperf_test *testp) + testp->stats_interval = testp->reporter_interval = 1; + testp->num_streams = 1; + ++ testp->socks5_host = NULL; ++ testp->socks5_port = SOCKS5_DEFAULT_PORT; ++ testp->socks5_username = NULL; ++ testp->socks5_password = NULL; ++ testp->socks5_bind_host = NULL; ++ + testp->settings->domain = AF_UNSPEC; + testp->settings->unit_format = 'a'; + testp->settings->socket_bufsize = 0; /* use autotuning */ +@@ -3100,6 +3154,14 @@ iperf_free_test(struct iperf_test *test) + free(test->remote_congestion_used); + if (test->timestamp_format) + free(test->timestamp_format); ++ if (test->socks5_host) ++ free(test->socks5_host); ++ if (test->socks5_username) ++ free(test->socks5_username); ++ if (test->socks5_password) ++ free(test->socks5_password); ++ if (test->socks5_bind_host) ++ free(test->socks5_bind_host); + if (test->omit_timer != NULL) + tmr_cancel(test->omit_timer); + if (test->timer != NULL) +@@ -3289,6 +3351,23 @@ iperf_reset_test(struct iperf_test *test) + free(test->extra_data); + test->extra_data = NULL; + } ++ if (test->socks5_host) { ++ free(test->socks5_host); ++ test->socks5_host = NULL; ++ } ++ test->socks5_port = SOCKS5_DEFAULT_PORT; ++ if (test->socks5_username) { ++ free(test->socks5_username); ++ test->socks5_username = NULL; ++ } ++ if (test->socks5_password) { ++ free(test->socks5_password); ++ test->socks5_password = NULL; ++ } ++ if (test->socks5_bind_host) { ++ free(test->socks5_bind_host); ++ test->socks5_bind_host = NULL; ++ } + + /* Free output line buffers, if any (on the server only) */ + struct iperf_textline *t; +@@ -4614,6 +4693,173 @@ iperf_add_stream(struct iperf_test *test, struct iperf_stream *sp) + } + } + ++/**************************************************************************/ ++ ++/* iperf_socks5_handshake ++ * ++ * Handshake with a SOCKS5 Proxy per RFC1928, RFC1929 ++ */ ++int ++iperf_socks5_handshake(struct iperf_test *test, int s) { ++ char req[1024]; ++ char res[1024]; ++ char selected_mthod; ++ char *p, *p1; ++ size_t len; ++ int ret; ++ uint16_t net_order_short; ++ ++ // Send method selection request [RFC1928] ++ p = req; ++ *p++ = 5; // VERSION ++ if (test->socks5_username) // Number of METHODs supported ++ *p++ = 2; ++ else ++ *p++ = 1; ++ *p++ = 0; // NO AUTHENTICATION REQUIRED ++ if (test->socks5_username) *p++ = 2; // USERNAME/PASSWORD ++ if (Nwrite(s, req, p - req, Ptcp) < 0) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Writing SOCKS5 auth methods message failed\n"); ++ return -1; ++ } ++ ++ // Receive selected method ++ if (Nread(s, res, 2, Ptcp) != 2) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Reading selected SOCKS5 method message failed\n"); ++ return -1; ++ } ++ ++ selected_mthod = res[1]; ++ if (res[0] != 5 || (selected_mthod != 0 && selected_mthod != 2)) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Ilegal SOCKS5 method selection response: version=%d, auth method=%d\n", res[0], selected_mthod); ++ return -1; ++ } ++ if (test->debug) { ++ iperf_printf(test, "SOCKS5 server selected authentication method %d\n", selected_mthod); ++ } ++ ++ // Send Username/Password request and receive the auth response [RFC1929] ++ if (selected_mthod == 2) { ++ p = req; ++ *p++ = 1; // VERSION ++ len = strlen(test->socks5_username); ++ *p++ = len; ++ memcpy(p, test->socks5_username, len); // USERNAME ++ p += len; ++ len = strlen(test->socks5_password); ++ *p++ = len; ++ memcpy(p, test->socks5_password, len); // PASSWORD ++ p += len; ++ ++ if (Nwrite(s, req, p - req, Ptcp) < 0) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Writing SOCKS5 Username/Password request message failed\n"); ++ return -1; ++ } ++ ++ if ((ret = Nread(s, res, 2, Ptcp)) != 2) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Reading SOCKS5 Username/Password response failed; Returned %d\n", ret); ++ return -1; ++ } ++ if (res[1] != 0) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "SOCKS5 Username/Password failed with error %d\n", res[1]); ++ return -1; ++ } ++ } ++ ++ // Send CONNECT request [RFC1928] ++ p = req; ++ *p++ = 5; // VERSION ++ *p++ = 1; // CMD = CONNECT ++ *p++ = 0; // RESERVED ++ *p++ = 3; // ATYPE = DOMAINNAME: ++ len = strlen(test->server_hostname); ++ if (len > 255) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "iperf3 host option length is limited to 255 chars when SOCKS5 is used\n"); ++ return -1; ++ } ++ *p++ = len; ++ memcpy(p, test->server_hostname, len); // ADDR ++ p += len; ++ net_order_short = htons(test->server_port); ++ p1 = (char *)&net_order_short; ++ *p++ = *p1++; // PORT ++ *p++ = *p1; ++ if (Nwrite(s, req, p - req, Ptcp) < 0) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Writing SOCKS5 CONNECT message failed\n"); ++ return -1; ++ } ++ ++ // Read CONNECT response [RFC1928] ++ if ((ret = Nread(s, res, 4, Ptcp)) != 4) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Reading SOCKS5 CONNECT response failed; Returned %d\n", ret); ++ return -1; ++ } ++ ++ if (res[0] != 5 || res[1] != 0 || res[2] != 0) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "SOCKS5 CONNECT failed with error %d\n", res[1]); ++ return -1; ++ } ++ ++ // Get BND.ADDR length ++ test->socks5_bind_atyp = res[3]; // ATYP ++ switch (test->socks5_bind_atyp) { ++ case 1: // IP V4 address ++ len = 4; ++ break; ++ case 3: // DOMAINNAME: ++ if ((ret = read(s, res, 1)) != 1) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Failed to read SOCKS5 CONNECT response BND.ADDR length; Returned %d\n", ret); ++ return -1; ++ } ++ len = (unsigned char)res[0]; ++ break; ++ case 4: // IP V6 address ++ len = 16; ++ break; ++ default: ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Illegal SOCKS5 CONNECT response ATYP %d\n", res[3]); ++ return -1; ++ } ++ // Read BND.ADDR ++ if ((ret = Nread(s, res, len, Ptcp)) != len) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Failed to read SOCKS5 detailes BND.ADDR; Returned %d\n", ret); ++ return -1; ++ } ++ res[len] = '\0'; ++ test->socks5_bind_host = strdup(res); ++ // Read BND.PORT ++ if ((ret = Nread(s, res, 2, Ptcp)) != 2) { ++ i_errno = IESOCKS5HANDSHAKE; ++ iperf_err(test, "Failed to read SOCKS5 detailes BND.PORT; Returned %d\n", ret); ++ return -1; ++ } ++ p1 = (char *)&net_order_short; ++ *p1++ = res[0]; ++ *p1 = res[1]; ++ test->socks5_bind_port = ntohs(net_order_short); ++ if (test->debug) { ++ iperf_printf(test, "SOCKS5 server BIND ADDR type=%d, PORT=%d\n", test->socks5_bind_atyp, test->socks5_bind_port); ++ } ++ ++ return 0; ++} ++ ++/**************************************************************************/ ++ ++ + /* This pair of routines gets inserted into the snd/rcv function pointers + ** when there's a -F flag. They handle the file stuff and call the real + ** snd/rcv functions, which have been saved in snd2/rcv2. +diff --git a/src/iperf_api.h b/src/iperf_api.h +index d2bbdfe96..01d63bf5e 100644 +--- a/src/iperf_api.h ++++ b/src/iperf_api.h +@@ -68,6 +68,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t; + #define DEFAULT_PACING_TIMER 1000 + #define DEFAULT_NO_MSG_RCVD_TIMEOUT 120000 + #define MIN_NO_MSG_RCVD_TIMEOUT 100 ++#define SOCKS5_DEFAULT_PORT 1080 + + #define WARN_STR_LEN 128 + +@@ -100,7 +101,8 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t; + #define OPT_RCV_TIMEOUT 27 + #define OPT_JSON_STREAM 28 + #define OPT_SND_TIMEOUT 29 + #define OPT_USE_PKCS1_PADDING 30 ++#define OPT_SOCKS5 31 + + /* states */ + #define TEST_START 1 +@@ -308,6 +310,12 @@ void iperf_free_stream(struct iperf_stream * sp); + */ + int iperf_common_sockopts(struct iperf_test *, int s); + ++/** ++ * iperf_socks5_handshake - handshake with a SOCKS5 Proxy per RFC1928, RFC1929 ++ * ++ */ ++int iperf_socks5_handshake(struct iperf_test *test, int s); ++ + int has_tcpinfo(void); + int has_tcpinfo_retransmits(void); + void save_tcpinfo(struct iperf_stream *sp, struct iperf_interval_results *irp); +@@ -419,6 +427,8 @@ enum { + IESNDTIMEOUT = 33, // Illegal message send timeout + IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP + IESERVERAUTHUSERS = 35, // Cannot access authorized users file ++ IESOCKS5HOST = 36, // Illegal SOCKS5 host / creadentials ++ IESOCKS5RTCPONLY = 37, // SOCKS5 Proxy is supported only for TCP + /* Test errors */ + IENEWTEST = 100, // Unable to create a new test (check perror) + IEINITTEST = 101, // Test initialization failed (check perror) +@@ -473,8 +483,9 @@ enum { + IEPTHREADCANCEL=151, // Unable to cancel thread (check perror) + IEPTHREADJOIN=152, // Unable to join thread (check perror) + IEPTHREADATTRINIT=153, // Unable to initialize thread attribute (check perror) + IEPTHREADATTRDESTROY=154, // Unable to destroy thread attribute (check perror) + IEPTHREADSIGMASK=155, // Unable to initialize sub thread signal mask (check perror) ++ IESOCKS5HANDSHAKE = 156, // SOCKS5 Handshake with the server failed + /* 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_client_api.c b/src/iperf_client_api.c +index 7ad4c939b..670e3521d 100644 +--- a/src/iperf_client_api.c ++++ b/src/iperf_client_api.c +@@ -385,6 +385,8 @@ iperf_connect(struct iperf_test *test) + { + int opt; + socklen_t len; ++ const char *connect_server; ++ int connect_port; + + if (NULL == test) + { +@@ -397,12 +399,20 @@ iperf_connect(struct iperf_test *test) + make_cookie(test->cookie); + + /* Create and connect the control channel */ +- if (test->ctrl_sck < 0) +- // Create the control channel using an ephemeral port +- 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) { +- i_errno = IECONNECT; +- return -1; ++ if (test->socks5_host) { ++ connect_server = test->socks5_host; ++ connect_port = test->socks5_port; ++ } else { ++ connect_server = test->server_hostname; ++ connect_port = test->server_port; ++ } ++ // Create the control channel using an ephemeral port ++ test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, 0, connect_server, connect_port, test->settings->connect_timeout); ++ if (test->ctrl_sck < 0) { ++ i_errno = IECONNECT; ++ return -1; ++ } + } + + // set TCP_NODELAY for lower latency on control messages +@@ -421,6 +431,13 @@ iperf_connect(struct iperf_test *test) + } + #endif /* HAVE_TCP_USER_TIMEOUT */ + ++ /* socks5 proxy handshake */ ++ if (test->socks5_host) { ++ if (0 != iperf_socks5_handshake(test, test->ctrl_sck)) { ++ return -1; ++ } ++ } ++ + if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) { + i_errno = IESENDCOOKIE; + return -1; +diff --git a/src/iperf_error.c b/src/iperf_error.c +index 6426554cf..a0bbb6844 100644 +--- a/src/iperf_error.c ++++ b/src/iperf_error.c +@@ -216,6 +216,9 @@ iperf_strerror(int int_errno) + case IEUNIMP: + snprintf(errstr, len, "an option you are trying to set is not implemented yet"); + break; ++ case IESOCKS5HOST: ++ snprintf(errstr, len, "ilegal SOCKS5 host / creadentials"); ++ break; + case IEFILE: + snprintf(errstr, len, "unable to open -F file"); + perr = 1; +@@ -375,6 +378,9 @@ iperf_strerror(int int_errno) + case IEUDPFILETRANSFER: + snprintf(errstr, len, "cannot transfer file using UDP"); + break; ++ case IESOCKS5RTCPONLY: ++ snprintf(errstr, len, "SOCKS5 Proxy is supported only for TCP"); ++ break; + case IERVRSONLYRCVTIMEOUT: + snprintf(errstr, len, "client receive timeout is valid only in receiving mode"); + perr = 1; +@@ -507,6 +513,10 @@ iperf_strerror(int int_errno) + snprintf(errstr, len, "unable to destroy thread attributes"); + perr = 1; + break; ++ case IESOCKS5HANDSHAKE: ++ snprintf(errstr, len, "socks5 Handshake with the server failed"); ++ perr = 1; ++ 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 ae0f63a41..c8b9a71d1 100644 +--- a/src/iperf_locale.c ++++ b/src/iperf_locale.c +@@ -194,6 +194,8 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" + " --dscp N or --dscp val set the IP dscp value, either 0-63 or symbolic.\n" + " Numeric values can be specified in decimal,\n" + " octal and hex (see --tos above).\n" ++ " --socks5 [user:password@][:port] use SOCKS5 Proxy for TCP connections,\n" ++ " using no auth or user:password. Default Proxy port is %d \n" + #if defined(HAVE_FLOWLABEL) + " -L, --flowlabel N set the IPv6 flow label (only supported on Linux)\n" + #endif /* HAVE_FLOWLABEL */ +diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c +index 184a1955e..515913581 100644 +--- a/src/iperf_tcp.c ++++ b/src/iperf_tcp.c +@@ -375,14 +375,24 @@ iperf_tcp_connect(struct iperf_test *test) + socklen_t optlen; + int saved_errno; + int rcvbuf_actual, sndbuf_actual; + int proto = 0; ++ const char *connect_server; ++ int connect_port; + + #if defined(HAVE_IPPROTO_MPTCP) + if (test->mptcp) + proto = IPPROTO_MPTCP; + #endif + +- s = create_socket(test->settings->domain, SOCK_STREAM, proto, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, &server_res); ++ if (test->socks5_host) { ++ connect_server = test->socks5_host; ++ connect_port = test->socks5_port; ++ } else { ++ connect_server = test->server_hostname; ++ connect_port = test->server_port; ++ } ++ ++ s = create_socket(test->settings->domain, SOCK_STREAM, proto, test->bind_address, test->bind_dev, test->bind_port, connect_server, connect_port, &server_res); + if (s < 0) { + i_errno = IESTREAMCONNECT; + return -1; +@@ -571,6 +581,16 @@ iperf_tcp_connect(struct iperf_test *test) + + freeaddrinfo(server_res); + ++ /* socks5 proxy handshake */ ++ if (test->socks5_host) { ++ if (0 != iperf_socks5_handshake(test, s)) { ++ saved_errno = errno; ++ close(s); ++ errno = saved_errno; ++ return -1; ++ } ++ } ++ + /* Send cookie for verification */ + if (Nwrite(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) { + saved_errno = errno; diff --git a/openmptcprouter/files/bin/omr-iperf b/openmptcprouter/files/bin/omr-iperf index 6ab611328..5ee506a69 100755 --- a/openmptcprouter/files/bin/omr-iperf +++ b/openmptcprouter/files/bin/omr-iperf @@ -10,7 +10,8 @@ PORTS=$(uci -q get iperf.$SERVER.ports | sed 's/,/ /g') PORT="${PORTS%% *}" echo $KEY | base64 -d > /tmp/iperf.pem if [ -n "$PASSWORD" ] && [ -n "$USER" ] && [ -n "$KEY" ]; then - IPERF3_PASSWORD=$PASSWORD iperf3 --username $USER --rsa-public-key-path /tmp/iperf.pem --use-pkcs1-padding -c $HOST -p $PORT ${@} + #IPERF3_PASSWORD=$PASSWORD iperf3 --username $USER --rsa-public-key-path /tmp/iperf.pem --use-pkcs1-padding -c $HOST -p $PORT ${@} + IPERF3_PASSWORD=$PASSWORD iperf3 --username $USER --rsa-public-key-path /tmp/iperf.pem -c $HOST -p $PORT ${@} else iperf3 -c $HOST -p $PORT ${@} fi