Package: dhcpcd
Version: 1:10.3.0-1
Severity: normal
Tags: patch  pending

Dear maintainer,

I've prepared an NMU for dhcpcd (versioned as 1:10.3.0-1.1) and
uploaded it to DELAYED/5. LMK if you have any concerns.

--Daniel
diffstat for dhcpcd-10.3.0 dhcpcd-10.3.0

 changelog                                                  |   10 
 dhcpcd.dhcpcd.init                                         |    2 
 dhcpcd.dhcpcd.service                                      |    2 
 [email protected]                                     |    3 
 patches/manager-Fix-loosing-iface-options-on-CARRIER.patch |  217 +++++++++++++
 patches/series                                             |    1 
 6 files changed, 232 insertions(+), 3 deletions(-)

diff -Nru dhcpcd-10.3.0/debian/changelog dhcpcd-10.3.0/debian/changelog
--- dhcpcd-10.3.0/debian/changelog	2025-11-14 20:14:38.000000000 +0100
+++ dhcpcd-10.3.0/debian/changelog	2025-11-19 15:39:30.000000000 +0100
@@ -1,3 +1,13 @@
+dhcpcd (1:10.3.0-1.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Add patch "manager: Fix loosing iface options on CARRIER".
+    (Closes: #1121014)
+  * Run Before=networking.service for ifupdown interoperability.
+    (Closes: #1121016)
+
+ -- Daniel Gröber <[email protected]>  Wed, 19 Nov 2025 15:39:30 +0100
+
 dhcpcd (1:10.3.0-1) unstable; urgency=low
 
   * New upstream version.
diff -Nru dhcpcd-10.3.0/debian/dhcpcd.dhcpcd.init dhcpcd-10.3.0/debian/dhcpcd.dhcpcd.init
--- dhcpcd-10.3.0/debian/dhcpcd.dhcpcd.init	2025-11-14 20:14:38.000000000 +0100
+++ dhcpcd-10.3.0/debian/dhcpcd.dhcpcd.init	2025-11-19 15:39:30.000000000 +0100
@@ -3,7 +3,7 @@
 # Provides:          dhcpcd
 # Required-Start:    $local_fs $remote_fs
 # Required-Stop:     $local_fs $remote_fs
-# Should-Start:      networking
+# X-Start-Before:    networking
 # Should-Stop:
 # Default-Start:     2 3 4 5
 # Default-Stop:      0 1 6
diff -Nru dhcpcd-10.3.0/debian/dhcpcd.dhcpcd.service dhcpcd-10.3.0/debian/dhcpcd.dhcpcd.service
--- dhcpcd-10.3.0/debian/dhcpcd.dhcpcd.service	2025-11-14 20:14:38.000000000 +0100
+++ dhcpcd-10.3.0/debian/dhcpcd.dhcpcd.service	2025-11-19 15:39:30.000000000 +0100
@@ -2,7 +2,7 @@
 [Unit]
 Description=DHCP Client Daemon on all interfaces
 Wants=network.target
-After=networking.service
+Before=networking.service
 Before=network.target
 Documentation=man:dhcpcd(8)
 
diff -Nru dhcpcd-10.3.0/debian/[email protected] dhcpcd-10.3.0/debian/[email protected]
--- dhcpcd-10.3.0/debian/[email protected]	2025-11-14 20:14:38.000000000 +0100
+++ dhcpcd-10.3.0/debian/[email protected]	2025-11-19 15:39:30.000000000 +0100
@@ -2,7 +2,8 @@
 [Unit]
 Description=DHCP Client Daemon on %I
 Wants=network.target
-After=networking.service sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+Before=networking.service
 Before=network.target
 BindsTo=sys-subsystem-net-devices-%i.device
 Documentation=man:dhcpcd(8)
diff -Nru dhcpcd-10.3.0/debian/patches/manager-Fix-loosing-iface-options-on-CARRIER.patch dhcpcd-10.3.0/debian/patches/manager-Fix-loosing-iface-options-on-CARRIER.patch
--- dhcpcd-10.3.0/debian/patches/manager-Fix-loosing-iface-options-on-CARRIER.patch	1970-01-01 01:00:00.000000000 +0100
+++ dhcpcd-10.3.0/debian/patches/manager-Fix-loosing-iface-options-on-CARRIER.patch	2025-11-19 15:39:30.000000000 +0100
@@ -0,0 +1,217 @@
+From 112cc12b8dabef6ca41558016e97bfa03b71df0d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= <[email protected]>
+Date: Thu, 6 Nov 2025 14:50:15 +0100
+Subject: manager: Fix loosing iface options on CARRIER
+
+When an interface (re-)gains carrier dhcpcd_handlecarrier() runs
+dhcpcd_initstate() to kick off profile re-selection. Previously this used
+args originally passed when starting the manager (ctx->argv).
+
+However interfaces started via the manager control
+interface (dhcpcd_initstate1() in dhcpcd_handleargs()) may be started with
+different args.
+
+For example if we start a manager with
+
+    dhcpcd -M --inactive
+
+and then start only IPv4 on an interface with
+
+    dhcpcd -4 iface0
+
+a subsequent CARRIER event will reset the interface to what amounts to
+"default config + `-M --inactive`" which in this case will enable ipv6
+also!
+
+To fix this we keep a copy of the arguments used to start an interface in
+the manager (dhcpcd_handleargs()) code path around around (ifp->argv).
+
+In the current implementation args passed for renew following the initial
+interface start will not be persisted. This causes the interface to reset
+to a state of "defaults + config + profile + start-cmdline".
+
+For example (continuing the scenario above) after enabling ipv6 with -n:
+
+    $ dhcpcd -6 -n iface0
+
+A subsequent CARRIER event will disable ipv6 again as the effective
+arguments remain `-4 iface0` as passed during interface start.
+
+Note the per-interface daemon code path wasn't affected as ctx->args
+already contains the interface start args.
+---
+ src/dhcpcd.c     | 21 +++++++++++++----
+ src/dhcpcd.h     |  3 +++
+ src/if-options.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
+ src/if-options.h |  3 +++
+ src/if.c         |  2 ++
+ 5 files changed, 84 insertions(+), 5 deletions(-)
+
+--- a/src/dhcpcd.c
++++ b/src/dhcpcd.c
+@@ -729,7 +729,10 @@ static void
+ dhcpcd_initstate(struct interface *ifp, unsigned long long options)
+ {
+ 
+-	dhcpcd_initstate1(ifp, ifp->ctx->argc, ifp->ctx->argv, options);
++	dhcpcd_initstate1(ifp,
++			  ifp->argc ?: ifp->ctx->argc,
++			  ifp->argv ?: ifp->ctx->argv,
++			  options);
+ }
+ 
+ static void
+@@ -1364,7 +1367,7 @@ if_reboot(struct interface *ifp, int arg
+ 	oldopts = ifp->options->options;
+ #endif
+ 	script_runreason(ifp, "RECONFIGURE");
+-	dhcpcd_initstate1(ifp, argc, argv, 0);
++	dhcpcd_initstate1(ifp, argc, argv, 0); // control or main argv
+ #ifdef INET
+ 	if (ifp->options->options & DHCPCD_DHCP)
+ 		dhcp_reboot_newopts(ifp, oldopts);
+@@ -1419,8 +1422,16 @@ reconf_reboot(struct dhcpcd_ctx *ctx, in
+ 				ipv4_applyaddr(ifp);
+ #endif
+ 		} else if (i != argc) {
++			/* iface wasnt found above -> it's new. start it. */
+ 			ifp->active = IF_ACTIVE_USER;
+-			dhcpcd_initstate1(ifp, argc, argv, 0);
++			dhcpcd_initstate1(ifp, argc, argv, 0); // control cmd args
++
++			if (ifp->argv)
++				free_argv_copy(ifp->argv);
++			ifp->argv = copy_argv(argc, argv);
++			if (ifp->argv)
++				ifp->argc = argc;
++
+ 			run_preinit(ifp);
+ 			dhcpcd_prestartinterface(ifp);
+ 		}
+@@ -1773,7 +1784,7 @@ dumperr:
+ 	}
+ 
+ 	reload_config(ctx);
+-	/* XXX: Respect initial commandline options? */
++	/* Respect control cmd options! */
+ 	reconf_reboot(ctx, do_reboot, argc, argv, oifind);
+ 	return 0;
+ }
+@@ -2680,7 +2691,7 @@ start_manager:
+ 
+ 	TAILQ_FOREACH(ifp, ctx.ifaces, next) {
+ 		if (ifp->active)
+-			dhcpcd_initstate1(ifp, argc, argv, 0);
++			dhcpcd_initstate1(ifp, argc, argv, 0); // main argv
+ 	}
+ 	if_learnaddrs(&ctx, ctx.ifaces, &ifaddrs);
+ 	if_freeifaddrs(&ctx, &ifaddrs);
+--- a/src/dhcpcd.h
++++ b/src/dhcpcd.h
+@@ -85,6 +85,9 @@ struct interface {
+ 	uint8_t ssid[IF_SSIDLEN];
+ 	unsigned int ssid_len;
+ 
++	int argc;
++	char **argv;
++
+ 	char profile[PROFILE_LEN];
+ 	struct if_options *options;
+ 	void *if_data[IF_DATA_MAX];
+--- a/src/if-options.c
++++ b/src/if-options.c
+@@ -44,6 +44,7 @@
+ #include <string.h>
+ #include <unistd.h>
+ #include <time.h>
++#include <assert.h>
+ 
+ #include "config.h"
+ #include "common.h"
+@@ -2986,6 +2987,65 @@ add_options(struct dhcpcd_ctx *ctx, cons
+ 	return r;
+ }
+ 
++#define ARGV_COPY_MAGIC ((char *)0x5a54292d273f3d34)
++/*^ intentional truncation on 32bit arches */
++
++char **copy_argv(int argc, char **argv)
++{
++	int i;
++	size_t strslen = 0;
++	for (i = 0; i < argc; i++) {
++		strslen += strlen(argv[i]) + 1;
++	}
++	if (strslen == 0) // also handles argc < 0
++		return NULL;
++
++	unsigned nptrs = 1 + (unsigned)argc + 1;
++	size_t ptrslen =  nptrs * sizeof(char *);
++	void *buf = malloc(ptrslen + strslen);
++	char **ptrs = buf;
++	if (!buf)
++		return NULL;
++
++	ptrs[0] = ARGV_COPY_MAGIC;
++	ptrs[nptrs - 1] = NULL;
++
++	if (argc == 0)
++		goto out;
++
++	char *strsp = (char *)&ptrs[nptrs];
++	for (i = 0; i < argc; i++) {
++		size_t len = strlcpy(strsp, argv[i], strslen);
++		if (len >= strslen) // truncated
++			goto err;
++
++		ptrs[1 + i] = strsp;
++
++		strsp += len + 1;
++		if (strslen < len + 1)
++			goto err;
++		strslen -= len + 1;
++	}
++
++	assert(strslen == 0);
++	assert(ptrs[nptrs - 1] == NULL);
++out:
++	return &ptrs[1];
++
++err:
++	free(buf);
++	return NULL;
++}
++
++void free_argv_copy(char **argv)
++{
++	assert(argv[-1] == ARGV_COPY_MAGIC);
++	if (argv[-1] != ARGV_COPY_MAGIC) {
++		logerrx("%s: invalid argv", __func__);
++	} else
++		free(&argv[-1]);
++}
++
+ void
+ free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)
+ {
+--- a/src/if-options.h
++++ b/src/if-options.h
+@@ -322,4 +322,7 @@ int add_options(struct dhcpcd_ctx *, con
+ void free_dhcp_opt_embenc(struct dhcp_opt *);
+ void free_options(struct dhcpcd_ctx *, struct if_options *);
+ 
++char **copy_argv(int argc, char **argv);
++void free_argv_copy(char **argv);
++
+ #endif
+--- a/src/if.c
++++ b/src/if.c
+@@ -100,6 +100,8 @@ if_free(struct interface *ifp)
+ #endif
+ 	rt_freeif(ifp);
+ 	free_options(ifp->ctx, ifp->options);
++	if (ifp->argv)
++		free_argv_copy(ifp->argv);
+ 	free(ifp);
+ }
+ 
diff -Nru dhcpcd-10.3.0/debian/patches/series dhcpcd-10.3.0/debian/patches/series
--- dhcpcd-10.3.0/debian/patches/series	2025-11-14 20:14:38.000000000 +0100
+++ dhcpcd-10.3.0/debian/patches/series	2025-11-19 15:39:30.000000000 +0100
@@ -1 +1,2 @@
 Debian_uncomment_hostname_in_dhcpcd_conf.patch
+manager-Fix-loosing-iface-options-on-CARRIER.patch

Attachment: signature.asc
Description: PGP signature

Reply via email to