From: Roy Marples Date: Fri, 30 Nov 2018 03:27:57 +0000 (+0000) Subject: BSD: don't listen to own route messages X-Git-Tag: v7.1.0~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8b9cf952a3d9f96b030e524593710663520b83f3;p=thirdparty%2Fdhcpcd.git BSD: don't listen to own route messages This makes the code a lot simpler and reduces the changes of overflowing the route socket. --- diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 55558c3c..a007923d 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -314,13 +314,6 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx) return 0; } - /* 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: logerr("%s: fork", __func__); diff --git a/src/dhcpcd.h b/src/dhcpcd.h index 4f1c2101..9743ae62 100644 --- a/src/dhcpcd.h +++ b/src/dhcpcd.h @@ -166,13 +166,6 @@ 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/src/if-bsd.c b/src/if-bsd.c index e9ebfdce..68914b87 100644 --- a/src/if-bsd.c +++ b/src/if-bsd.c @@ -125,9 +125,7 @@ int if_opensockets_os(struct dhcpcd_ctx *ctx) { struct priv *priv; -#ifdef SO_RERROR - int on = 1; -#endif + int n; #if defined(RO_MSGFILTER) || defined(ROUTE_MSGFILTER) unsigned char msgfilter[] = { RTM_IFINFO, @@ -164,12 +162,21 @@ if_opensockets_os(struct dhcpcd_ctx *ctx) if (ctx->link_fd == -1) return -1; + /* Ignore our own route(4) messages. + * Sadly there is no way of doing this for route(4) messages + * generated from addresses we add/delete. */ + n = 0; + if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_USELOOPBACK, + &n, sizeof(n)) == -1) + logerr("%s: SO_USELOOPBACK", __func__); + #ifdef SO_RERROR /* Tell recvmsg(2) to return ENOBUFS if the receiving socket overflows * from too many route(4) messages so we can re-sync our state * with reality. */ + n = 1; if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RERROR, - &on, sizeof(on)) == -1) + &n, sizeof(n)) == -1) logerr("%s: SO_RERROR", __func__); #endif @@ -477,11 +484,6 @@ if_route(unsigned char cmd, const struct rt *rt) assert(rt != NULL); ctx = rt->rt_ifp->ctx; - if ((cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE) && - ctx->options & DHCPCD_DAEMONISE && - !(ctx->options & DHCPCD_DAEMONISED)) - ctx->options |= DHCPCD_RTM_PPID; - #define ADDSA(sa) do { \ memcpy(bp, (sa), (sa)->sa_len); \ bp += RT_ROUNDUP((sa)->sa_len); \ @@ -596,7 +598,6 @@ if_route(unsigned char cmd, const struct rt *rt) rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm); if (write(ctx->link_fd, rtm, rtm->rtm_msglen) == -1) return -1; - ctx->sseq = ctx->seq; return 0; } @@ -873,7 +874,6 @@ if_address6(unsigned char cmd, const struct ipv6_addr *ia) cmd == RTM_DELADDR ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa); } -#if !(defined(HAVE_IFADDRS_ADDRFLAGS) && defined(HAVE_IFAM_ADDRFLAGS)) int if_addrflags6(const struct interface *ifp, const struct in6_addr *addr, __unused const char *alias) @@ -894,7 +894,6 @@ if_addrflags6(const struct interface *ifp, const struct in6_addr *addr, flags = -1; return flags; } -#endif int if_getlifetime6(struct ipv6_addr *ia) @@ -975,43 +974,11 @@ if_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm) (unsigned int)ifm->ifm_flags, ifp->name); } -static int -if_ownmsgpid(struct dhcpcd_ctx *ctx, pid_t pid, int seq) -{ - - /* Ignore messages generated by us */ - if (getpid() == pid) { - ctx->options &= ~DHCPCD_RTM_PPID; - return 1; - } - - /* Ignore messages sent by the parent after forking */ - if ((ctx->options & - (DHCPCD_RTM_PPID | DHCPCD_DAEMONISED)) == - (DHCPCD_RTM_PPID | DHCPCD_DAEMONISED) && - ctx->ppid == pid) - { - /* 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 routing table. */ - if (ctx->pseq == seq) - ctx->options &= ~DHCPCD_RTM_PPID; - return 1; - } - - /* Not a message we made. */ - return 0; -} - static void if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) { struct rt rt; - if (if_ownmsgpid(ctx, rtm->rtm_pid, rtm->rtm_seq)) - return; - /* Ignore errors. */ if (rtm->rtm_errno != 0) return; @@ -1058,26 +1025,6 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) return; #ifdef HAVE_IFAM_PID - if (if_ownmsgpid(ctx, ifam->ifam_pid, 0)) { -#ifdef HAVE_IFAM_ADDRFLAGS - /* If the kernel isn't doing DAD for the newly added - * address we need to let it through. */ - if (ifam->ifam_type != RTM_NEWADDR) - return; - switch (rti_info[RTAX_IFA]->sa_family) { - case AF_INET: - if (ifam->ifam_addrflags & IN_IFF_TENTATIVE) - return; - break; - case AF_INET6: - if (ifam->ifam_addrflags & IN6_IFF_TENTATIVE) - return; - break; - default: - return; - } -#endif - } pid = ifam->ifam_pid; #else pid = 0; @@ -1115,12 +1062,27 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) sin = (const void *)rti_info[RTAX_NETMASK]; mask.s_addr = sin != NULL && sin->sin_family == AF_INET ? sin->sin_addr.s_addr : INADDR_ANY; + sin = (const void *)rti_info[RTAX_BRD]; + bcast.s_addr = sin != NULL && sin->sin_family == AF_INET ? + sin->sin_addr.s_addr : INADDR_ANY; #if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000 - /* NetBSD-7 and older send an invalid broadcast address. + /* + * NetBSD-7 and older send an invalid broadcast address. * So we need to query the actual address to get - * the right one. */ + * the right one. + */ { +#else + /* + * If the address was deleted, lets check if it's + * a late message and it still exists (maybe modified). + * If so, ignore it as deleting an address causes + * dhcpcd to drop any lease to which it belongs. + */ + if (ifam->ifam_type == RTM_DELADDR) { +#endif +#ifdef SIOCGIFALIAS struct in_aliasreq ifra; memset(&ifra, 0, sizeof(ifra)); @@ -1132,38 +1094,38 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) if (ioctl(ctx->pf_inet_fd, SIOCGIFALIAS, &ifra) == -1) { if (errno != EADDRNOTAVAIL) logerr("%s: SIOCGIFALIAS", __func__); - break; + if (ifam->ifam_type != RTM_DELADDR) + break; } - bcast = ifra.ifra_broadaddr.sin_addr; - } -#else - sin = (const void *)rti_info[RTAX_BRD]; - bcast.s_addr = sin != NULL && sin->sin_family == AF_INET ? - sin->sin_addr.s_addr : INADDR_ANY; +#if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000 + else + bcast = ifra.ifra_broadaddr.sin_addr; #endif - -#if defined(__FreeBSD__) || defined(__DragonFly__) - /* FreeBSD sends RTM_DELADDR for each assigned address - * to an interface just brought down. - * This is wrong, because the address still exists. - * So we need to ignore it. - * Oddly enough this only happens for INET addresses. */ - if (ifam->ifam_type == RTM_DELADDR) { - struct ifreq ifr; - struct sockaddr_in *ifr_sin; - - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); - ifr_sin = (void *)&ifr.ifr_addr; - ifr_sin->sin_family = AF_INET; - ifr_sin->sin_addr = addr; - if (ioctl(ctx->pf_inet_fd, SIOCGIFADDR, &ifr) == 0) { - logwarnx("%s: ignored false RTM_DELADDR for %s", - ifp->name, inet_ntoa(addr)); - break; +#else +#warning No SIOCGIFALIAS support + /* + * No SIOCGIFALIAS? That sucks! + * This makes this call very heavy weight, but we + * really need to know if the message is late or not. + */ + struct ifaddrs *ifaddrs = NULL, *ifa; + struct in_addr *a; + + getifaddrs(&ifa); + for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || + ifa->ifa_addr->sa_family != AF_INET) + continue; + a = (void *)ifa->ifa_addr; + if (a->s_addr == addr.s_addr && + strcmp(ifa->ifa_name, ifp->name) == 0) + break; } - } + freeifaddrs(ifa); + if (ifa != NULL) + break; #endif + } #ifndef HAVE_IFAM_ADDRFLAGS if (ifam->ifam_type == RTM_DELADDR) @@ -1185,15 +1147,26 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) { struct in6_addr addr6, mask6; const struct sockaddr_in6 *sin6; + int flags; sin6 = (const void *)rti_info[RTAX_IFA]; addr6 = sin6->sin6_addr; sin6 = (const void *)rti_info[RTAX_NETMASK]; mask6 = sin6->sin6_addr; + /* + * If the address was deleted, lets check if it's + * a late message and it still exists (maybe modified). + * If so, ignore it as deleting an address causes + * dhcpcd to drop any lease to which it belongs. + */ + if (ifam->ifam_type == RTM_DELADDR) { + flags = if_addrflags6(ifp, &addr6, NULL); + if (flags != -1) + break; + addrflags = 0; + } #ifndef HAVE_IFAM_ADDRFLAGS - if (ifam->ifam_type == RTM_DELADDR) - addrflags = 0; else if ((addrflags = if_addrflags6(ifp, &addr6, NULL)) == -1) { if (errno != EADDRNOTAVAIL) logerr("%s: if_addrflags6", __func__); diff --git a/src/if-linux.c b/src/if-linux.c index b5f7983d..88e879cc 100644 --- a/src/if-linux.c +++ b/src/if-linux.c @@ -854,7 +854,6 @@ send_netlink(struct dhcpcd_ctx *ctx, struct interface *ifp, struct priv *priv; priv = (struct priv *)ctx->priv; - ctx->sseq = ctx->seq; r = get_netlink(ctx, priv->sndrcv_iov, ifp, s, 0, callback); } else r = -1; diff --git a/src/if-options.h b/src/if-options.h index 8dfe3486..8a3c4479 100644 --- a/src/if-options.h +++ b/src/if-options.h @@ -109,7 +109,7 @@ #define DHCPCD_DHCP6 (1ULL << 50) #define DHCPCD_IF_UP (1ULL << 51) #define DHCPCD_INFORM6 (1ULL << 52) -#define DHCPCD_RTM_PPID (1ULL << 53) +// unused (1ULL << 53) #define DHCPCD_IPV6RA_AUTOCONF (1ULL << 54) #define DHCPCD_ROUTER_HOST_ROUTE_WARNED (1ULL << 55) #define DHCPCD_LASTLEASE_EXTEND (1ULL << 56) diff --git a/src/if-sun.c b/src/if-sun.c index dfe7734a..4f902ba3 100644 --- a/src/if-sun.c +++ b/src/if-sun.c @@ -114,6 +114,7 @@ int if_opensockets_os(struct dhcpcd_ctx *ctx) { struct priv *priv; + int n; if ((priv = malloc(sizeof(*priv))) == NULL) return -1; @@ -128,11 +129,21 @@ if_opensockets_os(struct dhcpcd_ctx *ctx) ctx->link_fd = socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); -#ifdef INET - if (ctx->link_fd == -1) + + if (ctx->link_fd == -1) { free(ctx->priv); -#endif - return ctx->link_fd == -1 ? -1 : 0; + return -1; + } + + /* Ignore our own route(4) messages. + * Sadly there is no way of doing this for route(4) messages + * generated from addresses we add/delete. */ + n = 0; + if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_USELOOPBACK, + &n, sizeof(n)) == -1) + logerr("%s: SO_USELOOPBACK", __func__); + + return 0; } void @@ -537,27 +548,6 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) const struct sockaddr *sa; struct rt rt; - /* Ignore messages generated by us */ - if (rtm->rtm_pid == getpid()) { - ctx->options &= ~DHCPCD_RTM_PPID; - return; - } - - /* Ignore messages sent by the parent 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 therouting table. */ - if (rtm->rtm_seq == ctx->pseq) - ctx->options &= ~DHCPCD_RTM_PPID; - return; - } - sa = (const void *)(rtm + 1); switch (sa->sa_family) { #ifdef INET6 @@ -989,10 +979,6 @@ if_route(unsigned char cmd, const struct rt *rt) * This includes subnet/prefix routes. */ ctx = rt->rt_ifp->ctx; - if ((cmd == RTM_ADD || cmd == RTM_DELETE || cmd == RTM_CHANGE) && - ctx->options & DHCPCD_DAEMONISE && - !(ctx->options & DHCPCD_DAEMONISED)) - ctx->options |= DHCPCD_RTM_PPID; #define ADDSA(sa) do { \ l = RT_ROUNDUP(salen((sa))); \ @@ -1069,7 +1055,6 @@ if_route(unsigned char cmd, const struct rt *rt) rtm->rtm_msglen = (unsigned short)(bp - (char *)rtm); if (write(ctx->link_fd, rtm, rtm->rtm_msglen) == -1) return -1; - ctx->sseq = ctx->seq; return 0; } diff --git a/src/ipv4.c b/src/ipv4.c index 8008f9e6..3720f153 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -880,6 +880,9 @@ ipv4_handleifa(struct dhcpcd_ctx *ctx, case RTM_DELADDR: if (ia == NULL) return; + if (mask->s_addr != INADDR_ANY && + mask->s_addr != ia->mask.s_addr) + return; TAILQ_REMOVE(&state->addrs, ia, next); break; default: