Mercurial > hg > dhcpcd
changeset 4235:27bad70c0d9c draft
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.
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Mon, 19 Mar 2018 15:39:05 +0000 |
| parents | 56c1ab3b654e |
| children | f6be46401658 |
| files | src/dhcpcd.c src/dhcpcd.h src/if-bsd.c src/if.c src/if.h src/ipv4.c src/ipv4.h src/ipv6.c src/ipv6.h src/ipv6nd.c |
| diffstat | 10 files changed, 188 insertions(+), 21 deletions(-) [+] |
line wrap: on
line diff
--- a/src/dhcpcd.c Thu Mar 15 20:31:14 2018 +0000 +++ b/src/dhcpcd.c Mon Mar 19 15:39:05 2018 +0000 @@ -955,20 +955,6 @@ } } -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 @@ 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)
--- a/src/dhcpcd.h Thu Mar 15 20:31:14 2018 +0000 +++ b/src/dhcpcd.h Mon Mar 19 15:39:05 2018 +0000 @@ -230,6 +230,7 @@ int dhcpcd_afwaiting(const struct dhcpcd_ctx *); pid_t dhcpcd_daemonise(struct dhcpcd_ctx *); +void dhcpcd_linkoverflow(struct dhcpcd_ctx *); int dhcpcd_handleargs(struct dhcpcd_ctx *, struct fd_list *, int, char **); void dhcpcd_handlecarrier(struct dhcpcd_ctx *, int, unsigned int, const char *); int dhcpcd_handleinterface(void *, int, const char *);
--- a/src/if-bsd.c Thu Mar 15 20:31:14 2018 +0000 +++ b/src/if-bsd.c Mon Mar 19 15:39:05 2018 +0000 @@ -1210,6 +1210,11 @@ case RTM_NEWADDR: if_ifa(ctx, (const void *)rtm); break; +#ifdef RTM_DESYNC + case RTM_DESYNC: + dhcpcd_linkoverflow(ctx); + break; +#endif } } @@ -1223,7 +1228,8 @@ msg.msg_iov = ctx->iov; msg.msg_iovlen = 1; - if ((len = recvmsg_realloc(ctx->link_fd, &msg, 0)) == -1) + len = recvmsg_realloc(ctx->link_fd, &msg, 0); + if (len == -1) return -1; if (len != 0) if_dispatch(ctx, ctx->iov[0].iov_base); @@ -1480,9 +1486,11 @@ char ifname[IFNAMSIZ + 8]; strlcpy(ifname, ifp->name, sizeof(ifname)); - if (ioctl(s, SIOCSRTRFLUSH_IN6, (void *)&ifname) == -1) + if (ioctl(s, SIOCSRTRFLUSH_IN6, (void *)&ifname) == -1 && + errno != ENOTSUP) logwarn("SIOCSRTRFLUSH_IN6"); - if (ioctl(s, SIOCSPFXFLUSH_IN6, (void *)&ifname) == -1) + if (ioctl(s, SIOCSPFXFLUSH_IN6, (void *)&ifname) == -1 && + errno != ENOTSUP) logwarn("SIOCSPFXFLUSH_IN6"); } #endif
--- a/src/if.c Thu Mar 15 20:31:14 2018 +0000 +++ b/src/if.c Mon Mar 19 15:39:05 2018 +0000 @@ -191,6 +191,21 @@ } void +if_markaddrsstale(struct if_head *ifs) +{ + struct interface *ifp; + + TAILQ_FOREACH(ifp, ifs, next) { +#ifdef INET + ipv4_markaddrsstale(ifp); +#endif +#ifdef INET6 + ipv6_markaddrsstale(ifp, 0); +#endif + } +} + +void if_learnaddrs(struct dhcpcd_ctx *ctx, struct if_head *ifs, struct ifaddrs **ifaddrs) { @@ -268,6 +283,21 @@ *ifaddrs = NULL; } +void +if_deletestaleaddrs(struct if_head *ifs) +{ + struct interface *ifp; + + TAILQ_FOREACH(ifp, ifs, next) { +#ifdef INET + ipv4_deletestaleaddrs(ifp); +#endif +#ifdef INET6 + ipv6_deletestaleaddrs(ifp); +#endif + } +} + bool if_valid_hwaddr(const uint8_t *hwaddr, size_t hwlen) {
--- a/src/if.h Thu Mar 15 20:31:14 2018 +0000 +++ b/src/if.h Mon Mar 19 15:39:05 2018 +0000 @@ -116,7 +116,9 @@ bool if_valid_hwaddr(const uint8_t *, size_t); struct if_head *if_discover(struct dhcpcd_ctx *, struct ifaddrs **, int, char * const *); +void if_markaddrsstale(struct if_head *); void if_learnaddrs(struct dhcpcd_ctx *, struct if_head *, struct ifaddrs **); +void if_deletestaleaddrs(struct if_head *); struct interface *if_find(struct if_head *, const char *); struct interface *if_findindex(struct if_head *, unsigned int); struct interface *if_loopback(struct dhcpcd_ctx *);
--- a/src/ipv4.c Thu Mar 15 20:31:14 2018 +0000 +++ b/src/ipv4.c Mon Mar 19 15:39:05 2018 +0000 @@ -762,6 +762,39 @@ } void +ipv4_markaddrsstale(struct interface *ifp) +{ + struct ipv4_state *state; + struct ipv4_addr *ia; + + state = IPV4_STATE(ifp); + if (state == NULL) + return; + + TAILQ_FOREACH(ia, &state->addrs, next) { + ia->flags |= IPV4_AF_STALE; + } +} + +void +ipv4_deletestaleaddrs(struct interface *ifp) +{ + struct ipv4_state *state; + struct ipv4_addr *ia, *ia1; + + state = IPV4_STATE(ifp); + if (state == NULL) + return; + + TAILQ_FOREACH_SAFE(ia, &state->addrs, next, ia1) { + if (ia->flags & IPV4_AF_STALE) + ipv4_handleifa(ifp->ctx, RTM_DELADDR, + ifp->ctx->ifaces, ifp->name, + &ia->addr, &ia->mask, &ia->brd, 0, 0); + } +} + +void ipv4_handleifa(struct dhcpcd_ctx *ctx, int cmd, struct if_head *ifs, const char *ifname, const struct in_addr *addr, const struct in_addr *mask, @@ -796,6 +829,7 @@ ia->iface = ifp; ia->addr = *addr; ia->mask = *mask; + ia->flags = 0; ia_is_new = true; #ifdef ALIAS_ADDR strlcpy(ia->alias, ifname, sizeof(ia->alias)); @@ -817,6 +851,7 @@ else ia->brd.s_addr = INADDR_ANY; ia->addr_flags = addrflags; + ia->flags &= ~IPV4_AF_STALE; break; case RTM_DELADDR: if (ia == NULL)
--- a/src/ipv4.h Thu Mar 15 20:31:14 2018 +0000 +++ b/src/ipv4.h Mon Mar 19 15:39:05 2018 +0000 @@ -79,6 +79,7 @@ struct in_addr brd; struct interface *iface; int addr_flags; + unsigned int flags; char saddr[INET_ADDRSTRLEN + 3]; #ifdef ALIAS_ADDR char alias[IF_NAMESIZE]; @@ -86,6 +87,8 @@ }; TAILQ_HEAD(ipv4_addrhead, ipv4_addr); +#define IPV4_AF_STALE (1U << 0) + #define IPV4_ADDR_EQ(a1, a2) ((a1) && (a1)->addr.s_addr == (a2)->addr.s_addr) #define IPV4_MASK1_EQ(a1, a2) ((a1) && (a1)->mask.s_addr == (a2)->mask.s_addr) #define IPV4_MASK_EQ(a1, a2) (IPV4_ADDR_EQ(a1, a2) && IPV4_MASK1_EQ(a1, a2)) @@ -129,6 +132,8 @@ struct ipv4_addr *ipv4_findaddr(struct dhcpcd_ctx *, const struct in_addr *); struct ipv4_addr *ipv4_findmaskaddr(struct dhcpcd_ctx *, const struct in_addr *); +void ipv4_markaddrsstale(struct interface *); +void ipv4_deletestaleaddrs(struct interface *); void ipv4_handleifa(struct dhcpcd_ctx *, int, struct if_head *, const char *, const struct in_addr *, const struct in_addr *, const struct in_addr *, int, pid_t);
--- a/src/ipv6.c Thu Mar 15 20:31:14 2018 +0000 +++ b/src/ipv6.c Mon Mar 19 15:39:05 2018 +0000 @@ -1134,6 +1134,7 @@ TAILQ_INSERT_TAIL(&state->addrs, ia, next); } ia->addr_flags = addrflags; + ia->flags &= ~IPV6_AF_STALE; #ifdef IPV6_MANAGETEMPADDR if (ia->addr_flags & IN6_IFF_TEMPORARY) ia->flags |= IPV6_AF_TEMPORARY; @@ -1973,18 +1974,39 @@ } void -ipv6_settempstale(struct interface *ifp) +ipv6_markaddrsstale(struct interface *ifp, unsigned int flags) { struct ipv6_state *state; struct ipv6_addr *ia; state = IPV6_STATE(ifp); + if (state == NULL) + return; + TAILQ_FOREACH(ia, &state->addrs, next) { - if (ia->flags & IPV6_AF_TEMPORARY) + if (flags == 0 || ia->flags & flags) ia->flags |= IPV6_AF_STALE; } } +void +ipv6_deletestaleaddrs(struct interface *ifp) +{ + struct ipv6_state *state; + struct ipv6_addr *ia, *ia1; + + state = IPV6_STATE(ifp); + if (state == NULL) + return; + + TAILQ_FOREACH_SAFE(ia, &state->addrs, next, ia1) { + if (ia->flags & IPV6_AF_STALE) + ipv6_handleifa(ifp->ctx, RTM_DELADDR, + ifp->ctx->ifaces, ifp->name, + &ia->addr, ia->prefix_len, 0, 0); + } +} + struct ipv6_addr * ipv6_settemptime(struct ipv6_addr *ia, int flags) {
--- a/src/ipv6.h Thu Mar 15 20:31:14 2018 +0000 +++ b/src/ipv6.h Mon Mar 19 15:39:05 2018 +0000 @@ -231,6 +231,8 @@ int ipv6_userprefix( const struct in6_addr *, short prefix_len, uint64_t user_number, struct in6_addr *result, short result_len); void ipv6_checkaddrflags(void *); +void ipv6_markaddrsstale(struct interface *, unsigned int); +void ipv6_deletestaleaddrs(struct interface *); int ipv6_addaddr(struct ipv6_addr *, const struct timespec *); ssize_t ipv6_addaddrs(struct ipv6_addrhead *addrs); void ipv6_deleteaddr(struct ipv6_addr *); @@ -260,7 +262,6 @@ #ifdef IPV6_MANAGETEMPADDR void ipv6_gentempifid(struct interface *); -void ipv6_settempstale(struct interface *); struct ipv6_addr *ipv6_createtempaddr(struct ipv6_addr *, const struct timespec *); struct ipv6_addr *ipv6_settemptime(struct ipv6_addr *, int);
