]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
inet6: Don't install a default route if no global address
authorRoy Marples <roy@marples.name>
Sun, 25 Aug 2019 13:40:20 +0000 (14:40 +0100)
committerRoy Marples <roy@marples.name>
Sun, 25 Aug 2019 13:40:20 +0000 (14:40 +0100)
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.

src/if-options.h
src/ipv6.c
src/ipv6.h
src/ipv6nd.c
src/ipv6nd.h
src/route.c

index d0eb764ab52d8d8b18b4b4c350e016f29d0daab7..79947885ce880801b2ff917000d0ea3c1229340d 100644 (file)
@@ -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)
index 31a0cae085661fd9454e8f2d53bb6f1698834cf5..8669f9c094ce70d2fbb66ed95af50f1b40661501 100644 (file)
@@ -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;
index f07bc0f15cde6f09b52322ced2f2a671580048cf..a95d92689bfca8f54b4bb2c58bdf724dcc92b0ce 100644 (file)
@@ -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 *,
index 5f9c72cf3ff9d67372e2032391041a64bd01c224..2a02a2a65615057c1ab53d94dbbfed64982a82d3 100644 (file)
@@ -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 *
index e51271eb3536b205001db09655cd9f22091f3efc..b169b8bea4322501a99f8200478457c1e8263340 100644 (file)
@@ -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 *);
index c1d19eb048a931b397f348abfb781029e03c05df..01bf8df9dfaa6c094bb716f95601e6d66f2ea27b 100644 (file)
@@ -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