}
# Extract any ND DNS options from the RA
-# For now, we ignore the lifetime of the DNS options unless they
-# are absent or zero.
-# In this case they are removed from consideration.
-# See draft-gont-6man-slaac-dns-config-issues-01 for issues
-# regarding DNS option lifetime in ND messages.
+# Obey the lifetimes
eval_nd_dns()
{
- eval ltime=\$nd${i}_rdnss${j}_lifetime
- if [ -z "$ltime" ] || [ "$ltime" = 0 ]; then
- rdnss=
- else
+
+ eval rdnsstime=\$nd${i}_rdnss${j}_lifetime
+ [ -z "$rdnsstime" ] && return 1
+ ltime=$(($rdnsstime - $offset))
+ if [ "$ltime" -gt 0 ]; then
eval rdnss=\$nd${i}_rdnss${j}_servers
+ [ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
fi
- eval ltime=\$nd${i}_dnssl${j}_lifetime
- if [ -z "$ltime" ] || [ "$ltime" = 0 ]; then
- dnssl=
- else
+
+ eval dnssltime=\$nd${i}_dnssl${j}_lifetime
+ [ -z "$dnssltime" ] && return 1
+ ltime=$(($dnssltime - $offset))
+ if [ "$ltime" -gt 0 ]; then
eval dnssl=\$nd${i}_dnssl${j}_search
+ [ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
fi
- [ -z "${rdnss}${dnssl}" ] && return 1
-
- [ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
- [ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
j=$(($j + 1))
return 0
}
i=1
j=1
while true; do
+ eval acquired=\$nd${i}_acquired
+ [ -z "$acquired" ] && break
+ eval now=\$nd${i}_now
+ [ -z "$now" ] && break
+ offset=$(($now - $acquired))
while true; do
eval_nd_dns || break
done
i=$(($i + 1))
j=1
- eval_nd_dns || break
done
[ -n "$new_rdnss" ] && \
new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"
}
static int
-inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, int expired,
- bool *have_default)
+inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx)
{
struct rt *rt;
struct ra *rap;
const struct ipv6_addr *addr;
TAILQ_FOREACH(rap, ctx->ra_routers, next) {
- if (rap->expired != expired)
+ if (rap->expired)
continue;
TAILQ_FOREACH(addr, &rap->addrs, next) {
if (addr->prefix_vltime == 0)
TAILQ_INSERT_TAIL(routes, rt, rt_next);
}
}
- if (rap->lifetime) {
- rt = inet6_makerouter(rap);
- if (rt) {
- rt->rt_dflags |= RTDF_RA;
- TAILQ_INSERT_TAIL(routes, rt, rt_next);
- if (have_default)
- *have_default = true;
- }
- }
+ if (rap->lifetime == 0)
+ continue;
+ rt = inet6_makerouter(rap);
+ if (rt == NULL)
+ continue;
+ rt->rt_dflags |= RTDF_RA;
+ TAILQ_INSERT_TAIL(routes, rt, rt_next);
}
return 0;
}
bool
inet6_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes)
{
- bool have_default;
/* Should static take priority? */
if (inet6_staticroutes(routes, ctx) == -1)
return false;
/* First add reachable routers and their prefixes */
- have_default = false;
- if (inet6_raroutes(routes, ctx, 0, &have_default) == -1)
+ if (inet6_raroutes(routes, ctx) == -1)
return false;
#ifdef DHCP6
return false;
#endif
-#ifdef HAVE_ROUTE_METRIC
- /* If we have an unreachable router, we really do need to remove the
- * route to it beause it could be a lower metric than a reachable
- * router. Of course, we should at least have some routers if all
- * are unreachable. */
- if (!have_default) {
-#endif
- /* Add our non-reachable routers and prefixes
- * Unsure if this is needed, but it's a close match to kernel
- * behaviour */
- if (inet6_raroutes(routes, ctx, 1, NULL) == -1)
- return false;
-#ifdef HAVE_ROUTE_METRIC
- }
-#endif
-
return true;
}
#define RTPREF_RESERVED (-2)
#define RTPREF_INVALID (-3) /* internal */
+#define EXPIRED_MAX 5 /* Remember 5 expired routers to avoid
+ logspam. */
+
#define MIN_RANDOM_FACTOR 500 /* millisecs */
#define MAX_RANDOM_FACTOR 1500 /* millisecs */
#define MIN_RANDOM_FACTOR_U MIN_RANDOM_FACTOR * 1000 /* usecs */
break;
case ND_OPT_MTU:
+ if (len < sizeof(mtu)) {
+ logerrx("%s: short MTU option", ifp->name);
+ break;
+ }
memcpy(&mtu, p, sizeof(mtu));
mtu.nd_opt_mtu_mtu = ntohl(mtu.nd_opt_mtu_mtu);
if (mtu.nd_opt_mtu_mtu < IPV6_MMTU) {
break;
case ND_OPT_RDNSS:
+ if (len < sizeof(rdnss)) {
+ logerrx("%s: short RDNSS option", ifp->name);
+ break;
+ }
memcpy(&rdnss, p, sizeof(rdnss));
if (rdnss.nd_opt_rdnss_lifetime &&
rdnss.nd_opt_rdnss_len > 1)
* from the prefix information options as well. */
j = 0;
TAILQ_FOREACH(ia, &rap->addrs, next) {
- if (!(ia->flags & IPV6_AF_AUTOCONF)
+ if (!(ia->flags & IPV6_AF_AUTOCONF) ||
#ifdef IPV6_AF_TEMPORARY
- || ia->flags & IPV6_AF_TEMPORARY
+ ia->flags & IPV6_AF_TEMPORARY ||
#endif
- )
+ !(ia->flags & IPV6_AF_ADDED) ||
+ ia->prefix_vltime == 0)
continue;
j++;
if (env) {
struct timespec now, lt, expire, next;
bool expired, valid, validone;
struct ipv6_addr *ia;
+ size_t len, olen;
+ uint8_t *p;
+ struct nd_opt_hdr ndo;
+#if 0
+ struct nd_opt_prefix_info pi;
+#endif
+ struct nd_opt_dnssl dnssl;
+ struct nd_opt_rdnss rdnss;
+ uint32_t ltime;
+ size_t nexpired = 0;
ifp = arg;
clock_gettime(CLOCK_MONOTONIC, &now);
lt.tv_sec = (time_t)rap->lifetime;
lt.tv_nsec = 0;
timespecadd(&rap->acquired, <, &expire);
- if (rap->lifetime == 0 || timespeccmp(&now, &expire, >))
- {
+ if (timespeccmp(&now, &expire, >)) {
if (!rap->expired) {
logwarnx("%s: %s: router expired",
ifp->name, rap->sfrom);
}
}
- /* XXX FixMe!
- * We need to extract the lifetime from each option and check
- * if that has expired or not.
- * If it has, zero the option out in the returned data. */
+ /* Work out expiry for ND options */
+ len = rap->data_len - sizeof(struct nd_router_advert);
+ for (p = rap->data + sizeof(struct nd_router_advert);
+ len >= sizeof(ndo);
+ p += olen, len -= olen)
+ {
+ memcpy(&ndo, p, sizeof(ndo));
+ olen = (size_t)(ndo.nd_opt_len * 8);
+ if (olen > len) {
+ errno = EINVAL;
+ break;
+ }
+
+ if (has_option_mask(rap->iface->options->nomasknd,
+ ndo.nd_opt_type))
+ continue;
+
+ switch (ndo.nd_opt_type) {
+ /* Prefix info is already checked in the above loop. */
+#if 0
+ case ND_OPT_PREFIX_INFORMATION:
+ if (len < sizeof(pi))
+ break;
+ memcpy(&pi, p, sizeof(pi));
+ ltime = pi.nd_opt_pi_valid_time;
+ break;
+#endif
+ case ND_OPT_DNSSL:
+ if (len < sizeof(dnssl))
+ break;
+ memcpy(&dnssl, p, sizeof(dnssl));
+ ltime = dnssl.nd_opt_dnssl_lifetime;
+ break;
+ case ND_OPT_RDNSS:
+ if (len < sizeof(rdnss))
+ break;
+ memcpy(&rdnss, p, sizeof(rdnss));
+ ltime = rdnss.nd_opt_rdnss_lifetime;
+ break;
+ default:
+ continue;
+ }
+
+ if (ltime == 0)
+ continue;
+ if (ltime == ND6_INFINITE_LIFETIME) {
+ validone = true;
+ continue;
+ }
+
+ lt.tv_sec = (time_t)ntohl(ltime);
+ lt.tv_nsec = 0;
+ timespecadd(&rap->acquired, <, &expire);
+ if (timespeccmp(&now, &expire, >)) {
+ expired = true;
+ continue;
+ }
+
+ timespecsub(&expire, &now, <);
+ if (!timespecisset(&next) ||
+ timespeccmp(&next, <, >))
+ {
+ next = lt;
+ validone = true;
+ }
+ }
+
+ if (valid || validone)
+ continue;
- /* No valid lifetimes are left on the RA, so we might
- * as well punt it. */
- if (!valid && !validone)
+ /* Router has expired. Let's not keep a lot of them.
+ * We should work out if all the options have expired .... */
+ if (++nexpired > EXPIRED_MAX)
ipv6nd_free_ra(rap);
}
eloop_timeout_add_tv(ifp->ctx->eloop,
&next, ipv6nd_expirera, ifp);
if (expired) {
+ logwarnx("%s: part of Router Advertisement expired", ifp->name);
rt_build(ifp->ctx, AF_INET6);
script_runreason(ifp, "ROUTERADVERT");
}