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.
state->state = DHS_BOUND;
if (ifo->options & DHCPCD_ARP) {
state->claims = 0;
- arp_announce(iface);
+ if (state->added)
+ arp_announce(iface);
}
}
}
} 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 "
} 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;
/* 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;
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;
}
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) {
(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
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) {
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;
#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 *);
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)
!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;
}
}
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;
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)
{
} 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;
(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;
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;
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;
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;
free(rt);
}
+routes:
ipv4_buildroutes(ifp->ctx);
if (!state->lease.frominfo &&
!(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
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) {
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 *);
}
#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");
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);
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);
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);
}
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);
}
}
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;
}
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 */
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 *);
}
}
-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) {
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)
{
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,
#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)
#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)