Path: csiph.com!xmission!news.neodome.net!nntp.xenet.de!news.nntp4.net!news.hispagatos.org!srl.newsdeef.eu!news.corradoroberto.it!gothmog.csi.it!bofh.it!news.nic.it!robomod From: Daniel =?utf-8?Q?Gr=C3=B6ber?= Newsgroups: linux.debian.bugs.dist Subject: Bug#1121014: dhcpcd-base: Manager forgets iface cmdline opts on CARRIER event Date: Wed, 19 Nov 2025 16:10:02 +0100 Message-ID: X-Mailbox-Line: From debian-bugs-dist-request@lists.debian.org Wed Nov 19 15:09:11 2025 Old-Return-Path: X-Spam-Flag: NO X-Spam-Score: -2.599 Reply-To: Daniel =?UTF-8?Q?Gr=C3=B6ber?= , 1121014@bugs.debian.org Resent-To: debian-bugs-dist@lists.debian.org Resent-Cc: dxld@darkboxed.org, martin-eric.racine@iki.fi X-Debian-Pr-Message: report 1121014 X-Debian-Pr-Package: dhcpcd-base X-Debian-Pr-Source: dhcpcd, dhcpcd5 X-Reportbug-Version: 13.2.0 MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="hs3yityyadmocazc" Content-Disposition: inline X-Debian-Message: from BTS X-Mailing-List: archive/latest/1937587 List-ID: List-URL: Approved: robomod@news.nic.it Lines: 289 Organization: linux.* mail to news gateway Sender: robomod@news.nic.it X-Original-Date: Wed, 19 Nov 2025 16:08:00 +0100 X-Original-Message-ID: Xref: csiph.com linux.debian.bugs.dist:1270810 --hs3yityyadmocazc Content-Type: multipart/mixed; protected-headers=v1; boundary="qwveatom6yh56t6s" Content-Disposition: inline Subject: dhcpcd-base: Manager forgets iface cmdline opts on CARRIER event MIME-Version: 1.0 --qwveatom6yh56t6s Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Package: dhcpcd-base Version: 1:10.1.0-11 Severity: important Forwarded: https://github.com/NetworkConfiguration/dhcpcd/pull/548 X-Debbugs-Cc: dxld@darkboxed.org Dear Maintainer, While working on dhcpcd<>ifupdown integration I found that interface UP/DOWN events (cable re-plugging) can clear options that were passed when asking the global manager (dhcpcd.service) to start the interface initially. This doesn't happen when using per-iface (as used by ifupdown=0.8.44) or per-iface-per-af. The attached patch fixes the issue and has more detailed rationale. My preferred design for future-proof ifupdown<>dhcpcd.service interoperability relies on this not being broken, I will be uploading an NMU (1:10.3.0-1.1) to DELAYED/5 soon so my pending ifupdown=0.9 upload will not regress or have to hack around this. Thanks, --Daniel FYI: Find nomenclature and discussion in https://lists.debian.org/debian-devel/2025/10/msg00201.html --qwveatom6yh56t6s Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="manager-Fix-loosing-iface-options-on-CARRIER.patch" Content-Transfer-Encoding: quoted-printable =46rom 112cc12b8dabef6ca41558016e97bfa03b71df0d Mon Sep 17 00:00:00 2001 =46rom: =3D?UTF-8?q?Daniel=3D20Gr=3DC3=3DB6ber?=3D 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) { =20 - dhcpcd_initstate1(ifp, ifp->ctx->argc, ifp->ctx->argv, options); + dhcpcd_initstate1(ifp, + ifp->argc ?: ifp->ctx->argc, + ifp->argv ?: ifp->ctx->argv, + options); } =20 static void @@ -1364,7 +1367,7 @@ if_reboot(struct interface *ifp, int arg oldopts =3D 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 !=3D argc) { + /* iface wasnt found above -> it's new. start it. */ ifp->active =3D 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 =3D copy_argv(argc, argv); + if (ifp->argv) + ifp->argc =3D argc; + run_preinit(ifp); dhcpcd_prestartinterface(ifp); } @@ -1773,7 +1784,7 @@ dumperr: } =20 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: =20 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; =20 + 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 #include #include +#include =20 #include "config.h" #include "common.h" @@ -2986,6 +2987,65 @@ add_options(struct dhcpcd_ctx *ctx, cons return r; } =20 +#define ARGV_COPY_MAGIC ((char *)0x5a54292d273f3d34) +/*^ intentional truncation on 32bit arches */ + +char **copy_argv(int argc, char **argv) +{ + int i; + size_t strslen =3D 0; + for (i =3D 0; i < argc; i++) { + strslen +=3D strlen(argv[i]) + 1; + } + if (strslen =3D=3D 0) // also handles argc < 0 + return NULL; + + unsigned nptrs =3D 1 + (unsigned)argc + 1; + size_t ptrslen =3D nptrs * sizeof(char *); + void *buf =3D malloc(ptrslen + strslen); + char **ptrs =3D buf; + if (!buf) + return NULL; + + ptrs[0] =3D ARGV_COPY_MAGIC; + ptrs[nptrs - 1] =3D NULL; + + if (argc =3D=3D 0) + goto out; + + char *strsp =3D (char *)&ptrs[nptrs]; + for (i =3D 0; i < argc; i++) { + size_t len =3D strlcpy(strsp, argv[i], strslen); + if (len >=3D strslen) // truncated + goto err; + + ptrs[1 + i] =3D strsp; + + strsp +=3D len + 1; + if (strslen < len + 1) + goto err; + strslen -=3D len + 1; + } + + assert(strslen =3D=3D 0); + assert(ptrs[nptrs - 1] =3D=3D NULL); +out: + return &ptrs[1]; + +err: + free(buf); + return NULL; +} + +void free_argv_copy(char **argv) +{ + assert(argv[-1] =3D=3D ARGV_COPY_MAGIC); + if (argv[-1] !=3D 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 *); =20 +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); } =20 --qwveatom6yh56t6s-- --hs3yityyadmocazc Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEV6G/FbT2+ZuJ7bKf05SBrh55rPcFAmkd3UkACgkQ05SBrh55 rPeBKA//SI4ICOvgMGkdm6vRvQYDojNyFZbZ2ZBI0JdfMXfI/LOiCBIa8Xr/DIFU N8u+mFD2LfJN66T5DYrrGPPI0I+7x+ziwL4qClhz10QqMh5z60fOASIzbuPryE98 JPnSt1lKVrzSokjCNt5CK8RzM0PIlKGt2+5f+xah/zdbFMMOLlLnhUsU8B3567V2 hy6y60rn5kxGz/Sh8wgopyMDK261rUfR5kwm0Vks1ry4y+P+YC+IB686KRPkkRSw Q+IHvGMIcftLuOfBE2vR8AJ3IBegOt9//B5apUDQaSxzrJ9qjFwP6rhMK21+Zvoe 4iJy6SnxHhsz3+yPuPqaa64oE5fZA01QuL4ZH51EaqEOZdYhxVBB+j3/NANhChkv Xvmx9BUYlESSwAgzj2P7ynI1XBoQPCXTt1yD2A/2ZrQCg/Ff3zby0KQ9u3IUV5do pyn/qV4slYsTd18vaKO6CDrzbAxYKhcHpiQQAe5Dhv19arxhO1lfMxyiQ/ALkK/d zRCIb/ChPv94tYqsBIh6tKabq3Yw+DUklXQogdWkW9muAxkyLf8GdWKqT3NU5r4B RqfKr0LQoWSzkYcw/mIQnDQwO9CgWGlJKugwGxN4adQ/Xz7liKmEbWIzpRn2vNxF rpaxuxfKBXz0fbS65l3HpJm6hHJ6e62vj03bQKNhjNShLtWDwLA= =vHPg -----END PGP SIGNATURE----- --hs3yityyadmocazc--