#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)
{
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;
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]);
#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]);
break;
#endif
}
+ break;
#ifdef RTM_CHGADDR
case RTM_CHGADDR: /* FALLTHROUGH */
#endif
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)
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)
{
#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
//
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",
}
}
-#ifdef RTM_NEWNEIGH
void
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)
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);
/* 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
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
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;
}
}
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");
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 */
}