]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Stop making IPv4LL into a DHCP lease and treat it independantly from any
authorRoy Marples <roy@marples.name>
Fri, 12 Jun 2015 19:21:32 +0000 (19:21 +0000)
committerRoy Marples <roy@marples.name>
Fri, 12 Jun 2015 19:21:32 +0000 (19:21 +0000)
DHCP lease.
This allows us to manage IPv4LL and DHCP at the same time a lot easier.

12 files changed:
arp.c
dhcp.c
dhcp.h
dhcpcd.c
dhcpcd.h
if.c
if.h
ipv4.c
ipv4.h
ipv4ll.c
ipv4ll.h
script.c

diff --git a/arp.c b/arp.c
index c53e4c7b0e70fad77a77e6a44209450bbc3c5cee..e381b42ebfeca876a3f9c26ede657368342d3a7f 100644 (file)
--- a/arp.c
+++ b/arp.c
@@ -348,10 +348,6 @@ arp_free(struct arp_state *astate)
                eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate);
                state = D_STATE(astate->iface);
                TAILQ_REMOVE(&state->arp_states, astate, next);
-               if (state->arp_ipv4ll == astate) {
-                       ipv4ll_stop(astate->iface);
-                       state->arp_ipv4ll = NULL;
-               }
                free(astate);
        }
 }
diff --git a/dhcp.c b/dhcp.c
index 749ca7031b03b5065b433e24efffc66c34bed145..b250db778cd77abab0a9bfa671cdad626f104138 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -1782,8 +1782,7 @@ dhcp_discover(void *arg)
        if (ifo->fallback)
                eloop_timeout_add_sec(ifp->ctx->eloop,
                    ifo->reboot, dhcp_fallback, ifp);
-       else if (ifo->options & DHCPCD_IPV4LL &&
-           !IN_LINKLOCAL(htonl(state->addr.s_addr)))
+       else if (ifo->options & DHCPCD_IPV4LL)
                eloop_timeout_add_sec(ifp->ctx->eloop,
                    ifo->reboot, ipv4ll_start, ifp);
        if (ifo->options & DHCPCD_REQUEST)
@@ -1895,6 +1894,10 @@ dhcp_arp_probed(struct arp_state *astate)
                return;
        }
        dhcp_close(astate->iface);
+
+       /* Stop IPv4LL now we have a working DHCP address */
+       ipv4ll_drop(astate->iface);
+
        eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate->iface);
 #ifdef IN_IFF_TENTATIVE
        logger(astate->iface->ctx, LOG_DEBUG, "%s: DAD completed for %s",
@@ -1980,7 +1983,6 @@ dhcp_bind(struct interface *ifp, struct arp_state *astate)
        struct dhcp_state *state = D_STATE(ifp);
        struct if_options *ifo = ifp->options;
        struct dhcp_lease *lease = &state->lease;
-       uint8_t ipv4ll = 0;
 
        if (state->state == DHS_BOUND)
                goto applyaddr;
@@ -1999,12 +2001,6 @@ dhcp_bind(struct interface *ifp, struct arp_state *astate)
                    inet_ntocidr(lease->net));
                lease->leasetime = ~0U;
                state->reason = "STATIC";
-       } else if (state->new->cookie != htonl(MAGIC_COOKIE)) {
-               logger(ifp->ctx, LOG_INFO, "%s: using IPv4LL address %s",
-                   ifp->name, inet_ntoa(lease->addr));
-               lease->leasetime = ~0U;
-               state->reason = "IPV4LL";
-               ipv4ll = 1;
        } else if (ifo->options & DHCPCD_INFORM) {
                if (ifo->req_addr.s_addr != 0)
                        lease->addr.s_addr = ifo->req_addr.s_addr;
@@ -2121,7 +2117,7 @@ applyaddr:
 #ifdef IN_IFF_TENTATIVE
                if (astate)
                        arp_free_but(astate);
-               else if (!ipv4ll)
+               else
                        arp_close(ifp);
 #else
                if (state->added) {
@@ -2132,10 +2128,9 @@ applyaddr:
                        }
                        if (astate) {
                                arp_announce(astate);
-                               if (!ipv4ll)
-                                       arp_free_but(astate);
+                               arp_free_but(astate);
                        }
-               } else if (!ipv4ll)
+               } else
                        arp_close(ifp);
 #endif
        }
@@ -2299,8 +2294,7 @@ dhcp_reboot(struct interface *ifp)
        eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
 
        /* Need to add this before dhcp_expire and friends. */
-       if (!ifo->fallback && ifo->options & DHCPCD_IPV4LL &&
-           !IN_LINKLOCAL(htonl(state->addr.s_addr)))
+       if (!ifo->fallback && ifo->options & DHCPCD_IPV4LL)
                eloop_timeout_add_sec(ifp->ctx->eloop,
                    ifo->reboot, ipv4ll_start, ifp);
 
@@ -2657,24 +2651,13 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
                        case 0:
                                log_dhcp(LOG_WARNING, "IPv4LL disabled from",
                                    ifp, dhcp, from);
-                               dhcp_drop(ifp, "EXPIRE");
+                               ipv4ll_drop(ifp);
                                arp_close(ifp);
-                               eloop_timeout_delete(ifp->ctx->eloop,
-                                   NULL, ifp);
-                               eloop_timeout_add_sec(ifp->ctx->eloop,
-                                   DHCP_MAX, dhcp_discover,
-                                   ifp);
                                break;
                        case 1:
                                log_dhcp(LOG_WARNING, "IPv4LL enabled from",
                                    ifp, dhcp, from);
-                               eloop_timeout_delete(ifp->ctx->eloop,
-                                   NULL, ifp);
-                               if (IN_LINKLOCAL(htonl(state->addr.s_addr)))
-                                       eloop_timeout_add_sec(ifp->ctx->eloop,
-                                           DHCP_MAX, dhcp_discover, ifp);
-                               else
-                                       ipv4ll_start(ifp);
+                               ipv4ll_start(ifp);
                                break;
                        default:
                                logger(ifp->ctx, LOG_ERR,
@@ -2682,6 +2665,9 @@ dhcp_handledhcp(struct interface *ifp, struct dhcp_message **dhcpp,
                                    ifp->name, tmp);
                                break;
                        }
+                       eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+                       eloop_timeout_add_sec(ifp->ctx->eloop,
+                           DHCP_MAX, dhcp_discover, ifp);
                }
                return;
        }
diff --git a/dhcp.h b/dhcp.h
index 85ce06fd05cb07079e2232590c327036c7b972c6..1ac6fa1aa3b2373521cc2b4a9a12a601adca10b4 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -231,11 +231,6 @@ struct dhcp_state {
        struct arp_statehead arp_states;
 
        size_t arping_index;
-
-       struct arp_state *arp_ipv4ll;
-       unsigned int conflicts;
-       time_t defend;
-       char randomstate[128];
 };
 
 #define D_STATE(ifp)                                                          \
@@ -258,7 +253,6 @@ void dhcp_printoptions(const struct dhcpcd_ctx *,
 int get_option_addr(struct dhcpcd_ctx *,struct in_addr *,
     const struct dhcp_message *, uint8_t);
 #define IS_BOOTP(i, m) ((m) &&                                             \
-           !IN_LINKLOCAL(htonl((m)->yiaddr)) &&                            \
            get_option_uint8((i)->ctx, NULL, (m), DHO_MESSAGETYPE) == -1)
 struct rt_head *get_option_routes(struct interface *,
     const struct dhcp_message *);
index 94a424337f663d130edf79b38f1215419c094c7d..e68ed1051323da3f8582e417f4d1eb274052d583 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -61,6 +61,7 @@ const char dhcpcd_copyright[] = "Copyright (c) 2006-2015 Roy Marples";
 #include "if.h"
 #include "if-options.h"
 #include "ipv4.h"
+#include "ipv4ll.h"
 #include "ipv6.h"
 #include "ipv6nd.h"
 #include "script.h"
@@ -380,6 +381,7 @@ dhcpcd_drop(struct interface *ifp, int stop)
        dhcp6_drop(ifp, stop ? NULL : "EXPIRE6");
        ipv6nd_drop(ifp);
        ipv6_drop(ifp);
+       ipv4ll_drop(ifp);
        dhcp_drop(ifp, stop ? "STOP" : "EXPIRE");
        arp_close(ifp);
 }
index c2f9befe7f25c195d7132b8f9464c1ff9366ea1b..f79c01d3d499b9be82ea8f91c7bf7d360a428233 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
 #define LINK_DOWN      -1
 
 #define IF_DATA_IPV4   0
-#define IF_DATA_DHCP   1
-#define IF_DATA_IPV6   2
-#define IF_DATA_IPV6ND 3
-#define IF_DATA_DHCP6  4
-#define IF_DATA_MAX    5
+#define IF_DATA_IPV4LL 1
+#define IF_DATA_DHCP   2
+#define IF_DATA_IPV6   3
+#define IF_DATA_IPV6ND 4
+#define IF_DATA_DHCP6  5
+#define IF_DATA_MAX    6
 
 /* If the interface does not support carrier status (ie PPP),
  * dhcpcd can poll it for the relevant flags periodically */
diff --git a/if.c b/if.c
index 201cc48ccf0c5e0c0483f13b018d7a4a930f9d8b..46857b02c24c998ba069aede3328cc9fa8c9b111 100644 (file)
--- a/if.c
+++ b/if.c
@@ -67,6 +67,7 @@
 #include "if.h"
 #include "if-options.h"
 #include "ipv4.h"
+#include "ipv4ll.h"
 #include "ipv6nd.h"
 
 #ifdef __QNX__
@@ -80,8 +81,9 @@ if_free(struct interface *ifp)
 
        if (ifp == NULL)
                return;
-       ipv4_free(ifp);
+       ipv4ll_free(ifp);
        dhcp_free(ifp);
+       ipv4_free(ifp);
        dhcp6_free(ifp);
        ipv6nd_free(ifp);
        ipv6_free(ifp);
diff --git a/if.h b/if.h
index d9ef718f6e7d3b7ee0f1c87c5bfb1ea39e8d0ae8..123fc37a5fef52318e1de74996931699d9f054f1 100644 (file)
--- a/if.h
+++ b/if.h
            ((addr & IN_CLASSB_NET) == 0xc0a80000))
 #endif
 
-#define LINKLOCAL_ADDR 0xa9fe0000
-#define LINKLOCAL_MASK IN_CLASSB_NET
-#define LINKLOCAL_BRDC (LINKLOCAL_ADDR | ~LINKLOCAL_MASK)
-
-#ifndef IN_LINKLOCAL
-# define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
-#endif
-
 #define RAW_EOF                        1 << 0
 #define RAW_PARTIALCSUM                2 << 0
 
diff --git a/ipv4.c b/ipv4.c
index d5855a6aa7ff053d3c64b0f04e07b6bcbeff63f7..657a3089277d36bf615a4d5a652622a5af93768f 100644 (file)
--- a/ipv4.c
+++ b/ipv4.c
@@ -47,6 +47,7 @@
 #include "if.h"
 #include "if-options.h"
 #include "ipv4.h"
+#include "ipv4ll.h"
 #include "script.h"
 
 #define IPV4_LOOPBACK_ROUTE
@@ -633,6 +634,8 @@ ipv4_buildroutes(struct dhcpcd_ctx *ctx)
                        continue;
                dnr = get_routes(ifp);
                dnr = add_subnet_route(dnr, ifp);
+               if ((rt = ipv4ll_subnet_route(ifp)) != NULL)
+                       TAILQ_INSERT_HEAD(dnr, rt, next);
 #ifdef IPV4_LOOPBACK_ROUTE
                dnr = add_loopback_route(dnr, ifp);
 #endif
@@ -782,59 +785,67 @@ ipv4_getstate(struct interface *ifp)
        return state;
 }
 
-static int
-ipv4_addaddr(struct interface *ifp, const struct dhcp_lease *lease)
+struct ipv4_addr *
+ipv4_addaddr(struct interface *ifp, const struct in_addr *addr,
+    const struct in_addr *mask, const struct in_addr *bcast)
 {
        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__);
-               return -1;
+               return NULL;
        }
        if (ifp->options->options & DHCPCD_NOALIAS) {
                struct ipv4_addr *ian;
 
                TAILQ_FOREACH_SAFE(ia, &state->addrs, next, ian) {
-                       if (ia->addr.s_addr != lease->addr.s_addr)
+                       if (ia->addr.s_addr != addr->s_addr)
                                ipv4_deladdr(ifp, &ia->addr, &ia->net);
                }
        }
 
        if ((ia = malloc(sizeof(*ia))) == NULL) {
                logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-               return -1;
+               return NULL;
        }
 
        logger(ifp->ctx, LOG_DEBUG, "%s: adding IP address %s/%d",
-           ifp->name, inet_ntoa(lease->addr),
-           inet_ntocidr(lease->net));
-       if (if_addaddress(ifp, &lease->addr, &lease->net, &lease->brd) == -1) {
+           ifp->name, inet_ntoa(*addr), inet_ntocidr(*mask));
+       if (if_addaddress(ifp, addr, mask, bcast) == -1) {
                if (errno != EEXIST)
                        logger(ifp->ctx, LOG_ERR, "%s: if_addaddress: %m",
                            __func__);
                free(ia);
-               return -1;
+               return NULL;
        }
 
        ia->iface = ifp;
-       ia->addr = lease->addr;
-       ia->net = lease->net;
+       ia->addr = *addr;
+       ia->net = *mask;
 #ifdef IN_IFF_TENTATIVE
        ia->addr_flags = IN_IFF_TENTATIVE;
 #endif
        TAILQ_INSERT_TAIL(&state->addrs, ia, next);
+       return ia;
+}
 
-       dstate = D_STATE(ifp);
+static int
+ipv4_daddaddr(struct interface *ifp, const struct dhcp_lease *lease)
+{
+       struct dhcp_state *state;
+
+       if (ipv4_addaddr(ifp, &lease->addr, &lease->net, &lease->brd) == NULL)
+               return -1;
+
+       state = D_STATE(ifp);
 #ifdef IN_IFF_TENTATIVE
-       dstate->added |= STATE_ADDED;
+       state->added |= STATE_ADDED;
 #else
-       dstate->added = STATE_ADDED;
+       state->added = STATE_ADDED;
 #endif
-       dstate->defend = 0;
-       dstate->addr.s_addr = lease->addr.s_addr;
-       dstate->net.s_addr = lease->net.s_addr;
+       state->addr.s_addr = lease->addr.s_addr;
+       state->net.s_addr = lease->net.s_addr;
 
        return 0;
 }
@@ -900,7 +911,7 @@ ipv4_preferanother(struct interface *ifp)
                        if (ifn->options->options & DHCPCD_ARP)
                                dhcp_bind(ifn, NULL);
                        else {
-                               ipv4_addaddr(ifn, &nstate->lease);
+                               ipv4_daddaddr(ifn, &nstate->lease);
                                nstate->added = STATE_ADDED;
                        }
                        break;
@@ -994,7 +1005,7 @@ ipv4_applyaddr(void *arg)
                    ifp->name, inet_ntoa(lease->addr),
                    inet_ntocidr(lease->net));
        else {
-               r = ipv4_addaddr(ifp, lease);
+               r = ipv4_daddaddr(ifp, lease);
                if (r == -1 && errno != EEXIST)
                        return;
        }
diff --git a/ipv4.h b/ipv4.h
index 53d279509622ecf2d140bb8f988c52eb16e8eaa0..83ef4a83382af6e34e4427beb32b9d3209c0d2c8 100644 (file)
--- a/ipv4.h
+++ b/ipv4.h
@@ -83,9 +83,11 @@ int ipv4_hasaddr(const struct interface *);
 
 void ipv4_buildroutes(struct dhcpcd_ctx *);
 void ipv4_finaliseaddr(struct interface *);
-int ipv4_deladdr(struct interface *ifp, const struct in_addr *,
+int ipv4_deladdr(struct interface *, const struct in_addr *,
     const struct in_addr *);
-int ipv4_preferanother(struct interface *ifp);
+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 *);
 int ipv4_handlert(struct dhcpcd_ctx *, int, struct rt *);
 void ipv4_freerts(struct rt_head *);
index 5f08980eee2bb631bdb6c6e0b7de9027957b9e71..ee75006f8eae10f3f4938b0d55a70b45a18a2031 100644 (file)
--- a/ipv4ll.c
+++ b/ipv4ll.c
@@ -25,6 +25,7 @@
  * SUCH DAMAGE.
  */
 
+#include <assert.h>
 #include <errno.h>
 #include <signal.h>
 #include <stdlib.h>
 #include "if.h"
 #include "if-options.h"
 #include "ipv4ll.h"
+#include "script.h"
 
-static struct dhcp_message *
-ipv4ll_make_lease(uint32_t addr)
-{
-       uint32_t u32;
-       struct dhcp_message *dhcp;
-       uint8_t *p;
-
-       dhcp = calloc(1, sizeof(*dhcp));
-       if (dhcp == NULL)
-               return NULL;
-       /* Put some LL options in */
-       dhcp->yiaddr = addr;
-       p = dhcp->options;
-       *p++ = DHO_SUBNETMASK;
-       *p++ = sizeof(u32);
-       u32 = htonl(LINKLOCAL_MASK);
-       memcpy(p, &u32, sizeof(u32));
-       p += sizeof(u32);
-       *p++ = DHO_BROADCAST;
-       *p++ = sizeof(u32);
-       u32 = htonl(LINKLOCAL_BRDC);
-       memcpy(p, &u32, sizeof(u32));
-       p += sizeof(u32);
-       *p++ = DHO_END;
-
-       return dhcp;
-}
+static const struct in_addr inaddr_llmask = { htonl(LINKLOCAL_MASK) };
+static const struct in_addr inaddr_llbcast = { htonl(LINKLOCAL_BRDC) };
 
 static in_addr_t
 ipv4ll_pick_addr(const struct arp_state *astate)
 {
-       in_addr_t addr;
-       struct interface *ifp;
-       const struct dhcp_state *state;
+       struct in_addr addr;
 
-       for (;;) {
+       do {
                /* RFC 3927 Section 2.1 states that the first 256 and
                 * last 256 addresses are reserved for future use.
                 * See ipv4ll_start for why we don't use arc4_random. */
-               addr = ntohl(LINKLOCAL_ADDR |
+               addr.s_addr = ntohl(LINKLOCAL_ADDR |
                    ((uint32_t)(random() % 0xFD00) + 0x0100));
 
                /* No point using a failed address */
-               if (addr == astate->failed.s_addr)
+               if (addr.s_addr == astate->failed.s_addr)
                        continue;
-
                /* Ensure we don't have the address on another interface */
-               TAILQ_FOREACH(ifp, astate->iface->ctx->ifaces, next) {
-                       state = D_CSTATE(ifp);
-                       if (state && state->addr.s_addr == addr)
-                               break;
-               }
+       } while (ipv4_findaddr(astate->iface->ctx, &addr) != NULL);
+
+       return addr.s_addr;
+}
 
-               /* Yay, this should be a unique and workable IPv4LL address */
-               if (ifp == NULL)
-                       break;
+struct rt *
+ipv4ll_subnet_route(const struct interface *ifp)
+{
+       const struct ipv4ll_state *state;
+       struct rt *rt;
+
+       assert(ifp != NULL);
+       if ((state = IPV4LL_CSTATE(ifp)) == NULL ||
+           state->addr.s_addr == INADDR_ANY)
+               return NULL;
+
+       if ((rt = malloc(sizeof(*rt))) == NULL) {
+               logger(ifp->ctx, LOG_ERR, "%s: malloc: %m", __func__);
+               return NULL;
        }
-       return addr;
+       rt->iface = ifp;
+       rt->dest.s_addr = state->addr.s_addr & inaddr_llmask.s_addr;
+       rt->net = inaddr_llmask;
+       rt->gate.s_addr = INADDR_ANY;
+       return rt;
+}
+
+ssize_t
+ipv4ll_env(char **env, const char *prefix, const struct interface *ifp)
+{
+       const struct ipv4ll_state *state;
+       const char *pf = prefix == NULL ? "" : "_";
+       struct in_addr netnum;
+
+       assert(ifp != NULL);
+       if ((state = IPV4LL_CSTATE(ifp)) == NULL)
+               return 0;
+
+       if (env == NULL)
+               return 5;
+
+       /* Emulate a DHCP environment */
+       if (asprintf(&env[0], "%s%sip_address=%s",
+           prefix, pf, inet_ntoa(state->addr)) == -1)
+               return -1;
+       if (asprintf(&env[1], "%s%ssubnet_mask=%s",
+           prefix, pf, inet_ntoa(inaddr_llmask)) == -1)
+               return -1;
+       if (asprintf(&env[2], "%s%ssubnet_cidr=%d",
+           prefix, pf, inet_ntocidr(inaddr_llmask)) == -1)
+               return -1;
+       if (asprintf(&env[3], "%s%sbroadcast_address=%s",
+           prefix, pf, inet_ntoa(inaddr_llbcast)) == -1)
+               return -1;
+       netnum.s_addr = state->addr.s_addr & inaddr_llmask.s_addr;
+       if (asprintf(&env[4], "%s%snetwork_number=%s",
+           prefix, pf, inet_ntoa(inaddr_llbcast)) == -1)
+               return -1;
+       return 5;
 }
 
 static void
 ipv4ll_probed(struct arp_state *astate)
 {
-       struct dhcp_state *state = D_STATE(astate->iface);
-
-       if (state->state == DHS_IPV4LL_BOUND) {
-               ipv4_finaliseaddr(astate->iface);
+       struct interface *ifp;
+       struct ipv4ll_state *state;
+       struct ipv4_addr *ia;
+
+       assert(astate != NULL);
+       assert(astate->iface != NULL);
+
+       ifp = astate->iface;
+       state = IPV4LL_STATE(ifp);
+       assert(state != NULL);
+
+       logger(ifp->ctx, LOG_INFO, "%s: using IPv4LL address %s",
+           ifp->name, inet_ntoa(astate->addr));
+       ia = ipv4_iffindaddr(ifp, &astate->addr, &inaddr_llmask);
+       if (ia == NULL)
+               ia = ipv4_addaddr(ifp, &astate->addr,
+                   &inaddr_llmask, &inaddr_llbcast);
+       if (ia == NULL)
                return;
-       }
-
-       if (state->state != DHS_BOUND) {
-               struct dhcp_message *offer;
-
-               /* A DHCP lease could have already been offered.
-                * 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)
-                       logger(astate->iface->ctx, LOG_ERR, "%s: %m", __func__);
-               else
-                       dhcp_bind(astate->iface, astate);
-               state->offer = offer;
-       }
+#ifdef IN_IFF_NOTREADY
+       if (ia->addr_flags & IN_IFF_NOTREADY)
+               return;
+       logger(ifp->ctx, LOG_DEBUG, "%s: DAD completed for %s",
+           ifp->name, inet_ntoa(astate->addr));
+#endif
+       state->addr = astate->addr;
+       state->defend = 0;
+       script_runreason(ifp, "IPV4LL");
 }
 
 static void
 ipv4ll_announced(struct arp_state *astate)
 {
-       struct dhcp_state *state = D_STATE(astate->iface);
+       struct ipv4ll_state *state = IPV4LL_STATE(astate->iface);
 
        state->conflicts = 0;
        /* Need to keep the arp state so we can defend our IP. */
@@ -149,9 +179,14 @@ ipv4ll_probe(void *arg)
 static void
 ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
 {
-       struct dhcp_state *state = D_STATE(astate->iface);
+       struct ipv4ll_state *state;
        in_addr_t fail;
 
+       assert(astate != NULL);
+       assert(astate->iface != NULL);
+       state = IPV4LL_STATE(astate->iface);
+       assert(state != NULL);
+
        fail = 0;
        /* RFC 3927 2.2.1, Probe Conflict Detection */
        if (amsg == NULL ||
@@ -205,12 +240,24 @@ ipv4ll_conflicted(struct arp_state *astate, const struct arp_msg *amsg)
 void
 ipv4ll_start(void *arg)
 {
-       struct interface *ifp = arg;
-       struct dhcp_state *state = D_STATE(ifp);
+       struct interface *ifp;
+       struct ipv4ll_state *state;
        struct arp_state *astate;
-       struct ipv4_addr *ap;
+       struct ipv4_addr *ia;
+
+       assert(arg != NULL);
+       ifp = arg;
+       if ((state = IPV4LL_STATE(ifp)) == NULL) {
+               ifp->if_data[IF_DATA_IPV4LL] = calloc(1, sizeof(*state));
+               if ((state = IPV4LL_STATE(ifp)) == NULL) {
+                       syslog(LOG_ERR, "%s: calloc %m", __func__);
+                       return;
+               }
+
+               state->addr.s_addr = INADDR_ANY;
+       }
 
-       if (state->arp_ipv4ll)
+       if (state->arp != NULL)
                return;
 
        /* RFC 3927 Section 2.1 states that the random number generator
@@ -232,39 +279,37 @@ ipv4ll_start(void *arg)
        if ((astate = arp_new(ifp, NULL)) == NULL)
                return;
 
-       state->arp_ipv4ll = astate;
+       state->arp = astate;
        astate->probed_cb = ipv4ll_probed;
        astate->announced_cb = ipv4ll_announced;
        astate->conflicted_cb = ipv4ll_conflicted;
 
-       if (IN_LINKLOCAL(htonl(state->addr.s_addr))) {
-               astate->addr = state->addr;
-               arp_announce(astate);
-               return;
+       /* Find an existing IPv4LL address and ensure we can work with it. */
+       ia = ipv4_iffindlladdr(ifp);
+#ifdef IN_IFF_TENTATIVE
+       if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) {
+               ipv4_deladdr(ifp, &ia->addr, &ia->net);
+               ia = NULL;
        }
-
-       if (state->offer && IN_LINKLOCAL(ntohl(state->offer->yiaddr))) {
-               astate->addr.s_addr = state->offer->yiaddr;
-               free(state->offer);
-               state->offer = NULL;
-               ap = ipv4_iffindaddr(ifp, &astate->addr, NULL);
-       } else
-               ap = ipv4_iffindlladdr(ifp);
-       if (ap) {
-               astate->addr = ap->addr;
+#endif
+       if (ia != NULL) {
+               astate->addr = ia->addr;
+#ifdef IN_IFF_TENTATIVE
+               if (ia->addr_flags & (IN_IFF_TENTATIVE | IN_IFF_DETACHED)) {
+                       logger(ifp->ctx, LOG_INFO,
+                           "%s: waiting for DAD to complete on %s",
+                           ifp->name, inet_ntoa(ia->addr));
+                       return;
+               }
+#endif
                ipv4ll_probed(astate);
                return;
        }
 
        setstate(state->randomstate);
-       /* We maybe rebooting an IPv4LL address. */
-       if (!IN_LINKLOCAL(htonl(astate->addr.s_addr))) {
-               logger(ifp->ctx, LOG_INFO, "%s: probing for an IPv4LL address",
-                   ifp->name);
-               astate->addr.s_addr = INADDR_ANY;
-       }
-       if (astate->addr.s_addr == INADDR_ANY)
-               astate->addr.s_addr = ipv4ll_pick_addr(astate);
+       logger(ifp->ctx, LOG_INFO, "%s: probing for an IPv4LL address",
+           ifp->name);
+       astate->addr.s_addr = ipv4ll_pick_addr(astate);
 #ifdef IN_IFF_TENTATIVE
        ipv4ll_probed(astate);
 #else
@@ -273,9 +318,29 @@ ipv4ll_start(void *arg)
 }
 
 void
-ipv4ll_stop(struct interface *ifp)
+ipv4ll_freedrop(struct interface *ifp, int drop)
 {
-       struct dhcp_state *state = D_STATE(ifp);
+       struct ipv4ll_state *state;
 
-       eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp_ipv4ll);
+       assert(ifp != NULL);
+       if ((state = IPV4LL_STATE(ifp)) == NULL)
+               return;
+
+       /* Free ARP state first because ipv4_deladdr might also ... */
+       if (state->arp) {
+               eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp);
+               arp_free(state->arp);
+               state->arp = NULL;
+       }
+
+       /* Unlike other protocols, we don't run a script on stopping IPv4LL
+        * because we piggy back on the state of DHCP. */
+       if (drop) {
+               if (state->addr.s_addr != INADDR_ANY) {
+                       ipv4_deladdr(ifp, &state->addr, &inaddr_llmask);
+                       state->addr.s_addr = INADDR_ANY;
+               }
+       }
+       free(state);
+       ifp->if_data[IF_DATA_IPV4LL] = NULL;
 }
index 19349ded852b32065229736b6868bbe68420fa95..9846b8b2e37d7c20c91e931657638b5dc730a288 100644 (file)
--- a/ipv4ll.h
+++ b/ipv4ll.h
 #ifndef IPV4LL_H
 #define IPV4LL_H
 
+#include "arp.h"
+
+#define LINKLOCAL_ADDR 0xa9fe0000
+#define LINKLOCAL_MASK IN_CLASSB_NET
+#define LINKLOCAL_BRDC (LINKLOCAL_ADDR | ~LINKLOCAL_MASK)
+
+#ifndef IN_LINKLOCAL
+# define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
+#endif
+
+struct ipv4ll_state {
+       struct in_addr addr;
+       struct arp_state *arp;
+       unsigned int conflicts;
+       time_t defend;
+       char randomstate[128];
+};
+
+#define IPV4LL_STATE(ifp)                                                     \
+       ((struct ipv4ll_state *)(ifp)->if_data[IF_DATA_IPV4LL])
+#define IPV4LL_CSTATE(ifp)                                                    \
+       ((const struct ipv4ll_state *)(ifp)->if_data[IF_DATA_IPV4LL])
+
+struct rt* ipv4ll_subnet_route(const struct interface *);
+ssize_t ipv4ll_env(char **, const char *, const struct interface *);
 void ipv4ll_start(void *);
 void ipv4ll_claimed(void *);
 void ipv4ll_handle_failure(void *);
-void ipv4ll_stop(struct interface *);
+
+#define ipv4ll_free(ifp) ipv4ll_freedrop((ifp), 0);
+#define ipv4ll_drop(ifp) ipv4ll_freedrop((ifp), 1);
+void ipv4ll_freedrop(struct interface *, int);
 
 #endif
index 4adb261d80ad0e624b96f87deb053d34f1cd14f8..1774793e889d37cf3c0f09c35a4f1ca5a7db71dd 100644 (file)
--- a/script.c
+++ b/script.c
@@ -47,6 +47,7 @@
 #include "dhcp6.h"
 #include "if.h"
 #include "if-options.h"
+#include "ipv4ll.h"
 #include "ipv6nd.h"
 #include "script.h"
 
@@ -232,7 +233,7 @@ make_env(const struct interface *ifp, const char *reason, char ***argv)
        const struct interface *ifp2;
        int af;
 #ifdef INET
-       int dhcp;
+       int dhcp, ipv4ll;
        const struct dhcp_state *state;
 #endif
 #ifdef INET6
@@ -241,7 +242,7 @@ make_env(const struct interface *ifp, const char *reason, char ***argv)
 #endif
 
 #ifdef INET
-       dhcp = 0;
+       dhcp = ipv4ll = 0;
        state = D_STATE(ifp);
 #endif
 #ifdef INET6
@@ -257,6 +258,8 @@ make_env(const struct interface *ifp, const char *reason, char ***argv)
                        ra = 1;
 #endif
 #ifdef INET
+               else if (strcmp(reason, "IPV4LL") == 0)
+                       ipv4ll = 1;
                else
                        dhcp = 1;
 #endif
@@ -449,6 +452,19 @@ make_env(const struct interface *ifp, const char *reason, char ***argv)
 
 dumplease:
 #ifdef INET
+       if (ipv4ll) {
+               n = ipv4ll_env(NULL, NULL, ifp);
+               if (n > 0) {
+                       nenv = realloc(env, sizeof(char *) *
+                           (elen + (size_t)n + 1));
+                       if (nenv == NULL)
+                               goto eexit;
+                       env = nenv;
+                       if ((n = ipv4ll_env(env + elen, "new", ifp)) == -1)
+                               goto eexit;
+                       elen += (size_t)n;
+               }
+       }
        if (dhcp && state && state->new) {
                n = dhcp_env(NULL, NULL, state->new, ifp);
                if (n > 0) {