From: Roy Marples Date: Wed, 6 Jan 2016 10:54:36 +0000 (+0000) Subject: If we are to add/delete/change a route but have not yet daemonised, X-Git-Tag: v6.10.0~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3369605a5aae21f131738d1a7ce6eafc06998fbb;p=thirdparty%2Fdhcpcd.git If we are to add/delete/change a route but have not yet daemonised, 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. --- diff --git a/dhcpcd.c b/dhcpcd.c index aee3219a..9ac56c6b 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -322,29 +322,38 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx) 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); diff --git a/dhcpcd.h b/dhcpcd.h index 947afe13..ea1a7df5 100644 --- a/dhcpcd.h +++ b/dhcpcd.h @@ -122,6 +122,7 @@ struct dhcpcd_ctx { #endif int link_fd; int seq; /* route message sequence no */ + int sseq; /* successful seq no sent */ #ifdef USE_SIGNALS sigset_t sigset; @@ -140,6 +141,13 @@ struct dhcpcd_ctx { 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; diff --git a/if-bsd.c b/if-bsd.c index db173805..9d263249 100644 --- a/if-bsd.c +++ b/if-bsd.c @@ -541,6 +541,11 @@ if_route(unsigned char cmd, const struct rt *rt) 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); \ @@ -658,8 +663,10 @@ if_route(unsigned char cmd, const struct rt *rt) #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 @@ -926,6 +933,11 @@ if_route6(unsigned char cmd, const struct rt6 *rt) 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); \ @@ -1010,8 +1022,10 @@ if_route6(unsigned char cmd, const struct rt6 *rt) #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 @@ -1136,9 +1150,26 @@ if_managelink(struct dhcpcd_ctx *ctx) 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: diff --git a/if-linux.c b/if-linux.c index b78f1800..05144908 100644 --- a/if-linux.c +++ b/if-linux.c @@ -886,9 +886,10 @@ send_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, 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; diff --git a/if-options.h b/if-options.h index 429e8b5e..df33784d 100644 --- a/if-options.h +++ b/if-options.h @@ -106,7 +106,7 @@ #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)