Skip to content

Commit 112cc12

Browse files
committed
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.
1 parent 8abf6b4 commit 112cc12

File tree

5 files changed

+84
-5
lines changed

5 files changed

+84
-5
lines changed

src/dhcpcd.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,10 @@ static void
694694
dhcpcd_initstate(struct interface *ifp, unsigned long long options)
695695
{
696696

697-
dhcpcd_initstate1(ifp, ifp->ctx->argc, ifp->ctx->argv, options);
697+
dhcpcd_initstate1(ifp,
698+
ifp->argc ?: ifp->ctx->argc,
699+
ifp->argv ?: ifp->ctx->argv,
700+
options);
698701
}
699702

700703
static void
@@ -1315,7 +1318,7 @@ if_reboot(struct interface *ifp, int argc, char **argv)
13151318
oldopts = ifp->options->options;
13161319
#endif
13171320
script_runreason(ifp, "RECONFIGURE");
1318-
dhcpcd_initstate1(ifp, argc, argv, 0);
1321+
dhcpcd_initstate1(ifp, argc, argv, 0); // control or main argv
13191322
#ifdef INET
13201323
if (ifp->options->options & DHCPCD_DHCP)
13211324
dhcp_reboot_newopts(ifp, oldopts);
@@ -1370,8 +1373,16 @@ reconf_reboot(struct dhcpcd_ctx *ctx, int action, int argc, char **argv, int oi)
13701373
ipv4_applyaddr(ifp);
13711374
#endif
13721375
} else if (i != argc) {
1376+
/* iface wasnt found above -> it's new. start it. */
13731377
ifp->active = IF_ACTIVE_USER;
1374-
dhcpcd_initstate1(ifp, argc, argv, 0);
1378+
dhcpcd_initstate1(ifp, argc, argv, 0); // control cmd args
1379+
1380+
if (ifp->argv)
1381+
free_argv_copy(ifp->argv);
1382+
ifp->argv = copy_argv(argc, argv);
1383+
if (ifp->argv)
1384+
ifp->argc = argc;
1385+
13751386
run_preinit(ifp);
13761387
dhcpcd_prestartinterface(ifp);
13771388
}
@@ -1715,7 +1726,7 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
17151726
}
17161727

17171728
reload_config(ctx);
1718-
/* XXX: Respect initial commandline options? */
1729+
/* Respect control cmd options! */
17191730
reconf_reboot(ctx, do_reboot, argc, argv, oifind);
17201731
return 0;
17211732
}
@@ -2605,7 +2616,7 @@ main(int argc, char **argv, char **envp)
26052616

26062617
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
26072618
if (ifp->active)
2608-
dhcpcd_initstate1(ifp, argc, argv, 0);
2619+
dhcpcd_initstate1(ifp, argc, argv, 0); // main argv
26092620
}
26102621
if_learnaddrs(&ctx, ctx.ifaces, &ifaddrs);
26112622
if_freeifaddrs(&ctx, &ifaddrs);

src/dhcpcd.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ struct interface {
8585
uint8_t ssid[IF_SSIDLEN];
8686
unsigned int ssid_len;
8787

88+
int argc;
89+
char **argv;
90+
8891
char profile[PROFILE_LEN];
8992
struct if_options *options;
9093
void *if_data[IF_DATA_MAX];

src/if-options.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include <string.h>
4545
#include <unistd.h>
4646
#include <time.h>
47+
#include <assert.h>
4748

4849
#include "config.h"
4950
#include "common.h"
@@ -2986,6 +2987,65 @@ add_options(struct dhcpcd_ctx *ctx, const char *ifname,
29862987
return r;
29872988
}
29882989

2990+
#define ARGV_COPY_MAGIC ((char *)0x5a54292d273f3d34)
2991+
/*^ intentional truncation on 32bit arches */
2992+
2993+
char **copy_argv(int argc, char **argv)
2994+
{
2995+
int i;
2996+
size_t strslen = 0;
2997+
for (i = 0; i < argc; i++) {
2998+
strslen += strlen(argv[i]) + 1;
2999+
}
3000+
if (strslen == 0) // also handles argc < 0
3001+
return NULL;
3002+
3003+
unsigned nptrs = 1 + (unsigned)argc + 1;
3004+
size_t ptrslen = nptrs * sizeof(char *);
3005+
void *buf = malloc(ptrslen + strslen);
3006+
char **ptrs = buf;
3007+
if (!buf)
3008+
return NULL;
3009+
3010+
ptrs[0] = ARGV_COPY_MAGIC;
3011+
ptrs[nptrs - 1] = NULL;
3012+
3013+
if (argc == 0)
3014+
goto out;
3015+
3016+
char *strsp = (char *)&ptrs[nptrs];
3017+
for (i = 0; i < argc; i++) {
3018+
size_t len = strlcpy(strsp, argv[i], strslen);
3019+
if (len >= strslen) // truncated
3020+
goto err;
3021+
3022+
ptrs[1 + i] = strsp;
3023+
3024+
strsp += len + 1;
3025+
if (strslen < len + 1)
3026+
goto err;
3027+
strslen -= len + 1;
3028+
}
3029+
3030+
assert(strslen == 0);
3031+
assert(ptrs[nptrs - 1] == NULL);
3032+
out:
3033+
return &ptrs[1];
3034+
3035+
err:
3036+
free(buf);
3037+
return NULL;
3038+
}
3039+
3040+
void free_argv_copy(char **argv)
3041+
{
3042+
assert(argv[-1] == ARGV_COPY_MAGIC);
3043+
if (argv[-1] != ARGV_COPY_MAGIC) {
3044+
logerrx("%s: invalid argv", __func__);
3045+
} else
3046+
free(&argv[-1]);
3047+
}
3048+
29893049
void
29903050
free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)
29913051
{

src/if-options.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,7 @@ int add_options(struct dhcpcd_ctx *, const char *,
322322
void free_dhcp_opt_embenc(struct dhcp_opt *);
323323
void free_options(struct dhcpcd_ctx *, struct if_options *);
324324

325+
char **copy_argv(int argc, char **argv);
326+
void free_argv_copy(char **argv);
327+
325328
#endif

src/if.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ if_free(struct interface *ifp)
100100
#endif
101101
rt_freeif(ifp);
102102
free_options(ifp->ctx, ifp->options);
103+
if (ifp->argv)
104+
free_argv_copy(ifp->argv);
103105
free(ifp);
104106
}
105107

0 commit comments

Comments
 (0)