From: Roy Marples Date: Fri, 30 Aug 2019 10:06:20 +0000 (+0100) Subject: inet6: Stop reacting to kernel neighbour messages about a router X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2c340935edbe3a6e9b1ff50ff7f9f23414986180;p=thirdparty%2Fdhcpcd.git inet6: Stop reacting to kernel neighbour messages about a router It's unreliable and very corner case. See the comment for the ipv6nd_neighbour() function. --- diff --git a/src/if-bsd.c b/src/if-bsd.c index ed5041d5..47219e1a 100644 --- a/src/if-bsd.c +++ b/src/if-bsd.c @@ -1083,20 +1083,17 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) * BSD announces host routes. * As such, we should be notified of reachability by its * existance with a hardware address. - * Ensure we don't call this for an incomplete state. + * Ensure we don't call this for a newly incomplete state. */ if (rt.rt_dest.sa_family == AF_INET6 && rt.rt_flags & RTF_HOST && !(rtm->rtm_type == RTM_ADD && !(rt.rt_dflags & RTDF_GATELINK))) { - int flags = 0; + bool reachable; - if (rt.rt_dflags & RTDF_GATELINK) - flags |= IPV6ND_ROUTER; - if (rtm->rtm_type != RTM_DELETE) - flags |= IPV6ND_REACHABLE; - - ipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, flags); + reachable = rtm->rtm_type != RTM_DELETE && + rt.rt_dflags & RTDF_GATELINK; + ipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, reachable); } #endif diff --git a/src/if-linux.c b/src/if-linux.c index 1b7e6164..287b7f1b 100644 --- a/src/if-linux.c +++ b/src/if-linux.c @@ -680,8 +680,6 @@ link_neigh(struct dhcpcd_ctx *ctx, __unused struct interface *ifp, struct ndmsg *r; struct rtattr *rta; size_t len; - struct in6_addr addr6; - int flags; if (nlm->nlmsg_type != RTM_NEWNEIGH && nlm->nlmsg_type != RTM_DELNEIGH) return 0; @@ -692,14 +690,13 @@ link_neigh(struct dhcpcd_ctx *ctx, __unused struct interface *ifp, rta = (struct rtattr *)RTM_RTA(r); len = RTM_PAYLOAD(nlm); if (r->ndm_family == AF_INET6) { - flags = 0; - if (r->ndm_flags & NTF_ROUTER) - flags |= IPV6ND_ROUTER; - if (nlm->nlmsg_type == RTM_NEWNEIGH && + bool reachable; + struct in6_addr addr6; + + reachable = (nlm->nlmsg_type == RTM_NEWNEIGH && r->ndm_state & (NUD_REACHABLE | NUD_STALE | NUD_DELAY | NUD_PROBE | NUD_PERMANENT)) - flags |= IPV6ND_REACHABLE; memset(&addr6, 0, sizeof(addr6)); while (RTA_OK(rta, len)) { switch (rta->rta_type) { @@ -710,7 +707,7 @@ link_neigh(struct dhcpcd_ctx *ctx, __unused struct interface *ifp, } rta = RTA_NEXT(rta, len); } - ipv6nd_neighbour(ctx, &addr6, flags); + ipv6nd_neighbour(ctx, &addr6, reachable); } return 0; diff --git a/src/if-sun.c b/src/if-sun.c index 0db7371c..2e776cc5 100644 --- a/src/if-sun.c +++ b/src/if-sun.c @@ -812,8 +812,7 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) else sdl.sdl_alen = 0; ipv6nd_neighbour(ctx, &dst6, - rtm->rtm_type != RTM_DELETE && sdl.sdl_alen ? - IPV6ND_REACHABLE : 0); + rtm->rtm_type != RTM_DELETE && sdl.sdl_alen); } break; } diff --git a/src/ipv6nd.c b/src/ipv6nd.c index cb9e1086..35401b75 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -554,8 +554,21 @@ ipv6nd_startexpire(struct interface *ifp) ipv6nd_expire, ifp); } +/* + * Neighbour reachability. + * + * RFC 4681 6.2.5 says when a node is no longer a router it MUST + * send a RA with a zero lifetime. + * All OS's I know of set the NA router flag if they are a router + * or not and disregard that they are actively advertising or + * shutting down. If the interface is disabled, it cant't send a NA at all. + * + * As such we CANNOT rely on the NA Router flag and MUST use + * unreachability or receive a RA with a lifetime of zero to remove + * the node as a default router. + */ void -ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags) +ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, bool reachable) { struct ra *rap, *rapr; @@ -570,11 +583,7 @@ ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags) if (rap == NULL || rap->expired) return; - 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 (reachable) { if (rap->isreachable) return; loginfox("%s: %s is reachable again", diff --git a/src/ipv6nd.h b/src/ipv6nd.h index b169b8be..16c4dc79 100644 --- a/src/ipv6nd.h +++ b/src/ipv6nd.h @@ -91,9 +91,6 @@ struct rs_state { #define RETRANS_TIMER 1000 /* milliseconds */ #define DELAY_FIRST_PROBE_TIME 5 /* seconds */ -#define IPV6ND_REACHABLE (1 << 0) -#define IPV6ND_ROUTER (1 << 1) - void ipv6nd_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *, size_t); void ipv6nd_startrs(struct interface *); @@ -112,7 +109,7 @@ int ipv6nd_dadcompleted(const struct interface *); void ipv6nd_advertise(struct ipv6_addr *); void ipv6nd_startexpire(struct interface *); void ipv6nd_drop(struct interface *); -void ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, int); +void ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, bool); #endif /* INET6 */ #endif /* IPV6ND_H */