]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Protocols will notify when dhcpcd can exit (#536)
authorRoy Marples <roy@marples.name>
Tue, 11 Nov 2025 13:13:03 +0000 (13:13 +0000)
committerGitHub <noreply@github.com>
Tue, 11 Nov 2025 13:13:03 +0000 (13:13 +0000)
* Protocols will notify when dhcpcd can exit

DHCPv6 RELEASE requires the addresses to be dropped before
a RELEASE message is sent.
We now wait for an acknowledgement or a timeout before notifying
that DHCPv6 has stopped for the interface.

DHCPv4 RELEASE is the other way around, there is no acknowledgement.
So we wait for 1 second after sending the message before removing
the address and notifying DHCP has stopped for the interface.

If we are not releasing then we notify dhcpcd that the protocol has
stopped right away when we drop the lease.

dhcpcd will exit once there are no running protocols for the
interfaces.

Fixes #513.
Hopefully #535, #519 and #509 as well.

Co-authored-by: Sime Zupanovic (EXT) <sime.zupanovic.ext@ericsson.com>
12 files changed:
hooks/dhcpcd-run-hooks.8.in
src/dhcp.c
src/dhcp6.c
src/dhcpcd.c
src/dhcpcd.h
src/eloop.c
src/eloop.h
src/ipv4ll.c
src/ipv6nd.c
src/privsep-linux.c
src/privsep.c
src/route.c

index 73859b37fdc0edcdac5af8cd76979c350f724a05..4bed386bce96c62a152d87215fe2413f6edf9c18 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd October 11, 2024
+.Dd October 6, 2025
 .Dt DHCPCD-RUN-HOOKS 8
 .Os
 .Sh NAME
@@ -105,6 +105,8 @@ dhcpcd renewed its lease.
 dhcpcd has rebound to a new DHCP server.
 .It Dv REBOOT | Dv REBOOT6
 dhcpcd successfully requested a lease from a DHCP server.
+.It Dv RELEASE | Dv RELEASE6
+dhcpcd has released the lease.
 .It Dv DELEGATED6
 dhcpcd assigned a delegated prefix to the interface.
 .It Dv IPV4LL
index c4de0e4f08dc7349e0bc537bb4d401394972ece0..89be71a52eb368b47e0e6965a1f2c439c217e3d8 100644 (file)
@@ -2866,6 +2866,51 @@ dhcp_reboot(struct interface *ifp)
        send_request(ifp);
 }
 
+static void
+dhcp_deconfigure(void *arg)
+{
+       struct interface *ifp = arg;
+       struct dhcp_state *state = D_STATE(ifp);
+       struct if_options *ifo = ifp->options;
+       const char *reason;
+
+#ifdef AUTH
+       dhcp_auth_reset(&state->auth);
+#endif
+
+       if (state->state == DHS_RELEASE)
+               reason = "RELEASE";
+       else
+               reason = state->reason;
+       state->state = DHS_NONE;
+       free(state->offer);
+       state->offer = NULL;
+       state->offer_len = 0;
+       free(state->old);
+       state->old = state->new;
+       state->old_len = state->new_len;
+       state->new = NULL;
+       state->new_len = 0;
+       if (ifo->options & DHCPCD_CONFIGURE)
+               ipv4_applyaddr(ifp);
+       else {
+               state->addr = NULL;
+               state->added = 0;
+       }
+       script_runreason(ifp, reason);
+       free(state->old);
+       state->old = NULL;
+       state->old_len = 0;
+       state->lease.addr.s_addr = 0;
+       ifo->options &= ~(DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED);
+
+       if (ifo->options & DHCPCD_STOPPING) {
+               dhcp_free(ifp);
+               dhcpcd_dropped(ifp);
+       } else
+               dhcp_close(ifp);
+}
+
 void
 dhcp_drop(struct interface *ifp, const char *reason)
 {
@@ -2876,6 +2921,7 @@ dhcp_drop(struct interface *ifp, const char *reason)
         * but we do have a timeout, so punt it. */
        if (state == NULL || state->state == DHS_NONE) {
                eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+               dhcpcd_dropped(ifp);
                return;
        }
 
@@ -2886,6 +2932,7 @@ dhcp_drop(struct interface *ifp, const char *reason)
 #ifdef ARPING
        state->arping_index = -1;
 #endif
+       state->reason = reason;
 
        if (ifo->options & DHCPCD_RELEASE && !(ifo->options & DHCPCD_INFORM)) {
                /* Failure to send the release may cause this function to
@@ -2899,10 +2946,21 @@ dhcp_drop(struct interface *ifp, const char *reason)
                    state->new != NULL &&
                    state->lease.server.s_addr != INADDR_ANY)
                {
+                       /* We need to delay removal of the IP address so the
+                        * message can be sent.
+                        * Unlike DHCPv6, there is no acknowledgement. */
+                       const struct timespec delay = {
+                               .tv_sec = 1,
+                       };
+
                        loginfox("%s: releasing lease of %s",
                            ifp->name, inet_ntoa(state->lease.addr));
                        dhcp_new_xid(ifp);
                        send_message(ifp, DHCP_RELEASE, NULL);
+                       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+                       eloop_timeout_add_tv(ifp->ctx->eloop,
+                           &delay, dhcp_deconfigure, ifp);
+                       return;
                }
        }
 #ifdef AUTH
@@ -2919,36 +2977,7 @@ dhcp_drop(struct interface *ifp, const char *reason)
 #endif
 
        eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-#ifdef AUTH
-       dhcp_auth_reset(&state->auth);
-#endif
-
-       state->state = DHS_NONE;
-       free(state->offer);
-       state->offer = NULL;
-       state->offer_len = 0;
-       free(state->old);
-       state->old = state->new;
-       state->old_len = state->new_len;
-       state->new = NULL;
-       state->new_len = 0;
-       state->reason = reason;
-       if (ifo->options & DHCPCD_CONFIGURE)
-               ipv4_applyaddr(ifp);
-       else {
-               state->addr = NULL;
-               state->added = 0;
-               script_runreason(ifp, state->reason);
-       }
-       free(state->old);
-       state->old = NULL;
-       state->old_len = 0;
-       state->lease.addr.s_addr = 0;
-       ifo->options &= ~(DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED);
-
-       /* Close DHCP ports so a changed interface family is picked
-        * up by a new BPF state. */
-       dhcp_close(ifp);
+       dhcp_deconfigure(ifp);
 }
 
 static int
@@ -3108,6 +3137,12 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
 #define IS_STATE_ACTIVE(s) ((s)-state != DHS_NONE && \
        (s)->state != DHS_INIT && (s)->state != DHS_BOUND)
 
+       /* Don't do anything if the user hasn't configured it. */
+       if (ifp->active != IF_ACTIVE_USER ||
+           ifp->options->options & DHCPCD_STOPPING ||
+           !(ifp->options->options & DHCPCD_DHCP))
+               return;
+
        if (bootp->op != BOOTREPLY) {
                if (IS_STATE_ACTIVE(state))
                        logdebugx("%s: op (%d) is not BOOTREPLY",
@@ -3932,6 +3967,7 @@ dhcp_free(struct interface *ifp)
                free(state->offer);
                free(state->clientid);
                free(state);
+               ifp->if_data[IF_DATA_DHCP] = NULL;
        }
 
        ctx = ifp->ctx;
index dbe73de21ee856e363d70236e077d230e8d3892c..2cca6e6dc249eaa565bb992424c0257299ccab2e 100644 (file)
@@ -2110,29 +2110,25 @@ dhcp6_startrelease(struct interface *ifp)
        struct dhcp6_state *state;
 
        state = D6_STATE(ifp);
-       if (state->state != DH6S_BOUND)
+       if (state->state != DH6S_BOUND) {
+               dhcp6_finishrelease(ifp);
                return;
+       }
 
        state->state = DH6S_RELEASE;
        state->RTC = 0;
        state->IMD = REL_MAX_DELAY;
        state->IRT = REL_TIMEOUT;
        state->MRT = REL_MAX_RT;
-       /* MRC of REL_MAX_RC is optional in RFC 3315 18.1.6 */
-#if 0
        state->MRC = REL_MAX_RC;
        state->MRCcallback = dhcp6_finishrelease;
-#else
-       state->MRC = 0;
-       state->MRCcallback = NULL;
-#endif
 
-       if (dhcp6_makemessage(ifp) == -1)
+       if (dhcp6_makemessage(ifp) == -1) {
                logerr("%s: %s", __func__, ifp->name);
-       else {
-               dhcp6_sendrelease(ifp);
+               /* not much we can do apart from finish now */
                dhcp6_finishrelease(ifp);
-       }
+       } else
+               dhcp6_sendrelease(ifp);
 }
 
 static int
@@ -3610,6 +3606,11 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
                            ifp->name, sfrom);
                        dhcp6_fail(ifp, true);
                        return;
+               case DH6S_RELEASE:
+                       loginfox("%s: %s acknowledged RELEASE6",
+                           ifp->name, sfrom);
+                       dhcp6_finishrelease(ifp);
+                       return;
                default:
                        valid_op = false;
                        break;
@@ -4293,6 +4294,7 @@ dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
                free(state);
                ifp->if_data[IF_DATA_DHCP6] = NULL;
        }
+       dhcpcd_dropped(ifp);
 
        /* If we don't have any more DHCP6 enabled interfaces,
         * close the global socket and release resources */
index 2b84ca015f3c1f163ad81ad0912ac2d99d86b8cd..4881f7dd90dc1d65fc9d3eefbd55c7130267c391 100644 (file)
@@ -438,27 +438,62 @@ dhcpcd_drop(struct interface *ifp, int stop)
        dhcpcd_drop_af(ifp, stop, AF_UNSPEC);
 }
 
-static void
-stop_interface(struct interface *ifp, const char *reason)
+static bool
+dhcpcd_ifrunning(struct interface *ifp)
 {
-       struct dhcpcd_ctx *ctx;
 
-       ctx = ifp->ctx;
-       loginfox("%s: removing interface", ifp->name);
-       ifp->options->options |= DHCPCD_STOPPING;
+#ifdef INET
+       if (D_CSTATE(ifp) != NULL)
+               return true;
+#ifdef IPV4LL
+       if (IPV4LL_CSTATE(ifp) != NULL)
+               return true;
+#endif
+#endif
+#ifdef DHCP6
+       if (D6_CSTATE(ifp) != NULL)
+               return true;
+#endif
+       return false;
+}
 
-       dhcpcd_drop(ifp, 1);
-       script_runreason(ifp, reason == NULL ? "STOPPED" : reason);
+void
+dhcpcd_dropped(struct interface *ifp)
+{
+       struct dhcpcd_ctx *ctx = ifp->ctx;
 
-       /* Delete all timeouts for the interfaces */
-       eloop_q_timeout_delete(ctx->eloop, ELOOP_QUEUE_ALL, NULL, ifp);
+       if (ifp->options == NULL ||
+           !(ifp->options->options & DHCPCD_STOPPING) ||
+           dhcpcd_ifrunning(ifp))
+               return;
 
        /* De-activate the interface */
-       ifp->active = IF_INACTIVE;
-       ifp->options->options &= ~DHCPCD_STOPPING;
+       if (ifp->active) {
+               ifp->active = IF_INACTIVE;
+               ifp->options->options &= ~DHCPCD_STOPPING;
+               script_runreason(ifp, "STOPPED");
+       }
 
-       if (!(ctx->options & (DHCPCD_MANAGER | DHCPCD_TEST)))
-               eloop_exit(ctx->eloop, EXIT_FAILURE);
+       if (!(ctx->options & DHCPCD_EXITING))
+               return;
+
+       TAILQ_FOREACH(ifp, ctx->ifaces, next) {
+               if (dhcpcd_ifrunning(ifp))
+                       break;
+       }
+
+       /* All interfaces have stopped, we can exit */
+       if (ifp == NULL)
+               eloop_exit(ctx->eloop, EXIT_SUCCESS);
+}
+
+static void
+stop_interface(struct interface *ifp)
+{
+
+       loginfox("%s: removing interface", ifp->name);
+       ifp->options->options |= DHCPCD_STOPPING;
+       dhcpcd_drop(ifp, 1);
 }
 
 static void
@@ -744,6 +779,17 @@ dhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags)
        ifp->carrier = carrier;
        ifp->flags = flags;
 
+       /*
+        * Inactive interfaces may not have options, we so check the
+        * global context if we are stopping or not.
+        * This allows an active interface to stop even if dhcpcd is not.
+        */
+       if (ifp->options != NULL) {
+               if (ifp->options->options & DHCPCD_STOPPING)
+                       return;
+       } else if (ifp->ctx->options & DHCPCD_EXITING)
+               return;
+
        if (!if_is_link_up(ifp)) {
                if (!ifp->active || (!was_link_up && !was_roaming))
                        return;
@@ -1070,13 +1116,16 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname)
                }
                if (ifp->active) {
                        logdebugx("%s: interface departed", ifp->name);
-                       stop_interface(ifp, "DEPARTED");
+                       stop_interface(ifp);
                }
                TAILQ_REMOVE(ctx->ifaces, ifp, next);
                if_free(ifp);
                return 0;
        }
 
+       if (ctx->options & DHCPCD_EXITING)
+               return 0;
+
        ifs = if_discover(ctx, &ifaddrs, -1, UNCONST(argv));
        if (ifs == NULL) {
                logerr(__func__);
@@ -1378,14 +1427,15 @@ reconf_reboot(struct dhcpcd_ctx *ctx, int action, int argc, char **argv, int oi)
        }
 }
 
-static void
+static bool
 stop_all_interfaces(struct dhcpcd_ctx *ctx, unsigned long long opts)
 {
        struct interface *ifp;
+       bool anystopped = false;
 
        ctx->options |= opts;
        if (ctx->ifaces == NULL)
-               return;
+               return anystopped;
 
        if (ctx->options & DHCPCD_RELEASE)
                ctx->options &= ~DHCPCD_PERSISTENT;
@@ -1398,8 +1448,10 @@ stop_all_interfaces(struct dhcpcd_ctx *ctx, unsigned long long opts)
                if (ifp->options->options & DHCPCD_RELEASE)
                        ifp->options->options &= ~DHCPCD_PERSISTENT;
                ifp->options->options |= DHCPCD_EXITING;
-               stop_interface(ifp, NULL);
+               anystopped = true;
+               stop_interface(ifp);
        }
+       return anystopped;
 }
 
 static void
@@ -1437,7 +1489,6 @@ dhcpcd_renew(struct dhcpcd_ctx *ctx)
 
 #ifdef USE_SIGNALS
 #define sigmsg "received %s, %s"
-static volatile bool dhcpcd_exiting = false;
 void
 dhcpcd_signal_cb(int sig, void *arg)
 {
@@ -1517,14 +1568,19 @@ dhcpcd_signal_cb(int sig, void *arg)
         * During teardown we don't want to process SIGTERM or SIGINT again,
         * as that could trigger memory issues.
         */
-       if (dhcpcd_exiting)
+       if (ctx->options & DHCPCD_EXITING)
                return;
 
-       dhcpcd_exiting = true;
-       if (!(ctx->options & DHCPCD_TEST))
-               stop_all_interfaces(ctx, opts);
+       ctx->options |= DHCPCD_EXITING;
+       if (!(ctx->options & DHCPCD_TEST) &&
+           stop_all_interfaces(ctx, opts))
+       {
+               /* We stopped something, we will exit once that is done. */
+               eloop_exitallinners(exit_code);
+               return;
+       }
+
        eloop_exitall(exit_code);
-       dhcpcd_exiting = false;
 }
 #endif
 
@@ -1664,8 +1720,11 @@ dumperr:
 
        if (opts & (DHCPCD_EXITING | DHCPCD_RELEASE)) {
                if (oifind == argc && af == AF_UNSPEC) {
-                       stop_all_interfaces(ctx, opts);
-                       eloop_exit(ctx->eloop, EXIT_SUCCESS);
+                       ctx->options |= DHCPCD_EXITING;
+                       if (stop_all_interfaces(ctx, opts) == false)
+                               eloop_exit(ctx->eloop, EXIT_SUCCESS);
+                       /* We did stop an interface, it will notify us once
+                        * dropped so we can exit. */
                        return 0;
                }
 
@@ -1695,7 +1754,7 @@ dumperr:
                        if (af != AF_UNSPEC)
                                dhcpcd_drop_af(ifp, 1, af);
                        else
-                               stop_interface(ifp, NULL);
+                               stop_interface(ifp);
                        ifo->options = orig_opts;
                }
                return 0;
@@ -1897,17 +1956,28 @@ dhcpcd_pidfile_timeout(void *arg)
        pid_t pid;
 
        pid = pidfile_read(ctx->pidfile);
-
-       if(pid == -1)
+       if (pid == -1)
                eloop_exit(ctx->eloop, EXIT_SUCCESS);
-       else if (++ctx->duid_len >= 100) { /* overload duid_len */
-               logerrx("pid %d failed to exit", (int)pid);
-               eloop_exit(ctx->eloop, EXIT_FAILURE);
-       } else
+       else
                eloop_timeout_add_msec(ctx->eloop, 100,
                    dhcpcd_pidfile_timeout, ctx);
 }
 
+static void
+dhcpcd_exit_timeout(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+       pid_t pid;
+
+       pid = pidfile_read(ctx->pidfile);
+       if (pid == -1)
+               eloop_exit(ctx->eloop, EXIT_SUCCESS);
+       else {
+               logwarnx("pid %lld failed to exit", (long long)pid);
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+       }
+}
+
 static int dup_null(int fd)
 {
        int fd_null = open(_PATH_DEVNULL, O_WRONLY);
@@ -2271,6 +2341,8 @@ printpidfile:
                        /* Spin until it exits */
                        loginfox("waiting for pid %d to exit", (int)pid);
                        dhcpcd_pidfile_timeout(&ctx);
+                       eloop_timeout_add_sec(ctx.eloop, 50,
+                               dhcpcd_exit_timeout, &ctx);
                        goto run_loop;
                }
        }
@@ -2723,7 +2795,7 @@ exit1:
        if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED))
                loginfox(PACKAGE " exited");
 #ifdef PRIVSEP
-       if (ctx.ps_root != NULL && ps_root_stop(&ctx) == -1)
+       if (ps_root_stop(&ctx) == -1)
                i = EXIT_FAILURE;
        eloop_free(ctx.ps_eloop);
 #endif
index 527399d344b3c7e364f4abcf0c7301d45c5abab4..c4dbe9993014152bb18cf7461f8053a92851ce58 100644 (file)
@@ -267,6 +267,7 @@ void dhcpcd_handlecarrier(struct interface *, int, unsigned int);
 int dhcpcd_handleinterface(void *, int, const char *);
 void dhcpcd_handlehwaddr(struct interface *, uint16_t, const void *, uint8_t);
 void dhcpcd_dropinterface(struct interface *, const char *);
+void dhcpcd_dropped(struct interface *);
 int dhcpcd_selectprofile(struct interface *, const char *);
 
 void dhcpcd_startinterface(void *);
index b2d2eed15ecd5472dda7aaea8aa423f5171ba1f9..ac6db7711ad8e0e8c8ad7ff99e6ae92a7fc70866 100644 (file)
 #define KEVENT_N int
 #endif
 #elif defined(__linux__)
+#include <linux/version.h>
 #include <sys/epoll.h>
+#include <poll.h>
 #define USE_EPOLL
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
+#define HAVE_EPOLL_PWAIT2
+#endif
 #else
 #include <poll.h>
 #define USE_PPOLL
@@ -143,6 +148,9 @@ struct eloop {
        bool exitnow;
        bool events_need_setup;
        bool events_invalid;
+#ifdef HAVE_EPOLL_PWAIT2
+       bool epoll_pwait2_nosys;
+#endif
 };
 
 TAILQ_HEAD(eloop_head, eloop) eloops = TAILQ_HEAD_INITIALIZER(eloops);
@@ -617,6 +625,19 @@ eloop_exitall(int code)
        }
 }
 
+void
+eloop_exitallinners(int code)
+{
+       struct eloop *eloop;
+
+       TAILQ_FOREACH(eloop, &eloops, next) {
+               if (eloop == TAILQ_FIRST(&eloops))
+                       continue;
+               eloop->exitcode = code;
+               eloop->exitnow = true;
+       }
+}
+
 #if defined(USE_KQUEUE) || defined(USE_EPOLL)
 static int
 eloop_open(struct eloop *eloop)
@@ -924,24 +945,48 @@ eloop_run_kqueue(struct eloop *eloop, const struct timespec *ts)
 static int
 eloop_run_epoll(struct eloop *eloop, const struct timespec *ts)
 {
-       int timeout, n, nn;
+       int n, nn;
        struct epoll_event *epe;
        struct eloop_event *e;
        unsigned short events;
 
-       if (ts != NULL) {
-               if (ts->tv_sec > INT_MAX / 1000 ||
-                   (ts->tv_sec == INT_MAX / 1000 &&
-                       ((ts->tv_nsec + 999999) / 1000000 > INT_MAX % 1000000)))
-                       timeout = INT_MAX;
-               else
-                       timeout = (int)(ts->tv_sec * 1000 +
-                           (ts->tv_nsec + 999999) / 1000000);
-       } else
-               timeout = -1;
+       /* epoll does not work with zero events */
+       if (eloop->nfds == 0) {
+               n = ppoll(NULL, 0, ts, &eloop->sigset);
+#ifdef HAVE_EPOLL_PWAIT2
+       } else if (!eloop->epoll_pwait2_nosys) {
+               /* Many linux distros are dumb in shipping newer
+                * kernel headers than the target kernel they are using.
+                * So we jump through this stupid hoop and have to write
+                * more complex code. */
+               n = epoll_pwait2(eloop->fd, eloop->fds, (int)eloop->nfds,
+                   ts, &eloop->sigset);
+               if (n == -1 && errno == ENOSYS) {
+                       eloop->epoll_pwait2_nosys = true;
+                       goto epoll_pwait2_nosys;
+               }
+#endif
+       } else {
+               int timeout;
 
-       n = epoll_pwait(eloop->fd, eloop->fds, (int)eloop->nfds, timeout,
-           &eloop->sigset);
+#ifdef HAVE_EPOLL_PWAIT2
+epoll_pwait2_nosys:
+#endif
+               if (ts != NULL) {
+                       if (ts->tv_sec > INT_MAX / 1000 ||
+                           (ts->tv_sec == INT_MAX / 1000 &&
+                           ((ts->tv_nsec + 999999) / 1000000 >
+                            INT_MAX % 1000000)))
+                               timeout = INT_MAX;
+                       else
+                               timeout = (int)(ts->tv_sec * 1000 +
+                                   (ts->tv_nsec + 999999) / 1000000);
+               } else
+                       timeout = -1;
+
+               n = epoll_pwait(eloop->fd, eloop->fds, (int)eloop->nfds,
+                   timeout, &eloop->sigset);
+       }
        if (n == -1)
                return -1;
 
index b342c37db6065d62dd068156e8986f3030c7e902..f498ddac74e4792b89e23c1288a40dcc00871ce4 100644 (file)
@@ -98,6 +98,7 @@ struct eloop *eloop_new_with_signals(struct eloop *);
 void eloop_free(struct eloop *);
 void eloop_exit(struct eloop *, int);
 void eloop_exitall(int);
+void eloop_exitallinners(int);
 int eloop_forked(struct eloop *, unsigned short);
 int eloop_start(struct eloop *);
 
index c2c7a8e7d3fcc2b526e09497860b418eb7058180..c16a1dca983ac755c42a9a143f8e040214deb039 100644 (file)
@@ -448,10 +448,11 @@ ipv4ll_drop(struct interface *ifp)
 
        assert(ifp != NULL);
 
+       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
        ipv4ll_freearp(ifp);
 
        if ((ifp->options->options & DHCPCD_NODROP) == DHCPCD_NODROP)
-               return;
+               goto free;
 
        state = IPV4LL_STATE(ifp);
        if (state) {
@@ -481,6 +482,10 @@ ipv4ll_drop(struct interface *ifp)
                rt_build(ifp->ctx, AF_INET);
                script_runreason(ifp, "IPV4LL");
        }
+
+free:
+       ipv4ll_free(ifp);
+       dhcpcd_dropped(ifp);
 }
 
 void
index 09e0d16ed2e7b1dd390f131a753fafcd54e17ad1..c8c18fc4fccdf7faf2c855b1a09ee2a1bd3b2aa1 100644 (file)
@@ -1927,6 +1927,7 @@ ipv6nd_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)
 
        /* Don't do anything if the user hasn't configured it. */
        if (ifp->active != IF_ACTIVE_USER ||
+           ifp->options->options & DHCPCD_STOPPING ||
            !(ifp->options->options & DHCPCD_IPV6))
                return;
 
index 036a35fefdb9820da4fb605070b0590eb990b304..43470a2d6fd48adbf0c0164f072d495b1e45b808 100644 (file)
@@ -325,6 +325,9 @@ static struct sock_filter ps_seccomp_filter[] = {
 #ifdef __NR_epoll_pwait
        SECCOMP_ALLOW(__NR_epoll_pwait),
 #endif
+#ifdef __NR_epoll_pwait2
+       SECCOMP_ALLOW(__NR_epoll_pwait2),
+#endif
 #ifdef __NR_exit_group
        SECCOMP_ALLOW(__NR_exit_group),
 #endif
index 1fff0798b0964603dd02d573bf0e77198309b5fc..e7f488ed468e569f30fea2c9484cf58406cfb204 100644 (file)
@@ -760,7 +760,7 @@ ps_stopwait(struct dhcpcd_ctx *ctx)
 #endif
 
        error = eloop_start(ctx->ps_eloop);
-       if (error != EXIT_SUCCESS)
+       if (error < 0)
                logerr("%s: eloop_start", __func__);
 
        eloop_timeout_delete(ctx->ps_eloop, ps_process_timeout, ctx);
@@ -1136,6 +1136,10 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events,
        struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 };
        bool stop = false;
 
+       if (events & ELE_HANGUP) {
+               len = 0;
+               goto stop;
+       }
        if (!(events & ELE_READ))
                logerrx("%s: unexpected event 0x%04x", __func__, events);
 
@@ -1163,12 +1167,13 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events,
        }
 
        if (stop) {
+stop:
                ctx->options |= DHCPCD_EXITING;
 #ifdef PRIVSEP_DEBUG
                logdebugx("process %d stopping", getpid());
 #endif
                ps_free(ctx);
-               eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
+               eloop_exitall(len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
                return len;
        }
        dlen -= sizeof(psm.psm_hdr);
index 32d132b1e331114868ac9a42bb5b999f7c471a73..7861546efdd9c7bdc1ef40cfdb13a385c10ae2b3 100644 (file)
@@ -824,7 +824,8 @@ rt_build(struct dhcpcd_ctx *ctx, int af)
        }
 
 #ifdef BSD
-       if (if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)
+       if (!(ctx->options & DHCPCD_EXITING) &&
+           if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)
                logerr("if_missfilter_apply");
 #endif