From: Roy Marples Date: Thu, 29 Aug 2019 18:10:53 +0000 (+0100) Subject: inet6: Stop listening to NA messages X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=402aef841f4be948e5823fc09008df48e846c1d6;p=thirdparty%2Fdhcpcd.git inet6: Stop listening to NA messages 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. --- diff --git a/src/ipv6nd.c b/src/ipv6nd.c index 2a02a2a6..cb9e1086 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -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);