diff options
| author | Roy Marples <roy@marples.name> | 2018-03-19 15:39:05 +0000 |
|---|---|---|
| committer | Roy Marples <roy@marples.name> | 2018-03-19 15:39:05 +0000 |
| commit | 1437db1d25f09c5235ed792bd278ee3f0f17dd65 (patch) | |
| tree | decb2c14336f00dff8f505e394e099ec8a2c40ba /src/dhcpcd.c | |
| parent | 81edfe77f1ab8c5e9d5761fda73506d2a28710fb (diff) | |
| download | dhcpcd-1437db1d25f09c5235ed792bd278ee3f0f17dd65.tar.xz | |
link: detect buffer overflow / desync and relearn interface state
It's possible for the internal kernel buffer that reports network
events to overflow.
On Linux and NetBSD* this is handled by ENOBUFS being returned
by recv(2). On OpenBSD there is a special route(4) message RTM_DESYNC.
All other OS's don't seem to report this error, so dhcpcd cannot
detect it.
* I will commit a patch to NetBSD soon for this and will request a
pullup to NetBSD-8.
Diffstat (limited to 'src/dhcpcd.c')
| -rw-r--r-- | src/dhcpcd.c | 91 |
1 files changed, 77 insertions, 14 deletions
diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 8b7da6ca..5b345d5f 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -955,20 +955,6 @@ dhcpcd_activateinterface(struct interface *ifp, unsigned long long options) } } -static void -dhcpcd_handlelink(void *arg) -{ - struct dhcpcd_ctx *ctx; - - ctx = arg; - if (if_handlelink(ctx) == -1) { - logerr(__func__); - eloop_event_delete(ctx->eloop, ctx->link_fd); - close(ctx->link_fd); - ctx->link_fd = -1; - } -} - int dhcpcd_handleinterface(void *arg, int action, const char *ifname) { @@ -1041,6 +1027,83 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname) return 1; } +static void +dhcpcd_handlelink(void *arg) +{ + struct dhcpcd_ctx *ctx = arg; + + if (if_handlelink(ctx) == -1) { + if (errno == ENOBUFS) { + dhcpcd_linkoverflow(ctx); + return; + } + logerr(__func__); + } +} + +static void +dhcpcd_checkcarrier(void *arg) +{ + struct interface *ifp = arg; + + dhcpcd_handlecarrier(ifp->ctx, LINK_UNKNOWN, ifp->flags, ifp->name); +} + +void +dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx) +{ + struct if_head *ifaces; + struct ifaddrs *ifaddrs; + struct interface *ifp, *ifn, *ifp1; + + loginfox("route socket overflowed - learning interface state"); + + /* Close the existing socket and open a new one. + * This is easier than draining the kernel buffer of an + * in-determinate size. */ + eloop_event_delete(ctx->eloop, ctx->link_fd); + close(ctx->link_fd); + if_closesockets_os(ctx); + if (if_opensockets_os(ctx) == -1) { + logerr("%s: if_opensockets", __func__); + eloop_exit(ctx->eloop, EXIT_FAILURE); + return; + } + eloop_event_add(ctx->eloop, ctx->link_fd, dhcpcd_handlelink, ctx); + + /* Work out the current interfaces. */ + ifaces = if_discover(ctx, &ifaddrs, ctx->ifc, ctx->ifv); + + /* Punt departed interfaces */ + TAILQ_FOREACH_SAFE(ifp, ctx->ifaces, next, ifn) { + if (if_find(ifaces, ifp->name) != NULL) + continue; + dhcpcd_handleinterface(ctx, -1, ifp->name); + } + + /* Add new interfaces */ + TAILQ_FOREACH_SAFE(ifp, ifaces, next, ifn) { + ifp1 = if_find(ctx->ifaces, ifp->name); + if (ifp1 != NULL) { + /* If the interface already exists, + * check carrier state. */ + eloop_timeout_add_sec(ctx->eloop, 0, + dhcpcd_checkcarrier, ifp1); + continue; + } + TAILQ_REMOVE(ifaces, ifp, next); + TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next); + if (ifp->active) + eloop_timeout_add_sec(ctx->eloop, 0, + dhcpcd_prestartinterface, ifp); + } + + /* Update address state. */ + if_markaddrsstale(ctx->ifaces); + if_learnaddrs(ctx, ctx->ifaces, &ifaddrs); + if_deletestaleaddrs(ctx->ifaces); +} + void dhcpcd_handlehwaddr(struct dhcpcd_ctx *ctx, const char *ifname, const void *hwaddr, uint8_t hwlen) |
