]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
inet6: Stop listening to NA messages
authorRoy Marples <roy@marples.name>
Thu, 29 Aug 2019 18:10:53 +0000 (19:10 +0100)
committerRoy Marples <roy@marples.name>
Thu, 29 Aug 2019 18:10:53 +0000 (19:10 +0100)
This is very expensive, and we only listen for them for the
Router -> Host flag change. Linux and now NetBSD announce this
change via the netlink/routing socket so there is no longer a
need to listen for it.

RFC 4861 6.2.5 only says departing routers *SHOULD* send RA
with lifetime of zero and *MUST* send all subsequent NA messages
with the router flag unset, so dhcpcd *SHOULD* still work.

For the corner case where it doesn't, consider patching your
kernel.

src/ipv6nd.c

index 2a02a2a65615057c1ab53d94dbbfed64982a82d3..cb9e1086227d0152ce5a725885b48f6bb3c3fa50 100644 (file)
@@ -217,7 +217,6 @@ ipv6nd_open0(void)
                goto eexit;
 
        ICMP6_FILTER_SETBLOCKALL(&filt);
-       ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filt);
        ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
        if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER,
            &filt, sizeof(filt)) == -1)
@@ -555,19 +554,33 @@ ipv6nd_startexpire(struct interface *ifp)
            ipv6nd_expire, ifp);
 }
 
-static void
-ipv6nd_reachable(struct ra *rap, int flags)
+void
+ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags)
 {
+       struct ra *rap, *rapr;
+
+       if (ctx->ra_routers == NULL)
+               return;
+
+       TAILQ_FOREACH(rap, ctx->ra_routers, next) {
+               if (IN6_ARE_ADDR_EQUAL(&rap->from, addr))
+                       break;
+       }
 
-       if (rap->expired)
+       if (rap == NULL || rap->expired)
                return;
 
-       if (flags & IPV6ND_REACHABLE) {
+       if (!(flags & IPV6ND_ROUTER) && rap->lifetime != 0) {
+               loginfox("%s: %s is no longer a router",
+                   rap->iface->name, rap->sfrom);
+               rap->lifetime = 0;
+       } else if (flags & IPV6ND_REACHABLE) {
                if (rap->isreachable)
                        return;
                loginfox("%s: %s is reachable again",
                    rap->iface->name, rap->sfrom);
                rap->isreachable = true;
+               return;
        } else {
                if (!rap->isreachable)
                        return;
@@ -575,24 +588,6 @@ ipv6nd_reachable(struct ra *rap, int flags)
                    rap->iface->name, rap->sfrom);
                rap->isreachable = false;
        }
-}
-
-void
-ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags)
-{
-       struct ra *rap, *rapr;
-
-       if (ctx->ra_routers == NULL)
-               return;
-
-       TAILQ_FOREACH(rap, ctx->ra_routers, next) {
-               if (IN6_ARE_ADDR_EQUAL(&rap->from, addr)) {
-                       ipv6nd_reachable(rap, flags);
-                       break;
-               }
-       }
-       if (rap == NULL || flags & IPV6ND_REACHABLE)
-               return;
 
        /* If we have no reachable default routers, try and solicit one. */
        TAILQ_FOREACH(rapr, ctx->ra_routers, next) {
@@ -1709,83 +1704,6 @@ ipv6nd_drop(struct interface *ifp)
        }
 }
 
-static void
-ipv6nd_handlena(struct dhcpcd_ctx *ctx, const char *sfrom,
-    struct interface *ifp, struct icmp6_hdr *icp, size_t len, int hoplimit)
-{
-       struct nd_neighbor_advert *nd_na;
-       struct in6_addr nd_na_target;
-       struct ra *rap;
-       uint32_t is_router, is_solicited;
-       char buf[INET6_ADDRSTRLEN];
-       const char *taddr;
-
-       if (ifp == NULL) {
-#ifdef DEBUG_NS
-               logdebugx("NA for unexpected interface from %s", sfrom);
-#endif
-               return;
-       }
-
-       if ((size_t)len < sizeof(struct nd_neighbor_advert)) {
-               logerrx("%s: IPv6 NA too short from %s", ifp->name, sfrom);
-               return;
-       }
-
-       /* RFC 4861 7.1.2 */
-       if (hoplimit != 255) {
-               logerrx("invalid hoplimit(%d) in NA from %s",
-                   hoplimit, 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);
-
-       /* nd_na->nd_na_target is not aligned. */
-       memcpy(&nd_na_target, &nd_na->nd_na_target, sizeof(nd_na_target));
-       if (IN6_IS_ADDR_MULTICAST(&nd_na_target)) {
-               logerrx("%s: NA multicast address %s (%s)",
-                   ifp->name, taddr, sfrom);
-               return;
-       }
-
-       TAILQ_FOREACH(rap, ctx->ra_routers, next) {
-               if (rap->iface == ifp &&
-                   IN6_ARE_ADDR_EQUAL(&rap->from, &nd_na_target))
-                       break;
-       }
-       if (rap == NULL) {
-#ifdef DEBUG_NS
-               logdebugx("%s: unexpected NA from %s for %s",
-                   ifp->name, sfrom, taddr);
-#endif
-               return;
-       }
-
-#ifdef DEBUG_NS
-       logdebugx("%s: %sNA for %s from %s",
-           ifp->name, is_solicited ? "solicited " : "", taddr, sfrom);
-#endif
-
-       /* Node is no longer a router, so remove it from consideration */
-       if (!is_router && !rap->expired) {
-               loginfox("%s: %s not a router (%s)",
-                   ifp->name, taddr, sfrom);
-               rap->expired = true;
-               rap->lifetime = 0;
-               rt_build(ifp->ctx,  AF_INET6);
-               script_runreason(ifp, "ROUTERADVERT");
-               return;
-       }
-
-       if (is_solicited && is_router && rap->lifetime)
-               ipv6nd_reachable(rap, IPV6ND_REACHABLE);
-}
-
 static void
 ipv6nd_handledata(void *arg)
 {
@@ -1849,10 +1767,6 @@ ipv6nd_handledata(void *arg)
        icp = (struct icmp6_hdr *)buf;
        if (icp->icmp6_code == 0) {
                switch(icp->icmp6_type) {
-                       case ND_NEIGHBOR_ADVERT:
-                               ipv6nd_handlena(ctx, sfrom,
-                                   ifp, icp, (size_t)len, hoplimit);
-                               return;
                        case ND_ROUTER_ADVERT:
                                ipv6nd_handlera(ctx, &from, sfrom,
                                    ifp, icp, (size_t)len, hoplimit);