This means we don't have to open an ARP socket unless we need to ARPing.
Allow ARP an ARPing to be optional features.
* --disable-inet6
Or by removing the following features:
* --disable-auth
+ * --disable-arp
+ * --disable-arping
* --disable-ipv4ll
* --disable-dhcp6
#include "if-options.h"
#include "ipv4ll.h"
+#if defined(ARP) && (!defined(KERNEL_RFC5227) || defined(ARPING))
#define ARP_LEN \
(sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN))
return -1;
}
-void
-arp_report_conflicted(const struct arp_state *astate,
- const struct arp_msg *amsg)
-{
-
- if (amsg != NULL) {
- 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(struct interface *ifp, uint8_t *data, size_t len)
{
}
}
+void
+arp_close(struct interface *ifp)
+{
+ struct iarp_state *state;
+
+ if ((state = ARP_STATE(ifp)) != NULL && state->fd != -1) {
+ eloop_event_delete(ifp->ctx->eloop, state->fd);
+ if_closeraw(ifp, state->fd);
+ state->fd = -1;
+ }
+}
+
static void
arp_read(void *arg)
{
{
struct iarp_state *state;
+printf("ARP OPENED!!!\n");
+
state = ARP_STATE(ifp);
if (state->fd == -1) {
state->fd = if_openraw(ifp, ETHERTYPE_ARP);
return state->fd;
}
+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)
+{
+
+ if (arp_open(astate->iface) == -1) {
+ logger(astate->iface->ctx, LOG_ERR,
+ "%s: %s: %m", __func__, astate->iface->name);
+ return;
+ }
+ astate->probes = 0;
+ logger(astate->iface->ctx, LOG_DEBUG, "%s: probing for %s",
+ astate->iface->name, inet_ntoa(astate->addr));
+ arp_probe1(astate);
+}
+#else /* !ARP */
+#define arp_close(ifp) {}
+#endif /* ARP */
+
static void
arp_announced(void *arg)
{
return;
}
- /* As there is no announced callback, free the ARP state. */
- arp_free(astate);
+ /* Keep the ARP state open to handle ongoing ACD. */
}
static void
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)
logger(ifp->ctx, LOG_DEBUG,
"%s: ARP announcing %s (%d of %d), "
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");
+#endif
eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT,
astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced,
astate);
arp_announce(struct arp_state *astate)
{
+#ifndef KERNEL_RFC5227
if (arp_open(astate->iface) == -1) {
logger(astate->iface->ctx, LOG_ERR,
"%s: %s: %m", __func__, astate->iface->name);
return;
}
+#endif
+
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_report_conflicted(const struct arp_state *astate,
+ const struct arp_msg *amsg)
{
- if (arp_open(astate->iface) == -1) {
+ if (amsg != NULL) {
+ char buf[HWADDR_LEN * 3];
+
logger(astate->iface->ctx, LOG_ERR,
- "%s: %s: %m", __func__, astate->iface->name);
- return;
- }
- astate->probes = 0;
- logger(astate->iface->ctx, LOG_DEBUG, "%s: probing for %s",
- astate->iface->name, inet_ntoa(astate->addr));
- arp_probe1(astate);
+ "%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));
}
struct arp_state *
free(astate);
/* If there are no more ARP states, close the socket. */
- if (state->fd != -1 && TAILQ_FIRST(&state->arp_states) == NULL) {
- eloop_event_delete(ifp->ctx->eloop, state->fd);
- if_closeraw(ifp, state->fd);
+ if (TAILQ_FIRST(&state->arp_states) == NULL) {
+ arp_close(ifp);
free(state);
ifp->if_data[IF_DATA_ARP] = NULL;
}
}
void
-arp_close(struct interface *ifp)
+arp_drop(struct interface *ifp)
{
arp_free_but1(ifp, NULL);
+ arp_close(ifp);
}
void
#include "dhcpcd.h"
#include "if.h"
+#ifdef IN_IFF_DUPLICATED
+/* NetBSD gained RFC 5227 support in the kernel.
+ * This means dhcpcd doesn't need ARP except for ARPing support. */
+#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 799003900
+#define KERNEL_RFC5227
+#endif
+#endif
+
struct arp_msg {
uint16_t op;
unsigned char sha[HWADDR_LEN];
#define ARP_CSTATE(ifp) \
((const struct iarp_state *)(ifp)->if_data[IF_DATA_ARP])
-#ifdef INET
+#if defined(ARP) && (!defined(KERNEL_RFC5227) || defined(ARPING))
int arp_open(struct interface *);
ssize_t arp_request(const struct interface *, in_addr_t, in_addr_t);
-void arp_report_conflicted(const struct arp_state *, const struct arp_msg *);
-void arp_announce(struct arp_state *);
void arp_probe(struct arp_state *);
+void arp_close(struct interface *);
+#endif
+
+#ifdef ARP
+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_cancel(struct arp_state *);
void arp_free(struct arp_state *);
void arp_free_but(struct arp_state *);
-struct arp_state *arp_find(struct interface *, const struct in_addr *);
-void arp_close(struct interface *);
+void arp_drop(struct interface *);
void arp_handleifa(int, struct ipv4_addr *);
#else
-#define arp_close(a) {}
+#define arp_drop(a) {}
#endif
#endif
# Ensure that we do not inherit these from env
HOOKSET=false
INET=
+ARP=
+ARPING=
IPV4LL=
INET6=
ARC4RANDOM=
--enable-fork) FORK=yes;;
--disable-static) STATIC=no;;
--enable-static) STATIC=yes;;
- --disable-ipv4|--disable-inet) INET=no;;
+ --disable-ipv4|--disable-inet) INET=no; ARP=no; ARPING=no; IPV4LL=no;;
--enable-ipv4|--enable-inet) INET=yes;;
+ --disable-arp) ARP=no; ARPING=no; IPV4LL=no;;
+ --enable-arp) ARP=yes; INET=yes;;
+ --disable-arping) ARPING=no;;
+ --enable-arping) ARPING=yes; ARP=yes; INET=yes;;
--disable-ipv4ll) IPV4LL=no;;
- --enable-ipv4ll) IPV4LL=yes; INET=yes;;
- --disable-ipv6|--disable-inet6) INET6=no;;
+ --enable-ipv4ll) IPV4LL=yes; ARP=yes; INET=yes;;
+ --disable-ipv6|--disable-inet6) INET6=no; DHCP6=no;;
--enable-ipv6|--enable-inet6) INET6=yes;;
--disable-dhcp6) DHCP6=no;;
--enable-dhcp6) DHCP6=yes;;
if [ -z "$INET" -o "$INET" = yes ]; then
echo "Enabling INET support"
echo "CPPFLAGS+= -DINET" >>$CONFIG_MK
- echo "DHCPCD_SRCS+= arp.c dhcp.c ipv4.c" >>$CONFIG_MK
+ echo "DHCPCD_SRCS+= dhcp.c ipv4.c" >>$CONFIG_MK
+ if [ -z "$ARP" -o "$ARP" = yes ]; then
+ echo "Enabling ARP support"
+ echo "CPPFLAGS+= -DARP" >>$CONFIG_MK
+ echo "DHCPCD_SRCS+= arp.c" >>$CONFIG_MK
+ fi
+ if [ -z "$ARPING" -o "$ARPING" = yes ]; then
+ echo "Enabling ARPing support"
+ echo "CPPFLAGS+= -DARPING" >>$CONFIG_MK
+ fi
if [ -z "$IPV4LL" -o "$IPV4LL" = yes ]; then
echo "Enabling IPv4LL support"
echo "CPPFLAGS+= -DIPV4LL" >>$CONFIG_MK
};
static int dhcp_open(struct interface *);
+#ifdef ARP
static void dhcp_arp_conflicted(struct arp_state *, const struct arp_msg *);
+#endif
void
dhcp_printoptions(const struct dhcpcd_ctx *ctx,
dhcp_leaseextend(struct interface *ifp)
{
+#ifdef ARP
if (ifp->options->options & DHCPCD_ARP) {
+ const struct dhcp_state *state;
struct arp_state *astate;
- if ((astate = arp_new(ifp, NULL)) == NULL)
+ state = D_CSTATE(ifp);
+ if ((astate = arp_new(ifp, &state->lease.addr)) == NULL)
return -1;
+ astate->conflicted_cb = dhcp_arp_conflicted;
+#ifndef KERNEL_RFC5227
if (arp_open(ifp) == -1)
return -1;
+#endif
- astate->conflicted_cb = dhcp_arp_conflicted;
logger(ifp->ctx, LOG_WARNING,
"%s: extending lease until DaD failure or DHCP", ifp->name);
return 0;
}
+#endif
logger(ifp->ctx, LOG_WARNING, "%s: extending lease", ifp->name);
return 0;
dhcp_expire1(ifp);
}
+#ifdef ARP
static void
dhcp_decline(struct interface *ifp)
{
send_message(ifp, DHCP_DECLINE, NULL);
}
+#endif
static void
dhcp_startrenew(void *arg)
send_rebind(ifp);
}
+#ifdef ARP
static void
dhcp_arp_probed(struct arp_state *astate)
{
+ struct interface *ifp;
struct dhcp_state *state;
struct if_options *ifo;
- state = D_STATE(astate->iface);
- ifo = astate->iface->options;
+ ifp = astate->iface;
+ state = D_STATE(ifp);
+ ifo = ifp->options;
+#ifdef ARPING
if (state->arping_index < ifo->arping_len) {
/* We didn't find a profile for this
* address or hwaddr, so move to the next
astate->addr.s_addr =
ifo->arping[state->arping_index - 1];
arp_probe(astate);
+ return;
}
- dhcpcd_startinterface(astate->iface);
+ arp_free(astate);
+#ifdef KERNEL_RFC5227
+ /* As arping is finished, close the ARP socket.
+ * The kernel will handle ACD from here. */
+ arp_close(ifp);
+#endif
+ dhcpcd_startinterface(ifp);
return;
}
+#endif
/* Already bound so DAD has worked */
if (state->state == DHS_BOUND)
return;
- logger(astate->iface->ctx, LOG_DEBUG, "%s: DAD completed for %s",
- astate->iface->name, inet_ntoa(astate->addr));
+ logger(ifp->ctx, LOG_DEBUG, "%s: DAD completed for %s",
+ ifp->name, inet_ntoa(astate->addr));
if (state->state != DHS_INFORM)
- dhcp_bind(astate->iface);
+ dhcp_bind(ifp);
#ifndef IN_IFF_TENTATIVE
else {
struct bootp *bootp;
len = state->new_len;
state->new = state->offer;
state->new_len = state->offer_len;
- get_lease(astate->iface, &state->lease,
- state->new, state->new_len);
+ get_lease(ifp, &state->lease, state->new, state->new_len);
ipv4_applyaddr(astate->iface);
state->new = bootp;
state->new_len = len;
#endif
/* If we forked, stop here. */
- if (astate->iface->ctx->options & DHCPCD_FORKED)
+ if (ifp->ctx->options & DHCPCD_FORKED)
return;
/* Stop IPv4LL now we have a working DHCP address */
- ipv4ll_drop(astate->iface);
+ ipv4ll_drop(ifp);
if (ifo->options & DHCPCD_INFORM)
- dhcp_inform(astate->iface);
+ dhcp_inform(ifp);
}
static void
{
struct interface *ifp;
struct dhcp_state *state;
+#ifdef ARPING
struct if_options *ifo;
+#endif
ifp = astate->iface;
- ifo = ifp->options;
state = D_STATE(ifp);
+
+#ifdef ARPING
+ ifo = ifp->options;
if (state->arping_index &&
state->arping_index <= ifo->arping_len &&
amsg &&
}
dhcp_close(ifp);
arp_free(astate);
+#ifdef KERNEL_RFC5227
+ /* As arping is finished, close the ARP socket.
+ * The kernel will handle ACD from here. */
+ arp_close(ifp);
+#endif
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
dhcpcd_startinterface(ifp);
return;
}
+#endif
/* RFC 2131 3.1.5, Client-server interaction
* NULL amsg means IN_IFF_DUPLICATED */
return;
}
}
+#endif
void
dhcp_bind(struct interface *ifp)
return sizeof(**bootp);
}
+#ifdef ARP
static int
dhcp_arp_address(struct interface *ifp)
{
/* If the interface already has the address configured
* then we can't ARP for duplicate detection. */
ia = ipv4_findaddr(ifp->ctx, &addr);
+ if ((astate = arp_new(ifp, &addr)) == NULL)
+ return 0;
+ astate->probed_cb = dhcp_arp_probed;
+ astate->conflicted_cb = dhcp_arp_conflicted;
#ifdef IN_IFF_TENTATIVE
if (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) {
- if ((astate = arp_new(ifp, &addr)) != NULL) {
- astate->probed_cb = dhcp_arp_probed;
- astate->conflicted_cb = dhcp_arp_conflicted;
- }
if (ia == NULL) {
struct dhcp_lease l;
get_lease(ifp, &l, state->offer, state->offer_len);
logger(ifp->ctx, LOG_INFO, "%s: probing address %s/%d",
ifp->name, inet_ntoa(l.addr), inet_ntocidr(l.mask));
- if ((astate = arp_new(ifp, &addr)) != NULL) {
- astate->probed_cb = dhcp_arp_probed;
- astate->conflicted_cb = dhcp_arp_conflicted;
- /* We need to handle DAD. */
- arp_probe(astate);
- }
+ /* We need to handle DAD. */
+ arp_probe(astate);
return 0;
}
#endif
if (dhcp_arp_address(ifp) == 1)
dhcp_bind(ifp);
}
+#endif
static void
dhcp_static(struct interface *ifp)
ia ? &ia->addr : &ifo->req_addr,
ia ? &ia->mask : &ifo->req_mask);
if (state->offer_len)
+#ifdef ARP
dhcp_arp_bind(ifp);
+#else
+ dhcp_bind(ifp);
+#endif
}
void
ipv4_deladdr(ia, 1);
state->offer_len = dhcp_message_new(&state->offer,
&ifo->req_addr, &ifo->req_mask);
+#ifdef ARP
if (dhcp_arp_address(ifp) == 0)
return;
+#endif
ia = ipv4_iffindaddr(ifp,
&ifo->req_addr, &ifo->req_mask);
assert(ia != NULL);
case 0:
LOGDHCP(LOG_WARNING, "IPv4LL disabled from");
ipv4ll_drop(ifp);
- arp_close(ifp);
+ arp_drop(ifp);
break;
case 1:
LOGDHCP(LOG_WARNING, "IPv4LL enabled from");
lease->frominfo = 0;
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
+#ifdef ARP
dhcp_arp_bind(ifp);
+#else
+ dhcp_bind(ifp);
+#endif
}
static void *
logger(ifp->ctx, LOG_ERR,
"%s: dhcp if_readrawpacket: %m", ifp->name);
dhcp_close(ifp);
- arp_close(ifp);
return;
}
dhcp_handlepacket(ifp, buf, (size_t)bytes, flags);
struct dhcpcd_ctx *ctx;
dhcp_close(ifp);
- arp_close(ifp);
+ arp_drop(ifp);
if (state) {
free(state->old);
free(state->new);
state->offer = NULL;
state->offer_len = 0;
+#ifdef ARPING
if (state->arping_index < ifo->arping_len) {
struct arp_state *astate;
}
return;
}
+#endif
if (ifo->options & DHCPCD_STATIC) {
dhcp_static(ifp);
struct timespec started;
unsigned char *clientid;
struct authstate auth;
+#ifdef ARPING
size_t arping_index;
+#endif
};
#define D_STATE(ifp) \
ipv6_drop(ifp);
ipv4ll_drop(ifp);
dhcp_drop(ifp, stop ? "STOP" : "EXPIRE");
- arp_close(ifp);
+ arp_drop(ifp);
}
static void
ifp->carrier = LINK_DOWN;
script_runreason(ifp, "NOCARRIER");
#ifdef NOCARRIER_PRESERVE_IP
- arp_close(ifp);
+ arp_drop(ifp);
dhcp_abort(ifp);
if_sortinterfaces(ctx);
ipv4_preferanother(ifp);
#ifdef INET
" INET"
#endif
+#ifdef ARP
+ " ARP"
+#endif
+#ifdef ARPING
+ " ARPing"
+#endif
#ifdef IPV4LL
" IPv4LL"
#endif
int r;
struct ipv4_state *state;
struct ipv4_addr *ap;
+#ifdef ARP
struct arp_state *astate;
+#else
+ UNUSED(keeparp);
+#endif
logger(addr->iface->ctx, LOG_DEBUG,
"%s: deleting IP address %s", addr->iface->name, addr->saddr);
logger(addr->iface->ctx, LOG_ERR, "%s: %s: %m",
addr->iface->name, __func__);
+#ifdef ARP
if (!keeparp && (astate = arp_find(addr->iface, &addr->addr)) != NULL)
arp_free(astate);
+#endif
state = IPV4_STATE(addr->iface);
TAILQ_FOREACH(ap, &state->addrs, next) {
ifp->name);
return;
}
-#ifdef IN_IFF_NOTUSEABLE
+#if defined(ARP) && defined(IN_IFF_NOTUSEABLE)
if (ia->addr_flags & IN_IFF_NOTUSEABLE)
return;
#endif
* notification right now via our link socket. */
if_initrt(ifp->ctx);
ipv4_buildroutes(ifp->ctx);
+
+#ifdef ARP
/* Announce the address */
if (ifo->options & DHCPCD_ARP) {
struct arp_state *astate;
- if ((astate = arp_new(ifp, &state->addr->addr)) != NULL)
+ if ((astate = arp_find(ifp, &state->addr->addr)) != NULL)
arp_announce(astate);
}
+#endif
+
if (state->state == DHS_BOUND) {
script_runreason(ifp, state->reason);
dhcpcd_daemonise(ifp->ctx);
}
if (addr->s_addr != INADDR_ANY && addr->s_addr != INADDR_BROADCAST) {
+#ifdef ARP
arp_handleifa(cmd, ia);
+#endif
dhcp_handleifa(cmd, ia);
}
if (state->addr != NULL &&
astate->failed.s_addr == state->addr->addr.s_addr)
{
+#ifdef KERNEL_RFC5227
+ logger(ifp->ctx, LOG_WARNING,
+ "%s: IPv4LL defence failed for %s",
+ ifp->name, state->addr->saddr);
+#else
struct timespec now, defend;
/* RFC 3927 Section 2.5 says a defence should
state->defend = now;
return;
}
-
+#endif
ipv4_deladdr(state->addr, 1);
state->down = 1;
state->addr = NULL;