Import dhcpcd 6.8.2
This is a simple import of dhcpcd 6.8.2.
SRC URI: http://roy.marples.name/downloads/dhcpcd/dhcpcd-6.8.2.tar.bz2
SHA1 Hash: 134a7c06bffc20789fedd5c0d259145d2dc4f058
(on the private dhcpcd repo 'trunk')
BUG: 22956197
Change-Id: I701af1d1532725b1058640fcb7ddeab1c24a797e
diff --git a/arp.c b/arp.c
new file mode 100644
index 0000000..01a8ba4
--- /dev/null
+++ b/arp.c
@@ -0,0 +1,422 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ELOOP_QUEUE 5
+#include "config.h"
+#include "arp.h"
+#include "if.h"
+#include "ipv4.h"
+#include "common.h"
+#include "dhcp.h"
+#include "dhcpcd.h"
+#include "eloop.h"
+#include "if.h"
+#include "if-options.h"
+#include "ipv4ll.h"
+
+#define ARP_LEN \
+ (sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN))
+
+static ssize_t
+arp_request(const struct interface *ifp, in_addr_t sip, in_addr_t tip)
+{
+ uint8_t arp_buffer[ARP_LEN];
+ struct arphdr ar;
+ size_t len;
+ uint8_t *p;
+
+ ar.ar_hrd = htons(ifp->family);
+ ar.ar_pro = htons(ETHERTYPE_IP);
+ ar.ar_hln = ifp->hwlen;
+ ar.ar_pln = sizeof(sip);
+ ar.ar_op = htons(ARPOP_REQUEST);
+
+ p = arp_buffer;
+ len = 0;
+
+#define CHECK(fun, b, l) \
+ do { \
+ if (len + (l) > sizeof(arp_buffer)) \
+ goto eexit; \
+ fun(p, (b), (l)); \
+ p += (l); \
+ len += (l); \
+ } while (/* CONSTCOND */ 0)
+#define APPEND(b, l) CHECK(memcpy, b, l)
+#define ZERO(l) CHECK(memset, 0, l)
+
+ APPEND(&ar, sizeof(ar));
+ APPEND(ifp->hwaddr, ifp->hwlen);
+ APPEND(&sip, sizeof(sip));
+ ZERO(ifp->hwlen);
+ APPEND(&tip, sizeof(tip));
+ return if_sendrawpacket(ifp, ETHERTYPE_ARP, arp_buffer, len);
+
+eexit:
+ errno = ENOBUFS;
+ return -1;
+}
+
+void
+arp_report_conflicted(const struct arp_state *astate, const struct arp_msg *amsg)
+{
+
+ 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
+arp_packet(void *arg)
+{
+ struct interface *ifp = arg;
+ const struct interface *ifn;
+ uint8_t arp_buffer[ARP_LEN];
+ struct arphdr ar;
+ struct arp_msg arm;
+ ssize_t bytes;
+ struct dhcp_state *state;
+ struct arp_state *astate, *astaten;
+ unsigned char *hw_s, *hw_t;
+ int flags;
+
+ state = D_STATE(ifp);
+ flags = 0;
+ while (!(flags & RAW_EOF)) {
+ bytes = if_readrawpacket(ifp, ETHERTYPE_ARP,
+ arp_buffer, sizeof(arp_buffer), &flags);
+ if (bytes == -1) {
+ logger(ifp->ctx, LOG_ERR,
+ "%s: arp if_readrawpacket: %m", ifp->name);
+ dhcp_close(ifp);
+ return;
+ }
+ /* We must have a full ARP header */
+ if ((size_t)bytes < sizeof(ar))
+ continue;
+ memcpy(&ar, arp_buffer, sizeof(ar));
+ /* Families must match */
+ if (ar.ar_hrd != htons(ifp->family))
+ continue;
+ /* Protocol must be IP. */
+ if (ar.ar_pro != htons(ETHERTYPE_IP))
+ continue;
+ if (ar.ar_pln != sizeof(arm.sip.s_addr))
+ continue;
+ /* Only these types are recognised */
+ if (ar.ar_op != htons(ARPOP_REPLY) &&
+ ar.ar_op != htons(ARPOP_REQUEST))
+ continue;
+
+ /* Get pointers to the hardware addreses */
+ hw_s = arp_buffer + sizeof(ar);
+ hw_t = hw_s + ar.ar_hln + ar.ar_pln;
+ /* Ensure we got all the data */
+ if ((hw_t + ar.ar_hln + ar.ar_pln) - arp_buffer > bytes)
+ continue;
+ /* Ignore messages from ourself */
+ TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
+ if (ar.ar_hln == ifn->hwlen &&
+ memcmp(hw_s, ifn->hwaddr, ifn->hwlen) == 0)
+ break;
+ }
+ if (ifn)
+ continue;
+ /* Copy out the HW and IP addresses */
+ memcpy(&arm.sha, hw_s, ar.ar_hln);
+ memcpy(&arm.sip.s_addr, hw_s + ar.ar_hln, ar.ar_pln);
+ memcpy(&arm.tha, hw_t, ar.ar_hln);
+ memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln);
+
+ /* Run the conflicts */
+ TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) {
+ if (astate->conflicted_cb)
+ astate->conflicted_cb(astate, &arm);
+ }
+ }
+}
+
+static void
+arp_open(struct interface *ifp)
+{
+ struct dhcp_state *state;
+
+ state = D_STATE(ifp);
+ if (state->arp_fd == -1) {
+ state->arp_fd = if_openrawsocket(ifp, ETHERTYPE_ARP);
+ if (state->arp_fd == -1) {
+ logger(ifp->ctx, LOG_ERR, "%s: %s: %m",
+ __func__, ifp->name);
+ return;
+ }
+ eloop_event_add(ifp->ctx->eloop, state->arp_fd,
+ arp_packet, ifp, NULL, NULL);
+ }
+}
+
+static void
+arp_announced(void *arg)
+{
+ struct arp_state *astate = arg;
+
+ if (astate->announced_cb) {
+ astate->announced_cb(astate);
+ return;
+ }
+
+ /* Nothing more to do, so free us */
+ arp_free(astate);
+}
+
+static void
+arp_announce1(void *arg)
+{
+ struct arp_state *astate = arg;
+ struct interface *ifp = astate->iface;
+
+ if (++astate->claims < ANNOUNCE_NUM)
+ logger(ifp->ctx, LOG_DEBUG,
+ "%s: ARP announcing %s (%d of %d), "
+ "next in %d.0 seconds",
+ ifp->name, inet_ntoa(astate->addr),
+ astate->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
+ else
+ logger(ifp->ctx, LOG_DEBUG,
+ "%s: ARP announcing %s (%d of %d)",
+ ifp->name, inet_ntoa(astate->addr),
+ astate->claims, ANNOUNCE_NUM);
+ if (arp_request(ifp, astate->addr.s_addr, astate->addr.s_addr) == -1)
+ logger(ifp->ctx, LOG_ERR, "send_arp: %m");
+ eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT,
+ astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced,
+ astate);
+}
+
+void
+arp_announce(struct arp_state *astate)
+{
+
+ arp_open(astate->iface);
+ astate->claims = 0;
+ arp_announce1(astate);
+}
+
+static void
+arp_probed(void *arg)
+{
+ struct arp_state *astate = arg;
+
+ astate->probed_cb(astate);
+}
+
+static void
+arp_probe1(void *arg)
+{
+ struct arp_state *astate = arg;
+ struct interface *ifp = astate->iface;
+ struct timespec tv;
+
+ if (++astate->probes < PROBE_NUM) {
+ tv.tv_sec = PROBE_MIN;
+ tv.tv_nsec = (suseconds_t)arc4random_uniform(
+ (PROBE_MAX - PROBE_MIN) * NSEC_PER_SEC);
+ timespecnorm(&tv);
+ eloop_timeout_add_tv(ifp->ctx->eloop, &tv, arp_probe1, astate);
+ } else {
+ tv.tv_sec = ANNOUNCE_WAIT;
+ tv.tv_nsec = 0;
+ eloop_timeout_add_tv(ifp->ctx->eloop, &tv, arp_probed, astate);
+ }
+ logger(ifp->ctx, LOG_DEBUG,
+ "%s: ARP probing %s (%d of %d), next in %0.1f seconds",
+ ifp->name, inet_ntoa(astate->addr),
+ astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM,
+ timespec_to_double(&tv));
+ if (arp_request(ifp, 0, astate->addr.s_addr) == -1)
+ logger(ifp->ctx, LOG_ERR, "send_arp: %m");
+}
+
+void
+arp_probe(struct arp_state *astate)
+{
+
+ arp_open(astate->iface);
+ astate->probes = 0;
+ logger(astate->iface->ctx, LOG_DEBUG, "%s: probing for %s",
+ astate->iface->name, inet_ntoa(astate->addr));
+ arp_probe1(astate);
+}
+
+static struct arp_state *
+arp_find(struct interface *ifp, const struct in_addr *addr)
+{
+ struct arp_state *astate;
+ struct dhcp_state *state;
+
+ state = D_STATE(ifp);
+ TAILQ_FOREACH(astate, &state->arp_states, next) {
+ if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp)
+ return astate;
+ }
+ errno = ESRCH;
+ return NULL;
+}
+
+struct arp_state *
+arp_new(struct interface *ifp, const struct in_addr *addr)
+{
+ struct arp_state *astate;
+ struct dhcp_state *state;
+
+ if (addr && (astate = arp_find(ifp, addr)))
+ return astate;
+
+ if ((astate = calloc(1, sizeof(*astate))) == NULL) {
+ logger(ifp->ctx, LOG_ERR, "%s: %s: %m", ifp->name, __func__);
+ return NULL;
+ }
+ state = D_STATE(ifp);
+ astate->iface = ifp;
+ if (addr)
+ astate->addr = *addr;
+ TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
+ return astate;
+}
+
+void
+arp_cancel(struct arp_state *astate)
+{
+
+ eloop_timeout_delete(astate->iface->ctx->eloop, NULL, astate);
+}
+
+void
+arp_free(struct arp_state *astate)
+{
+ struct dhcp_state *state;
+
+ if (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);
+ }
+}
+
+void
+arp_free_but(struct arp_state *astate)
+{
+ struct arp_state *p, *n;
+ struct dhcp_state *state;
+
+ state = D_STATE(astate->iface);
+ TAILQ_FOREACH_SAFE(p, &state->arp_states, next, n) {
+ if (p != astate)
+ arp_free(p);
+ }
+}
+
+void
+arp_close(struct interface *ifp)
+{
+ struct dhcp_state *state = D_STATE(ifp);
+ struct arp_state *astate;
+
+ if (state == NULL)
+ return;
+
+ if (state->arp_fd != -1) {
+ eloop_event_delete(ifp->ctx->eloop, state->arp_fd, 0);
+ close(state->arp_fd);
+ state->arp_fd = -1;
+ }
+
+ while ((astate = TAILQ_FIRST(&state->arp_states))) {
+#ifndef __clang_analyzer__
+ /* clang guard needed for a more compex variant on this bug:
+ * http://llvm.org/bugs/show_bug.cgi?id=18222 */
+ arp_free(astate);
+#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_NOTUSEABLE)) {
+ if (astate->probed_cb)
+ astate->probed_cb(astate);
+ }
+ }
+ }
+#else
+ UNUSED(cmd);
+ UNUSED(ifp);
+ UNUSED(addr);
+ UNUSED(flags);
+#endif
+}