send_rebind(ifp);
}
+static void
+dhcp_arp_probed(struct arp_state *astate)
+{
+ struct dhcp_state *state;
+ struct if_options *ifo;
+
+ /* We didn't find a profile for this
+ * address or hwaddr, so move to the next
+ * arping profile */
+ state = D_STATE(astate->iface);
+ ifo = astate->iface->options;
+ if (state->arping_index < ifo->arping_len) {
+ if (++state->arping_index < ifo->arping_len) {
+ astate->addr.s_addr =
+ ifo->arping[state->arping_index - 1];
+ arp_probe(astate);
+ }
+ dhcpcd_startinterface(astate->iface);
+ return;
+ }
+ dhcp_close(astate->iface);
+ eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate->iface);
+#ifdef IN_IFF_TENTATIVE
+ ipv4_finaliseaddr(astate->iface);
+ arp_close(astate->iface);
+#else
+ dhcp_bind(astate->iface, astate);
+#endif
+}
+
+static void
+dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
+{
+ struct dhcp_state *state;
+ struct if_options *ifo;
+
+ state = D_STATE(astate->iface);
+ ifo = astate->iface->options;
+ if (state->arping_index &&
+ state->arping_index <= ifo->arping_len &&
+ amsg &&
+ (amsg->sip.s_addr == ifo->arping[state->arping_index - 1] ||
+ (amsg->sip.s_addr == 0 &&
+ amsg->tip.s_addr == ifo->arping[state->arping_index - 1])))
+ {
+ char buf[HWADDR_LEN * 3];
+
+ astate->failed.s_addr = ifo->arping[state->arping_index - 1];
+ arp_report_conflicted(astate, amsg);
+ hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf));
+ if (dhcpcd_selectprofile(astate->iface, buf) == -1 &&
+ dhcpcd_selectprofile(astate->iface,
+ inet_ntoa(astate->failed)) == -1)
+ {
+ /* We didn't find a profile for this
+ * address or hwaddr, so move to the next
+ * arping profile */
+ dhcp_arp_probed(astate);
+ return;
+ }
+ dhcp_close(astate->iface);
+ arp_close(astate->iface);
+ eloop_timeout_delete(astate->iface->ctx->eloop, NULL,
+ astate->iface);
+ dhcpcd_startinterface(astate->iface);
+ }
+
+ /* RFC 2131 3.1.5, Client-server interaction
+ * NULL amsg means IN_IFF_DUPLICATED */
+ if (amsg == NULL || (state->offer &&
+ (amsg->sip.s_addr == state->offer->yiaddr ||
+ (amsg->sip.s_addr == 0 &&
+ amsg->tip.s_addr == state->offer->yiaddr))))
+ {
+#ifdef IN_IFF_DUPLICATED
+ struct ipv4_addr *ia;
+#endif
+
+ if (amsg)
+ astate->failed.s_addr = state->offer->yiaddr;
+ else
+ astate->failed = astate->addr;
+ arp_report_conflicted(astate, amsg);
+ unlink(state->leasefile);
+ if (!state->lease.frominfo)
+ dhcp_decline(astate->iface);
+#ifdef IN_IFF_DUPLICATED
+ ia = ipv4_iffindaddr(astate->iface, &astate->addr, NULL);
+ if (ia)
+ ipv4_deladdr(astate->iface, &ia->addr, &ia->net);
+#endif
+ eloop_timeout_delete(astate->iface->ctx->eloop, NULL,
+ astate->iface);
+ eloop_timeout_add_sec(astate->iface->ctx->eloop,
+ DHCP_RAND_MAX, dhcp_discover, astate->iface);
+ }
+}
+
void
dhcp_bind(struct interface *ifp, struct arp_state *astate)
{
if (state->state == DHS_BOUND)
goto applyaddr;
+#ifdef IN_IFF_TENTATIVE
+ state->added |= STATE_TENTATIVE;
+#endif
state->reason = NULL;
free(state->old);
state->old = state->new;
"%s: write_lease: %m", __func__);
applyaddr:
+#ifdef IN_IFF_TENTATIVE
+ if (astate == NULL) {
+ astate = arp_new(ifp, &lease->addr);
+ if (astate) {
+ astate->probed_cb = dhcp_arp_probed;
+ astate->conflicted_cb = dhcp_arp_conflicted;
+ }
+ }
+#endif
+
ipv4_applyaddr(ifp);
if (ifo->options & DHCPCD_ARP &&
!(ifp->ctx->options & DHCPCD_FORKED))
return 0;
}
-static void
-dhcp_arp_probed(struct arp_state *astate)
-{
- struct dhcp_state *state;
- struct if_options *ifo;
-
- /* We didn't find a profile for this
- * address or hwaddr, so move to the next
- * arping profile */
- state = D_STATE(astate->iface);
- ifo = astate->iface->options;
- if (state->arping_index < ifo->arping_len) {
- if (++state->arping_index < ifo->arping_len) {
- astate->addr.s_addr =
- ifo->arping[state->arping_index - 1];
- arp_probe(astate);
- }
- dhcpcd_startinterface(astate->iface);
- return;
- }
- dhcp_close(astate->iface);
- eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate->iface);
-#ifdef IN_IFF_TENTATIVE
- ipv4_finaliseaddr(astate->iface);
- arp_close(astate->iface);
-#else
- dhcp_bind(astate->iface, astate);
-#endif
-}
-
-static void
-dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
-{
- struct dhcp_state *state;
- struct if_options *ifo;
-
- state = D_STATE(astate->iface);
- ifo = astate->iface->options;
- if (state->arping_index &&
- state->arping_index <= ifo->arping_len &&
- amsg &&
- (amsg->sip.s_addr == ifo->arping[state->arping_index - 1] ||
- (amsg->sip.s_addr == 0 &&
- amsg->tip.s_addr == ifo->arping[state->arping_index - 1])))
- {
- char buf[HWADDR_LEN * 3];
-
- astate->failed.s_addr = ifo->arping[state->arping_index - 1];
- arp_report_conflicted(astate, amsg);
- hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf));
- if (dhcpcd_selectprofile(astate->iface, buf) == -1 &&
- dhcpcd_selectprofile(astate->iface,
- inet_ntoa(astate->failed)) == -1)
- {
- /* We didn't find a profile for this
- * address or hwaddr, so move to the next
- * arping profile */
- dhcp_arp_probed(astate);
- return;
- }
- dhcp_close(astate->iface);
- arp_close(astate->iface);
- eloop_timeout_delete(astate->iface->ctx->eloop, NULL,
- astate->iface);
- dhcpcd_startinterface(astate->iface);
- }
-
- /* RFC 2131 3.1.5, Client-server interaction
- * NULL amsg means IN_IFF_DUPLICATED */
- if (amsg == NULL || (state->offer &&
- (amsg->sip.s_addr == state->offer->yiaddr ||
- (amsg->sip.s_addr == 0 &&
- amsg->tip.s_addr == state->offer->yiaddr))))
- {
-#ifdef IN_IFF_DUPLICATED
- struct ipv4_addr *ia;
-#endif
-
- if (amsg)
- astate->failed.s_addr = state->offer->yiaddr;
- else
- astate->failed = astate->addr;
- arp_report_conflicted(astate, amsg);
- unlink(state->leasefile);
- if (!state->lease.frominfo)
- dhcp_decline(astate->iface);
-#ifdef IN_IFF_DUPLICATED
- ia = ipv4_iffindaddr(astate->iface, &astate->addr, NULL);
- if (ia)
- ipv4_deladdr(astate->iface, &ia->addr, &ia->net);
-#endif
- eloop_timeout_delete(astate->iface->ctx->eloop, NULL,
- astate->iface);
- eloop_timeout_add_sec(astate->iface->ctx->eloop,
- DHCP_RAND_MAX, dhcp_discover, astate->iface);
- }
-}
-
static void
dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
const struct in_addr *from)
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
astate = NULL;
-#ifdef IN_IFF_TENTATIVE
- addr.s_addr = state->offer->yiaddr;
- astate = arp_new(ifp, &addr);
- if (astate) {
- astate->probed_cb = dhcp_arp_probed;
- astate->conflicted_cb = dhcp_arp_conflicted;
- /* No need to start the probe as we'll
- * listen to the kernel stating DAD or not and
- * that action look look for our ARP state for
- * what to do. */
- }
-#else
+#ifndef IN_IFF_TENTATIVE
if (ifo->options & DHCPCD_ARP
&& state->addr.s_addr != state->offer->yiaddr)
{
int r;
struct ipv4_state *state;
struct ipv4_addr *ap;
+ struct arp_state *astate;
logger(ifp->ctx, LOG_DEBUG, "%s: deleting IP address %s/%d",
ifp->name, inet_ntoa(*addr), inet_ntocidr(*net));
errno != ENODEV)
logger(ifp->ctx, LOG_ERR, "%s: %s: %m", ifp->name, __func__);
- dstate = D_STATE(ifp);
- if (dstate &&
- dstate->addr.s_addr == addr->s_addr &&
- dstate->net.s_addr == net->s_addr)
- {
- dstate->added = 0;
- dstate->addr.s_addr = 0;
- dstate->net.s_addr = 0;
- }
+ if ((astate = arp_find(ifp, addr)) != NULL)
+ arp_free(astate);
state = IPV4_STATE(ifp);
TAILQ_FOREACH(ap, &state->addrs, next) {
break;
}
}
+
+ /* Have to do this last incase the function arguments
+ * were these very pointers. */
+ dstate = D_STATE(ifp);
+ if (dstate &&
+ dstate->addr.s_addr == addr->s_addr &&
+ dstate->net.s_addr == net->s_addr)
+ {
+ dstate->added = 0;
+ dstate->addr.s_addr = 0;
+ dstate->net.s_addr = 0;
+ }
return r;
}
{
struct ipv4_state *state;
struct ipv4_addr *ia;
+ struct dhcp_state *dstate;
if ((state = ipv4_getstate(ifp)) == NULL) {
logger(ifp->ctx, LOG_ERR, "%s: ipv4_getstate: %m", __func__);
ia->addr_flags = IN_IFF_TENTATIVE;
#endif
TAILQ_INSERT_TAIL(&state->addrs, ia, next);
+
+ dstate = D_STATE(ifp);
+#ifdef IN_IFF_TENTATIVE
+ dstate->added |= STATE_ADDED;
+#else
+ dstate->added = STATE_ADDED;
+#endif
+ dstate->defend = 0;
+ dstate->addr.s_addr = lease->addr.s_addr;
+ dstate->net.s_addr = lease->net.s_addr;
+
return 0;
}
ipv4_iffindaddr(ifp, &lease->addr, NULL))
delete_address(ifp);
- state->added = STATE_ADDED;
- state->defend = 0;
- state->addr.s_addr = lease->addr.s_addr;
- state->net.s_addr = lease->net.s_addr;
+ state->added &= ~(STATE_FAKE | STATE_TENTATIVE);
ipv4_finalisert(ifp);
}
+int
+ipv4_preferanother(struct interface *ifp)
+{
+ struct dhcp_state *state = D_STATE(ifp), *nstate;
+ struct interface *ifn;
+ int preferred;
+
+ preferred = 0;
+ if (!state->added)
+ goto out;
+
+ TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
+ if (ifn == ifp || strcmp(ifn->name, ifp->name) == 0)
+ break; /* We are already the most preferred */
+ nstate = D_STATE(ifn);
+ if (nstate && !nstate->added &&
+ nstate->lease.addr.s_addr == state->addr.s_addr)
+ {
+ preferred = 1;
+ delete_address(ifp);
+ if (ifn->options->options & DHCPCD_ARP)
+ dhcp_bind(ifn, NULL);
+ else {
+ ipv4_addaddr(ifn, &nstate->lease);
+ nstate->added = STATE_ADDED;
+ }
+ break;
+ }
+ }
+
+out:
+ ipv4_buildroutes(ifp->ctx);
+ return preferred;
+}
+
void
ipv4_applyaddr(void *arg)
{
dhcp = state->new;
lease = &state->lease;
+ if_sortinterfaces(ifp->ctx);
if (dhcp == NULL) {
if ((ifo->options & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
(DHCPCD_EXITING | DHCPCD_PERSISTENT))
{
- if (state->added) {
- struct in_addr addr;
-
- addr = state->addr;
+ if (state->added && !ipv4_preferanother(ifp)) {
delete_address(ifp);
- TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
- if (ifn == ifp ||
- strcmp(ifn->name, ifp->name) == 0)
- continue;
- nstate = D_STATE(ifn);
- if (nstate && !nstate->added &&
- nstate->addr.s_addr == addr.s_addr)
- {
- if (ifn->options->options &
- DHCPCD_ARP)
- {
- dhcp_bind(ifn, NULL);
- } else {
- ipv4_addaddr(ifn,
- &nstate->lease);
- nstate->added =
- STATE_ADDED;
- }
- break;
- }
- }
+ ipv4_buildroutes(ifp->ctx);
}
- ipv4_buildroutes(ifp->ctx);
script_runreason(ifp, state->reason);
} else
ipv4_buildroutes(ifp->ctx);
}
/* Ensure only one interface has the address */
+ r = 0;
TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
- if (ifn == ifp || strcmp(ifn->name, ifp->name) == 0)
+ if (ifn == ifp || strcmp(ifn->name, ifp->name) == 0) {
+ r = 1; /* past ourselves */
continue;
+ }
nstate = D_STATE(ifn);
if (nstate && nstate->added &&
nstate->addr.s_addr == lease->addr.s_addr)
{
- if (ifn->metric <= ifp->metric) {
+ if (r == 0) {
logger(ifp->ctx, 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;
- ipv4_finalisert(ifp);
+ state->added &= ~STATE_TENTATIVE;
+ return;
}
logger(ifp->ctx, LOG_INFO, "%s: preferring %s on %s",
ifn->name,
inet_ntoa(lease->addr),
ifp->name);
ipv4_deladdr(ifn, &nstate->addr, &nstate->net);
- nstate->added = 0;
break;
}
}