From: Roy Marples Date: Fri, 23 Aug 2019 19:30:24 +0000 (+0100) Subject: inet6: Only remove default route when router becomes unreachable X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a9287e98d2d47969fb2ba602eb3d1f72788eae19;p=thirdparty%2Fdhcpcd.git 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. --- diff --git a/src/ipv6.c b/src/ipv6.c index 31a0cae0..a024356c 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -2271,7 +2271,7 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) rt_proto_add(routes, rt); } } - if (rap->lifetime == 0) + if (rap->lifetime == 0 || !rap->isreachable) continue; rt = inet6_makerouter(rap); if (rt == NULL) 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, <, >)) 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, <, >)) { 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; diff --git a/src/ipv6nd.h b/src/ipv6nd.h index 7dbdcae7..e51271eb 100644 --- a/src/ipv6nd.h +++ b/src/ipv6nd.h @@ -51,8 +51,9 @@ struct ra { uint32_t retrans; uint32_t mtu; struct ipv6_addrhead addrs; - uint8_t hasdns; - uint8_t expired; + bool hasdns; + bool expired; + bool isreachable; }; TAILQ_HEAD(ra_head, ra);