]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Allow the same IP address to be leased for different interfaces.
authorRoy Marples <roy@marples.name>
Thu, 9 Oct 2014 18:59:30 +0000 (18:59 +0000)
committerRoy Marples <roy@marples.name>
Thu, 9 Oct 2014 18:59:30 +0000 (18:59 +0000)
The interface with the lowest metric gets the address.
When that interfaces loses the address, the next valid interface with the
lowest metric gets it.

dhcp.c
dhcp.h
dhcp6.c
dhcp6.h
dhcpcd.c
ipv4.c
ipv4.h
ipv6.c
ipv6.h
ipv6nd.c
ipv6nd.h

diff --git a/dhcp.c b/dhcp.c
index 8bf0aeea4a711bd774f8cad10fab6e296cb21942..ccc94b1ffbce00b10848f6e7db3cf1808843969f 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -1960,7 +1960,8 @@ dhcp_bind(void *arg)
                state->state = DHS_BOUND;
                if (ifo->options & DHCPCD_ARP) {
                        state->claims = 0;
-                       arp_announce(iface);
+                       if (state->added)
+                               arp_announce(iface);
                }
        }
 }
@@ -2036,7 +2037,7 @@ dhcp_inform(struct interface *ifp)
        } else {
                if (ifo->req_addr.s_addr == INADDR_ANY) {
                        state = D_STATE(ifp);
-                       ap = ipv4_findaddr(ifp, NULL, NULL);
+                       ap = ipv4_iffindaddr(ifp, NULL, NULL);
                        if (ap == NULL) {
                                syslog(LOG_INFO,
                                        "%s: waiting for 3rd party to "
@@ -2110,7 +2111,8 @@ dhcp_reboot(struct interface *ifp)
        } else if (state->offer->cookie == 0) {
                if (ifo->options & DHCPCD_IPV4LL) {
                        state->claims = 0;
-                       arp_announce(ifp);
+                       if (state->added)
+                               arp_announce(ifp);
                } else
                        dhcp_discover(ifp);
                return;
@@ -2563,7 +2565,7 @@ dhcp_handledhcp(struct interface *iface, struct dhcp_message **dhcpp,
                /* If the interface already has the address configured
                 * then we can't ARP for duplicate detection. */
                addr.s_addr = state->offer->yiaddr;
-               if (!ipv4_findaddr(iface, &addr, NULL)) {
+               if (!ipv4_findaddr(iface->ctx, &addr)) {
                        state->claims = 0;
                        state->probes = 0;
                        state->conflicts = 0;
diff --git a/dhcp.h b/dhcp.h
index fc8ed4bffdbdfa19c9319dc94ca48e385bd8ad3a..f147a9cc137bc5e3e3ccf20de8ae0609e45c1595 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -227,6 +227,7 @@ struct dhcp_state {
        struct in_addr addr;
        struct in_addr net;
        struct in_addr dst;
+       uint8_t added;
 
        char leasefile[sizeof(LEASEFILE) + IF_NAMESIZE];
        time_t start_uptime;
diff --git a/dhcp6.c b/dhcp6.c
index bbd7386d007982574dfac62b6e3fa3e6916f9cb8..8bd5c2b7f7becb4604f3f4699c59b3510c33a79f 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -1595,12 +1595,13 @@ dhcp6_checkstatusok(const struct interface *ifp,
 }
 
 static struct ipv6_addr *
-dhcp6_findaddr(struct interface *ifp, const struct in6_addr *addr)
+dhcp6_iffindaddr(struct interface *ifp, const struct in6_addr *addr,
+    short flags)
 {
-       const struct dhcp6_state *state;
+       struct dhcp6_state *state;
        struct ipv6_addr *ap;
 
-       state = D6_CSTATE(ifp);
+       state = D6_STATE(ifp);
        if (state) {
                TAILQ_FOREACH(ap, &state->addrs, next) {
                        if (addr == NULL) {
@@ -1608,23 +1609,28 @@ dhcp6_findaddr(struct interface *ifp, const struct in6_addr *addr)
                                    (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)) ==
                                    (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED))
                                        return ap;
-                       } else if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
+                       } else if (ap->prefix_vltime &&
+                           IN6_ARE_ADDR_EQUAL(&ap->addr, addr) &&
+                           (!flags || ap->flags & flags))
                                return ap;
                }
        }
        return NULL;
 }
 
-int
-dhcp6_addrexists(struct dhcpcd_ctx *ctx, const struct ipv6_addr *addr)
+struct ipv6_addr *
+dhcp6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,
+    short flags)
 {
        struct interface *ifp;
+       struct ipv6_addr *ap;
 
        TAILQ_FOREACH(ifp, ctx->ifaces, next) {
-               if (dhcp6_findaddr(ifp, addr == NULL ? NULL : &addr->addr))
-                       return 1;
+               ap = dhcp6_iffindaddr(ifp, addr, flags);
+               if (ap)
+                       return ap;
        }
-       return 0;
+       return NULL;
 }
 
 static int
@@ -1657,7 +1663,7 @@ dhcp6_findna(struct interface *ifp, uint16_t ot, const uint8_t *iaid,
                        continue;
                }
                iap = (const struct dhcp6_ia_addr *)D6_COPTION_DATA(o);
-               a = dhcp6_findaddr(ifp, &iap->addr);
+               a = dhcp6_iffindaddr(ifp, &iap->addr, 0);
                if (a == NULL) {
                        a = calloc(1, sizeof(*a));
                        if (a == NULL) {
@@ -2249,7 +2255,7 @@ dhcp6_script_try_run(struct interface *ifp)
        TAILQ_FOREACH(ap, &state->addrs, next) {
                if (ap->flags & IPV6_AF_ONLINK) {
                        if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
-                           ipv6_findaddr(ap->iface, &ap->addr))
+                           ipv6_iffindaddr(ap->iface, &ap->addr))
                                ap->flags |= IPV6_AF_DADCOMPLETED;
                        if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
                                completed = 0;
diff --git a/dhcp6.h b/dhcp6.h
index d037c788f347da04f7d66789dddf17235a605b66..0e7ee8061f31ceff678fd98fda88e152a8054333 100644 (file)
--- a/dhcp6.h
+++ b/dhcp6.h
@@ -231,7 +231,8 @@ struct dhcp6_state {
 #ifdef INET6
 void dhcp6_printoptions(const struct dhcpcd_ctx *,
     const struct dhcp_opt *, size_t);
-int dhcp6_addrexists(struct dhcpcd_ctx *, const struct ipv6_addr *);
+struct ipv6_addr *dhcp6_findaddr(struct dhcpcd_ctx *, const struct in6_addr *,
+    short);
 size_t dhcp6_find_delegates(struct interface *);
 int dhcp6_start(struct interface *, enum DH6S);
 void dhcp6_reboot(struct interface *);
@@ -244,7 +245,7 @@ int dhcp6_dadcompleted(const struct interface *);
 void dhcp6_drop(struct interface *, const char *);
 int dhcp6_dump(struct interface *);
 #else
-#define dhcp6_addrexists(a, b) (0)
+#define dhcp6_findaddr(a, b, c) (0)
 #define dhcp6_find_delegates(a)
 #define dhcp6_start(a, b) (0)
 #define dhcp6_reboot(a)
index 67ed5538ae6ddb020782885e4b54100102d80fab..69e43b458614849805f540c088d853e15f0d56be 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -231,14 +231,14 @@ dhcpcd_ipwaited(struct dhcpcd_ctx *ctx)
            !ipv4_addrexists(ctx, NULL))
                return 0;
        if (ctx->options & DHCPCD_WAITIP6 &&
-           !ipv6nd_addrexists(ctx, NULL) &&
-           !dhcp6_addrexists(ctx, NULL))
+           !ipv6nd_findaddr(ctx, NULL, 0) &&
+           !dhcp6_findaddr(ctx, NULL, 0))
                return 0;
        if (ctx->options & DHCPCD_WAITIP &&
            !(ctx->options & (DHCPCD_WAITIP4 | DHCPCD_WAITIP6)) &&
            !ipv4_addrexists(ctx, NULL) &&
-           !ipv6nd_addrexists(ctx, NULL) &&
-           !dhcp6_addrexists(ctx, NULL))
+           !ipv6nd_findaddr(ctx, NULL, 0) &&
+           !dhcp6_findaddr(ctx, NULL, 0))
                return 0;
        return 1;
 }
diff --git a/ipv4.c b/ipv4.c
index 68557fd0b863aa13b6a59fc6df82a2d4cdf8c4a8..8dd67de85cf8a13c2900e27da389d715459f281a 100644 (file)
--- a/ipv4.c
+++ b/ipv4.c
@@ -114,7 +114,7 @@ ipv4_getnetmask(uint32_t addr)
 }
 
 struct ipv4_addr *
-ipv4_findaddr(struct interface *ifp,
+ipv4_iffindaddr(struct interface *ifp,
     const struct in_addr *addr, const struct in_addr *net)
 {
        struct ipv4_state *state;
@@ -131,6 +131,20 @@ ipv4_findaddr(struct interface *ifp,
        return NULL;
 }
 
+struct ipv4_addr *
+ipv4_findaddr(struct dhcpcd_ctx *ctx, const struct in_addr *addr)
+{
+       struct interface *ifp;
+       struct ipv4_addr *ap;
+
+       TAILQ_FOREACH(ifp, ctx->ifaces, next) {
+               ap = ipv4_iffindaddr(ifp, addr, NULL);
+               if (ap)
+                       return ap;
+       }
+       return NULL;
+}
+
 int
 ipv4_addrexists(struct dhcpcd_ctx *ctx, const struct in_addr *addr)
 {
@@ -146,7 +160,7 @@ ipv4_addrexists(struct dhcpcd_ctx *ctx, const struct in_addr *addr)
                        } else if (addr->s_addr == state->addr.s_addr)
                                return 1;
                }
-               if (addr != NULL && ipv4_findaddr(ifp, addr, NULL))
+               if (addr != NULL && ipv4_iffindaddr(ifp, addr, NULL))
                        return 1;
        }
        return 0;
@@ -667,6 +681,7 @@ delete_address(struct interface *ifp)
            (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0))
                return 0;
        r = delete_address1(ifp, &state->addr, &state->net);
+       state->added = 0;
        state->addr.s_addr = 0;
        state->net.s_addr = 0;
        return r;
@@ -690,11 +705,30 @@ ipv4_getstate(struct interface *ifp)
        return state;
 }
 
+static int
+ipv4_addaddr(const struct interface *ifp, const struct dhcp_lease *lease)
+{
+       int r;
+
+       syslog(LOG_DEBUG, "%s: adding IP address %s/%d",
+           ifp->name, inet_ntoa(lease->addr),
+           inet_ntocidr(lease->net));
+       if (ifp->options->options & DHCPCD_NOALIAS)
+               r = if_setaddress(ifp,
+                   &lease->addr, &lease->net, &lease->brd);
+       else
+               r = if_addaddress(ifp,
+                   &lease->addr, &lease->net, &lease->brd);
+       if (r == -1 && errno != EEXIST)
+               syslog(LOG_ERR, "%s: if_addaddress: %m", __func__);
+       return r;
+}
+
 void
 ipv4_applyaddr(void *arg)
 {
-       struct interface *ifp = arg;
-       struct dhcp_state *state = D_STATE(ifp);
+       struct interface *ifp = arg, *ifn;
+       struct dhcp_state *state = D_STATE(ifp), *nstate;
        struct dhcp_message *dhcp;
        struct dhcp_lease *lease;
        struct if_options *ifo = ifp->options;
@@ -713,40 +747,85 @@ ipv4_applyaddr(void *arg)
        lease = &state->lease;
 
        if (dhcp == NULL) {
-               ipv4_buildroutes(ifp->ctx);
                if ((ifo->options & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
                    (DHCPCD_EXITING | DHCPCD_PERSISTENT))
                {
-                       if (state->addr.s_addr != 0)
+                       if (state->added) {
+                               struct in_addr addr;
+
+                               addr = state->addr;
                                delete_address(ifp);
+                               TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
+                                       if (ifn == ifp)
+                                               continue;
+                                       nstate = D_STATE(ifn);
+                                       if (nstate && !nstate->added &&
+                                           nstate->addr.s_addr == addr.s_addr)
+                                       {
+                                               ipv4_addaddr(ifn,
+                                                   &nstate->lease);
+                                               break;
+                                       }
+                               }
+                       }
+                       ipv4_buildroutes(ifp->ctx);
                        script_runreason(ifp, state->reason);
-               }
+               } else
+                       ipv4_buildroutes(ifp->ctx);
                return;
        }
 
+       /* Ensure only one interface has the address */
+       TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
+               if (ifn == ifp)
+                       continue;
+               nstate = D_STATE(ifn);
+               if (nstate && nstate->added &&
+                   nstate->addr.s_addr == lease->addr.s_addr)
+               {
+                       if (ifn->metric <= ifp->metric) {
+                               syslog(LOG_INFO, "%s: preferring %s on %s",
+                                   ifp->name,
+                                   inet_ntoa(lease->addr),
+                                   ifn->name);
+                               state->addr.s_addr = lease->addr.s_addr;
+                               state->net.s_addr = lease->net.s_addr;
+                               goto routes;
+                       }
+                       syslog(LOG_INFO, "%s: preferring %s on %s",
+                           ifn->name,
+                           inet_ntoa(lease->addr),
+                           ifp->name);
+                       delete_address1(ifn, &nstate->addr, &nstate->net);
+                       nstate->added = 0;
+                       break;
+               }
+       }
+
+       /* Does another interface already have the address from a prior boot? */
+       if (ifn == NULL) {
+               TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
+                       if (ifn == ifp)
+                               continue;
+                       ap = ipv4_iffindaddr(ifn, &lease->addr, NULL);
+                       if (ap)
+                               delete_address1(ifn, &ap->addr, &ap->net);
+               }
+       }
+
        /* If the netmask is different, delete the addresss */
-       ap = ipv4_findaddr(ifp, &lease->addr, NULL);
+       ap = ipv4_iffindaddr(ifp, &lease->addr, NULL);
        if (ap && ap->net.s_addr != lease->net.s_addr)
                delete_address1(ifp, &ap->addr, &ap->net);
 
-       if (ipv4_findaddr(ifp, &lease->addr, &lease->net))
+       if (ipv4_iffindaddr(ifp, &lease->addr, &lease->net))
                syslog(LOG_DEBUG, "%s: IP address %s/%d already exists",
                    ifp->name, inet_ntoa(lease->addr),
                    inet_ntocidr(lease->net));
        else {
-               syslog(LOG_DEBUG, "%s: adding IP address %s/%d",
-                   ifp->name, inet_ntoa(lease->addr),
-                   inet_ntocidr(lease->net));
-               if (ifo->options & DHCPCD_NOALIAS)
-                       r = if_setaddress(ifp,
-                           &lease->addr, &lease->net, &lease->brd);
-               else
-                       r = if_addaddress(ifp,
-                           &lease->addr, &lease->net, &lease->brd);
-               if (r == -1 && errno != EEXIST) {
-                       syslog(LOG_ERR, "%s: if_addaddress: %m", __func__);
+               r = ipv4_addaddr(ifp, lease);
+               if (r == -1 && errno != EEXIST)
                        return;
-               }
                istate = ipv4_getstate(ifp);
                ap = malloc(sizeof(*ap));
                ap->addr = lease->addr;
@@ -760,6 +839,7 @@ ipv4_applyaddr(void *arg)
            state->addr.s_addr != 0)
                delete_address(ifp);
 
+       state->added = 1;
        state->addr.s_addr = lease->addr.s_addr;
        state->net.s_addr = lease->net.s_addr;
 
@@ -774,6 +854,7 @@ ipv4_applyaddr(void *arg)
                free(rt);
        }
 
+routes:
        ipv4_buildroutes(ifp->ctx);
        if (!state->lease.frominfo &&
            !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
@@ -817,7 +898,7 @@ ipv4_handleifa(struct dhcpcd_ctx *ctx,
                return;
        }
 
-       ap = ipv4_findaddr(ifp, addr, net);
+       ap = ipv4_iffindaddr(ifp, addr, net);
        if (type == RTM_NEWADDR && ap == NULL) {
                ap = malloc(sizeof(*ap));
                if (ap == NULL) {
diff --git a/ipv4.h b/ipv4.h
index 4c831f3f904716827880861ccfb3c2b1cbe1bc29..5a4a78ed709c50a0ffff37be227c1ca98e03d85c 100644 (file)
--- a/ipv4.h
+++ b/ipv4.h
@@ -70,8 +70,9 @@ void ipv4_buildroutes(struct dhcpcd_ctx *);
 void ipv4_applyaddr(void *);
 int ipv4_routedeleted(struct dhcpcd_ctx *, const struct rt *);
 
-struct ipv4_addr *ipv4_findaddr(struct interface *,
+struct ipv4_addr *ipv4_iffindaddr(struct interface *,
     const struct in_addr *, const struct in_addr *);
+struct ipv4_addr *ipv4_findaddr(struct dhcpcd_ctx *, const struct in_addr *);
 void ipv4_handleifa(struct dhcpcd_ctx *, int, struct if_head *, const char *,
     const struct in_addr *, const struct in_addr *, const struct in_addr *);
 
diff --git a/ipv6.c b/ipv6.c
index daf30d71646e8b44d9edd808ca996d9416a2f12c..6a8afcdaa60503d3be16698ceb2f890ce784b481 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -590,14 +590,55 @@ ipv6_checkaddrflags(void *arg)
 }
 #endif
 
+
+static void
+ipv6_deleteaddr(struct ipv6_addr *addr)
+{
+       struct ipv6_state *state;
+       struct ipv6_addr *ap;
+
+       syslog(LOG_INFO, "%s: deleting address %s",
+           addr->iface->name, addr->saddr);
+       if (if_deladdress6(addr) == -1 &&
+           errno != EADDRNOTAVAIL && errno != ENXIO)
+               syslog(LOG_ERR, "if_deladdress6: :%m");
+
+       state = IPV6_STATE(addr->iface);
+       TAILQ_FOREACH(ap, &state->addrs, next) {
+               if (IN6_ARE_ADDR_EQUAL(&ap->addr, &addr->addr)) {
+                       TAILQ_REMOVE(&state->addrs, ap, next);
+                       free(ap);
+                       break;
+               }
+       }
+}
+
 int
 ipv6_addaddr(struct ipv6_addr *ap)
 {
+       struct interface *ifp;
+       struct ipv6_state *state;
+       struct ipv6_addr *nap;
+
+       /* Ensure no other interface has this address */
+       TAILQ_FOREACH(ifp, ap->iface->ctx->ifaces, next) {
+               if (ifp == ap->iface)
+                       continue;
+               state = IPV6_STATE(ifp);
+               if (state == NULL)
+                       continue;
+               TAILQ_FOREACH(nap, &state->addrs, next) {
+                       if (IN6_ARE_ADDR_EQUAL(&nap->addr, &ap->addr)) {
+                               ipv6_deleteaddr(nap);
+                               break;
+                       }
+               }
+       }
 
        syslog(ap->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG,
            "%s: adding address %s", ap->iface->name, ap->saddr);
        if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
-           ipv6_findaddr(ap->iface, &ap->addr))
+           ipv6_iffindaddr(ap->iface, &ap->addr))
                ap->flags |= IPV6_AF_DADCOMPLETED;
        if (if_addaddress6(ap) == -1) {
                syslog(LOG_ERR, "if_addaddress6: %m");
@@ -643,23 +684,36 @@ ipv6_addaddr(struct ipv6_addr *ap)
        return 0;
 }
 
+struct ipv6_addr *
+ipv6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr, short flags)
+{
+       struct ipv6_addr *dap, *nap;
+
+       dap = dhcp6_findaddr(ctx, addr, flags);
+       nap = ipv6nd_findaddr(ctx, addr, flags);
+       if (!dap && !nap)
+               return NULL;
+       if (dap && !nap)
+               return dap;
+       if (nap && !dap)
+               return nap;
+       if (nap->iface->metric < dap->iface->metric)
+               return nap;
+       return dap;
+}
+
 ssize_t
 ipv6_addaddrs(struct ipv6_addrhead *addrs)
 {
-       struct ipv6_addr *ap, *apn;
+       struct ipv6_addr *ap, *apn, *apf;
        ssize_t i;
 
        i = 0;
        TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
                if (ap->prefix_vltime == 0) {
                        if (ap->flags & IPV6_AF_ADDED) {
-                               syslog(LOG_INFO, "%s: deleting address %s",
-                                   ap->iface->name, ap->saddr);
+                               ipv6_deleteaddr(ap);
                                i++;
-                               if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr) &&
-                                   if_deladdress6(ap) == -1 &&
-                                   errno != EADDRNOTAVAIL && errno != ENXIO)
-                                       syslog(LOG_ERR, "if_deladdress6: %m");
                        }
                        eloop_q_timeout_delete(ap->iface->ctx->eloop,
                            0, NULL, ap);
@@ -670,6 +724,28 @@ ipv6_addaddrs(struct ipv6_addrhead *addrs)
                                free(ap);
                        }
                } else if (!IN6_IS_ADDR_UNSPECIFIED(&ap->addr)) {
+                       apf = ipv6_findaddr(ap->iface->ctx,
+                           &ap->addr, IPV6_AF_ADDED);
+                       if (apf && apf->iface != ap->iface) {
+                               if (apf->iface->metric <= ap->iface->metric) {
+                                       syslog(LOG_INFO,
+                                           "%s: preferring %s on %s",
+                                           ap->iface->name,
+                                           ap->saddr,
+                                           apf->iface->name);
+                                       continue;
+                               }
+                               syslog(LOG_INFO,
+                                   "%s: preferring %s on %s",
+                                   apf->iface->name,
+                                   ap->saddr,
+                                   ap->iface->name);
+                               if (if_deladdress6(apf) == -1 &&
+                                   errno != EADDRNOTAVAIL && errno != ENXIO)
+                                       syslog(LOG_ERR, "if_deladdress6: %m");
+                               apf->flags &= ~IPV6_AF_ADDED;
+                       } else if (apf)
+                               apf->flags &= ~IPV6_AF_ADDED;
                        if (ap->flags & IPV6_AF_NEW)
                                i++;
                        ipv6_addaddr(ap);
@@ -679,33 +755,29 @@ ipv6_addaddrs(struct ipv6_addrhead *addrs)
        return i;
 }
 
-
 void
 ipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop,
     const struct interface *ifd)
 {
-       struct ipv6_addr *ap, *apn;
+       struct ipv6_addr *ap, *apn, *apf;
 
        TAILQ_FOREACH_SAFE(ap, addrs, next, apn) {
                if (ifd && ap->delegating_iface != ifd)
                        continue;
                TAILQ_REMOVE(addrs, ap, next);
                eloop_q_timeout_delete(ap->iface->ctx->eloop, 0, NULL, ap);
-               /* Only drop the address if no other RAs have assigned it.
-                * This is safe because the RA is removed from the list
-                * before we are called. */
                if (drop && ap->flags & IPV6_AF_ADDED &&
-                   !ipv6nd_addrexists(ap->iface->ctx, ap) &&
-                   !dhcp6_addrexists(ap->iface->ctx, ap) &&
                    (ap->iface->options->options &
                    (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
                    (DHCPCD_EXITING | DHCPCD_PERSISTENT))
                {
-                       syslog(LOG_INFO, "%s: deleting address %s",
-                           ap->iface->name, ap->saddr);
-                       if (if_deladdress6(ap) == -1 &&
-                           errno != EADDRNOTAVAIL && errno != ENXIO)
-                               syslog(LOG_ERR, "if_deladdress6: :%m");
+                       /* Find the same address somewhere else */
+                       apf = ipv6_findaddr(ap->iface->ctx, &ap->addr, 0);
+                       if (apf == NULL || apf->iface != ap->iface)
+                               ipv6_deleteaddr(ap);
+                       if (!(ap->iface->options->options &
+                           DHCPCD_EXITING) && apf)
+                               ipv6_addaddr(apf);
                }
                free(ap);
        }
@@ -784,6 +856,8 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
                                ap = calloc(1, sizeof(*ap));
                                ap->iface = ifp;
                                ap->addr = *addr;
+                               inet_ntop(AF_INET6, &addr->s6_addr,
+                                   ap->saddr, sizeof(ap->saddr));
                                TAILQ_INSERT_TAIL(&state->addrs,
                                    ap, next);
                        }
@@ -822,7 +896,7 @@ ipv6_handleifa(struct dhcpcd_ctx *ctx,
 }
 
 const struct ipv6_addr *
-ipv6_findaddr(const struct interface *ifp, const struct in6_addr *addr)
+ipv6_iffindaddr(const struct interface *ifp, const struct in6_addr *addr)
 {
        const struct ipv6_state *state;
        const struct ipv6_addr *ap;
@@ -1066,10 +1140,11 @@ ipv6_handleifa_addrs(int cmd,
                }
                switch (cmd) {
                case RTM_DELADDR:
-                       syslog(LOG_INFO, "%s: deleted address %s",
-                           ap->iface->name, ap->saddr);
-                       TAILQ_REMOVE(addrs, ap, next);
-                       free(ap);
+                       if (ap->flags & IPV6_AF_ADDED) {
+                               syslog(LOG_INFO, "%s: deleted address %s",
+                                   ap->iface->name, ap->saddr);
+                               ap->flags &= ~IPV6_AF_ADDED;
+                       }
                        break;
                case RTM_NEWADDR:
                        /* Safety - ignore tentative announcements */
diff --git a/ipv6.h b/ipv6.h
index fac9458a03880dafce7145fdbd193c49b3219fb8..6d74d5a956390bcf3043b0ccf8c5567a03db067f 100644 (file)
--- a/ipv6.h
+++ b/ipv6.h
@@ -191,9 +191,11 @@ void ipv6_handleifa(struct dhcpcd_ctx *ctx, int, struct if_head *,
     const char *, const struct in6_addr *, int);
 int ipv6_handleifa_addrs(int, struct ipv6_addrhead *,
     const struct in6_addr *, int);
-const struct ipv6_addr *ipv6_findaddr(const struct interface *,
+const struct ipv6_addr *ipv6_iffindaddr(const struct interface *,
     const struct in6_addr *);
-#define ipv6_linklocal(ifp) (ipv6_findaddr((ifp), NULL))
+struct ipv6_addr *ipv6_findaddr(struct dhcpcd_ctx *,
+    const struct in6_addr *, short);
+#define ipv6_linklocal(ifp) (ipv6_iffindaddr((ifp), NULL))
 int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
 void ipv6_free_ll_callbacks(struct interface *);
 int ipv6_start(struct interface *);
index 0cf15780ab5413d532641f16f086c5d0d77478f4..662234395b75edfa7c1370da27dd47995d0f1f70 100644 (file)
--- a/ipv6nd.c
+++ b/ipv6nd.c
@@ -391,14 +391,15 @@ ipv6nd_free_opts(struct ra *rap)
        }
 }
 
-int
-ipv6nd_addrexists(struct dhcpcd_ctx *ctx, const struct ipv6_addr *addr)
+struct ipv6_addr *
+ipv6nd_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,
+    short flags)
 {
        struct ra *rap;
        struct ipv6_addr *ap;
 
        if (ctx->ipv6 == NULL)
-               return 0;
+               return NULL;
 
        TAILQ_FOREACH(rap, ctx->ipv6->ra_routers, next) {
                TAILQ_FOREACH(ap, &rap->addrs, next) {
@@ -406,12 +407,14 @@ ipv6nd_addrexists(struct dhcpcd_ctx *ctx, const struct ipv6_addr *addr)
                                if ((ap->flags &
                                    (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)) ==
                                    (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED))
-                                       return 1;
-                       } else if (IN6_ARE_ADDR_EQUAL(&ap->addr, &addr->addr))
-                               return 1;
+                                       return ap;
+                       } else if (ap->prefix_vltime &&
+                           IN6_ARE_ADDR_EQUAL(&ap->addr, addr) &&
+                           (!flags || ap->flags & flags))
+                               return ap;
                }
        }
-       return 0;
+       return NULL;
 }
 
 void ipv6nd_freedrop_ra(struct ra *rap, int drop)
@@ -525,7 +528,7 @@ ipv6nd_scriptrun(struct ra *rap)
                {
                        hasaddress = 1;
                        if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
-                           ipv6_findaddr(ap->iface, &ap->addr))
+                           ipv6_iffindaddr(ap->iface, &ap->addr))
                                ap->flags |= IPV6_AF_DADCOMPLETED;
                        if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
                                syslog(LOG_DEBUG,
index a2dc196f56508ed3eea94a09293e71918513e993..9a489ad54745028a73b103baa21870d0eef85d87 100644 (file)
--- a/ipv6nd.h
+++ b/ipv6nd.h
@@ -85,7 +85,8 @@ struct rs_state {
 #ifdef INET6
 void ipv6nd_startrs(struct interface *);
 ssize_t ipv6nd_env(char **, const char *, const struct interface *);
-int ipv6nd_addrexists(struct dhcpcd_ctx *, const struct ipv6_addr *);
+struct ipv6_addr *ipv6nd_findaddr(struct dhcpcd_ctx *,
+    const struct in6_addr *, short);
 void ipv6nd_freedrop_ra(struct ra *, int);
 #define ipv6nd_free_ra(ra) ipv6nd_freedrop_ra((ra),  0)
 #define ipv6nd_drop_ra(ra) ipv6nd_freedrop_ra((ra),  1)
@@ -103,7 +104,7 @@ void ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, int);
 #endif
 #else
 #define ipv6nd_startrs(a) {}
-#define ipv6nd_addrexists(a, b) (0)
+#define ipv6nd_findaddr(a, b, c) (0)
 #define ipv6nd_free(a)
 #define ipv6nd_hasra(a) (0)
 #define ipv6nd_dadcompleted(a) (0)