From: Roy Marples Date: Sun, 25 Aug 2019 13:40:20 +0000 (+0100) Subject: inet6: Don't install a default route if no global address X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a3830c3f3d7af8db0353f11a3625979e122819a7;p=thirdparty%2Fdhcpcd.git inet6: Don't install a default route if no global address This works around an issue where some routers advertise themselves as a default router, but offer no address to actually use. Thus, it is actually useless as a router. If an address is added by any other means (DHCPv6, static, etc) then the default route is installed once more. This is also dynamic - dhcpcd reacts to addresses being added, removed, duplicated, detached, etc. --- diff --git a/src/if-options.h b/src/if-options.h index d0eb764a..79947885 100644 --- a/src/if-options.h +++ b/src/if-options.h @@ -61,7 +61,7 @@ #define DHCPCD_ARP (1ULL << 0) #define DHCPCD_RELEASE (1ULL << 1) -// unused (1ULL << 2) +#define DHCPCD_RTBUILD (1ULL << 2) #define DHCPCD_GATEWAY (1ULL << 3) #define DHCPCD_STATIC (1ULL << 4) #define DHCPCD_DEBUG (1ULL << 5) diff --git a/src/ipv6.c b/src/ipv6.c index 31a0cae0..8669f9c0 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -1060,6 +1060,28 @@ ipv6_getstate(struct interface *ifp) return state; } +struct ipv6_addr * +ipv6_ifanyglobal(struct interface *ifp) +{ + struct ipv6_state *state; + struct ipv6_addr *ia; + + if (ifp->carrier == LINK_DOWN) + return NULL; + + state = IPV6_STATE(ifp); + if (state == NULL) + return NULL; + + TAILQ_FOREACH(ia, &state->addrs, next) { + if (IN6_IS_ADDR_LINKLOCAL(&ia->addr)) + continue; + if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE)) + break; + } + return ia; +} + void ipv6_handleifa(struct dhcpcd_ctx *ctx, int cmd, struct if_head *ifs, const char *ifname, @@ -1069,6 +1091,7 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, struct ipv6_state *state; struct ipv6_addr *ia; struct ll_callback *cb; + bool anyglobal; #if 0 char dbuf[INET6_ADDRSTRLEN]; @@ -1088,6 +1111,7 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, return; if ((state = ipv6_getstate(ifp)) == NULL) return; + anyglobal = ipv6_ifanyglobal(ifp) != NULL; TAILQ_FOREACH(ia, &state->addrs, next) { if (IN6_ARE_ADDR_EQUAL(&ia->addr, addr)) @@ -1187,6 +1211,7 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx, if (ia == NULL) return; + ctx->options &= ~DHCPCD_RTBUILD; ipv6nd_handleifa(cmd, ia, pid); #ifdef DHCP6 dhcp6_handleifa(cmd, ia, pid); @@ -1198,6 +1223,14 @@ out: ipv6_freeaddr(ia); else if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE)) ia->flags |= IPV6_AF_DADCOMPLETED; + + /* If we've not already called rt_build via the IPv6ND + * or DHCP6 handlers and the existance of any useable + * global address on the interface has changed, + * call rt_build to add/remove the default route. */ + if (!(ctx->options & DHCPCD_RTBUILD) && + (ipv6_ifanyglobal(ifp) != NULL) != anyglobal) + rt_build(ctx, AF_INET6); } int @@ -2273,6 +2306,8 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) } if (rap->lifetime == 0) continue; + if (ipv6_ifanyglobal(rap->iface) == NULL) + continue; rt = inet6_makerouter(rap); if (rt == NULL) continue; diff --git a/src/ipv6.h b/src/ipv6.h index f07bc0f1..a95d9268 100644 --- a/src/ipv6.h +++ b/src/ipv6.h @@ -273,6 +273,7 @@ int ipv6_handleifa_addrs(int, struct ipv6_addrhead *, const struct ipv6_addr *, struct ipv6_addr *ipv6_iffindaddr(struct interface *, const struct in6_addr *, int); int ipv6_hasaddr(const struct interface *); +struct ipv6_addr *ipv6_ifanyglobal(struct interface *); int ipv6_findaddrmatch(const struct ipv6_addr *, const struct in6_addr *, unsigned int); struct ipv6_addr *ipv6_findaddr(struct dhcpcd_ctx *, diff --git a/src/ipv6nd.c b/src/ipv6nd.c index 5f9c72cf..2a02a2a6 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -954,7 +954,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct in6_addr pi_prefix; struct ipv6_addr *ap; struct dhcp_opt *dho; - bool new_rap, new_data; + bool new_rap, new_data, has_address; uint32_t old_lifetime; __printflike(1, 2) void (*logfunc)(const char *, ...); #ifdef IPV6_MANAGETEMPADDR @@ -1055,7 +1055,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 || !rap->isreachable ? loginfox : logdebugx, + logfunc = new_data || !rap->isreachable ? loginfox : logdebugx, logfunc("%s: Router Advertisement from %s", ifp->name, rap->sfrom); clock_gettime(CLOCK_MONOTONIC, &rap->acquired); @@ -1078,6 +1078,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, rap->expired = false; rap->hasdns = false; rap->isreachable = true; + has_address = false; #ifdef IPV6_AF_TEMPORARY ipv6_markaddrsstale(ifp, IPV6_AF_TEMPORARY); @@ -1211,6 +1212,9 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, ntohl(pi.nd_opt_pi_valid_time); ap->prefix_pltime = ntohl(pi.nd_opt_pi_preferred_time); + if (ap->prefix_vltime != 0 && + ap->flags & IPV6_AF_AUTOCONF) + has_address = true; #ifdef IPV6_MANAGETEMPADDR /* RFC4941 Section 3.3.3 */ @@ -1277,6 +1281,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, } } + if (new_data && !has_address && rap->lifetime && !ipv6_ifanyglobal(ifp)) + logwarnx("%s: no global addresses for default route", + ifp->name); + if (new_rap) add_router(ifp->ctx, rap); @@ -1327,20 +1335,21 @@ nodhcp6: ipv6nd_expirera(ifp); } -int -ipv6nd_hasra(const struct interface *ifp) +bool +ipv6nd_hasralifetime(const struct interface *ifp, bool lifetime) { const struct ra *rap; if (ifp->ctx->ra_routers) { TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) - if (rap->iface == ifp && !rap->expired) - return 1; + if (rap->iface == ifp && !rap->expired && + (!lifetime ||rap->lifetime)) + return true; } - return 0; + return false; } -int +bool ipv6nd_hasradhcp(const struct interface *ifp) { const struct ra *rap; @@ -1349,11 +1358,11 @@ ipv6nd_hasradhcp(const struct interface *ifp) TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) { if (rap->iface == ifp && !rap->expired && - (rap->flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))) - return 1; + (rap->flags &(ND_RA_FLAG_MANAGED|ND_RA_FLAG_OTHER))) + return true; } } - return 0; + return false; } static const uint8_t * diff --git a/src/ipv6nd.h b/src/ipv6nd.h index e51271eb..b169b8be 100644 --- a/src/ipv6nd.h +++ b/src/ipv6nd.h @@ -104,8 +104,9 @@ struct ipv6_addr *ipv6nd_findaddr(struct dhcpcd_ctx *, const struct in6_addr *, unsigned int); ssize_t ipv6nd_free(struct interface *); void ipv6nd_expirera(void *arg); -int ipv6nd_hasra(const struct interface *); -int ipv6nd_hasradhcp(const struct interface *); +bool ipv6nd_hasralifetime(const struct interface *, bool); +#define ipv6nd_hasra(i) ipv6nd_hasralifetime((i), false) +bool ipv6nd_hasradhcp(const struct interface *); void ipv6nd_handleifa(int, struct ipv6_addr *, pid_t); int ipv6nd_dadcompleted(const struct interface *); void ipv6nd_advertise(struct ipv6_addr *); diff --git a/src/route.c b/src/route.c index c1d19eb0..01bf8df9 100644 --- a/src/route.c +++ b/src/route.c @@ -39,6 +39,7 @@ #include "common.h" #include "dhcpcd.h" #include "if.h" +#include "if-options.h" #include "ipv4.h" #include "ipv4ll.h" #include "ipv6.h" @@ -685,6 +686,7 @@ rt_build(struct dhcpcd_ctx *ctx, int af) rb_tree_init(&kroutes, &rt_compare_os_ops); if_initrt(ctx, &kroutes, af); ctx->rt_order = 0; + ctx->options |= DHCPCD_RTBUILD; switch (af) { #ifdef INET