From: Roy Marples Date: Mon, 23 Oct 2017 23:13:09 +0000 (+0100) Subject: dhcp: arp announce existing address before reboot X-Git-Tag: v7.0.0-rc4~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b0e19db886678d243ceae698cb66752d480f9158;p=thirdparty%2Fdhcpcd.git dhcp: arp announce existing address before reboot This updates upstream ARP tables to send their ACK to the interface we want to receive the message on and not one which is not in use but has the same IP address. Doing this means we no longer need to open DHCP sockets for interfaces we're not interested in. --- diff --git a/src/arp.c b/src/arp.c index 6e68bbb6..03e30b8c 100644 --- a/src/arp.c +++ b/src/arp.c @@ -373,10 +373,16 @@ void arp_announceaddr(struct dhcpcd_ctx *ctx, struct in_addr *ia) { struct interface *ifp; + struct ipv4_addr *iaf; struct arp_state *astate; TAILQ_FOREACH(ifp, ctx->ifaces, next) { - if (ipv4_iffindaddr(ifp, ia, NULL)) + iaf = ipv4_iffindaddr(ifp, ia, NULL); +#ifdef IN_IFF_NOTUSEABLE + if (iaf && !(iaf->addr_flags & IN_IFF_NOTUSEABLE)) +#else + if (iaf) +#endif break; } if (ifp == NULL) @@ -387,6 +393,16 @@ arp_announceaddr(struct dhcpcd_ctx *ctx, struct in_addr *ia) arp_announce(astate); } +void +arp_ifannounceaddr(struct interface *ifp, struct in_addr *ia) +{ + struct arp_state *astate; + + astate = arp_new(ifp, ia); + if (astate != NULL) + arp_announce(astate); +} + void arp_report_conflicted(const struct arp_state *astate, const struct arp_msg *amsg) diff --git a/src/arp.h b/src/arp.h index c1b06cda..f2746965 100644 --- a/src/arp.h +++ b/src/arp.h @@ -95,6 +95,7 @@ struct arp_state *arp_new(struct interface *, const struct in_addr *); struct arp_state *arp_find(struct interface *, const struct in_addr *); void arp_announce(struct arp_state *); void arp_announceaddr(struct dhcpcd_ctx *, struct in_addr *); +void arp_ifannounceaddr(struct interface *, struct in_addr *); void arp_cancel(struct arp_state *); void arp_free(struct arp_state *); void arp_free_but(struct arp_state *); diff --git a/src/dhcp.c b/src/dhcp.c index 10e9c98d..b138a6fc 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -2463,11 +2463,43 @@ dhcp_reboot_newopts(struct interface *ifp, unsigned long long oldopts) } } +#ifdef ARP +static int +dhcp_activeaddr(const struct interface *ifp, const struct in_addr *addr) +{ + const struct interface *ifp1; + const struct dhcp_state *state; + + TAILQ_FOREACH(ifp1, ifp->ctx->ifaces, next) { + if (ifp1 == ifp) + continue; + if ((state = D_CSTATE(ifp1)) == NULL) + continue; + switch(state->state) { + case DHS_REBOOT: + case DHS_RENEW: + case DHS_REBIND: + case DHS_BOUND: + case DHS_INFORM: + break; + default: + continue; + } + if (state->lease.addr.s_addr == addr->s_addr) + return 1; + } + return 0; +} +#endif + static void dhcp_reboot(struct interface *ifp) { struct if_options *ifo; struct dhcp_state *state = D_STATE(ifp); +#ifdef ARP + struct ipv4_addr *ia; +#endif if (state == NULL || state->state == DHS_NONE) return; @@ -2498,6 +2530,20 @@ dhcp_reboot(struct interface *ifp) loginfox("%s: rebinding lease of %s", ifp->name, inet_ntoa(state->lease.addr)); + +#ifdef ARP + /* If the address exists on the interface and no other interface + * is currently using it then announce it to ensure this + * interface gets the reply. */ + ia = ipv4_iffindaddr(ifp, &state->lease.addr, NULL); + if (ia != NULL && +#ifdef IN_IFF_NOTUSEABLE + !(ia->addr_flags & IN_IFF_NOTUSEABLE) && +#endif + dhcp_activeaddr(ifp, &state->lease.addr) == 0) + arp_ifannounceaddr(ifp, &state->lease.addr); +#endif + dhcp_new_xid(ifp); state->lease.server.s_addr = 0; eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); @@ -3287,51 +3333,30 @@ dhcp_handleudp(void *arg) } } - static int -dhcp_openbpf1(struct interface *ifp, bool logerror) +dhcp_openbpf(struct interface *ifp) { struct dhcp_state *state; state = D_STATE(ifp); + if (state->bpf_fd != -1) + return 0; + + state->bpf_fd = bpf_open(ifp, bpf_bootp); if (state->bpf_fd == -1) { - state->bpf_fd = bpf_open(ifp, bpf_bootp); - if (state->bpf_fd == -1) { - if (errno == ENOENT) { - logerrx("%s not found", bpf_name); - /* May as well disable IPv4 entirely at - * this point as we really need it. */ - ifp->options->options &= ~DHCPCD_IPV4; - } else if (logerror) - logerr("%s: %s", __func__, ifp->name); - return -1; - } - eloop_event_add(ifp->ctx->eloop, - state->bpf_fd, dhcp_readpacket, ifp); + if (errno == ENOENT) { + logerrx("%s not found", bpf_name); + /* May as well disable IPv4 entirely at + * this point as we really need it. */ + ifp->options->options &= ~DHCPCD_IPV4; + } else + logerr("%s: %s", __func__, ifp->name); + return -1; } - return 0; -} -/* This blows chunks. - * Because we may (although unlikely) get the same IP address - * across different interfaces we need to open a BPF socket - * on ALL interfaces as thanks to ARP magic we might get the - * DHCP reply on a different interface. */ -static int -dhcp_openbpf(struct interface *ifp) -{ - struct interface *ifn; - int r, o; - - r = -1; - TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) { - if (dhcp_initstate(ifn) == -1) - continue; - o = dhcp_openbpf1(ifn, ifn == ifp); - if (ifn == ifp) - r = o; - } - return r; + eloop_event_add(ifp->ctx->eloop, + state->bpf_fd, dhcp_readpacket, ifp); + return 0; } int