Skip to content

Commit 0ee7320

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 0ee7320

File tree

5 files changed

+82
-7
lines changed

5 files changed

+82
-7
lines changed

src/dhcpcd.c

Lines changed: 19 additions & 7 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);
@@ -1363,15 +1366,24 @@ reconf_reboot(struct dhcpcd_ctx *ctx, int action, int argc, char **argv, int oi)
13631366
if (oi != argc && i == argc)
13641367
continue;
13651368
if (ifp->active == IF_ACTIVE_USER) {
1366-
if (action)
1369+
if (action) {
13671370
if_reboot(ifp, argc, argv);
1371+
} else {
13681372
#ifdef INET
1369-
else
13701373
ipv4_applyaddr(ifp);
13711374
#endif
1375+
}
13721376
} else if (i != argc) {
1377+
/* iface wasnt found above -> it's new. start it. */
13731378
ifp->active = IF_ACTIVE_USER;
1374-
dhcpcd_initstate1(ifp, argc, argv, 0);
1379+
dhcpcd_initstate1(ifp, argc, argv, 0); // control cmd args
1380+
1381+
if (ifp->argv)
1382+
free_argv_copy(ifp->argv);
1383+
ifp->argv = copy_argv(argc, argv);
1384+
if (ifp->argv)
1385+
ifp->argc = argc;
1386+
13751387
run_preinit(ifp);
13761388
dhcpcd_prestartinterface(ifp);
13771389
}
@@ -1715,7 +1727,7 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
17151727
}
17161728

17171729
reload_config(ctx);
1718-
/* XXX: Respect initial commandline options? */
1730+
/* Respect control cmd options! */
17191731
reconf_reboot(ctx, do_reboot, argc, argv, oifind);
17201732
return 0;
17211733
}
@@ -2605,7 +2617,7 @@ main(int argc, char **argv, char **envp)
26052617

26062618
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
26072619
if (ifp->active)
2608-
dhcpcd_initstate1(ifp, argc, argv, 0);
2620+
dhcpcd_initstate1(ifp, argc, argv, 0); // main argv
26092621
}
26102622
if_learnaddrs(&ctx, ctx.ifaces, &ifaddrs);
26112623
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: 55 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,60 @@ 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+
3001+
unsigned nptrs = 1 + (unsigned)argc + 1;
3002+
size_t ptrslen = nptrs * sizeof(char *);
3003+
void *buf = malloc(ptrslen + strslen);
3004+
char **ptrs = buf;
3005+
if (!buf)
3006+
return NULL;
3007+
3008+
ptrs[0] = ARGV_COPY_MAGIC;
3009+
ptrs[nptrs - 1] = NULL;
3010+
3011+
if (argc == 0)
3012+
goto out;
3013+
3014+
char *strsp = (char *)&ptrs[nptrs];
3015+
for (i = 0; i < argc; i++) {
3016+
if (strslen <= 0)
3017+
goto err;
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+
strslen -= len + 1;
3026+
}
3027+
3028+
assert(strslen == 0);
3029+
assert(ptrs[nptrs -1 ] == NULL);
3030+
out:
3031+
return &ptrs[1];
3032+
3033+
err:
3034+
free(buf);
3035+
return NULL;
3036+
}
3037+
3038+
void free_argv_copy(char **argv)
3039+
{
3040+
assert(argv[-1] == ARGV_COPY_MAGIC);
3041+
free(&argv[-1]);
3042+
}
3043+
29893044
void
29903045
free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)
29913046
{

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)