From: Roy Marples Date: Tue, 25 Jul 2017 10:43:12 +0000 (+0100) Subject: Persist shared IP address on interfaces. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=df9ff183d6e9849c207be79097b43b516f6ca9fd;p=thirdparty%2Fdhcpcd.git Persist shared IP address on interfaces. Summary: Send unicast DHCP messages by BPF rather than a UDP socket. Open a BPF socket for all interfaces whether active or not. Redirect packets to the correct interface based on xid and chaddr. Announce the primary address via ARP when any address is added or deleted. Remove the now redundant prefer another address code. Fixes T126. Test Plan: Setup your DHCP server to assign the same address to many interfaces on the same host. On the host, start dhcpcd and bring up/down/activate the interfaces in a random order. Pinging another host on the network should be possible at all times if any interface is up. Maniphest Tasks: T126 Differential Revision: https://dev.marples.name/D122 --- diff --git a/src/arp.c b/src/arp.c index f05030cf..c35e00d1 100644 --- a/src/arp.c +++ b/src/arp.c @@ -304,11 +304,6 @@ arp_announce1(void *arg) struct arp_state *astate = arg; struct interface *ifp = astate->iface; -#ifdef KERNEL_RFC5227 - /* We rely on the kernel announcing correctly. - * As the timings are not random we can callback safely. */ - astate->claims++; -#else if (++astate->claims < ANNOUNCE_NUM) logdebugx("%s: ARP announcing %s (%d of %d), " "next in %d.0 seconds", @@ -320,27 +315,80 @@ arp_announce1(void *arg) astate->claims, ANNOUNCE_NUM); if (arp_request(ifp, astate->addr.s_addr, astate->addr.s_addr) == -1) logerr(__func__); -#endif eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT, astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced, astate); } +/* + * XXX FIXME + * Kernels supporting RFC5227 will announce the address when it's + * added. + * dhcpcd should not announce when this happens, nor need to open + * a BPF socket for it. + * Also, an address might be added to a non preferred inteface when + * the same address exists on a preferred one so we need to instruct + * the kernel not to announce the address somehow. + */ + void arp_announce(struct arp_state *astate) { + struct iarp_state *state; + struct interface *ifp; + struct arp_state *a2; + int r; -#ifndef KERNEL_RFC5227 if (arp_open(astate->iface) == -1) { logerr(__func__); return; } -#endif + + /* Cancel any other ARP announcements for this address. */ + TAILQ_FOREACH(ifp, astate->iface->ctx->ifaces, next) { + state = ARP_STATE(ifp); + if (state == NULL) + continue; + TAILQ_FOREACH(a2, &state->arp_states, next) { + if (astate == a2 || + a2->addr.s_addr != astate->addr.s_addr) + continue; + r = eloop_timeout_delete(a2->iface->ctx->eloop, + a2->claims < ANNOUNCE_NUM + ? arp_announce1 : arp_announced, + a2); + if (r == -1) + logerr(__func__); + else if (r != 0) + logdebugx("%s: ARP announcement " + "of %s cancelled", + a2->iface->name, + inet_ntoa(a2->addr)); + } + } astate->claims = 0; arp_announce1(astate); } +void +arp_announceaddr(struct dhcpcd_ctx *ctx, struct in_addr *ia) +{ + struct interface *ifp; + struct arp_state *astate; + + TAILQ_FOREACH(ifp, ctx->ifaces, next) { + if (ipv4_iffindaddr(ifp, ia, NULL)) + break; + } + if (ifp == NULL) + return; + + astate = arp_find(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 4e3ccb83..db9ee950 100644 --- a/src/arp.h +++ b/src/arp.h @@ -97,6 +97,7 @@ void arp_report_conflicted(const struct arp_state *, const struct arp_msg *); 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_cancel(struct arp_state *); void arp_free(struct arp_state *); void arp_free_but(struct arp_state *); diff --git a/src/bpf.c b/src/bpf.c index 2636248f..9a14d794 100644 --- a/src/bpf.c +++ b/src/bpf.c @@ -166,7 +166,9 @@ bpf_open(struct interface *ifp, int (*filter)(struct interface *, int)) if (ioctl(fd, BIOCGBLEN, &ibuf_len) == -1) goto eexit; buf_len = (size_t)ibuf_len; - state = IPV4_STATE(ifp); + state = ipv4_getstate(ifp); + if (state == NULL) + goto eexit; if (state->buffer_size != buf_len) { void *nb; @@ -589,6 +591,7 @@ static const struct bpf_insn bpf_bootp_filter[] = { BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), BPF_STMT(BPF_RET + BPF_K, 0), }; + #define BPF_BOOTP_FILTER_LEN __arraycount(bpf_bootp_filter) #define BPF_BOOTP_CHADDR_LEN ((BOOTP_CHADDR_LEN / 4) * 3) #define BPF_BOOTP_XID_LEN 4 /* BOUND check is 4 instructions */ @@ -599,7 +602,9 @@ static const struct bpf_insn bpf_bootp_filter[] = { int bpf_bootp(struct interface *ifp, int fd) { +#if 0 const struct dhcp_state *state = D_CSTATE(ifp); +#endif struct bpf_insn bpf[BPF_BOOTP_LEN]; struct bpf_insn *bp; @@ -622,6 +627,8 @@ bpf_bootp(struct interface *ifp, int fd) memcpy(bp, bpf_bootp_filter, sizeof(bpf_bootp_filter)); bp += BPF_BOOTP_FILTER_LEN; + /* These checks won't work when same IP exists on other interfaces. */ +#if 0 if (ifp->hwlen <= sizeof(((struct bootp *)0)->chaddr)) bp += bpf_cmp_hwaddr(bp, BPF_BOOTP_CHADDR_LEN, offsetof(struct bootp, chaddr), @@ -654,6 +661,7 @@ bpf_bootp(struct interface *ifp, int fd) BPF_SET_STMT(bp, BPF_RET + BPF_K, 0); bp++; } +#endif /* All passed, return the packet * (Frame length in M0, IP length in M2). */ diff --git a/src/dhcp.c b/src/dhcp.c index a519a183..d2f82f2a 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -118,10 +118,13 @@ static const char * const dhcp_params[] = { NULL }; -static int dhcp_open(struct interface *); +static int dhcp_openbpf(struct interface *); #ifdef ARP static void dhcp_arp_conflicted(struct arp_state *, const struct arp_msg *); #endif +static void dhcp_handledhcp(struct interface *, struct bootp *, size_t, + const struct in_addr *); +static int dhcp_initstate(struct interface *); void dhcp_printoptions(const struct dhcpcd_ctx *ctx, @@ -1527,11 +1530,14 @@ dhcp_new_xid(struct interface *ifp) else state->xid = arc4random(); + /* We can't do this when sharing leases across interfaes */ +#if 0 /* As the XID changes, re-apply the filter. */ if (state->bpf_fd != -1) { if (bpf_bootp(ifp, state->bpf_fd) == -1) logerr(__func__); /* try to continue */ } +#endif } void @@ -1672,17 +1678,11 @@ send_message(struct interface *ifp, uint8_t type, struct if_options *ifo = ifp->options; struct bootp *bootp; struct bootp_pkt *udp; - size_t len; + size_t len, ulen; ssize_t r; struct in_addr from, to; - struct ipv4_addr *iap; struct timespec tv; - int s; -#ifdef IN_IFF_NOTUSEABLE - struct ipv4_addr *ia; -#endif - s = -1; if (!callback) { /* No carrier? Don't bother sending the packet. */ if (ifp->carrier == LINK_DOWN) @@ -1714,91 +1714,51 @@ send_message(struct interface *ifp, uint8_t type, timespec_to_double(&tv)); } - if (dhcp_open(ifp) == -1) + if (dhcp_openbpf(ifp) == -1) return; - iap = state->addr; - if (state->added && !(state->added & STATE_FAKE) && - state->addr != NULL && state->new != NULL && -#ifdef IN_IFF_NOTUSEABLE - ((ia = ipv4_iffindaddr(ifp, &state->addr->addr, NULL)) && - !(ia->addr_flags & IN_IFF_NOTUSEABLE)) && -#endif - (state->lease.server.s_addr || - ifp->options->options & DHCPCD_INFORM) && - IS_DHCP(state->new)) - { - s = dhcp_openudp(ifp); - if (s == -1) { - if (errno != EADDRINUSE) - logerr("%s: dhcp_openudp", ifp->name); - /* We cannot renew */ - state->addr = NULL; - } - } - r = make_message(&bootp, ifp, type); - state->addr = iap; if (r == -1) goto fail; len = (size_t)r; from.s_addr = bootp->ciaddr; - if (s != -1 && from.s_addr != INADDR_ANY) + if (from.s_addr != INADDR_ANY) to.s_addr = state->lease.server.s_addr; else to.s_addr = INADDR_ANY; - if (to.s_addr && to.s_addr != INADDR_BROADCAST) { - struct sockaddr_in sin; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = to.s_addr; - sin.sin_port = htons(BOOTPS); - r = sendto(s, (uint8_t *)bootp, len, 0, - (struct sockaddr *)&sin, sizeof(sin)); - if (r == -1) - logerr("%s: dhcp_sendpacket", ifp->name); + udp = dhcp_makeudppacket(&ulen, (uint8_t *)bootp, len, from, to); + if (udp == NULL) { + logerr("%s: dhcp_makeudppacket", ifp->name); + r = 0; } else { - size_t ulen; - - udp = dhcp_makeudppacket(&ulen, (uint8_t *)bootp, len, from,to); - if (udp == NULL) { - logerr("%s: dhcp_makeudppacket", ifp->name); - r = 0; - } else { - r = bpf_send(ifp, state->bpf_fd, - ETHERTYPE_IP, (uint8_t *)udp, ulen); - free(udp); - } - /* If we failed to send a raw packet this normally means - * we don't have the ability to work beneath the IP layer - * for this interface. - * As such we remove it from consideration without actually - * stopping the interface. */ - if (r == -1) { - logerr("%s: if_sendraw", ifp->name); - switch(errno) { - case ENETDOWN: - case ENETRESET: - case ENETUNREACH: - case ENOBUFS: - break; - default: - if (!(ifp->ctx->options & DHCPCD_TEST)) - dhcp_drop(ifp, "FAIL"); - dhcp_free(ifp); - eloop_timeout_delete(ifp->ctx->eloop, - NULL, ifp); - callback = NULL; - } + r = bpf_send(ifp, state->bpf_fd, + ETHERTYPE_IP, (uint8_t *)udp, ulen); + free(udp); + } + /* If we failed to send a raw packet this normally means + * we don't have the ability to work beneath the IP layer + * for this interface. + * As such we remove it from consideration without actually + * stopping the interface. */ + if (r == -1) { + logerr("%s: if_sendraw", ifp->name); + switch(errno) { + case ENETDOWN: + case ENETRESET: + case ENETUNREACH: + case ENOBUFS: + break; + default: + if (!(ifp->ctx->options & DHCPCD_TEST)) + dhcp_drop(ifp, "FAIL"); + eloop_timeout_delete(ifp->ctx->eloop, + NULL, ifp); + callback = NULL; } } free(bootp); fail: - if (s != -1) - close(s); - /* Even if we fail to send a packet we should continue as we are * as our failure timeouts will change out codepath when needed. */ if (callback) @@ -2097,7 +2057,6 @@ dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg) dhcp_arp_probed(astate); return; } - dhcp_close(ifp); arp_free(astate); #ifdef KERNEL_RFC5227 /* As arping is finished, close the ARP socket. @@ -2347,7 +2306,7 @@ dhcp_arp_address(struct interface *ifp) state->offer->ciaddr : state->offer->yiaddr; /* If the interface already has the address configured * then we can't ARP for duplicate detection. */ - ia = ipv4_findaddr(ifp->ctx, &addr); + ia = ipv4_iffindaddr(ifp, &addr, NULL); if ((astate = arp_new(ifp, &addr)) == NULL) return -1; astate->probed_cb = dhcp_arp_probed; @@ -2490,7 +2449,7 @@ dhcp_reboot_newopts(struct interface *ifp, unsigned long long oldopts) struct if_options *ifo; struct dhcp_state *state = D_STATE(ifp); - if (state == NULL) + if (state == NULL || state->state == DHS_NONE) return; ifo = ifp->options; if ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC) && @@ -2509,7 +2468,7 @@ dhcp_reboot(struct interface *ifp) struct if_options *ifo; struct dhcp_state *state = D_STATE(ifp); - if (state == NULL) + if (state == NULL || state->state == DHS_NONE) return; ifo = ifp->options; state->state = DHS_REBOOT; @@ -2572,7 +2531,7 @@ dhcp_drop(struct interface *ifp, const char *reason) state = D_STATE(ifp); /* dhcp_start may just have been called and we don't yet have a state * but we do have a timeout, so punt it. */ - if (state == NULL) { + if (state == NULL || state->state == DHS_NONE) { eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); return; } @@ -2611,9 +2570,8 @@ dhcp_drop(struct interface *ifp, const char *reason) #ifdef AUTH dhcp_auth_reset(&state->auth); #endif - dhcp_close(ifp); - state->state = DHS_INIT; + state->state = DHS_NONE; free(state->offer); state->offer = NULL; state->offer_len = 0; @@ -2643,17 +2601,20 @@ blacklisted_ip(const struct if_options *ifo, in_addr_t addr) return 0; } -static int +#define WHTLST_NONE 0 +#define WHTLST_MATCH 1 +#define WHTLST_NOMATCH 2 +static unsigned int whitelisted_ip(const struct if_options *ifo, in_addr_t addr) { size_t i; if (ifo->whitelist_len == 0) - return -1; + return WHTLST_NONE; for (i = 0; i < ifo->whitelist_len; i += 2) if (ifo->whitelist[i] == (addr & ifo->whitelist[i + 1])) - return 1; - return 0; + return WHTLST_MATCH; + return WHTLST_NOMATCH; } static void @@ -2724,6 +2685,33 @@ log_dhcp(logfunc_t *logfunc, const char *msg, free(a); } +/* If we're sharing the same IP address with another interface on the + * same network, we may receive the DHCP reply on the wrong interface. + * Try and re-direct it here. */ +static void +dhcp_redirect_dhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len, + const struct in_addr *from) +{ + struct interface *ifn; + const struct dhcp_state *state; + uint32_t xid; + + xid = ntohl(bootp->xid); + TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) { + state = D_CSTATE(ifn); + if (state == NULL || state->state == DHS_NONE) + continue; + if (state->xid != xid) + continue; + if (ifn->hwlen <= sizeof(bootp->chaddr) && + memcmp(bootp->chaddr, ifn->hwaddr, ifn->hwlen)) + continue; + logdebugx("%s: redirecting DHCP message to %s", + ifp->name, ifn->name); + dhcp_handledhcp(ifn, bootp, bootp_len, from); + } +} + static void dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len, const struct in_addr *from) @@ -2756,6 +2744,16 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len, ifp->name, bootp->op); return; } +#endif + + if (state->xid != ntohl(bootp->xid)) { + if (state->state != DHS_BOUND) + logdebugx("%s: wrong xid 0x%x (expecting 0x%x) from %s", + ifp->name, ntohl(bootp->xid), state->xid, + inet_ntoa(*from)); + dhcp_redirect_dhcp(ifp, bootp, bootp_len, from); + return; + } if (ifp->hwlen <= sizeof(bootp->chaddr) && memcmp(bootp->chaddr, ifp->hwaddr, ifp->hwlen)) @@ -2766,9 +2764,28 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len, ifp->name, ntohl(bootp->xid), hwaddr_ntoa(bootp->chaddr, sizeof(bootp->chaddr), buf, sizeof(buf))); + dhcp_redirect_dhcp(ifp, bootp, bootp_len, from); return; } -#endif + + if (!ifp->active) + return; + + i = whitelisted_ip(ifp->options, from->s_addr); + switch (i) { + case WHTLST_NOMATCH: + logwarnx("%s: non whitelisted DHCP packet from %s", + ifp->name, inet_ntoa(*from)); + return; + case WHTLST_MATCH: + break; + case WHTLST_NONE: + if (blacklisted_ip(ifp->options, from->s_addr) == 1) { + logwarnx("%s: blacklisted DHCP packet from %s", + ifp->name, inet_ntoa(*from)); + return; + } + } /* We may have found a BOOTP server */ if (get_option_uint8(ifp->ctx, &type, @@ -2848,17 +2865,6 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len, return; } - /* Handled in our BPF filter. */ -#if 0 - /* Ensure it's the right transaction */ - if (state->xid != ntohl(bootp->xid)) { - logdebugx("%s: wrong xid 0x%x (expecting 0x%x) from %s", - ifp->name, ntohl(bootp->xid), state->xid, - inet_ntoa(*from)); - return; - } -#endif - if (state->state == DHS_PROBE) { /* Ignore any DHCP messages whilst probing a lease to bind. */ LOGDHCP(logdebugx, "probing, ignoring"); @@ -3185,7 +3191,6 @@ dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len, int flags) { struct bootp *bootp; struct in_addr from; - int i; size_t udp_len; const struct dhcp_state *state = D_CSTATE(ifp); @@ -3195,16 +3200,6 @@ dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len, int flags) ifp->name, inet_ntoa(from)); return; } - i = whitelisted_ip(ifp->options, from.s_addr); - if (i == 0) { - logwarnx("%s: non whitelisted DHCP packet from %s", - ifp->name, inet_ntoa(from)); - return; - } else if (i != 1 && blacklisted_ip(ifp->options, from.s_addr) == 1) { - logwarnx("%s: blacklisted DHCP packet from %s", - ifp->name, inet_ntoa(from)); - return; - } if (ifp->flags & IFF_POINTOPOINT && (state->addr == NULL || state->addr->brd.s_addr != from.s_addr)) { @@ -3254,8 +3249,10 @@ dhcp_readpacket(void *arg) while (!(flags & BPF_EOF)) { bytes = bpf_read(ifp, state->bpf_fd, buf, sizeof(buf), &flags); if (bytes == -1) { - logerr(__func__); - dhcp_close(ifp); + if (state->state != DHS_NONE) { + logerr("%s: %s", __func__, ifp->name); + dhcp_close(ifp); + } return; } dhcp_handlepacket(ifp, buf, (size_t)bytes, flags); @@ -3285,7 +3282,7 @@ dhcp_handleudp(void *arg) static int -dhcp_open(struct interface *ifp) +dhcp_openbpf1(struct interface *ifp, bool logerror) { struct dhcp_state *state; @@ -3298,8 +3295,8 @@ dhcp_open(struct interface *ifp) /* May as well disable IPv4 entirely at * this point as we really need it. */ ifp->options->options &= ~DHCPCD_IPV4; - } else - logerr(__func__); + } else if (logerror) + logerr("%s: %s", __func__, ifp->name); return -1; } eloop_event_add(ifp->ctx->eloop, @@ -3308,6 +3305,28 @@ dhcp_open(struct interface *ifp) 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; +} + int dhcp_dump(struct interface *ifp) { @@ -3342,12 +3361,12 @@ dhcp_free(struct interface *ifp) dhcp_close(ifp); arp_drop(ifp); if (state) { + state->state = DHS_NONE; free(state->old); free(state->new); free(state->offer); free(state->clientid); free(state); - ifp->if_data[IF_DATA_DHCP] = NULL; } ctx = ifp->ctx; @@ -3355,7 +3374,8 @@ dhcp_free(struct interface *ifp) * close the global socket and release resources */ if (ctx->ifaces) { TAILQ_FOREACH(ifp, ctx->ifaces, next) { - if (D_STATE(ifp)) + state = D_STATE(ifp); + if (state != NULL && state->state != DHS_NONE) break; } } @@ -3372,29 +3392,46 @@ dhcp_free(struct interface *ifp) } static int -dhcp_init(struct interface *ifp) +dhcp_initstate(struct interface *ifp) { struct dhcp_state *state; - const struct if_options *ifo; - uint8_t len; - char buf[(sizeof(ifo->clientid) - 1) * 3]; state = D_STATE(ifp); - if (state == NULL) { - ifp->if_data[IF_DATA_DHCP] = calloc(1, sizeof(*state)); - state = D_STATE(ifp); - if (state == NULL) - return -1; - /* 0 is a valid fd, so init to -1 */ - state->bpf_fd = -1; + if (state != NULL) + return 0; + + ifp->if_data[IF_DATA_DHCP] = calloc(1, sizeof(*state)); + state = D_STATE(ifp); + if (state == NULL) + return -1; + state->state = DHS_NONE; + /* 0 is a valid fd, so init to -1 */ + state->bpf_fd = -1; #ifdef ARPING - state->arping_index = -1; + state->arping_index = -1; #endif + return 1; +} + +static int +dhcp_init(struct interface *ifp) +{ + struct dhcp_state *state; + const struct if_options *ifo; + uint8_t len; + char buf[(sizeof(ifo->clientid) - 1) * 3]; + int r; + + r = dhcp_initstate(ifp); + if (r == -1) + return -1; + else if (r == 1) { /* Now is a good time to find IPv4 routes */ if_initrt(ifp->ctx, AF_INET); } + state = D_STATE(ifp); state->state = DHS_INIT; state->reason = "PREINIT"; state->nakoff = 0; @@ -3514,7 +3551,7 @@ dhcp_start1(void *arg) return; } - if (ifo->options & DHCPCD_DHCP && dhcp_open(ifp) == -1) + if (ifo->options & DHCPCD_DHCP && dhcp_openbpf(ifp) == -1) return; if (ifo->options & DHCPCD_INFORM) { @@ -3719,6 +3756,13 @@ dhcp_abort(struct interface *ifp) #endif eloop_timeout_delete(ifp->ctx->eloop, dhcp_start1, ifp); + + if (state != NULL && state->added) { + rt_build(ifp->ctx, AF_INET); +#ifdef ARP + arp_announceaddr(ifp->ctx, &state->addr->addr); +#endif + } } void @@ -3731,7 +3775,7 @@ dhcp_handleifa(int cmd, struct ipv4_addr *ia) ifp = ia->iface; state = D_STATE(ifp); - if (state == NULL) + if (state == NULL || state->state == DHS_NONE) return; if (cmd == RTM_DELADDR) { diff --git a/src/dhcp.h b/src/dhcp.h index 525e0147..08203dcc 100644 --- a/src/dhcp.h +++ b/src/dhcp.h @@ -183,6 +183,7 @@ struct dhcp_lease { }; enum DHS { + DHS_NONE, DHS_INIT, DHS_DISCOVER, DHS_REQUEST, diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 4213c490..6e8dba33 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -719,8 +719,6 @@ dhcpcd_handlecarrier(struct dhcpcd_ctx *ctx, int carrier, unsigned int flags, #ifdef NOCARRIER_PRESERVE_IP arp_drop(ifp); dhcp_abort(ifp); - if_sortinterfaces(ctx); - ipv4_preferanother(ifp); ipv6nd_expire(ifp, 0); #else dhcpcd_drop(ifp, 0); diff --git a/src/if-linux.c b/src/if-linux.c index 1716dc38..1a5a78e8 100644 --- a/src/if-linux.c +++ b/src/if-linux.c @@ -1310,7 +1310,7 @@ const char *bpf_name = "Packet Socket"; int bpf_open(struct interface *ifp, int (*filter)(struct interface *, int)) { - struct ipv4_state *state = IPV4_STATE(ifp); + struct ipv4_state *state; /* Linux is a special snowflake for opening BPF. */ int s; union sockunion { @@ -1328,6 +1328,9 @@ bpf_open(struct interface *ifp, int (*filter)(struct interface *, int)) #undef SF /* Allocate a suitably large buffer for a single packet. */ + state = ipv4_getstate(ifp); + if (state == NULL) + goto eexit; if (state->buffer_size < ETH_DATA_LEN) { void *nb; diff --git a/src/ipv4.c b/src/ipv4.c index b8ce222a..90693fb4 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -670,50 +670,11 @@ ipv4_daddaddr(struct interface *ifp, const struct dhcp_lease *lease) return 0; } -int -ipv4_preferanother(struct interface *ifp) -{ - struct dhcp_state *state = D_STATE(ifp), *nstate; - struct interface *ifn; - int preferred; - - if (state == NULL) - return 0; - - preferred = 0; - if (!state->added) - goto out; - - TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) { - if (ifn == ifp) - break; /* We are already the most preferred */ - nstate = D_STATE(ifn); - if (nstate && !nstate->added && - state->addr != NULL && - nstate->lease.addr.s_addr == state->addr->addr.s_addr) - { - preferred = 1; - delete_address(ifp); - if (ifn->options->options & DHCPCD_ARP) - dhcp_bind(ifn); - else { - ipv4_daddaddr(ifn, &nstate->lease); - nstate->added = STATE_ADDED; - } - break; - } - } - -out: - rt_build(ifp->ctx, AF_INET); - return preferred; -} - void ipv4_applyaddr(void *arg) { - struct interface *ifp = arg, *ifn; - struct dhcp_state *state = D_STATE(ifp), *nstate; + struct interface *ifp = arg; + struct dhcp_state *state = D_STATE(ifp); struct dhcp_lease *lease; struct if_options *ifo = ifp->options; struct ipv4_addr *ia; @@ -723,15 +684,21 @@ ipv4_applyaddr(void *arg) return; lease = &state->lease; - if_sortinterfaces(ifp->ctx); - if (state->new == NULL) { if ((ifo->options & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) != (DHCPCD_EXITING | DHCPCD_PERSISTENT)) { - if (state->added && !ipv4_preferanother(ifp)) { + if (state->added) { + struct in_addr addr; + + addr = lease->addr; delete_address(ifp); rt_build(ifp->ctx, AF_INET); +#ifdef ARP + /* Announce the preferred address to + * kick ARP caches. */ + arp_announceaddr(ifp->ctx, &addr); +#endif } script_runreason(ifp, state->reason); } else @@ -739,45 +706,6 @@ ipv4_applyaddr(void *arg) return; } - /* Ensure only one interface has the address */ - r = 0; - TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) { - if (ifn == ifp) { - r = 1; /* past ourselves */ - continue; - } - nstate = D_STATE(ifn); - if (nstate && nstate->added && - nstate->addr && - nstate->addr->addr.s_addr == lease->addr.s_addr) - { - if (r == 0) { - loginfox("%s: preferring %s on %s", - ifp->name, - inet_ntoa(lease->addr), - ifn->name); - return; - } - loginfox("%s: preferring %s on %s", - ifn->name, - inet_ntoa(lease->addr), - ifp->name); - ipv4_deladdr(nstate->addr, 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; - ia = ipv4_iffindaddr(ifn, &lease->addr, NULL); - if (ia != NULL) - ipv4_deladdr(ia, 0); - } - } - /* If the netmask or broadcast is different, re-add the addresss */ ia = ipv4_iffindaddr(ifp, &lease->addr, NULL); if (ia && @@ -824,13 +752,7 @@ ipv4_applyaddr(void *arg) rt_build(ifp->ctx, AF_INET); #ifdef ARP - /* Announce the address */ - if (ifo->options & DHCPCD_ARP) { - struct arp_state *astate; - - if ((astate = arp_find(ifp, &state->addr->addr)) != NULL) - arp_announce(astate); - } + arp_announceaddr(ifp->ctx, &state->addr->addr); #endif if (state->state == DHS_BOUND) { diff --git a/src/ipv4.h b/src/ipv4.h index 9d1858e9..e0ce2e32 100644 --- a/src/ipv4.h +++ b/src/ipv4.h @@ -119,7 +119,6 @@ bool inet_getroutes(struct dhcpcd_ctx *, struct rt_head *); #define STATE_FAKE 0x02 int ipv4_deladdr(struct ipv4_addr *, int); -int ipv4_preferanother(struct interface *); struct ipv4_addr *ipv4_addaddr(struct interface *, const struct in_addr *, const struct in_addr *, const struct in_addr *); void ipv4_applyaddr(void *); @@ -140,7 +139,6 @@ void ipv4_free(struct interface *); #define ipv4_applyaddr(a) {} #define ipv4_free(a) {} #define ipv4_hasaddr(a) (0) -#define ipv4_preferanother(a) {} #endif #endif