]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Persist shared IP address on interfaces.
authorRoy Marples <roy@marples.name>
Tue, 25 Jul 2017 10:43:12 +0000 (11:43 +0100)
committerRoy Marples <roy@marples.name>
Tue, 1 Aug 2017 18:46:16 +0000 (19:46 +0100)
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

src/arp.c
src/arp.h
src/bpf.c
src/dhcp.c
src/dhcp.h
src/dhcpcd.c
src/if-linux.c
src/ipv4.c
src/ipv4.h

index f05030cfa0b710314bdcbfe8887beac898a6b966..c35e00d1c2f2e98cfb018504d4beaf0a4b1acdac 100644 (file)
--- 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)
index 4e3ccb838f6eac0c512a8c7fac79efe787ade122..db9ee95082e13339c9f5c645d04ca610c5274127 100644 (file)
--- 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 *);
index 2636248f150780fd46b2c0e575a531326b98d218..9a14d7947d9d581069a0dd389bf450aa83aea907 100644 (file)
--- 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). */
index a519a1830aee6b49d69e8e78d2a776ac687918d9..d2f82f2a71d695f0a9f042b1c9f2304958bb5926 100644 (file)
@@ -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) {
index 525e01478744d647f208a040a92f72dc2c612c8e..08203dccf8565d04e008923ebdd990fbba7644a9 100644 (file)
@@ -183,6 +183,7 @@ struct dhcp_lease {
 };
 
 enum DHS {
+       DHS_NONE,
        DHS_INIT,
        DHS_DISCOVER,
        DHS_REQUEST,
index 4213c490b061496032139cb3ff8fb455d49f264f..6e8dba3335ff67dfc1ae4508a244c0423757212c 100644 (file)
@@ -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);
index 1716dc386951ef57338f18bf7a796ad135690690..1a5a78e8071ee212e5d911a967bbd542e002f8b6 100644 (file)
@@ -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;
 
index b8ce222af6377fbb012e2f037fd792a63b181c47..90693fb402ebb25ea6076e0a530cf27a347167dd 100644 (file)
@@ -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) {
index 9d1858e967147b21d3705aa2b48b0b90b8e31bf3..e0ce2e3240f1fbeeab76ba5ee7ba8dbb46551bb6 100644 (file)
@@ -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