]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
inet6: Ensure expired routers are cleared after a carrier loss
authorRoy Marples <roy@marples.name>
Thu, 6 Feb 2020 12:54:11 +0000 (12:54 +0000)
committerRoy Marples <roy@marples.name>
Thu, 6 Feb 2020 12:54:11 +0000 (12:54 +0000)
When we lose carrier, mark all RAs as willexpire and add
the timeout to expire to it's own queue.
On receipt of a matching RA, clear the willexpire flag.
When the above timeout occours, set doexpire on all
RA's with withexpire and then call the general expirera function.

This is needed because expirera can be called at any point.

src/ipv6nd.c
src/ipv6nd.h

index 9457545e821f84939c389e1ca547603f00f8e0a0..58da9562c1a45081b62ab5365527893e945da2b5 100644 (file)
@@ -391,10 +391,8 @@ sent:
        if (state->rsprobes++ < MAX_RTR_SOLICITATIONS)
                eloop_timeout_add_sec(ifp->ctx->eloop,
                    RTR_SOLICITATION_INTERVAL, ipv6nd_sendrsprobe, ifp);
-       else {
+       else
                logwarnx("%s: no IPv6 Routers available", ifp->name);
-               ipv6nd_drop(ifp);
-       }
 }
 
 #ifdef ND6_ADVERTISE
@@ -554,19 +552,13 @@ ipv6nd_expire(void *arg)
 {
        struct interface *ifp = arg;
        struct ra *rap;
-       struct ipv6_addr *ia;
-       struct timespec now = { .tv_sec = 1 };
 
        if (ifp->ctx->ra_routers == NULL)
                return;
 
        TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
-               if (rap->iface != ifp)
-                       continue;
-               rap->acquired = now;
-               TAILQ_FOREACH(ia, &rap->addrs, next) {
-                       ia->acquired = now;
-               }
+               if (rap->iface == ifp && rap->willexpire)
+                       rap->doexpire = true;
        }
        ipv6nd_expirera(ifp);
 }
@@ -574,9 +566,17 @@ ipv6nd_expire(void *arg)
 void
 ipv6nd_startexpire(struct interface *ifp)
 {
+       struct ra *rap;
+
+       if (ifp->ctx->ra_routers == NULL)
+               return;
 
-       eloop_timeout_add_sec(ifp->ctx->eloop, RTR_CARRIER_EXPIRE,
-           ipv6nd_expire, ifp);
+       TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
+               if (rap->iface == ifp)
+                       rap->willexpire = true;
+       }
+       eloop_q_timeout_add_sec(ifp->ctx->eloop, ELOOP_IPV6RA_EXPIRE,
+           RTR_CARRIER_EXPIRE, ipv6nd_expire, ifp);
 }
 
 static int
@@ -607,10 +607,12 @@ ipv6nd_sortrouters(struct dhcpcd_ctx *ctx)
        while ((ra1 = TAILQ_FIRST(ctx->ra_routers)) != NULL) {
                TAILQ_REMOVE(ctx->ra_routers, ra1, next);
                TAILQ_FOREACH(ra2, &sorted_routers, next) {
-                       if (ra1->iface->metric < ra2->iface->metric)
+                       if (ra1->iface->metric > ra2->iface->metric)
                                continue;
                        if (ra1->expired && !ra2->expired)
                                continue;
+                       if (ra1->willexpire && !ra2->willexpire)
+                               continue;
                        if (ra1->lifetime == 0 && ra2->lifetime != 0)
                                continue;
                        if (!ra1->isreachable && ra2->reachable)
@@ -1137,7 +1139,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
                rap->retrans = ntohl(nd_ra->nd_ra_retransmit);
        else
                rap->retrans = RETRANS_TIMER;
-       rap->expired = false;
+       rap->expired = rap->willexpire = rap->doexpire = false;
        rap->hasdns = false;
        rap->isreachable = true;
        has_address = false;
@@ -1416,7 +1418,8 @@ ipv6nd_hasralifetime(const struct interface *ifp, bool lifetime)
 
        if (ifp->ctx->ra_routers) {
                TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next)
-                       if (rap->iface == ifp && !rap->expired &&
+                       if (rap->iface == ifp &&
+                           !rap->expired &&
                            (!lifetime ||rap->lifetime))
                                return true;
        }
@@ -1424,15 +1427,16 @@ ipv6nd_hasralifetime(const struct interface *ifp, bool lifetime)
 }
 
 bool
-ipv6nd_hasradhcp(const struct interface *ifp)
+ipv6nd_hasradhcp(const struct interface *ifp, bool managed)
 {
        const struct ra *rap;
 
        if (ifp->ctx->ra_routers) {
                TAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {
                        if (rap->iface == ifp &&
-                           !rap->expired &&
-                           (rap->flags &(ND_RA_FLAG_MANAGED|ND_RA_FLAG_OTHER)))
+                           !rap->expired && !rap->willexpire &&
+                           ((managed && rap->flags & ND_RA_FLAG_MANAGED) ||
+                           (!managed && rap->flags & ND_RA_FLAG_OTHER)))
                                return true;
                }
        }
@@ -1622,7 +1626,7 @@ ipv6nd_expirera(void *arg)
                if (rap->lifetime) {
                        elapsed = (uint32_t)eloop_timespec_diff(&now,
                            &rap->acquired, NULL);
-                       if (elapsed > rap->lifetime) {
+                       if (elapsed > rap->lifetime || rap->doexpire) {
                                if (!rap->expired) {
                                        logwarnx("%s: %s: router expired",
                                            ifp->name, rap->sfrom);
@@ -1643,13 +1647,15 @@ ipv6nd_expirera(void *arg)
                TAILQ_FOREACH(ia, &rap->addrs, next) {
                        if (ia->prefix_vltime == 0)
                                continue;
-                       if (ia->prefix_vltime == ND6_INFINITE_LIFETIME) {
+                       if (ia->prefix_vltime == ND6_INFINITE_LIFETIME &&
+                           !rap->doexpire)
+                       {
                                valid = true;
                                continue;
                        }
                        elapsed = (uint32_t)eloop_timespec_diff(&now,
                            &ia->acquired, NULL);
-                       if (elapsed > ia->prefix_vltime) {
+                       if (elapsed > ia->prefix_vltime || rap->doexpire) {
                                if (ia->flags & IPV6_AF_ADDED) {
                                        logwarnx("%s: expired %s %s",
                                            ia->iface->name,
@@ -1720,6 +1726,10 @@ ipv6nd_expirera(void *arg)
 
                        if (ltime == 0)
                                continue;
+                       if (rap->doexpire) {
+                               expired = true;
+                               continue;
+                       }
                        if (ltime == ND6_INFINITE_LIFETIME) {
                                valid = true;
                                continue;
@@ -1750,7 +1760,8 @@ ipv6nd_expirera(void *arg)
                eloop_timeout_add_sec(ifp->ctx->eloop,
                    next, ipv6nd_expirera, ifp);
        if (expired) {
-               logwarnx("%s: part of Router Advertisement expired", ifp->name);
+               logwarnx("%s: part of a Router Advertisement expired",
+                   ifp->name);
                rt_build(ifp->ctx, AF_INET6);
                script_runreason(ifp, "ROUTERADVERT");
        }
index c0fa2967b48bda5b471970957212933762e3136b..0c85239d3b1ca7099a8a405cbb0f64ddb54c3994 100644 (file)
@@ -54,6 +54,8 @@ struct ra {
        struct ipv6_addrhead addrs;
        bool hasdns;
        bool expired;
+       bool willexpire;
+       bool doexpire;
        bool isreachable;
 };
 
@@ -114,7 +116,7 @@ ssize_t ipv6nd_free(struct interface *);
 void ipv6nd_expirera(void *arg);
 bool ipv6nd_hasralifetime(const struct interface *, bool);
 #define        ipv6nd_hasra(i)         ipv6nd_hasralifetime((i), false)
-bool ipv6nd_hasradhcp(const struct interface *);
+bool ipv6nd_hasradhcp(const struct interface *, bool);
 void ipv6nd_handleifa(int, struct ipv6_addr *, pid_t);
 int ipv6nd_dadcompleted(const struct interface *);
 void ipv6nd_advertise(struct ipv6_addr *);