]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
For BSD systems stop polling for IPv6 router reachability.
authorRoy Marples <roy@marples.name>
Tue, 16 Dec 2014 13:25:40 +0000 (13:25 +0000)
committerRoy Marples <roy@marples.name>
Tue, 16 Dec 2014 13:25:40 +0000 (13:25 +0000)
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.

README
if-bsd.c
if-sun.c
if.h
ipv6nd.c
ipv6nd.h

diff --git a/README b/README
index 10e84f5efe1ef38ed6f7a5ac286ad9bb02fcf391..cc2c1847f3dcc823245469a481751826145f2607 100644 (file)
--- 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,
index 6a9ddc1ed7024f5b83eaaa841a095a1a2d49c37d..b61d80c7ffd55759d6bdd8e6f587abae3348ff8a 100644 (file)
--- 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)
 {
index 2c9252d3d8e645d7aa50fbeab48819fc42783187..4627c90495a221d0a91a99f314831075f9420802 100644 (file)
--- 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 6df696749e646bb5813061cad88787de668add91..21ceaf0a4e8204722233ee67111991052f7903c6 100644 (file)
--- 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);
index f3ae8f380ab720ba3cb15c1642666c59c6c709f8..44fdd288af3b740c81698b80959714074fabbdf3 100644 (file)
--- 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 */
                }
index a3c9a2838f194bced297e776dca8691a387ef6ac..310eb68e60eccc19ef630bc3b08535b32a700d9a 100644 (file)
--- 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)