It's unreliable and very corner case.
See the comment for the ipv6nd_neighbour() function.
* 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
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;
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) {
}
rta = RTA_NEXT(rta, len);
}
- ipv6nd_neighbour(ctx, &addr6, flags);
+ ipv6nd_neighbour(ctx, &addr6, reachable);
}
return 0;
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;
}
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;
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",
#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 *);
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 */