Index | Thread | Search

From:
Klemens Nanni <kn@openbsd.org>
Subject:
iperf3: unbreak rcctl with IPv6 link-local bind/source addresses
To:
ports <ports@openbsd.org>
Date:
Thu, 30 Oct 2025 17:49:15 +0000

Download raw body.

Thread
If the argument contains a '%', the code will modify argv[] in place,
which causes rc.subr(8)'s pexp aka. pgrep(1) to mismatch and thus
rcctl(8) to report failure despite the service running fine:

	$ rcctl get iperf3 flags
	-6 --bind fe80::1%vport0
	$ rcctl check iperf3
	iperf3(failed)
	$ pgrep -fl iperf3
	33091 /usr/local/bin/iperf3 -s -D -6 --bind fe80::1


Funnily, the code knows that:

	 * 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) {


Simply pass a copy to that function to keep scope identifies in argv[]:

	$ rcctl check iperf3
	iperf3(ok)
	$ pgrep -fl iperf3
	98863 /usr/local/bin/iperf3 -s -D -6 --bind fe80::1%vport0

--client works the same as --bind, so fix it as well.

Feedback? OK?


Index: Makefile
===================================================================
RCS file: /cvs/ports/net/iperf3/Makefile,v
diff -u -p -r1.21 Makefile
--- Makefile	26 Dec 2024 18:12:38 -0000	1.21
+++ Makefile	30 Oct 2025 17:08:08 -0000
@@ -3,6 +3,7 @@ COMMENT=	tool to measure maximum achieva
 V=		3.18
 DISTNAME=	iperf-${V}
 PKGNAME=	iperf3-${V}
+REVISION=	0
 
 SHARED_LIBS +=	iperf                5.0      # 0.0
 
Index: patches/patch-src_iperf_api_c
===================================================================
RCS file: /cvs/ports/net/iperf3/patches/patch-src_iperf_api_c,v
diff -u -p -r1.12 patch-src_iperf_api_c
--- patches/patch-src_iperf_api_c	26 Dec 2024 18:12:38 -0000	1.12
+++ patches/patch-src_iperf_api_c	30 Oct 2025 17:31:59 -0000
@@ -1,9 +1,64 @@
-Default to IPv4.
+- Pass a copy of argv[] to prevent changes by strtok(3),
+  otherwise rc.subr(8) pexp might not match and cause rcctl failure.
+- Default to IPv4.
 
 Index: src/iperf_api.c
 --- src/iperf_api.c.orig
 +++ src/iperf_api.c
-@@ -2998,7 +2998,7 @@ iperf_defaults(struct iperf_test *testp)
+@@ -1243,7 +1243,7 @@ iperf_parse_arguments(struct iperf_test *test, int arg
+                 }
+ 		iperf_set_test_role(test, 's');
+                 break;
+-            case 'c':
++            case 'c': {
+                 if (test->role == 's') {
+                     i_errno = IESERVCLIENT;
+                     return -1;
+@@ -1251,7 +1251,8 @@ iperf_parse_arguments(struct iperf_test *test, int arg
+ 		iperf_set_test_role(test, 'c');
+ 		iperf_set_test_server_hostname(test, optarg);
+ 
+-                if (iperf_parse_hostname(test, optarg, &p, &p1)) {
++                char *arg = strdup(optarg);
++                if (iperf_parse_hostname(test, arg, &p, &p1)) {
+ #if defined(HAVE_SO_BINDTODEVICE)
+                     /* Get rid of the hostname we saved earlier. */
+                     free(iperf_get_test_server_hostname(test));
+@@ -1262,7 +1263,9 @@ iperf_parse_arguments(struct iperf_test *test, int arg
+                     return -1;
+ #endif /* HAVE_SO_BINDTODEVICE */
+                 }
++                free(arg);
+                 break;
++            }
+             case 'u':
+                 set_protocol(test, Pudp);
+ 		client_flag = 1;
+@@ -1374,10 +1377,11 @@ iperf_parse_arguments(struct iperf_test *test, int arg
+ 		client_flag = 1;
+                 break;
+ 
+-            case 'B':
++            case 'B': {
+                 iperf_set_test_bind_address(test, optarg);
+ 
+-                if (iperf_parse_hostname(test, optarg, &p, &p1)) {
++                char *arg = strdup(optarg);
++                if (iperf_parse_hostname(test, arg, &p, &p1)) {
+ #if defined(HAVE_SO_BINDTODEVICE)
+                     /* Get rid of the hostname we saved earlier. */
+                     free(iperf_get_test_bind_address(test));
+@@ -1388,7 +1392,9 @@ iperf_parse_arguments(struct iperf_test *test, int arg
+                     return -1;
+ #endif /* HAVE_SO_BINDTODEVICE */
+                 }
++                free(arg);
+                 break;
++            }
+ #if defined (HAVE_SO_BINDTODEVICE)
+             case OPT_BIND_DEV:
+                 iperf_set_test_bind_dev(test, optarg);
+@@ -2998,7 +3004,7 @@ iperf_defaults(struct iperf_test *testp)
      testp->stats_interval = testp->reporter_interval = 1;
      testp->num_streams = 1;