]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Support IN_IFF_TENTATIVE and IN_IFF_DUPLICATED on NetBSD.
authorRoy Marples <roy@marples.name>
Fri, 1 May 2015 09:41:54 +0000 (09:41 +0000)
committerRoy Marples <roy@marples.name>
Fri, 1 May 2015 09:41:54 +0000 (09:41 +0000)
arp.c
arp.h
dhcp.c
dhcp.h
if-bsd.c
if-linux.c
if.c
if.h
ipv4.c
ipv4.h
ipv4ll.c

diff --git a/arp.c b/arp.c
index 122752b629093bdae003a106209ee6217be6be70..4877a65f31a4cef73654f50fb1f2b65ce7ce52dc 100644 (file)
--- a/arp.c
+++ b/arp.c
@@ -96,12 +96,20 @@ eexit:
 void
 arp_report_conflicted(const struct arp_state *astate, const struct arp_msg *amsg)
 {
-       char buf[HWADDR_LEN * 3];
 
-       logger(astate->iface->ctx, LOG_ERR, "%s: hardware address %s claims %s",
-           astate->iface->name,
-           hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf)),
-           inet_ntoa(astate->failed));
+       if (amsg) {
+               char buf[HWADDR_LEN * 3];
+
+               logger(astate->iface->ctx, LOG_ERR,
+                   "%s: hardware address %s claims %s",
+                   astate->iface->name,
+                   hwaddr_ntoa(amsg->sha, astate->iface->hwlen,
+                   buf, sizeof(buf)),
+                   inet_ntoa(astate->failed));
+       } else
+               logger(astate->iface->ctx, LOG_ERR,
+                   "%s: DAD detected %s",
+                   astate->iface->name, inet_ntoa(astate->failed));
 }
 
 static void
@@ -315,7 +323,8 @@ arp_new(struct interface *ifp, const struct in_addr *addr)
        }
        state = D_STATE(ifp);
        astate->iface = ifp;
-       astate->addr = *addr;
+       if (addr)
+               astate->addr = *addr;
        TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
        return astate;
 }
@@ -380,3 +389,28 @@ arp_close(struct interface *ifp)
 #endif
        }
 }
+
+void
+arp_handleifa(int cmd, struct interface *ifp, const struct in_addr *addr,
+    int flags)
+{
+#ifdef IN_IFF_DUPLICATED
+       struct dhcp_state *state = D_STATE(ifp);
+       struct arp_state *astate, *asn;
+
+       if (cmd != RTM_NEWADDR || (state = D_STATE(ifp)) == NULL)
+               return;
+
+       TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, asn) {
+               if (astate->addr.s_addr == addr->s_addr) {
+                       if (flags & IN_IFF_DUPLICATED) {
+                               if (astate->conflicted_cb)
+                                       astate->conflicted_cb(astate, NULL);
+                       } else if (!(flags & IN_IFF_TENTATIVE)) {
+                               if (astate->probed_cb)
+                                       astate->probed_cb(astate);
+                       }
+               }
+       }
+#endif
+}
diff --git a/arp.h b/arp.h
index d0d4ed7df844f0f5e488776b4a776bdd56904c1d..83422fe1cfb997a2e119a3f8a79e98bd7c29dc84 100644 (file)
--- a/arp.h
+++ b/arp.h
@@ -74,6 +74,8 @@ void arp_cancel(struct arp_state *);
 void arp_free(struct arp_state *);
 void arp_free_but(struct arp_state *);
 void arp_close(struct interface *);
+
+void arp_handleifa(int, struct interface *, const struct in_addr *, int);
 #else
 #define arp_close(a) {}
 #endif
diff --git a/dhcp.c b/dhcp.c
index 88824f3f4e70ce44b3fd694c8dae345f2bf5c13b..2c962a2da2cffab00f843da9966a2f21d04710cd 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -1799,7 +1799,7 @@ dhcp_expire(void *arg)
        dhcp_discover(ifp);
 }
 
-void
+static void
 dhcp_decline(struct interface *ifp)
 {
 
@@ -1824,12 +1824,14 @@ dhcp_renew(void *arg)
        send_renew(ifp);
 }
 
+#ifndef IN_IFF_TENTATIVE
 static void
 dhcp_arp_announced(struct arp_state *astate)
 {
 
        arp_close(astate->iface);
 }
+#endif
 
 static void
 dhcp_rebind(void *arg)
@@ -1978,9 +1980,13 @@ dhcp_bind(struct interface *ifp, struct arp_state *astate)
 
 applyaddr:
        ipv4_applyaddr(ifp);
-       if (dhcpcd_daemonise(ifp->ctx))
-               return;
-       if (ifo->options & DHCPCD_ARP) {
+       if (ifo->options & DHCPCD_ARP &&
+           !(ifp->ctx->options & DHCPCD_FORKED))
+       {
+#ifdef IN_IFF_TENTATIVE
+               if (astate)
+                       arp_free_but(astate);
+#else
                if (state->added) {
                        if (astate == NULL) {
                                astate = arp_new(ifp, &state->addr);
@@ -1994,6 +2000,7 @@ applyaddr:
                        }
                } else if (!ipv4ll)
                        arp_close(ifp);
+#endif
        }
 }
 
@@ -2347,7 +2354,12 @@ dhcp_arp_probed(struct arp_state *astate)
        }
        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
@@ -2360,6 +2372,7 @@ dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
        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])))
@@ -2386,14 +2399,17 @@ dhcp_arp_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
                dhcpcd_startinterface(astate->iface);
        }
 
-       if (state->offer == NULL)
-               return;
-
-       /* RFC 2131 3.1.5, Client-server interaction */
-       if (amsg->sip.s_addr == state->offer->yiaddr ||
-           (amsg->sip.s_addr == 0 && amsg->tip.s_addr == state->offer->yiaddr))
+       /* 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))))
        {
-               astate->failed.s_addr = state->offer->yiaddr;
+               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)
@@ -2419,6 +2435,7 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
        unsigned int i;
        size_t auth_len;
        char *msg;
+       struct arp_state *astate;
 
        /* We may have found a BOOTP server */
        if (get_option_uint8(ifp->ctx, &type, dhcp, DHO_MESSAGETYPE) == -1)
@@ -2632,6 +2649,10 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
        if ((type == 0 || type == DHCP_OFFER) &&
            (state->state == DHS_DISCOVER || state->state == DHS_IPV4LL_BOUND))
        {
+#ifdef IN_IFF_DUPLICATED
+               struct ipv4_addr *ia;
+#endif
+
                lease->frominfo = 0;
                lease->addr.s_addr = dhcp->yiaddr;
                lease->cookie = dhcp->cookie;
@@ -2640,6 +2661,18 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
                    &lease->server, dhcp, DHO_SERVERID) != 0)
                        lease->server.s_addr = INADDR_ANY;
                log_dhcp(LOG_INFO, "offered", ifp, dhcp, from);
+#ifdef IN_IFF_DUPLICATED
+               ia = ipv4_iffindaddr(ifp, &lease->addr, NULL);
+               if (ia && ia->addr_flags & IN_IFF_DUPLICATED) {
+                       log_dhcp(LOG_WARNING, "declined duplicate address",
+                           ifp, dhcp, from);
+                       dhcp_decline(ifp);
+                       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+                       eloop_timeout_add_sec(ifp->ctx->eloop,
+                           DHCP_RAND_MAX, dhcp_discover, ifp);
+                       return;
+               }
+#endif
                free(state->offer);
                state->offer = dhcp;
                *dhcpp = NULL;
@@ -2703,7 +2736,7 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
 
        lease->frominfo = 0;
        eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
-
+       astate = NULL;
        if (ifo->options & DHCPCD_ARP &&
            state->addr.s_addr != state->offer->yiaddr)
        {
@@ -2711,19 +2744,21 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
                 * then we can't ARP for duplicate detection. */
                addr.s_addr = state->offer->yiaddr;
                if (!ipv4_iffindaddr(ifp, &addr, NULL)) {
-                       struct arp_state *astate;
-
                        astate = arp_new(ifp, &addr);
                        if (astate) {
                                astate->probed_cb = dhcp_arp_probed;
                                astate->conflicted_cb = dhcp_arp_conflicted;
+#ifndef IN_IFF_TENTATIVE
                                arp_probe(astate);
+#endif
                        }
+#ifndef IN_IFF_TENTATIVE
                        return;
+#endif
                }
        }
 
-       dhcp_bind(ifp, NULL);
+       dhcp_bind(ifp, astate);
 }
 
 static size_t
@@ -3158,8 +3193,16 @@ dhcp_start1(void *arg)
                state->offer = read_lease(ifp);
                /* Check the saved lease matches the type we want */
                if (state->offer) {
+                       struct ipv4_addr *ia;
+                       struct in_addr addr;
+
+                       addr.s_addr = state->offer->yiaddr;
+                       ia = ipv4_iffindaddr(ifp, &addr, NULL);
                        if ((IS_BOOTP(ifp, state->offer) &&
                            !(ifo->options & DHCPCD_BOOTP)) ||
+#ifdef IN_IFF_DUPLICATED
+                           (ia && ia->addr_flags & IN_IFF_DUPLICATED) ||
+#endif
                            (!IS_BOOTP(ifp, state->offer) &&
                            ifo->options & DHCPCD_BOOTP))
                        {
@@ -3271,10 +3314,11 @@ dhcp_start(struct interface *ifp)
 }
 
 void
-dhcp_handleifa(int type, struct interface *ifp,
+dhcp_handleifa(int cmd, struct interface *ifp,
        const struct in_addr *addr,
        const struct in_addr *net,
-       const struct in_addr *dst)
+       const struct in_addr *dst,
+       __unused int flags)
 {
        struct dhcp_state *state;
        struct if_options *ifo;
@@ -3284,7 +3328,7 @@ dhcp_handleifa(int type, struct interface *ifp,
        if (state == NULL)
                return;
 
-       if (type == RTM_DELADDR) {
+       if (cmd == RTM_DELADDR) {
                if (state->new &&
                    (state->new->yiaddr == addr->s_addr ||
                    (state->new->yiaddr == INADDR_ANY &&
@@ -3299,7 +3343,7 @@ dhcp_handleifa(int type, struct interface *ifp,
                return;
        }
 
-       if (type != RTM_NEWADDR)
+       if (cmd != RTM_NEWADDR)
                return;
 
        ifo = ifp->options;
diff --git a/dhcp.h b/dhcp.h
index 008870d99a58ff66047009f643df104caffc5a5d..85ce06fd05cb07079e2232590c327036c7b972c6 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -274,12 +274,12 @@ ssize_t make_message(struct dhcp_message **, const struct interface *,
 int valid_dhcp_packet(unsigned char *);
 
 void dhcp_handleifa(int, struct interface *,
-    const struct in_addr *, const struct in_addr *, const struct in_addr *);
+    const struct in_addr *, const struct in_addr *, const struct in_addr *,
+    int);
 
 void dhcp_drop(struct interface *, const char *);
 void dhcp_start(struct interface *);
 void dhcp_stop(struct interface *);
-void dhcp_decline(struct interface *);
 void dhcp_discover(void *);
 void dhcp_inform(struct interface *);
 void dhcp_bind(struct interface *, struct arp_state *);
index 775c857da1c8b2515a82333dca0892daf3890162..ac9b6ec96d388a9f7ffc9c836e531d1830ae64b3 100644 (file)
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -713,6 +713,33 @@ if_initrt(struct interface *ifp)
        free(buf);
        return 0;
 }
+
+int
+if_addrflags(const struct in_addr *addr, const struct interface *ifp)
+{
+#ifdef SIOCGIFAFLAG_IN
+       int s, flags;
+       struct ifreq ifr;
+       struct sockaddr_in *sin;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       flags = -1;
+       if (s != -1) {
+               memset(&ifr, 0, sizeof(ifr));
+               strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
+               sin = (struct sockaddr_in *)(void *)&ifr.ifr_addr;
+               sin->sin_family = AF_INET;
+               sin->sin_addr = *addr;
+               if (ioctl(s, SIOCGIFAFLAG_IN, &ifr) != -1)
+                       flags = ifr.ifr_addrflags;
+               close(s);
+       }
+       return flags;
+#else
+       errno = ENOTSUP;
+       return 0;
+#endif
+}
 #endif
 
 #ifdef INET6
@@ -1127,6 +1154,8 @@ if_managelink(struct dhcpcd_ctx *ctx)
        struct rt6 rt6;
        struct in6_addr ia6, net6;
        struct sockaddr_in6 *sin6;
+#endif
+#if (defined(INET) && defined(IN_IFF_TENTATIVE)) || defined(INET6)
        int ifa_flags;
 #endif
 
@@ -1261,9 +1290,15 @@ if_managelink(struct dhcpcd_ctx *ctx)
                                COPYOUT(rt.dest, rti_info[RTAX_IFA]);
                                COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
                                COPYOUT(rt.gate, rti_info[RTAX_BRD]);
+                               if (rtm->rtm_type == RTM_NEWADDR) {
+                                       ifa_flags = if_addrflags(&rt.dest, ifp);
+                                       if (ifa_flags == -1)
+                                               break;
+                               } else
+                                       ifa_flags = 0;
                                ipv4_handleifa(ctx, rtm->rtm_type,
                                    NULL, ifp->name,
-                                   &rt.dest, &rt.net, &rt.gate);
+                                   &rt.dest, &rt.net, &rt.gate, ifa_flags);
                                break;
 #endif
 #ifdef INET6
index ca89bae1530f4b7dab5eda8f41b75aa9f417b229..f99de8d946dacf25d95b35589da0c5564a85670d 100644 (file)
@@ -1412,6 +1412,15 @@ if_initrt(struct interface *ifp)
        return send_netlink(ifp->ctx, ifp,
            NETLINK_ROUTE, &nlm.hdr, &_if_initrt);
 }
+
+int
+if_addrflags(__unused const struct in_addr *addr,
+    __unused const struct interface *ifp)
+{
+
+       /* Linux has no support for IPv4 address flags */
+       return 0;
+}
 #endif
 
 #ifdef INET6
diff --git a/if.c b/if.c
index e297c305f0c9cea091a44395d89f45fcaf79f62b..c2cc4ae2d3580157f72f99f55beae241153cd18d 100644 (file)
--- a/if.c
+++ b/if.c
@@ -490,10 +490,11 @@ if_discover(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
                                    (void *)ifa->ifa_dstaddr;
                        else
                                dst = NULL;
+                       ifa_flags = if_addrflags(&addr->sin_addr, ifp);
                        ipv4_handleifa(ctx, RTM_NEWADDR, ifs, ifa->ifa_name,
                                &addr->sin_addr,
                                &net->sin_addr,
-                               dst ? &dst->sin_addr : NULL);
+                               dst ? &dst->sin_addr : NULL, ifa_flags);
                        break;
 #endif
 #ifdef INET6
diff --git a/if.h b/if.h
index 4efc63641d5c1c35d49d12c5e99a744a4ffb74e2..8330ad7528014dd85904b66fa4712f25c09d44b5 100644 (file)
--- a/if.h
+++ b/if.h
@@ -31,6 +31,9 @@
 #include <net/if.h>
 #include <net/route.h>         /* for RTM_ADD et all */
 #include <netinet/in.h>
+#ifdef BSD
+#include <netinet/in_var.h>    /* for IN_IFF_TENTATIVE et all */
+#endif
 
 /* Some systems have route metrics.
  * OpenBSD route priority is not this. */
@@ -125,6 +128,8 @@ int if_address(const struct interface *,
 #define if_deladdress(ifp, addr, net)          \
        if_address(ifp, addr, net, NULL, -1)
 
+int if_addrflags(const struct in_addr *, const struct interface *);
+
 int if_route(unsigned char, const struct rt *rt);
 int if_initrt(struct interface *);
 #endif
diff --git a/ipv4.c b/ipv4.c
index 9c5162c96c9681917eb7cf5b63090d0d33b198ed..d49984a01ebc2fa9101c6a45f756553323c7889d 100644 (file)
--- a/ipv4.c
+++ b/ipv4.c
@@ -800,26 +800,82 @@ ipv4_getstate(struct interface *ifp)
 static int
 ipv4_addaddr(struct interface *ifp, const struct dhcp_lease *lease)
 {
-       int r;
+       struct ipv4_state *state;
+       struct ipv4_addr *ia;
 
+       if ((state = ipv4_getstate(ifp)) == NULL) {
+               logger(ifp->ctx, LOG_ERR, "%s: ipv4_getstate: %m", __func__);
+               return -1;
+       }
        if (ifp->options->options & DHCPCD_NOALIAS) {
-               struct ipv4_state *state;
-               struct ipv4_addr *ap, *apn;
+               struct ipv4_addr *ian;
 
-               state = IPV4_STATE(ifp);
-               TAILQ_FOREACH_SAFE(ap, &state->addrs, next, apn) {
-                       if (ap->addr.s_addr != lease->addr.s_addr)
-                               delete_address1(ifp, &ap->addr, &ap->net);
+               TAILQ_FOREACH_SAFE(ia, &state->addrs, next, ian) {
+                       if (ia->addr.s_addr != lease->addr.s_addr)
+                               delete_address1(ifp, &ia->addr, &ia->net);
                }
        }
 
+       if ((ia = malloc(sizeof(*ia))) == NULL) {
+               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
+               return -1;
+       }
+
        logger(ifp->ctx, LOG_DEBUG, "%s: adding IP address %s/%d",
            ifp->name, inet_ntoa(lease->addr),
            inet_ntocidr(lease->net));
-       r = if_addaddress(ifp, &lease->addr, &lease->net, &lease->brd);
-       if (r == -1 && errno != EEXIST)
-               logger(ifp->ctx, LOG_ERR, "%s: if_addaddress: %m", __func__);
-       return r;
+       if (if_addaddress(ifp, &lease->addr, &lease->net, &lease->brd) == -1) {
+               if (errno != EEXIST)
+                       logger(ifp->ctx, LOG_ERR, "%s: if_addaddress: %m",
+                           __func__);
+               free(ia);
+               return -1;
+       }
+
+       ia->iface = ifp;
+       ia->addr = lease->addr;
+       ia->net = lease->net;
+#ifdef IN_IFF_TENTATIVE
+       ia->addr_flags = IN_IFF_TENTATIVE;
+#endif
+       TAILQ_INSERT_TAIL(&state->addrs, ia, next);
+       return 0;
+}
+
+static void
+ipv4_finalisert(struct interface *ifp)
+{
+       const struct dhcp_state *state = D_CSTATE(ifp);
+
+       /* Find any freshly added routes, such as the subnet route.
+        * We do this because we cannot rely on recieving the kernel
+        * notification right now via our link socket. */
+       if_initrt(ifp);
+       ipv4_buildroutes(ifp->ctx);
+       script_runreason(ifp, state->reason);
+
+       dhcpcd_daemonise(ifp->ctx);
+}
+
+void
+ipv4_finaliseaddr(struct interface *ifp)
+{
+       struct dhcp_state *state = D_STATE(ifp);
+       struct dhcp_lease *lease;
+
+       lease = &state->lease;
+
+       /* Delete the old address if different */
+       if (state->addr.s_addr != lease->addr.s_addr &&
+           state->addr.s_addr != 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;
+       ipv4_finalisert(ifp);
 }
 
 void
@@ -831,7 +887,6 @@ ipv4_applyaddr(void *arg)
        struct dhcp_lease *lease;
        struct if_options *ifo = ifp->options;
        struct ipv4_addr *ap;
-       struct ipv4_state *istate = NULL;
        int r;
 
        if (state == NULL)
@@ -893,7 +948,7 @@ ipv4_applyaddr(void *arg)
                                    ifn->name);
                                state->addr.s_addr = lease->addr.s_addr;
                                state->net.s_addr = lease->net.s_addr;
-                               goto routes;
+                               ipv4_finalisert(ifp);
                        }
                        logger(ifp->ctx, LOG_INFO, "%s: preferring %s on %s",
                            ifn->name,
@@ -930,40 +985,26 @@ ipv4_applyaddr(void *arg)
                r = ipv4_addaddr(ifp, lease);
                if (r == -1 && errno != EEXIST)
                        return;
-               istate = ipv4_getstate(ifp);
-               ap = malloc(sizeof(*ap));
-               ap->iface = ifp;
-               ap->addr = lease->addr;
-               ap->net = lease->net;
-               ap->dst.s_addr = INADDR_ANY;
-               TAILQ_INSERT_TAIL(&istate->addrs, ap, next);
        }
 
-       /* Now delete the old address if different */
-       if (state->addr.s_addr != lease->addr.s_addr &&
-           state->addr.s_addr != 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;
+#ifdef IN_IFF_TENTATIVE
+       ap = ipv4_iffindaddr(ifp, &lease->addr, NULL);
+       if (ap == NULL) {
+               logger(ifp->ctx, LOG_ERR, "%s: added address vanished",
+                   ifp->name);
+               return;
+       } else if (ap->addr_flags & IN_IFF_TENTATIVE)
+               return;
+#endif
 
-routes:
-       /* Find any freshly added routes, such as the subnet route.
-        * We do this because we cannot rely on recieving the kernel
-        * notification right now via our link socket. */
-       if_initrt(ifp);
-       ipv4_buildroutes(ifp->ctx);
-       script_runreason(ifp, state->reason);
+       ipv4_finaliseaddr(ifp);
 }
 
 void
 ipv4_handleifa(struct dhcpcd_ctx *ctx,
-    int type, struct if_head *ifs, const char *ifname,
+    int cmd, struct if_head *ifs, const char *ifname,
     const struct in_addr *addr, const struct in_addr *net,
-    const struct in_addr *dst)
+    const struct in_addr *dst, int flags)
 {
        struct interface *ifp;
        struct ipv4_state *state;
@@ -979,44 +1020,39 @@ ipv4_handleifa(struct dhcpcd_ctx *ctx,
                errno = EINVAL;
                return;
        }
-
-       TAILQ_FOREACH(ifp, ifs, next) {
-               if (strcmp(ifp->name, ifname) == 0)
-                       break;
-       }
-       if (ifp == NULL) {
-               errno = ESRCH;
+       if ((ifp = if_find(ifs, ifname)) == NULL)
                return;
-       }
-       state = ipv4_getstate(ifp);
-       if (state == NULL) {
+       if ((state = ipv4_getstate(ifp)) == NULL) {
                errno = ENOENT;
                return;
        }
 
        ap = ipv4_iffindaddr(ifp, addr, net);
-       if (type == RTM_NEWADDR && ap == NULL) {
-               ap = malloc(sizeof(*ap));
+       if (cmd == RTM_NEWADDR) {
                if (ap == NULL) {
-                       logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-                       return;
+                       if ((ap = malloc(sizeof(*ap))) == NULL) {
+                               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
+                               return;
+                       }
+                       ap->iface = ifp;
+                       ap->addr = *addr;
+                       ap->net = *net;
+                       if (dst)
+                               ap->dst.s_addr = dst->s_addr;
+                       else
+                               ap->dst.s_addr = INADDR_ANY;
+                       TAILQ_INSERT_TAIL(&state->addrs, ap, next);
+               }
+               ap->addr_flags = flags;
+       } else if (cmd == RTM_DELADDR) {
+               if (ap) {
+                       TAILQ_REMOVE(&state->addrs, ap, next);
+                       free(ap);
                }
-               ap->iface = ifp;
-               ap->addr.s_addr = addr->s_addr;
-               ap->net.s_addr = net->s_addr;
-               if (dst)
-                       ap->dst.s_addr = dst->s_addr;
-               else
-                       ap->dst.s_addr = INADDR_ANY;
-               TAILQ_INSERT_TAIL(&state->addrs, ap, next);
-       } else if (type == RTM_DELADDR) {
-               if (ap == NULL)
-                       return;
-               TAILQ_REMOVE(&state->addrs, ap, next);
-               free(ap);
        }
 
-       dhcp_handleifa(type, ifp, addr, net, dst);
+       dhcp_handleifa(cmd, ifp, addr, net, dst, flags);
+       arp_handleifa(cmd, ifp, addr, flags);
 }
 
 void
diff --git a/ipv4.h b/ipv4.h
index 2b7f64da55e5c0190cd7b9155498c237d97855e9..a33e6737a10df881c36e9f4848e4762d9085c7fc 100644 (file)
--- a/ipv4.h
+++ b/ipv4.h
@@ -50,6 +50,7 @@ struct ipv4_addr {
        struct in_addr net;
        struct in_addr dst;
        struct interface *iface;
+       int addr_flags;
 };
 TAILQ_HEAD(ipv4_addrhead, ipv4_addr);
 
@@ -75,6 +76,7 @@ int ipv4_addrexists(struct dhcpcd_ctx *, const struct in_addr *);
 #define STATE_FAKE             0x02
 
 void ipv4_buildroutes(struct dhcpcd_ctx *);
+void ipv4_finaliseaddr(struct interface *);
 void ipv4_applyaddr(void *);
 int ipv4_handlert(struct dhcpcd_ctx *, int, struct rt *);
 void ipv4_freerts(struct rt_head *);
@@ -84,7 +86,8 @@ struct ipv4_addr *ipv4_iffindaddr(struct interface *,
 struct ipv4_addr *ipv4_iffindlladdr(struct interface *);
 struct ipv4_addr *ipv4_findaddr(struct dhcpcd_ctx *, const struct in_addr *);
 void ipv4_handleifa(struct dhcpcd_ctx *, int, struct if_head *, const char *,
-    const struct in_addr *, const struct in_addr *, const struct in_addr *);
+    const struct in_addr *, const struct in_addr *, const struct in_addr *,
+    int);
 
 void ipv4_freeroutes(struct rt_head *);
 
index e6569faa5b163f73d427b02f230d1e2b28b72b7c..5f08980eee2bb631bdb6c6e0b7de9027957b9e71 100644 (file)
--- a/ipv4ll.c
+++ b/ipv4ll.c
@@ -105,11 +105,17 @@ static void
 ipv4ll_probed(struct arp_state *astate)
 {
        struct dhcp_state *state = D_STATE(astate->iface);
-       struct dhcp_message *offer;
+
+       if (state->state == DHS_IPV4LL_BOUND) {
+               ipv4_finaliseaddr(astate->iface);
+               return;
+       }
 
        if (state->state != DHS_BOUND) {
+               struct dhcp_message *offer;
+
                /* A DHCP lease could have already been offered.
-                * Backup and replace once the IPv4LL addres is bound */
+                * Backup and replace once the IPv4LL address is bound */
                offer = state->offer;
                state->offer = ipv4ll_make_lease(astate->addr.s_addr);
                if (state->offer == NULL)
@@ -133,7 +139,11 @@ static void
 ipv4ll_probe(void *arg)
 {
 
+#ifdef IN_IFF_TENTATIVE
+       ipv4ll_probed(arg);
+#else
        arp_probe(arg);
+#endif
 }
 
 static void
@@ -144,13 +154,14 @@ ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
 
        fail = 0;
        /* RFC 3927 2.2.1, Probe Conflict Detection */
-       if (amsg->sip.s_addr == astate->addr.s_addr ||
-           (amsg->sip.s_addr == 0 && amsg->tip.s_addr == astate->addr.s_addr))
+       if (amsg == NULL ||
+           (amsg->sip.s_addr == astate->addr.s_addr ||
+           (amsg->sip.s_addr == 0 && amsg->tip.s_addr == astate->addr.s_addr)))
                fail = astate->addr.s_addr;
 
        /* RFC 3927 2.5, Conflict Defense */
        if (IN_LINKLOCAL(htonl(state->addr.s_addr)) &&
-           amsg->sip.s_addr == state->addr.s_addr)
+           amsg && amsg->sip.s_addr == state->addr.s_addr)
                fail = state->addr.s_addr;
 
        if (fail == 0)
@@ -254,7 +265,11 @@ ipv4ll_start(void *arg)
        }
        if (astate->addr.s_addr == INADDR_ANY)
                astate->addr.s_addr = ipv4ll_pick_addr(astate);
+#ifdef IN_IFF_TENTATIVE
+       ipv4ll_probed(astate);
+#else
        arp_probe(astate);
+#endif
 }
 
 void