flag that we are to track the parent pid.
On successful route message, remember the sequence number.
Once daemonised, ignore messages from the parent pid until we find
the saved sequence number - at this point remove the flag to track parent pid.
This fixes an issue where we didn't correctly ignore messages we generated
before forking but catching them in the child process.
if (ctx->options & DHCPCD_DAEMONISED ||
!(ctx->options & DHCPCD_DAEMONISE))
return 0;
+ logger(ctx, LOG_DEBUG, "forking to background");
+
/* Setup a signal pipe so parent knows when to exit. */
if (pipe(sidpipe) == -1) {
logger(ctx, LOG_ERR, "pipe: %m");
return 0;
}
- logger(ctx, LOG_DEBUG, "forking to background");
+
+ /* Store the pid and routing message seq number so we can identify
+ * the last message successfully sent to the kernel.
+ * This allows us to ignore all messages we sent after forking
+ * and detaching. */
+ ctx->ppid = getpid();
+ ctx->pseq = ctx->sseq;
+
switch (pid = fork()) {
case -1:
logger(ctx, LOG_ERR, "fork: %m");
return 0;
case 0:
setsid();
+ /* Notify parent it's safe to exit as we've detached. */
+ close(sidpipe[0]);
+ if (write(sidpipe[1], &buf, 1) == -1)
+ logger(ctx, LOG_ERR, "failed to notify parent: %m");
+ close(sidpipe[1]);
/* Some polling methods don't survive after forking,
* so ensure we can requeue all our events. */
if (eloop_requeue(ctx->eloop) == -1) {
logger(ctx, LOG_ERR, "eloop_requeue: %m");
eloop_exit(ctx->eloop, EXIT_FAILURE);
}
- /* Notify parent it's safe to exit as we've detached. */
- close(sidpipe[0]);
- if (write(sidpipe[1], &buf, 1) == -1)
- logger(ctx, LOG_ERR, "failed to notify parent: %m");
- close(sidpipe[1]);
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
#endif
int link_fd;
int seq; /* route message sequence no */
+ int sseq; /* successful seq no sent */
#ifdef USE_SIGNALS
sigset_t sigset;
char *randomstate; /* original state */
+ /* Used to track the last routing message,
+ * so we can ignore messages the parent process sent
+ * but the child receives when forking.
+ * getppid(2) is unreliable because we detach. */
+ pid_t ppid; /* parent pid */
+ int pseq; /* last seq in parent */
+
#ifdef INET
struct dhcp_opt *dhcp_opts;
size_t dhcp_opts_len;
size_t l;
struct in_addr src_addr;
+ if ((cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE) &&
+ rt->iface->ctx->options & DHCPCD_DAEMONISE &&
+ !(rt->iface->ctx->options & DHCPCD_DAEMONISED))
+ rt->iface->ctx->options |= DHCPCD_RTM_PPID;
+
#define ADDSU { \
l = RT_ROUNDUP(su.sa.sa_len); \
memcpy(bp, &su, l); \
#undef ADDSU
rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm);
- return write(rt->iface->ctx->link_fd,
- &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0;
+ if (write(rt->iface->ctx->link_fd, &rtm, rtm.hdr.rtm_msglen) == -1)
+ return -1;
+ rt->iface->ctx->sseq = rt->iface->ctx->seq;
+ return 0;
}
int
char *bp = rtm.buffer;
size_t l;
+ if ((cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE) &&
+ rt->iface->ctx->options & DHCPCD_DAEMONISE &&
+ !(rt->iface->ctx->options & DHCPCD_DAEMONISED))
+ rt->iface->ctx->options |= DHCPCD_RTM_PPID;
+
#define ADDSU { \
l = RT_ROUNDUP(su.sa.sa_len); \
memcpy(bp, &su, l); \
#undef ADDSU
rtm.hdr.rtm_msglen = (unsigned short)(bp - (char *)&rtm);
- return write(rt->iface->ctx->link_fd,
- &rtm, rtm.hdr.rtm_msglen) == -1 ? -1 : 0;
+ if (write(rt->iface->ctx->link_fd, &rtm, rtm.hdr.rtm_msglen) == -1)
+ return -1;
+ rt->iface->ctx->sseq = rt->iface->ctx->seq;
+ return 0;
}
int
e = msg + bytes;
for (p = msg; p < e; p += rtm->rtm_msglen) {
rtm = (struct rt_msghdr *)(void *)p;
- // Ignore messages generated by us
- if (rtm->rtm_pid == getpid())
- break;
+ if (rtm->rtm_type == RTM_MISS)
+ continue;
+ /* Ignore messages generated by us */
+ if (rtm->rtm_pid == getpid()) {
+ ctx->options &= ~DHCPCD_RTM_PPID;
+ continue;
+ }
+ /* Ignore messages sent by the parent process after forking */
+ if ((ctx->options & (DHCPCD_RTM_PPID | DHCPCD_DAEMONISED)) ==
+ (DHCPCD_RTM_PPID | DHCPCD_DAEMONISED) &&
+ rtm->rtm_pid == ctx->ppid)
+ {
+ /* If this is the last successful message sent clear
+ * the check flag as it's possible another process could
+ * re-use the same pid and also manipulate the kernel
+ * routing table. */
+ if (rtm->rtm_seq == ctx->pseq)
+ ctx->options &= ~DHCPCD_RTM_PPID;
+ continue;
+ }
switch(rtm->rtm_type) {
#ifdef RTM_IFANNOUNCE
case RTM_IFANNOUNCE:
hdr->nlmsg_flags |= NLM_F_ACK;
hdr->nlmsg_seq = ++ctx->seq;
- if (sendmsg(s, &msg, 0) != -1)
+ if (sendmsg(s, &msg, 0) != -1) {
+ ctx->sseq = ctx-seq;
r = get_netlink(ctx, ifp, s, 0, callback);
- else
+ } else
r = -1;
close(s);
return r;
#define DHCPCD_DHCP6 (1ULL << 50)
#define DHCPCD_IF_UP (1ULL << 51)
#define DHCPCD_INFORM6 (1ULL << 52)
-// unassinged (1ULL << 53)
+#define DHCPCD_RTM_PPID (1ULL << 53)
#define DHCPCD_IPV6RA_AUTOCONF (1ULL << 54)
#define DHCPCD_ROUTER_HOST_ROUTE_WARNED (1ULL << 55)
#define DHCPCD_IPV6RA_ACCEPT_NOPUBLIC (1ULL << 56)