From: Roy Marples Date: Fri, 12 Jun 2015 19:21:32 +0000 (+0000) Subject: Stop making IPv4LL into a DHCP lease and treat it independantly from any X-Git-Tag: v6.9.1~60 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=accc0bb8716dd3c381d706a95076bcc69a64a17c;p=thirdparty%2Fdhcpcd.git Stop making IPv4LL into a DHCP lease and treat it independantly from any DHCP lease. This allows us to manage IPv4LL and DHCP at the same time a lot easier. --- diff --git a/arp.c b/arp.c index c53e4c7b..e381b42e 100644 --- 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 749ca703..b250db77 100644 --- 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 85ce06fd..1ac6fa1a 100644 --- 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 *); diff --git a/dhcpcd.c b/dhcpcd.c index 94a42433..e68ed105 100644 --- 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); } diff --git a/dhcpcd.h b/dhcpcd.h index c2f9befe..f79c01d3 100644 --- a/dhcpcd.h +++ b/dhcpcd.h @@ -50,11 +50,12 @@ #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 201cc48c..46857b02 100644 --- 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 d9ef718f..123fc37a 100644 --- a/if.h +++ b/if.h @@ -83,14 +83,6 @@ ((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 d5855a6a..657a3089 100644 --- 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 53d27950..83ef4a83 100644 --- 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 *); diff --git a/ipv4ll.c b/ipv4ll.c index 5f08980e..ee75006f 100644 --- a/ipv4ll.c +++ b/ipv4ll.c @@ -25,6 +25,7 @@ * SUCH DAMAGE. */ +#include #include #include #include @@ -40,96 +41,125 @@ #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; } diff --git a/ipv4ll.h b/ipv4ll.h index 19349ded..9846b8b2 100644 --- a/ipv4ll.h +++ b/ipv4ll.h @@ -28,9 +28,37 @@ #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 diff --git a/script.c b/script.c index 4adb261d..1774793e 100644 --- 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) {