summaryrefslogtreecommitdiffstats
path: root/src/ipv6nd.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2019-08-23 20:30:24 +0100
committerRoy Marples <roy@marples.name>2019-08-23 20:30:24 +0100
commita9287e98d2d47969fb2ba602eb3d1f72788eae19 (patch)
tree2e5a0a8235fd538f4640b6547395ce51feaa4585 /src/ipv6nd.c
parent0bec93e409a8ba10b3192d6d70e6f11f6674a278 (diff)
downloaddhcpcd-a9287e98d2d47969fb2ba602eb3d1f72788eae19.tar.xz
inet6: Only remove default route when router becomes unreachable
Rather than everything. DNS servers, subnets, etc could all still be valid. If no default router is available, start the solicitation process again.
Diffstat (limited to 'src/ipv6nd.c')
-rw-r--r--src/ipv6nd.c84
1 files changed, 52 insertions, 32 deletions
diff --git a/src/ipv6nd.c b/src/ipv6nd.c
index bb8cb4dc..0169caef 100644
--- a/src/ipv6nd.c
+++ b/src/ipv6nd.c
@@ -127,6 +127,7 @@ __CTASSERT(sizeof(struct nd_opt_rdnss) == 8);
//
static void ipv6nd_handledata(void *);
+static void ipv6nd_startrs1(void *);
/*
* Android ships buggy ICMP6 filter headers.
@@ -559,21 +560,21 @@ static void
ipv6nd_reachable(struct ra *rap, int flags)
{
- if (rap->lifetime == 0)
+ if (rap->expired)
return;
if (flags & IPV6ND_REACHABLE) {
- if (rap->expired == 0)
+ if (rap->isreachable)
return;
loginfox("%s: %s is reachable again",
rap->iface->name, rap->sfrom);
- rap->expired = 0;
+ rap->isreachable = true;
} else {
- if (rap->expired != 0)
+ if (!rap->isreachable)
return;
- logwarnx("%s: %s is unreachable, expiring it",
+ logwarnx("%s: %s is unreachable",
rap->iface->name, rap->sfrom);
- rap->expired = 1;
+ rap->isreachable = false;
}
rt_build(rap->iface->ctx, AF_INET6);
@@ -584,16 +585,30 @@ ipv6nd_reachable(struct ra *rap, int flags)
void
ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags)
{
- struct ra *rap;
+ struct ra *rap, *rapr;
- if (ctx->ra_routers) {
- TAILQ_FOREACH(rap, ctx->ra_routers, next) {
- if (IN6_ARE_ADDR_EQUAL(&rap->from, addr)) {
- ipv6nd_reachable(rap, flags);
- break;
- }
+ 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) {
+ if (rap == rapr || rap->iface != rapr->iface)
+ continue;
+ if (rapr->isreachable && !rapr->expired && rapr->lifetime)
+ break;
+ }
+
+ if (rapr == NULL)
+ ipv6nd_startrs1(rap->iface);
}
const struct ipv6_addr *
@@ -1026,6 +1041,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom));
TAILQ_INIT(&rap->addrs);
new_rap = true;
+ rap->isreachable = true;
} else
new_rap = false;
if (rap->data_len == 0) {
@@ -1044,7 +1060,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
* routers like to decrease the advertised valid and preferred times
* in accordance with the own prefix times which would result in too
* much needless log spam. */
- logfunc = new_rap ? loginfox : logdebugx,
+ logfunc = new_rap || !rap->isreachable ? loginfox : logdebugx,
logfunc("%s: Router Advertisement from %s", ifp->name, rap->sfrom);
clock_gettime(CLOCK_MONOTONIC, &rap->acquired);
@@ -1064,9 +1080,9 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
state->retrans = rap->retrans = ntohl(nd_ra->nd_ra_retransmit);
}
- if (rap->lifetime)
- rap->expired = 0;
- rap->hasdns = 0;
+ rap->expired = false;
+ rap->hasdns = false;
+ rap->isreachable = true;
#ifdef IPV6_AF_TEMPORARY
ipv6_markaddrsstale(ifp, IPV6_AF_TEMPORARY);
@@ -1399,7 +1415,7 @@ ipv6nd_env(FILE *fp, const struct interface *ifp)
clock_gettime(CLOCK_MONOTONIC, &now);
i = n = 0;
TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
- if (rap->iface != ifp)
+ if (rap->iface != ifp || rap->expired)
continue;
i++;
snprintf(ndprefix, sizeof(ndprefix), "nd%zu", i);
@@ -1411,6 +1427,9 @@ ipv6nd_env(FILE *fp, const struct interface *ifp)
if (efprintf(fp, "%s_now=%lld", ndprefix,
(long long)now.tv_sec) == -1)
return -1;
+ if (efprintf(fp, "%s_isreachable=%s", ndprefix,
+ rap->isreachable ? "true" : "false") == -1)
+ return -1;
/* Zero our indexes */
for (j = 0, opt = rap->iface->ctx->nd_opts;
@@ -1503,7 +1522,7 @@ ipv6nd_expirera(void *arg)
struct interface *ifp;
struct ra *rap, *ran;
struct timespec now, lt, expire, next;
- bool expired, valid, validone;
+ bool expired, valid;
struct ipv6_addr *ia;
size_t len, olen;
uint8_t *p;
@@ -1522,9 +1541,9 @@ ipv6nd_expirera(void *arg)
timespecclear(&next);
TAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) {
- if (rap->iface != ifp)
+ if (rap->iface != ifp || rap->expired)
continue;
- valid = validone = false;
+ valid = false;
if (rap->lifetime) {
lt.tv_sec = (time_t)rap->lifetime;
lt.tv_nsec = 0;
@@ -1533,8 +1552,8 @@ ipv6nd_expirera(void *arg)
if (!rap->expired) {
logwarnx("%s: %s: router expired",
ifp->name, rap->sfrom);
- rap->expired = expired = 1;
rap->lifetime = 0;
+ expired = true;
}
} else {
valid = true;
@@ -1552,7 +1571,7 @@ ipv6nd_expirera(void *arg)
if (ia->prefix_vltime == 0)
continue;
if (ia->prefix_vltime == ND6_INFINITE_LIFETIME) {
- validone = true;
+ valid = true;
continue;
}
lt.tv_sec = (time_t)ia->prefix_vltime;
@@ -1576,7 +1595,7 @@ ipv6nd_expirera(void *arg)
if (!timespecisset(&next) ||
timespeccmp(&next, &lt, >))
next = lt;
- validone = true;
+ valid = true;
}
}
@@ -1626,7 +1645,7 @@ ipv6nd_expirera(void *arg)
if (ltime == 0)
continue;
if (ltime == ND6_INFINITE_LIFETIME) {
- validone = true;
+ valid = true;
continue;
}
@@ -1643,15 +1662,15 @@ ipv6nd_expirera(void *arg)
timespeccmp(&next, &lt, >))
{
next = lt;
- validone = true;
+ valid = true;
}
}
- if (valid || validone)
+ if (valid)
continue;
- /* Router has expired. Let's not keep a lot of them.
- * We should work out if all the options have expired .... */
+ /* Router has expired. Let's not keep a lot of them. */
+ rap->expired = true;
if (++nexpired > EXPIRED_MAX)
ipv6nd_free_ra(rap);
}
@@ -1670,7 +1689,7 @@ void
ipv6nd_drop(struct interface *ifp)
{
struct ra *rap, *ran;
- uint8_t expired = 0;
+ bool expired = false;
if (ifp->ctx->ra_routers == NULL)
return;
@@ -1678,7 +1697,7 @@ ipv6nd_drop(struct interface *ifp)
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
TAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) {
if (rap->iface == ifp) {
- rap->expired = expired = 1;
+ rap->expired = expired = true;
ipv6nd_drop_ra(rap);
}
}
@@ -1755,7 +1774,8 @@ ipv6nd_handlena(struct dhcpcd_ctx *ctx, const char *sfrom,
if (!is_router && !rap->expired) {
loginfox("%s: %s not a router (%s)",
ifp->name, taddr, sfrom);
- rap->expired = 1;
+ rap->expired = true;
+ rap->lifetime = 0;
rt_build(ifp->ctx, AF_INET6);
script_runreason(ifp, "ROUTERADVERT");
return;