From: Roy Marples Date: Tue, 16 Dec 2014 13:25:40 +0000 (+0000) Subject: For BSD systems stop polling for IPv6 router reachability. X-Git-Tag: v6.6.6~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3852009b98d8a5f3447c95e45cdcf7e995a22613;p=thirdparty%2Fdhcpcd.git For BSD systems stop polling for IPv6 router reachability. This didn't work on all BSD platforms and where it did work, it didn't work reliably. Instead, expect for cached neighour route additions/changes/removals to be announced by the kernel. Currently only NetBSD-7.99.3 supports this so this is added to the README. --- diff --git a/README b/README index 10e84f5e..cc2c1847 100644 --- a/README +++ b/README @@ -48,6 +48,12 @@ BSD systems where this has been fixed or is known to work are: OpenBSD-5.0 patch submitted against FreeBSD-10.0 +Some BSD systems do not announce cached neighbour route changes based +on reachability to userland. For such systems, IPv6 routers will always +be assumed to be reachable until they either stop being a router or expire. +BSD systems where this has been fixed or is known to work are: + NetBSD-7.99.3 + We try and detect how dhcpcd should interact with system services at runtime. If we cannot auto-detect how do to this, or it is wrong then you can change this by passing shell commands to --serviceexists, diff --git a/if-bsd.c b/if-bsd.c index 6a9ddc1e..b61d80c7 100644 --- a/if-bsd.c +++ b/if-bsd.c @@ -580,6 +580,15 @@ ifa_scope(struct sockaddr_in6 *sin, unsigned int ifindex) #endif } +#ifdef __KAME__ +#define DESCOPE(ia6) do { \ + if (IN6_IS_ADDR_LINKLOCAL((ia6))) \ + (ia6)->s6_addr[2] = (ia6)->s6_addr[3] = '\0'; \ + } while (/*CONSTCOND */0) +#else +#define DESCOPE(ia6) +#endif + int if_address6(const struct ipv6_addr *a, int action) { @@ -746,11 +755,6 @@ get_addrs(int type, char *cp, struct sockaddr **sa) for (i = 0; i < RTAX_MAX; i++) { if (type & (1 << i)) { sa[i] = (struct sockaddr *)cp; -#ifdef DEBUG - printf ("got %d %d %s\n", i, sa[i]->sa_family, - inet_ntoa(((struct sockaddr_in *)sa[i])-> - sin_addr)); -#endif RT_ADVANCE(cp, sa[i]); } else sa[i] = NULL; @@ -856,16 +860,20 @@ if_managelink(struct dhcpcd_ctx *ctx) dhcpcd_handlecarrier(ctx, len, (unsigned int)ifm->ifm_flags, ifp->name); break; + case RTM_ADD: + case RTM_CHANGE: case RTM_DELETE: - if (~rtm->rtm_addrs & - (RTA_DST | RTA_GATEWAY | RTA_NETMASK)) - break; cp = (char *)(void *)(rtm + 1); sa = (struct sockaddr *)(void *)cp; get_addrs(rtm->rtm_addrs, cp, rti_info); switch (sa->sa_family) { #ifdef INET case AF_INET: + if (rtm->rtm_type != RTM_DELETE) + break; + if (~rtm->rtm_addrs & + (RTA_DST | RTA_GATEWAY | RTA_NETMASK)) + break; memset(&rt, 0, sizeof(rt)); rt.iface = NULL; COPYOUT(rt.dest, rti_info[RTAX_DST]); @@ -876,6 +884,37 @@ if_managelink(struct dhcpcd_ctx *ctx) #endif #ifdef INET6 case AF_INET6: + if (~rtm->rtm_addrs & + (RTA_DST | RTA_GATEWAY)) + break; + /* + * BSD caches host routes in the + * routing table. + * As such, we should be notified of + * reachability by its existance + * with a hardware address + */ + if (rtm->rtm_flags & (RTF_HOST)) { + COPYOUT6(ia6, rti_info[RTAX_DST]); + DESCOPE(&ia6); + if (rti_info[RTAX_GATEWAY]->sa_family + == AF_LINK) + memcpy(&sdl, + rti_info[RTAX_GATEWAY], + sizeof(sdl)); + else + sdl.sdl_alen = 0; + ipv6nd_neighbour(ctx, &ia6, + rtm->rtm_type != RTM_DELETE && + sdl.sdl_alen ? + IPV6ND_REACHABLE : 0); + break; + } + + if (rtm->rtm_type != RTM_DELETE) + break; + if (!(rtm->rtm_addrs & RTA_NETMASK)) + break; memset(&rt6, 0, sizeof(rt6)); rt6.iface = NULL; COPYOUT6(rt6.dest, rti_info[RTAX_DST]); @@ -885,6 +924,7 @@ if_managelink(struct dhcpcd_ctx *ctx) break; #endif } + break; #ifdef RTM_CHGADDR case RTM_CHGADDR: /* FALLTHROUGH */ #endif @@ -928,10 +968,7 @@ if_managelink(struct dhcpcd_ctx *ctx) sin6 = (struct sockaddr_in6*)(void *) rti_info[RTAX_IFA]; ia6 = sin6->sin6_addr; -#ifdef __KAME__ - if (IN6_IS_ADDR_LINKLOCAL(&ia6)) - ia6.s6_addr[2] = ia6.s6_addr[3] = '\0'; -#endif + DESCOPE(&ia6); if (rtm->rtm_type == RTM_NEWADDR) { ifa_flags = if_addrflags6(&ia6, ifp); if (ifa_flags == -1) @@ -1031,43 +1068,6 @@ eexit: return error; } -int -if_nd6reachable(const char *ifname, struct in6_addr *addr) -{ - int s, flags; - struct in6_nbrinfo nbi; - - if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) - return -1; - - memset(&nbi, 0, sizeof(nbi)); - strlcpy(nbi.ifname, ifname, sizeof(nbi.ifname)); - nbi.addr = *addr; - if (ioctl(s, SIOCGNBRINFO_IN6, &nbi) == -1) { -#ifdef __FreeBSD__ - /* FreeBSD doesn't support reachable routers? */ - if (errno == EINVAL) - errno = ENOTSUP; -#endif - flags = -1; - } else { - flags = 0; - switch(nbi.state) { - case ND6_LLINFO_NOSTATE: /* just added */ - case ND6_LLINFO_REACHABLE: - case ND6_LLINFO_STALE: - case ND6_LLINFO_DELAY: - case ND6_LLINFO_PROBE: - flags |= IPV6ND_REACHABLE; - break; - } - if (nbi.isrouter) - flags |= IPV6ND_ROUTER; - } - close(s); - return flags; -} - static int if_raflush(void) { diff --git a/if-sun.c b/if-sun.c index 2c9252d3..4627c904 100644 --- a/if-sun.c +++ b/if-sun.c @@ -167,14 +167,6 @@ if_machinearch(char *str, size_t len) } #ifdef INET6 -int -if_nd6reachable(const char *ifname, struct in6_addr *addr) -{ - - errno = ENOTSUP; - return -1; -} - void if_rarestore(struct dhcpcd_ctx *ctx) { diff --git a/if.h b/if.h index 6df69674..21ceaf0a 100644 --- a/if.h +++ b/if.h @@ -124,11 +124,11 @@ int if_route(const struct rt *rt, int); #ifdef INET6 int if_checkipv6(struct dhcpcd_ctx *ctx, const struct interface *, int); -int if_nd6reachable(const char *ifname, struct in6_addr *addr); int if_address6(const struct ipv6_addr *, int); #define if_addaddress6(a) if_address6(a, 1) #define if_deladdress6(a) if_address6(a, -1) + int if_addrflags6(const struct in6_addr *, const struct interface *); int if_route6(const struct rt6 *rt, int); diff --git a/ipv6nd.c b/ipv6nd.c index f3ae8f38..44fdd288 100644 --- a/ipv6nd.c +++ b/ipv6nd.c @@ -116,8 +116,6 @@ struct nd_opt_dnssl { /* DNSSL option RFC 6106 */ #define IPV6_ADDR_INT16_MLL 0x02ff #endif -#define ND6REACHABLE_TIMER 1 - /* Debugging Neighbor Solicitations is a lot of spam, so disable it */ //#define DEBUG_NS // @@ -330,8 +328,6 @@ ipv6nd_reachable(struct ra *rap, int flags) script_runreason(rap->iface, "ROUTERADVERT"); } } else { - /* Any error means it's really gone from the kernel - * neighbour database */ if (rap->lifetime && !rap->expired) { syslog(LOG_WARNING, "%s: %s is unreachable, expiring it", @@ -344,7 +340,6 @@ ipv6nd_reachable(struct ra *rap, int flags) } } -#ifdef RTM_NEWNEIGH void ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags) { @@ -359,31 +354,6 @@ ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags) } } } -#else -static void -ipv6nd_checkreachablerouters(void *arg) -{ - struct dhcpcd_ctx *ctx = arg; - struct ra *rap; - int flags; - - TAILQ_FOREACH(rap, ctx->ipv6->ra_routers, next) { - flags = if_nd6reachable(rap->iface->name, &rap->from); - if (flags == -1) { - if (errno == ENOTSUP) - /* Unsupported? We have to assume reachable */ - flags = IPV6ND_REACHABLE; - else - /* An error occured, so it's unreachable */ - flags = 0; - } - ipv6nd_reachable(rap, flags); - } - - eloop_timeout_add_sec(ctx->eloop, ND6REACHABLE_TIMER, - ipv6nd_checkreachablerouters, ctx); -} -#endif static void ipv6nd_free_opts(struct ra *rap) @@ -430,11 +400,6 @@ void ipv6nd_freedrop_ra(struct ra *rap, int drop) eloop_timeout_delete(rap->iface->ctx->eloop, NULL, rap); if (!drop) TAILQ_REMOVE(rap->iface->ctx->ipv6->ra_routers, rap, next); -#ifndef RTM_NEWNEIGH - if (TAILQ_FIRST(rap->iface->ctx->ipv6->ra_routers) == NULL) - eloop_timeout_delete(rap->iface->ctx->eloop, - ipv6nd_checkreachablerouters, rap->iface->ctx); -#endif ipv6_freedrop_addrs(&rap->addrs, drop, NULL); ipv6nd_free_opts(rap); free(rap->data); @@ -1126,12 +1091,6 @@ nodhcp6: /* Expire should be called last as the rap object could be destroyed */ ipv6nd_expirera(ifp); - -#ifndef RTM_NEWNEIGH - /* Start our reachability tests now */ - eloop_timeout_add_sec(ifp->ctx->eloop, ND6REACHABLE_TIMER, - ipv6nd_checkreachablerouters, ifp->ctx); -#endif } int @@ -1419,11 +1378,8 @@ ipv6nd_handlena(struct ipv6_ctx *ctx, struct interface *ifp, struct nd_neighbor_advert *nd_na; struct ra *rap; int is_router, is_solicited; - - if ((size_t)len < sizeof(struct nd_neighbor_advert)) { - syslog(LOG_ERR, "IPv6 NA packet too short from %s", ctx->sfrom); - return; - } + char buf[INET6_ADDRSTRLEN]; + const char *taddr; if (ifp == NULL) { #ifdef DEBUG_NS @@ -1433,13 +1389,21 @@ ipv6nd_handlena(struct ipv6_ctx *ctx, struct interface *ifp, return; } + if ((size_t)len < sizeof(struct nd_neighbor_advert)) { + syslog(LOG_ERR, "%s: IPv6 NA too short from %s", + ifp->name, ctx->sfrom); + return; + } + nd_na = (struct nd_neighbor_advert *)icp; is_router = nd_na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER; is_solicited = nd_na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED; + taddr = inet_ntop(AF_INET6, &nd_na->nd_na_target, + buf, INET6_ADDRSTRLEN); if (IN6_IS_ADDR_MULTICAST(&nd_na->nd_na_target)) { - syslog(LOG_ERR, "%s: NA for multicast address from %s", - ifp->name, ctx->sfrom); + syslog(LOG_ERR, "%s: NA multicast address %s (%s)", + ifp->name, taddr, ctx->sfrom); return; } @@ -1450,21 +1414,21 @@ ipv6nd_handlena(struct ipv6_ctx *ctx, struct interface *ifp, } if (rap == NULL) { #ifdef DEBUG_NS - syslog(LOG_DEBUG, "%s: unexpected NA from s", - ifp->name, ctx->sfrom); + syslog(LOG_DEBUG, "%s: unexpected NA from %s for %s", + ifp->name, ctx->sfrom, taddr); #endif return; } #ifdef DEBUG_NS - syslog(LOG_DEBUG, "%s: %sNA from %s", - ifp->name, is_solicited ? "solicited " : "", ctx->sfrom); + syslog(LOG_DEBUG, "%s: %sNA for %s from %s", + ifp->name, is_solicited ? "solicited " : "", taddr, ctx->sfrom); #endif /* Node is no longer a router, so remove it from consideration */ if (!is_router && !rap->expired) { - syslog(LOG_INFO, "%s: %s is no longer a router", - ifp->name, ctx->sfrom); + syslog(LOG_INFO, "%s: %s not a router (%s)", + ifp->name, taddr, ctx->sfrom); rap->expired = 1; ipv6_buildroutes(ifp->ctx); script_runreason(ifp, "ROUTERADVERT"); @@ -1474,8 +1438,8 @@ ipv6nd_handlena(struct ipv6_ctx *ctx, struct interface *ifp, if (is_solicited && is_router && rap->lifetime) { if (rap->expired) { rap->expired = 0; - syslog(LOG_INFO, "%s: %s is reachable again", - ifp->name, ctx->sfrom); + syslog(LOG_INFO, "%s: %s reachable (%s)", + ifp->name, taddr, ctx->sfrom); ipv6_buildroutes(ifp->ctx); script_runreason(rap->iface, "ROUTERADVERT"); /* XXX */ } diff --git a/ipv6nd.h b/ipv6nd.h index a3c9a283..310eb68e 100644 --- a/ipv6nd.h +++ b/ipv6nd.h @@ -97,10 +97,7 @@ void ipv6nd_handleifa(struct dhcpcd_ctx *, int, const char *, const struct in6_addr *, int); int ipv6nd_dadcompleted(const struct interface *); void ipv6nd_drop(struct interface *); - -#ifdef RTM_NEWNEIGH void ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, int); -#endif #else #define ipv6nd_startrs(a) {} #define ipv6nd_findaddr(a, b, c) (0)