From: Roy Marples Date: Sat, 16 May 2015 20:42:05 +0000 (+0000) Subject: Improve IN_IFF_TENTATIVE with ip sharing. X-Git-Tag: v6.9.0~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dc1979d93275bec87c3025c995b9672ae29cb131;p=thirdparty%2Fdhcpcd.git Improve IN_IFF_TENTATIVE with ip sharing. --- diff --git a/arp.c b/arp.c index d49ee1af..c53e4c7b 100644 --- a/arp.c +++ b/arp.c @@ -294,17 +294,19 @@ arp_probe(struct arp_state *astate) arp_probe1(astate); } -static struct arp_state * +struct arp_state * arp_find(struct interface *ifp, const struct in_addr *addr) { struct arp_state *astate; struct dhcp_state *state; - state = D_STATE(ifp); + if ((state = D_STATE(ifp)) == NULL) + goto out; TAILQ_FOREACH(astate, &state->arp_states, next) { if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp) return astate; } +out: errno = ESRCH; return NULL; } diff --git a/arp.h b/arp.h index 83422fe1..5e513b29 100644 --- a/arp.h +++ b/arp.h @@ -73,6 +73,7 @@ struct arp_state *arp_new(struct interface *, const struct in_addr *); void arp_cancel(struct arp_state *); void arp_free(struct arp_state *); void arp_free_but(struct arp_state *); +struct arp_state *arp_find(struct interface *, const struct in_addr *); void arp_close(struct interface *); void arp_handleifa(int, struct interface *, const struct in_addr *, int); diff --git a/dhcp.c b/dhcp.c index 9280eb37..afcef4fb 100644 --- a/dhcp.c +++ b/dhcp.c @@ -1866,6 +1866,104 @@ dhcp_rebind(void *arg) 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) { @@ -1876,6 +1974,9 @@ 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; @@ -1993,6 +2094,16 @@ dhcp_bind(struct interface *ifp, struct arp_state *astate) "%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)) @@ -2348,104 +2459,6 @@ whitelisted_ip(const struct if_options *ifo, in_addr_t addr) 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) @@ -2763,18 +2776,7 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp, 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) { diff --git a/dhcpcd.c b/dhcpcd.c index 84036264..cb07e88d 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -669,7 +669,8 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags, script_runreason(ifp, "NOCARRIER"); #ifdef NOCARRIER_PRESERVE_IP arp_close(ifp); - ipv4_buildroutes(ifp->ctx); + if_sortinterfaces(ctx); + ipv4_preferanother(ifp); ipv6nd_expire(ifp, 0); #else dhcpcd_drop(ifp, 0); diff --git a/ipv4.c b/ipv4.c index f73e4097..9fba88a7 100644 --- a/ipv4.c +++ b/ipv4.c @@ -750,6 +750,7 @@ ipv4_deladdr(struct interface *ifp, 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)); @@ -759,15 +760,8 @@ ipv4_deladdr(struct interface *ifp, 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) { @@ -779,6 +773,18 @@ ipv4_deladdr(struct interface *ifp, 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; } @@ -822,6 +828,7 @@ ipv4_addaddr(struct interface *ifp, const struct dhcp_lease *lease) { 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__); @@ -859,6 +866,17 @@ ipv4_addaddr(struct interface *ifp, const struct dhcp_lease *lease) 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; } @@ -891,13 +909,45 @@ ipv4_finaliseaddr(struct interface *ifp) 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) { @@ -914,38 +964,15 @@ 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); @@ -953,29 +980,30 @@ ipv4_applyaddr(void *arg) } /* 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; } } diff --git a/ipv4.h b/ipv4.h index 08b1c213..76c32c66 100644 --- a/ipv4.h +++ b/ipv4.h @@ -80,11 +80,13 @@ int ipv4_addrexists(struct dhcpcd_ctx *, const struct in_addr *); #define STATE_ADDED 0x01 #define STATE_FAKE 0x02 +#define STATE_TENTATIVE 0x04 void ipv4_buildroutes(struct dhcpcd_ctx *); void ipv4_finaliseaddr(struct interface *); int ipv4_deladdr(struct interface *ifp, const struct in_addr *, const struct in_addr *); +int ipv4_preferanother(struct interface *ifp); void ipv4_applyaddr(void *); int ipv4_handlert(struct dhcpcd_ctx *, int, struct rt *); void ipv4_freerts(struct rt_head *);