Skip to content

Commit c8a09bb

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 2de751b commit c8a09bb

File tree

5 files changed

+87
-5
lines changed

5 files changed

+87
-5
lines changed

src/dhcpcd.c

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

732-
dhcpcd_initstate1(ifp, ifp->ctx->argc, ifp->ctx->argv, options);
732+
dhcpcd_initstate1(ifp,
733+
ifp->argc ? ifp->argc : ifp->ctx->argc,
734+
ifp->argv ? ifp->argv : ifp->ctx->argv,
735+
options);
733736
}
734737

735738
static void
@@ -1364,7 +1367,7 @@ if_reboot(struct interface *ifp, int argc, char **argv)
13641367
oldopts = ifp->options->options;
13651368
#endif
13661369
script_runreason(ifp, "RECONFIGURE");
1367-
dhcpcd_initstate1(ifp, argc, argv, 0);
1370+
dhcpcd_initstate1(ifp, argc, argv, 0); // control or main argv
13681371
#ifdef INET
13691372
if (ifp->options->options & DHCPCD_DHCP)
13701373
dhcp_reboot_newopts(ifp, oldopts);
@@ -1419,8 +1422,16 @@ reconf_reboot(struct dhcpcd_ctx *ctx, int action, int argc, char **argv, int oi)
14191422
ipv4_applyaddr(ifp);
14201423
#endif
14211424
} else if (i != argc) {
1425+
/* iface wasnt found above -> it's new. start it. */
14221426
ifp->active = IF_ACTIVE_USER;
1423-
dhcpcd_initstate1(ifp, argc, argv, 0);
1427+
dhcpcd_initstate1(ifp, argc, argv, 0); // control cmd args
1428+
1429+
if (ifp->argv)
1430+
free_argv_copy(ifp->argv);
1431+
ifp->argv = copy_argv(argc, argv);
1432+
if (ifp->argv)
1433+
ifp->argc = argc;
1434+
14241435
run_preinit(ifp);
14251436
dhcpcd_prestartinterface(ifp);
14261437
}
@@ -1773,7 +1784,7 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
17731784
}
17741785

17751786
reload_config(ctx);
1776-
/* XXX: Respect initial commandline options? */
1787+
/* Respect control cmd options! */
17771788
reconf_reboot(ctx, do_reboot, argc, argv, oifind);
17781789
return 0;
17791790
}
@@ -2680,7 +2691,7 @@ main(int argc, char **argv, char **envp)
26802691

26812692
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
26822693
if (ifp->active)
2683-
dhcpcd_initstate1(ifp, argc, argv, 0);
2694+
dhcpcd_initstate1(ifp, argc, argv, 0); // main argv
26842695
}
26852696
if_learnaddrs(&ctx, ctx.ifaces, &ifaddrs);
26862697
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: 63 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,68 @@ 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+
2998+
for (i = 0; i < argc; i++) {
2999+
strslen += strlen(argv[i]) + 1;
3000+
}
3001+
if (strslen == 0) // also handles argc < 0
3002+
return NULL;
3003+
3004+
unsigned nptrs = 1 + (unsigned)argc + 1;
3005+
size_t ptrslen = nptrs * sizeof(char *);
3006+
void *buf = malloc(ptrslen + strslen);
3007+
char **ptrs = buf;
3008+
3009+
if (!buf)
3010+
return NULL;
3011+
3012+
ptrs[0] = ARGV_COPY_MAGIC;
3013+
ptrs[nptrs - 1] = NULL;
3014+
3015+
if (argc == 0)
3016+
goto out;
3017+
3018+
char *strsp = (char *)&ptrs[nptrs];
3019+
3020+
for (i = 0; i < argc; i++) {
3021+
size_t len = strlcpy(strsp, argv[i], strslen);
3022+
if (len >= strslen) // truncated
3023+
goto err;
3024+
3025+
ptrs[1 + i] = strsp;
3026+
3027+
strsp += len + 1;
3028+
if (strslen < len + 1)
3029+
goto err;
3030+
strslen -= len + 1;
3031+
}
3032+
3033+
assert(strslen == 0);
3034+
assert(ptrs[nptrs - 1] == NULL);
3035+
out:
3036+
return &ptrs[1];
3037+
3038+
err:
3039+
free(buf);
3040+
return NULL;
3041+
}
3042+
3043+
void free_argv_copy(char **argv)
3044+
{
3045+
assert(argv[-1] == ARGV_COPY_MAGIC);
3046+
if (argv[-1] != ARGV_COPY_MAGIC) {
3047+
logerrx("%s: invalid argv", __func__);
3048+
} else
3049+
free(&argv[-1]);
3050+
}
3051+
29893052
void
29903053
free_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)
29913054
{

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)