Initial Contribution
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..b1e0e55
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,59 @@
+# Copyright 2006 The Android Open Source Project
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_PATH:= $(call my-dir)
+
+etc_dir := $(TARGET_OUT)/etc/dhcpcd
+hooks_dir := dhcpcd-hooks
+hooks_target := $(etc_dir)/$(hooks_dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := common.c dhcp.c dhcpcd.c logger.c net.c \
+	signals.c configure.c client.c if-linux.c lpf.c
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_CFLAGS := -DDISABLE_ARP
+LOCAL_SHARED_LIBRARIES := libc
+LOCAL_MODULE = dhcpcd
+LOCAL_MODULE_TAGS := user development
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dhcpcd.conf
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(etc_dir)
+LOCAL_SRC_FILES := android.conf
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := dhcpcd-run-hooks
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(etc_dir)
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := 01-test
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(hooks_target)
+LOCAL_SRC_FILES := $(hooks_dir)/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := 20-dns.conf
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(hooks_target)
+LOCAL_SRC_FILES := $(hooks_dir)/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := 95-configured
+LOCAL_MODULE_TAGS := user development
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(hooks_target)
+LOCAL_SRC_FILES := $(hooks_dir)/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+endif
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f93d256
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,51 @@
+# Makefile based on BSD make.
+# Our mk stubs also work with GNU make.
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+PROG=		dhcpcd
+SRCS=		common.c dhcp.c dhcpcd.c logger.c net.c signals.c
+SRCS+=		configure.c client.c
+SRCS+=		${SRC_IF} ${SRC_PF}
+
+LIBEXECDIR?=	${PREFIX}/system/etc/dhcpcd
+SCRIPT=		${LIBEXECDIR}/dhcpcd-run-hooks
+HOOKDIR=	${LIBEXECDIR}/dhcpcd-hooks
+
+BINDIR=		${PREFIX}/sbin
+DBDIR=		/data/misc/dhcp
+SYSCONFDIR?=	${PREFIX}/system/etc/dhcpcd
+
+MAN=		dhcpcd.conf.5 dhcpcd.8 dhcpcd-run-hooks.8
+CLEANFILES=	dhcpcd.conf.5 dhcpcd.8 dhcpcd-run-hooks.8
+
+SCRIPTS=	dhcpcd-run-hooks
+SCRIPTSDIR=	${LIBEXECDIR}
+CLEANFILES+=	dhcpcd-run-hooks
+
+FILES=		dhcpcd.conf
+FILESDIR=	${SYSCONFDIR}
+
+CPPFLAGS+=	-DDBDIR=\"${DBDIR}\"
+CPPFLAGS+=	-DSCRIPT=\"${SCRIPT}\"
+CPPFLAGS+=	-DSYSCONFDIR=\"${SYSCONFDIR}\"
+LDADD+=		${LIBRT}
+
+SUBDIRS=	dhcpcd-hooks
+
+.SUFFIXES:	.in .sh.in
+
+SED_DBDIR=	-e 's:@DBDIR@:${DBDIR}:g'
+SED_HOOKDIR=	-e 's:@HOOKDIR@:${HOOKDIR}:g'
+SED_SCRIPT=	-e 's:@SCRIPT@:${SCRIPT}:g'
+SED_SYS=	-e 's:@SYSCONFDIR@:${SYSCONFDIR}:g'
+
+.in:
+	${SED} ${SED_DBDIR} ${SED_HOOKDIR} ${SED_SCRIPT} ${SED_SYS} $< > $@
+
+.sh.in.sh:
+	${SED} ${SED_HOOKDIR} ${SED_SCRIPT} ${SED_SYS} $< > $@
+
+MK=		mk
+include ${MK}/os.mk
+include ${MK}/sys.mk
+include ${MK}/prog.mk
diff --git a/README b/README
new file mode 100644
index 0000000..27aadf0
--- /dev/null
+++ b/README
@@ -0,0 +1,69 @@
+dhcpcd-4 - DHCP client daemon
+Copyright 2006-2008 Roy Marples <roy@marples.name>
+
+
+Installation
+------------
+Edit config.h to match your building requirements.
+Then just make; make install
+man dhcpcd for command line options
+
+
+Notes
+-----
+If you're cross compiling you may need to set the below knobs to avoid
+automatic tests.
+OS=BSD | Linux
+
+If size is your thing, you can remove all non-essential userland options
+by adding -DMINIMAL to your CPPFLAGS. This currently shaves off around 6k.
+You can save a futher 600 bytes or so by using the small make target.
+
+If you're building for a NOMMU system where fork() does not work, you should
+add -DTHERE_IS_NO_FORK to your CPPFLAGS.
+
+You can change the default dir with these knobs.
+For example, to satisfy FHS compliance you would do this:-
+LIBEXECDIR=/lib/dhcpcd
+DBDIR=/var/lib/dhcpcd
+
+We now default to using -std=c99. For 64-bit linux, this always works, but
+for 32-bit linux it requires either gnu99 or a patch to asm/types.h.
+Most distros patch linux headers so this should work fine.
+linux-2.6.24 finally ships with a working 32-bit header.
+If your linux headers are older, or your distro hasn't patched them you can
+set CSTD=gnu99 to work around this.
+
+
+Hooks
+-----
+Not all the hooks in dhcpcd-hooks are installed by default.
+By default we install 01-test, 10-mtu, 20-resolv.conf and 30-hostname.
+To add more simply add them in the HOOKSCRIPTS variable.
+make HOOKSCRIPTS=50-ntp install
+
+
+Compatibility
+-------------
+If you require compatibility with dhcpcd-3 and older style variables,
+you can install 50-dhcpcd-compat into the directory $LIBEXECDIR/dhcpcd-hooks
+We don't install this by default.
+You should also add -DCMDLINE_COMPAT to your CPPFLAGS if you need to be fully
+commandline compatible with prior versions.
+
+dhcpcd-3 enabled DUID support by default - this has changed in dhcpcd-4.
+You can enable it via the --duid, -D command line option or by using the
+duid directive in dhcpcd.conf.
+If CMDLINE_COMPAT is defined the we renable DUID support by default IF
+the dhcpcd.duid file exits. This keeps the clients working as they were,
+which is good.
+
+dhcpcd-4 is NOT fully commandline compatible with dhcpcd-2 and older and
+changes the meaning of some options.
+
+
+ChangeLog
+---------
+We no longer supply a ChangeLog.
+However, you're more than welcome to read the git commit comments at
+http://git.marples.name/?p=dhcpcd/.git;a=summary
diff --git a/android.conf b/android.conf
new file mode 100644
index 0000000..5aa418d
--- /dev/null
+++ b/android.conf
@@ -0,0 +1,6 @@
+# dhcpcd configuration for Android Wi-Fi interface
+# See dhcpcd.conf(5) for details.
+
+interface tiwlan0
+# dhcpcd-run-hooks uses these options.
+option subnet_mask, routers, domain_name_servers
diff --git a/bpf-filter.h b/bpf-filter.h
new file mode 100644
index 0000000..adcc8bb
--- /dev/null
+++ b/bpf-filter.h
@@ -0,0 +1,107 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * 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.
+ */
+
+#ifndef BPF_ETHCOOK
+# define BPF_ETHCOOK 0
+#endif
+#ifndef BPF_WHOLEPACKET
+# define BPF_WHOLEPACKET ~0U
+#endif
+static const struct bpf_insn const arp_bpf_filter [] = {
+#ifndef BPF_SKIPTYPE
+	/* Make sure this is an ARP packet... */
+	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 0, 3),
+#endif
+
+	/* Make sure this is an ARP REPLY... */
+	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 + BPF_ETHCOOK),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1),
+
+	/* If we passed all the tests, ask for the whole packet. */
+	BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
+
+	/* Otherwise, drop it. */
+	BPF_STMT(BPF_RET + BPF_K, 0),
+};
+static const size_t arp_bpf_filter_len =
+    sizeof(arp_bpf_filter) / sizeof(arp_bpf_filter[0]);
+
+
+/* dhcp_bpf_filter taken from bpf.c in dhcp-3.1.0
+ *
+ * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *   Internet Systems Consortium, Inc.
+ *   950 Charter Street
+ *   Redwood City, CA 94063
+ *   <info@isc.org>
+ *   http://www.isc.org/
+ */
+
+static const struct bpf_insn const dhcp_bpf_filter [] = {
+#ifndef BPF_SKIPTYPE
+	/* Make sure this is an IP packet... */
+	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+#endif
+
+	/* Make sure it's a UDP packet... */
+	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23 + BPF_ETHCOOK),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+
+	/* Make sure this isn't a fragment... */
+	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 + BPF_ETHCOOK),
+	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+
+	/* Get the IP header length... */
+	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14 + BPF_ETHCOOK),
+
+	/* Make sure it's to the right port... */
+	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16 + BPF_ETHCOOK),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_CLIENT_PORT, 0, 1),
+
+	/* If we passed all the tests, ask for the whole packet. */
+	BPF_STMT(BPF_RET + BPF_K, BPF_WHOLEPACKET),
+
+	/* Otherwise, drop it. */
+	BPF_STMT(BPF_RET + BPF_K, 0),
+};
+static const size_t dhcp_bpf_filter_len =
+    sizeof(dhcp_bpf_filter) / sizeof(dhcp_bpf_filter[0]);
diff --git a/bpf.c b/bpf.c
new file mode 100644
index 0000000..f015344
--- /dev/null
+++ b/bpf.c
@@ -0,0 +1,205 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * 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/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "logger.h"
+#include "net.h"
+#include "bpf-filter.h"
+
+int
+open_socket(struct interface *iface, int protocol)
+{
+	int fd = -1;
+	int *fdp = NULL;
+	struct ifreq ifr;
+	int buf_len = 0;
+	struct bpf_version pv;
+	struct bpf_program pf;
+#ifdef BIOCIMMEDIATE
+	int flags;
+#endif
+#ifdef _PATH_BPF
+	fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK);
+#else
+	char *device;
+	int n = 0;
+
+	device = xmalloc(sizeof(char) * PATH_MAX);
+	do {
+		snprintf(device, PATH_MAX, "/dev/bpf%d", n++);
+		fd = open(device, O_RDWR | O_NONBLOCK);
+	} while (fd == -1 && errno == EBUSY);
+	free(device);
+#endif
+
+	if (fd == -1)
+		return -1;
+
+	if (ioctl(fd, BIOCVERSION, &pv) == -1)
+		goto eexit;
+	if (pv.bv_major != BPF_MAJOR_VERSION ||
+	    pv.bv_minor < BPF_MINOR_VERSION) {
+		logger(LOG_ERR, "BPF version mismatch - recompile " PACKAGE);
+		goto eexit;
+	}
+
+	memset(&ifr, 0, sizeof(ifr));
+	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
+	if (ioctl(fd, BIOCSETIF, &ifr) == -1)
+		goto eexit;
+
+	/* Get the required BPF buffer length from the kernel. */
+	if (ioctl(fd, BIOCGBLEN, &buf_len) == -1)
+		goto eexit;
+	if (iface->buffer_size != (size_t)buf_len) {
+		free(iface->buffer);
+		iface->buffer_size = buf_len;
+		iface->buffer = xmalloc(buf_len);
+		iface->buffer_len = iface->buffer_pos = 0;
+	}
+
+#ifdef BIOCIMMEDIATE
+	flags = 1;
+	if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
+		goto eexit;
+#endif
+
+	/* Install the DHCP filter */
+	if (protocol == ETHERTYPE_ARP) {
+#ifdef ENABLE_ARP
+		pf.bf_insns = UNCONST(arp_bpf_filter);
+		pf.bf_len = arp_bpf_filter_len;
+		fdp = &iface->arp_fd;
+#endif
+	} else {
+		pf.bf_insns = UNCONST(dhcp_bpf_filter);
+		pf.bf_len = dhcp_bpf_filter_len;
+		fdp = &iface->fd;
+	}
+	if (ioctl(fd, BIOCSETF, &pf) == -1)
+		goto eexit;
+	if (set_cloexec(fd) == -1)
+		goto eexit;
+	if (fdp) {
+		if (*fdp != -1)
+			close(*fdp);
+		*fdp = fd;
+	}
+	return fd;
+
+eexit:
+	free(iface->buffer);
+	iface->buffer = NULL;
+	close(fd);
+	return -1;
+}
+
+ssize_t
+send_raw_packet(const struct interface *iface, int protocol,
+		const void *data, ssize_t len)
+{
+	struct iovec iov[2];
+	struct ether_header hw;
+
+	memset(&hw, 0, ETHER_HDR_LEN);
+	memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
+	hw.ether_type = htons(protocol);
+	iov[0].iov_base = &hw;
+	iov[0].iov_len = ETHER_HDR_LEN;
+	iov[1].iov_base = UNCONST(data);
+	iov[1].iov_len = len;
+	return writev(iface->fd, iov, 2);
+}
+
+/* BPF requires that we read the entire buffer.
+ * So we pass the buffer in the API so we can loop on >1 packet. */
+ssize_t
+get_raw_packet(struct interface *iface, int protocol,
+	       void *data, ssize_t len)
+{
+	int fd = -1;
+	struct bpf_hdr packet;
+	ssize_t bytes;
+	const unsigned char *payload;
+
+	if (protocol == ETHERTYPE_ARP) {
+#ifdef ENABLE_ARP
+		fd = iface->arp_fd;
+#endif
+	} else
+		fd = iface->fd;
+
+	for (;;) {
+		if (iface->buffer_len == 0) {
+			bytes = read(fd, iface->buffer, iface->buffer_size);
+			if (bytes == -1)
+				return errno == EAGAIN ? 0 : -1;
+			else if ((size_t)bytes < sizeof(packet))
+				return -1;
+			iface->buffer_len = bytes;
+			iface->buffer_pos = 0;
+		}
+		bytes = -1;
+		memcpy(&packet, iface->buffer + iface->buffer_pos,
+		       sizeof(packet));
+		if (packet.bh_caplen != packet.bh_datalen)
+			goto next; /* Incomplete packet, drop. */
+		if (iface->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
+		    iface->buffer_len)
+			goto next; /* Packet beyond buffer, drop. */
+		payload = iface->buffer + packet.bh_hdrlen + ETHER_HDR_LEN;
+		bytes = packet.bh_caplen - ETHER_HDR_LEN;
+		if (bytes > len)
+			bytes = len;
+		memcpy(data, payload, bytes);
+next:
+		iface->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
+						   packet.bh_caplen);
+		if (iface->buffer_pos >= iface->buffer_len)
+			iface->buffer_len = iface->buffer_pos = 0;
+		if (bytes != -1)
+			return bytes;
+	}
+}
diff --git a/client.c b/client.c
new file mode 100644
index 0000000..99d0fa8
--- /dev/null
+++ b/client.c
@@ -0,0 +1,1545 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#ifdef __linux__
+# include <netinet/ether.h>
+#endif
+
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "client.h"
+#include "configure.h"
+#include "dhcp.h"
+#include "dhcpcd.h"
+#include "net.h"
+#include "logger.h"
+#include "signals.h"
+
+#ifdef ENABLE_IPV4LL
+# ifndef ENABLE_ARP
+ # error "IPv4LL requires ENABLE_ARP to work"
+# endif
+# define IPV4LL_LEASETIME 	2
+#endif
+
+/* Some platforms don't define INFTIM */
+#ifndef INFTIM
+# define INFTIM                 -1
+#endif
+
+#define STATE_INIT              0
+#define STATE_DISCOVERING	1
+#define STATE_REQUESTING        2
+#define STATE_BOUND             3
+#define STATE_RENEWING          4
+#define STATE_REBINDING         5
+#define STATE_REBOOT            6
+#define STATE_RENEW_REQUESTED   7
+#define STATE_PROBING		8
+#define STATE_ANNOUNCING	9
+
+/* Constants taken from RFC 2131. 
+ * We multiply some numbers by 1000 so they are suitable for use in poll(). */
+#define T1			0.5
+#define T2			0.875
+#define DHCP_BASE		4 * 1000
+#define DHCP_RAND_MIN		-1 * 1000
+#define DHCP_RAND_MAX		1 * 1000
+#define DHCP_MAX		64 * 1000
+
+/* We should define a maximum for the NAK exponential backoff */ 
+#define NAKOFF_MAX              60
+
+#define SOCKET_CLOSED           0
+#define SOCKET_OPEN             1
+
+/* Indexes for pollfds */
+#define POLLFD_SIGNAL           0
+#define POLLFD_IFACE            1 
+#define POLLFD_ARP		2
+
+/* These are really for IPV4LL, RFC 3927.
+ * We multiply some numbers by 1000 so they are suitable for use in poll(). */
+#define PROBE_WAIT		 1 * 1000
+#define PROBE_NUM		 3
+#define PROBE_MIN		 1 * 1000
+#define PROBE_MAX		 2 * 1000
+#define ANNOUNCE_WAIT		 2 * 1000
+#define ANNOUNCE_NUM		 2
+#define ANNOUNCE_INTERVAL	 2 * 1000
+#define MAX_CONFLICTS		10
+#define RATE_LIMIT_INTERVAL	60
+#define DEFEND_INTERVAL		10
+
+struct if_state {
+	int options;
+	struct interface *interface;
+	struct dhcp_message *offer;
+	struct dhcp_message *new;
+	struct dhcp_message *old;
+	struct dhcp_lease lease;
+	struct timeval start;
+	struct timeval stop;
+	int state;
+	int messages;
+	long timeout;
+	time_t nakoff;
+	uint32_t xid;
+	int socket;
+	int *pid_fd;
+	int signal_fd;
+#ifdef ENABLE_ARP
+	int probes;
+	int claims;
+	int conflicts;
+	time_t defend;
+	struct in_addr fail;
+#endif
+};
+
+struct dhcp_op {
+        uint8_t value;
+        const char *name;
+};
+
+static const struct dhcp_op const dhcp_ops[] = {
+	{ DHCP_DISCOVER, "DHCP_DISCOVER" },
+	{ DHCP_OFFER,    "DHCP_OFFER" },
+	{ DHCP_REQUEST,  "DHCP_REQUEST" },
+	{ DHCP_DECLINE,  "DHCP_DECLINE" },
+	{ DHCP_ACK,      "DHCP_ACK" },
+	{ DHCP_NAK,      "DHCP_NAK" },
+	{ DHCP_RELEASE,  "DHCP_RELEASE" },
+	{ DHCP_INFORM,   "DHCP_INFORM" },
+	{ 0, NULL }
+};
+
+static const char *
+get_dhcp_op(uint8_t type)
+{
+	const struct dhcp_op *d;
+
+	for (d = dhcp_ops; d->name; d++)
+		if (d->value == type)
+			return d->name;
+	return NULL;
+}
+
+static int
+daemonise(struct if_state *state, const struct options *options)
+{
+	pid_t pid;
+	sigset_t full;
+	sigset_t old;
+#ifdef THERE_IS_NO_FORK
+	char **argv;
+	int i;
+#else
+	char buf = '\0';
+	int sidpipe[2];
+#endif
+
+	if (state->options & DHCPCD_DAEMONISED ||
+	    !(options->options & DHCPCD_DAEMONISE))
+		return 0;
+
+	sigfillset(&full);
+	sigprocmask(SIG_SETMASK, &full, &old);
+
+#ifndef THERE_IS_NO_FORK
+	/* Setup a signal pipe so parent knows when to exit. */
+	if (pipe(sidpipe) == -1) {
+		logger(LOG_ERR,"pipe: %s", strerror(errno));
+		return -1;
+	}
+
+	logger(LOG_DEBUG, "forking to background");
+	switch (pid = fork()) {
+		case -1:
+			logger(LOG_ERR, "fork: %s", strerror(errno));
+			exit(EXIT_FAILURE);
+			/* NOTREACHED */
+		case 0:
+			setsid();
+			/* Notify parent it's safe to exit as we've detached. */
+			close(sidpipe[0]);
+			write(sidpipe[1], &buf, 1);
+			close(sidpipe[1]);
+			close_fds();
+			break;
+		default:
+			/* Reset signals as we're the parent about to exit. */
+			signal_reset();
+			/* Wait for child to detach */
+			close(sidpipe[1]);
+			read(sidpipe[0], &buf, 1);
+			close(sidpipe[0]);
+			break;
+	}
+#else
+	logger(LOG_INFO, "forking to background");
+
+	/* We need to add --daemonise to our options */
+	argv = xmalloc(sizeof(char *) * (dhcpcd_argc + 4));
+	argv[0] = dhcpcd;
+	for (i = 1; i < dhcpcd_argc; i++)
+		argv[i] = dhcpcd_argv[i];
+	argv[i] = (char *)"--daemonised";
+	if (dhcpcd_skiproutes) {
+		argv[++i] = (char *)"--skiproutes";
+		argv[++i] = dhcpcd_skiproutes;
+	}
+	argv[i + 1] = NULL;
+
+	switch (pid = vfork()) {
+		case -1:
+			logger(LOG_ERR, "vfork: %s", strerror(errno));
+			_exit(EXIT_FAILURE);
+		case 0:
+			signal_reset();
+			sigprocmask(SIG_SETMASK, &old, NULL);
+			execvp(dhcpcd, argv);
+			/* Must not use stdio here. */
+			write(STDERR_FILENO, "exec failed\n", 12);
+			_exit(EXIT_FAILURE);
+	}
+
+	free(argv);
+#endif
+
+	/* Done with the fd now */
+	if (pid != 0) {
+		writepid(*state->pid_fd, pid);
+		close(*state->pid_fd);
+		*state->pid_fd = -1;
+	}
+
+	sigprocmask(SIG_SETMASK, &old, NULL);
+
+	state->state = STATE_BOUND;
+	if (pid == 0) {
+		state->options |= DHCPCD_DAEMONISED;
+		return 0;
+	}
+
+	state->options |= DHCPCD_PERSISTENT | DHCPCD_FORKED;
+	return -1;
+}
+
+#ifndef MINIMAL
+#define THIRTY_YEARS_IN_SECONDS    946707779
+static size_t
+get_duid(unsigned char *duid, const struct interface *iface)
+{
+	FILE *f;
+	uint16_t type = 0;
+	uint16_t hw = 0;
+	uint32_t ul;
+	time_t t;
+	int x = 0;
+	unsigned char *p = duid;
+	size_t len = 0, l = 0;
+	char *buffer = NULL, *line, *option;
+
+	/* If we already have a DUID then use it as it's never supposed
+	 * to change once we have one even if the interfaces do */
+	if ((f = fopen(DUID, "r"))) {
+		while ((get_line(&buffer, &len, f))) {
+			line = buffer;
+			while ((option = strsep(&line, " \t")))
+				if (*option != '\0')
+					break;
+			if (!option || *option == '\0' || *option == '#')
+				continue;
+			l = hwaddr_aton(NULL, option);
+			if (l && l <= DUID_LEN) {
+				hwaddr_aton(duid, option);
+				break;
+			}
+			l = 0;
+		}
+		fclose(f);
+		free(buffer);
+		if (l)
+			return l;
+	} else {
+		if (errno != ENOENT)
+			return 0;
+	}
+
+	/* No file? OK, lets make one based on our interface */
+	if (!(f = fopen(DUID, "w")))
+		return 0;
+	type = htons(1); /* DUI-D-LLT */
+	memcpy(p, &type, 2);
+	p += 2;
+	hw = htons(iface->family);
+	memcpy(p, &hw, 2);
+	p += 2;
+	/* time returns seconds from jan 1 1970, but DUID-LLT is
+	 * seconds from jan 1 2000 modulo 2^32 */
+	t = time(NULL) - THIRTY_YEARS_IN_SECONDS;
+	ul = htonl(t & 0xffffffff);
+	memcpy(p, &ul, 4);
+	p += 4;
+	/* Finally, add the MAC address of the interface */
+	memcpy(p, iface->hwaddr, iface->hwlen);
+	p += iface->hwlen;
+	len = p - duid;
+	x = fprintf(f, "%s\n", hwaddr_ntoa(duid, len));
+	fclose(f);
+	/* Failed to write the duid? scrub it, we cannot use it */
+	if (x < 1) {
+		len = 0;
+		unlink(DUID);
+	}
+	return len;
+}
+#endif
+
+#ifdef ENABLE_IPV4LL
+static struct dhcp_message*
+ipv4ll_get_dhcp(uint32_t old_addr)
+{
+	uint32_t u32;
+	struct dhcp_message *dhcp;
+	uint8_t *p;
+
+	dhcp = xzalloc(sizeof(*dhcp));
+	/* Put some LL options in */
+	p = dhcp->options;
+	*p++ = DHCP_SUBNETMASK;
+	*p += sizeof(u32);
+	u32 = LINKLOCAL_MASK;
+	memcpy(p, &u32, sizeof(u32));
+	p += sizeof(u32);
+	*p++ = DHCP_BROADCAST;
+	*p += sizeof(u32);
+	u32 = LINKLOCAL_BRDC;
+	memcpy(p, &u32, sizeof(u32));
+	p += sizeof(u32);
+	*p++ = DHCP_END;
+
+	for (;;) {
+		dhcp->yiaddr = htonl(LINKLOCAL_ADDR |
+				     (((uint32_t)abs((int)arc4random())
+				       % 0xFD00) + 0x0100));
+		if (dhcp->yiaddr != old_addr)
+			break;
+	}
+	return dhcp;
+}
+#endif
+
+static void
+get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
+{
+	lease->frominfo = 0;
+	lease->addr.s_addr = dhcp->yiaddr;
+
+	if (get_option_addr(&lease->net.s_addr, dhcp, DHCP_SUBNETMASK) == -1)
+		lease->net.s_addr = get_netmask(dhcp->yiaddr);
+	if (get_option_uint32(&lease->leasetime, dhcp, DHCP_LEASETIME) != 0)
+		lease->leasetime = DEFAULT_LEASETIME;
+	if (get_option_uint32(&lease->renewaltime, dhcp, DHCP_RENEWALTIME) != 0)
+		lease->renewaltime = 0;
+	if (get_option_uint32(&lease->rebindtime, dhcp, DHCP_REBINDTIME) != 0)
+		lease->rebindtime = 0;
+}
+
+static int
+get_old_lease(struct if_state *state)
+{
+	struct interface *iface = state->interface;
+	struct dhcp_lease *lease = &state->lease;
+	struct dhcp_message *dhcp;
+	struct timeval tv;
+	unsigned int offset = 0;
+	struct stat sb;
+
+	if (!IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+		logger(LOG_INFO, "trying to use old lease in `%s'",
+		       iface->leasefile);
+	if ((dhcp = read_lease(iface)) == NULL) {
+		if (errno != ENOENT)
+			logger(LOG_INFO, "read_lease: %s", strerror(errno));
+		goto eexit;
+	}
+	if (stat(iface->leasefile, &sb) == -1) {
+		logger(LOG_ERR, "stat: %s", strerror(errno));
+		goto eexit;
+	}
+	get_lease(&state->lease, dhcp);
+	lease->frominfo = 1;
+	lease->leasedfrom = sb.st_mtime;
+
+	/* Vitaly important we remove the server information here */
+	state->lease.server.s_addr = 0;
+	dhcp->servername[0] = '\0';
+
+	if (!IN_LINKLOCAL(ntohl(dhcp->yiaddr))) {
+#ifndef THERE_IS_NO_FORK
+		if (!(state->options & DHCPCD_LASTLEASE))
+			goto eexit;
+#endif
+
+		/* Ensure that we can still use the lease */
+		if (gettimeofday(&tv, NULL) == -1) {
+			logger(LOG_ERR, "gettimeofday: %s", strerror(errno));
+			goto eexit;
+		}
+
+		offset = tv.tv_sec - lease->leasedfrom;
+		if (lease->leasedfrom &&
+		    tv.tv_sec - lease->leasedfrom > lease->leasetime)
+		{
+			logger(LOG_ERR, "lease expired %u seconds ago",
+			       offset + lease->leasetime);
+			/* Persistent interfaces should still try and use the
+			 * lease if we can't contact a DHCP server.
+			 * We just set the timeout to 1 second. */
+			if (state->options & DHCPCD_PERSISTENT)
+				offset = lease->renewaltime - 1;
+			else
+				goto eexit;
+		}
+	}
+
+	if (lease->leasedfrom == 0)
+		offset = 0;
+	state->timeout = lease->renewaltime - offset;
+	iface->start_uptime = uptime();
+	free(state->old);
+	state->old = state->new;
+	state->new = NULL;
+	state->offer = dhcp;
+	return 0;
+
+eexit:
+	lease->addr.s_addr = 0;
+	free(dhcp);
+	return -1;
+}
+
+static int
+client_setup(struct if_state *state, const struct options *options)
+{
+	struct interface *iface = state->interface;
+	struct dhcp_lease *lease = &state->lease;
+	struct in_addr addr;
+#ifndef MINIMAL
+	size_t len = 0;
+	unsigned char *duid = NULL;
+	uint32_t ul;
+#endif
+
+	state->state = STATE_INIT;
+	state->nakoff = 1;
+	state->options = options->options;
+
+	if (options->request_address.s_addr == 0 &&
+	    (options->options & DHCPCD_INFORM ||
+	     options->options & DHCPCD_REQUEST ||
+	     options->options & DHCPCD_DAEMONISED))
+	{
+		if (get_old_lease(state) != 0)
+			return -1;
+		state->timeout = 0;
+
+		if (!(options->options & DHCPCD_DAEMONISED) &&
+		    IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
+		{
+			logger(LOG_ERR, "cannot request a link local address");
+			return -1;
+		}
+#ifdef THERE_IS_NO_FORK
+		if (options->options & DHCPCD_DAEMONISED) {
+			state->state = STATE_BOUND;
+			state->timeout = state->lease.renewaltime;
+			iface->addr.s_addr = lease->addr.s_addr;
+			iface->net.s_addr = lease->net.s_addr;
+			get_option_addr(&lease->server.s_addr,
+					state->offer, DHCP_SERVERID);
+		}
+#endif
+	} else {
+		lease->addr.s_addr = options->request_address.s_addr;
+		lease->net.s_addr = options->request_netmask.s_addr;
+	}
+
+	/* If INFORMing, ensure the interface has the address */
+	if (state->options & DHCPCD_INFORM &&
+	    has_address(iface->name, &lease->addr, &lease->net) < 1)
+	{
+		addr.s_addr = lease->addr.s_addr | ~lease->net.s_addr;
+		logger(LOG_DEBUG, "adding IP address %s/%d",
+		       inet_ntoa(lease->addr), inet_ntocidr(lease->net));
+		if (add_address(iface->name, &lease->addr,
+				&lease->net, &addr) == -1)
+		{
+			logger(LOG_ERR, "add_address: %s", strerror(errno));
+			return -1;
+		}
+		iface->addr.s_addr = lease->addr.s_addr;
+		iface->net.s_addr = lease->net.s_addr;
+	}
+
+#ifndef MINIMAL
+	if (*options->clientid) {
+		iface->clientid = xmalloc(options->clientid[0] + 1);
+		memcpy(iface->clientid,
+		       options->clientid, options->clientid[0] + 1);
+	} else if (options->options & DHCPCD_CLIENTID) {
+		if (options->options & DHCPCD_DUID) {
+			duid = xmalloc(DUID_LEN);
+			if ((len = get_duid(duid, iface)) == 0)
+				logger(LOG_ERR, "get_duid: %s",
+				       strerror(errno));
+		}
+
+		if (len > 0) {
+			logger(LOG_INFO, "DUID = %s",
+			       hwaddr_ntoa(duid, len));
+
+			iface->clientid = xmalloc(len + 6);
+			iface->clientid[0] = len + 5;
+			iface->clientid[1] = 255; /* RFC 4361 */
+
+			/* IAID is 4 bytes, so if the iface name is 4 bytes
+			 * or less, use it */
+			ul = strlen(iface->name);
+			if (ul < 5) {
+				memcpy(iface->clientid + 2, iface->name, ul);
+				if (ul < 4)
+					memset(iface->clientid + 2 + ul,
+					       0, 4 - ul);
+			} else {
+				/* Name isn't 4 bytes, so use the index */
+				ul = htonl(if_nametoindex(iface->name));
+				memcpy(iface->clientid + 2, &ul, 4);
+			}
+
+			memcpy(iface->clientid + 6, duid, len);
+			free(duid);
+		}
+		if (len == 0) {
+			len = iface->hwlen + 1;
+			iface->clientid = xmalloc(len + 1);
+			iface->clientid[0] = len;
+			iface->clientid[1] = iface->family;
+			memcpy(iface->clientid + 2, iface->hwaddr, iface->hwlen);
+		}
+	}
+#endif
+
+	return 0;
+}
+
+static int 
+do_socket(struct if_state *state, int mode)
+{
+	if (state->interface->fd >= 0) {
+		close(state->interface->fd);
+		state->interface->fd = -1;
+	}
+	if (mode == SOCKET_CLOSED && state->interface->udp_fd >= 0) {
+		close(state->interface->udp_fd);
+		state->interface->udp_fd = -1;
+	}
+
+	/* We need to bind to a port, otherwise we generate ICMP messages
+	 * that cannot connect the port when we have an address.
+	 * We don't actually use this fd at all, instead using our packet
+	 * filter socket. */
+	if (mode == SOCKET_OPEN &&
+	    state->interface->udp_fd == -1 &&
+	    state->lease.addr.s_addr != 0)
+		if (open_udp_socket(state->interface) == -1) {
+			logger(LOG_ERR, "open_udp_socket: %s", strerror(errno));
+			return -1;
+		}
+
+	if (mode == SOCKET_OPEN) 
+		if (open_socket(state->interface, ETHERTYPE_IP) == -1) {
+			logger(LOG_ERR, "open_socket: %s", strerror(errno));
+			return -1;
+		}
+	state->socket = mode;
+	return 0;
+}
+
+static ssize_t
+send_message(struct if_state *state, int type, const struct options *options)
+{
+	struct dhcp_message *dhcp;
+	uint8_t *udp;
+	ssize_t len;
+	ssize_t r;
+	struct in_addr from;
+	struct in_addr to;
+
+	logger(LOG_DEBUG, "sending %s with xid 0x%x",
+	       get_dhcp_op(type), state->xid);
+	state->messages++;
+	len = make_message(&dhcp, state->interface, &state->lease, state->xid,
+			   type, options);
+	from.s_addr = dhcp->ciaddr;
+	if (from.s_addr)
+		to.s_addr = state->lease.server.s_addr;
+	else
+		to.s_addr = 0;
+	if (to.s_addr) {
+		r = send_packet(state->interface, to, (uint8_t *)dhcp, len);
+		if (r == -1)
+			logger(LOG_ERR, "send_packet: %s", strerror(errno));
+	} else {
+		len = make_udp_packet(&udp, (uint8_t *)dhcp, len, from, to);
+		free(dhcp);
+		r = send_raw_packet(state->interface, ETHERTYPE_IP, udp, len);
+		if (r == -1)
+			logger(LOG_ERR, "send_raw_packet: %s", strerror(errno));
+		free(udp);
+	}
+	return r;
+}
+
+static void
+drop_config(struct if_state *state, const char *reason, const struct options *options)
+{
+	configure(state->interface, reason, NULL, state->new,
+		  &state->lease, options, 0);
+	free(state->old);
+	state->old = NULL;
+	free(state->new);
+	state->new = NULL;
+
+	state->lease.addr.s_addr = 0;
+}
+
+static int
+wait_for_packet(struct if_state *state)
+{
+	struct pollfd fds[3]; /* iface, arp, signal */
+	int retval, timeout, nfds = 0;
+	time_t start;
+	struct timeval now, d;
+
+	/* We always listen to signals */
+	fds[nfds].fd = state->signal_fd;
+	fds[nfds].events = POLLIN;
+	nfds++;
+
+	if (state->lease.leasetime == ~0U && state->state == STATE_BOUND) {
+		logger(LOG_DEBUG, "waiting for infinity");
+		timeout = INFTIM;
+	} else {
+		timeout = state->timeout;
+		if (timerisset(&state->stop)) {
+			get_time(&now);
+			if (timercmp(&state->stop, &now, >)) {
+				timersub(&state->stop, &now, &d);
+				retval = d.tv_sec * 1000 + (d.tv_usec + 999) / 1000;
+				if (retval < timeout)
+					timeout = retval;
+			}
+		}
+		if (timeout <= 0)
+			return 0;
+		if (state->interface->fd != -1) {
+			fds[nfds].fd = state->interface->fd;
+			fds[nfds].events = POLLIN;
+			nfds++;
+		}
+#ifdef ENABLE_ARP
+		if (state->interface->arp_fd != -1) {
+			fds[nfds].fd = state->interface->arp_fd;
+			fds[nfds].events = POLLIN;
+			nfds++;
+		}
+#endif
+		logger(LOG_DEBUG, "waiting for %0.3f seconds",
+		       (float)timeout / 1000);
+	}
+
+	start = uptime();
+	retval = poll(fds, nfds, timeout);
+	if (timeout != INFTIM) {
+		state->timeout -= uptime() - start;
+		if (state->timeout < 0)
+			state->timeout = 0;
+	}
+	if (retval == -1) {
+		if (errno == EINTR)
+			return 0;
+		logger(LOG_ERR, "poll: %s", strerror(errno));
+	}
+	return retval;
+}
+
+static int
+handle_signal(int sig, struct if_state *state,  const struct options *options)
+{
+	struct dhcp_lease *lease = &state->lease;
+
+	switch (sig) {
+	case SIGINT:
+		logger(LOG_INFO, "received SIGINT, stopping");
+		if (!(state->options & DHCPCD_PERSISTENT))
+			drop_config(state, "STOP", options);
+		return -1;
+	case SIGTERM:
+		logger(LOG_INFO, "received SIGTERM, stopping");
+		if (!(state->options & DHCPCD_PERSISTENT))
+			drop_config(state, "STOP", options);
+		return -1;
+
+	case SIGALRM:
+		logger (LOG_INFO, "received SIGALRM, renewing lease");
+		switch (state->state) {
+		case STATE_BOUND:
+		case STATE_RENEWING:
+		case STATE_REBINDING:
+		case STATE_ANNOUNCING:
+			state->state = STATE_RENEW_REQUESTED;
+			break;
+		case STATE_RENEW_REQUESTED:
+		case STATE_REQUESTING:
+			state->state = STATE_INIT;
+			break;
+		}
+		timerclear(&state->stop);
+		state->timeout = 0;
+		return 0;
+
+	case SIGHUP:
+		if (state->state != STATE_BOUND &&
+		    state->state != STATE_RENEWING &&
+		    state->state != STATE_REBINDING)
+		{
+			logger(LOG_ERR,
+			       "received SIGHUP, but no lease to release");
+			return -1;
+		}
+
+		logger (LOG_INFO, "received SIGHUP, releasing lease");
+		if (!IN_LINKLOCAL(ntohl(lease->addr.s_addr))) {
+			do_socket(state, SOCKET_OPEN);
+			state->xid = arc4random();
+			send_message(state, DHCP_RELEASE, options);
+			do_socket(state, SOCKET_CLOSED);
+		}
+		drop_config(state, "RELEASE", options);
+		return -1;
+
+	default:
+		logger (LOG_ERR,
+			"received signal %d, but don't know what to do with it",
+			sig);
+	}
+
+	return -1;
+}
+
+static int bind_dhcp(struct if_state *state, const struct options *options)
+{
+	struct interface *iface = state->interface;
+	struct dhcp_lease *lease = &state->lease;
+	const char *reason = NULL;
+	struct timeval tv;
+	int retval;
+
+	free(state->old);
+	state->old = state->new;
+	state->new = state->offer;
+	state->offer = NULL;
+#ifdef ENABLE_ARP
+	state->conflicts = 0;
+	state->defend = 0;
+#endif
+
+	if (options->options & DHCPCD_INFORM) {
+		if (options->request_address.s_addr != 0)
+			lease->addr.s_addr = options->request_address.s_addr;
+		else
+			lease->addr.s_addr = iface->addr.s_addr;
+		logger(LOG_INFO, "received approval for %s",
+		       inet_ntoa(lease->addr));
+		state->state = STATE_BOUND;
+		state->lease.leasetime = ~0U;
+		reason = "INFORM";
+	} else if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
+		get_lease(lease, state->new);
+		logger(LOG_INFO, "using IPv4LL address %s",
+		       inet_ntoa(lease->addr));
+		state->state = STATE_INIT;
+		state->timeout = 0;
+		reason = "IPV4LL";
+	} else {
+		if (gettimeofday(&tv, NULL) == 0)
+			lease->leasedfrom = tv.tv_sec;
+
+		get_lease(lease, state->new);
+		if (lease->frominfo)
+			reason = "TIMEOUT";
+
+		if (lease->leasetime == ~0U) {
+			lease->renewaltime = lease->rebindtime = lease->leasetime;
+			state->timeout = 1; /* So we wait for infinity */
+			logger(LOG_INFO, "leased %s for infinity",
+			       inet_ntoa(lease->addr));
+			state->state = STATE_BOUND;
+		} else {
+			logger(LOG_INFO, "leased %s for %u seconds",
+			       inet_ntoa(lease->addr), lease->leasetime);
+
+			if (lease->rebindtime >= lease->leasetime) {
+				lease->rebindtime = (lease->leasetime * T2);
+				logger(LOG_ERR,
+				       "rebind time greater than lease "
+				       "time, forcing to %u seconds",
+				       lease->rebindtime);
+			}
+
+			if (lease->renewaltime > lease->rebindtime) {
+				lease->renewaltime = (lease->leasetime * T1);
+				logger(LOG_ERR,
+				       "renewal time greater than rebind time, "
+				       "forcing to %u seconds",
+				       lease->renewaltime);
+			}
+
+			if (!lease->renewaltime) {
+				lease->renewaltime = (lease->leasetime * T1);
+				logger(LOG_INFO,
+				       "no renewal time supplied, assuming %d seconds",
+				       lease->renewaltime);
+			} else
+				logger(LOG_DEBUG, "renew in %u seconds",
+				       lease->renewaltime);
+
+			if (!lease->rebindtime) {
+				lease->rebindtime = (lease->leasetime * T2);
+				logger(LOG_INFO,
+				       "no rebind time supplied, assuming %d seconds",
+				       lease->rebindtime);
+			} else
+				logger(LOG_DEBUG, "rebind in %u seconds",
+				       lease->rebindtime);
+
+			state->timeout = lease->renewaltime * 1000;
+		}
+		state->state = STATE_BOUND;
+	}
+
+	state->xid = 0;
+	timerclear(&state->stop);
+	if (!reason) {
+		if (state->old) {
+			if (state->old->yiaddr == state->new->yiaddr &&
+			    lease->server.s_addr)
+				reason = "RENEW";
+			else
+				reason = "REBIND";
+		} else
+			reason = "BOUND";
+	}
+	retval = configure(iface, reason, state->new, state->old,
+			   &state->lease, options, 1);
+	if (retval != 0)
+		return -1;
+	return daemonise(state, options);
+}
+
+static int
+handle_timeout_fail(struct if_state *state, const struct options *options)
+{
+	struct dhcp_lease *lease = &state->lease;
+	struct interface *iface = state->interface;
+	int gotlease = -1;
+	const char *reason = NULL;
+	struct timeval tv;
+
+	timerclear(&tv);
+	/* Clear our timers and counters as we've failed.
+	 * We'll either abort or move to another state with new timers */
+	timerclear(&state->stop);
+	state->messages = 0;
+	state->timeout = 0;
+
+	switch (state->state) {
+	case STATE_DISCOVERING:
+		/* FALLTHROUGH */
+	case STATE_REQUESTING:
+		if (IN_LINKLOCAL(ntohl(iface->addr.s_addr))) {
+			if (!(state->options & DHCPCD_DAEMONISED))
+				logger(LOG_ERR, "timed out");
+		} else {
+			if (iface->addr.s_addr != 0 &&
+			    !(state->options & DHCPCD_INFORM))
+				logger(LOG_ERR, "lost lease");
+			else
+				logger(LOG_ERR, "timed out");
+		}
+		do_socket(state, SOCKET_CLOSED);
+		if (state->options & DHCPCD_INFORM ||
+		    state->options & DHCPCD_TEST)
+			return -1;
+
+		if (state->options & DHCPCD_IPV4LL ||
+		    state->options & DHCPCD_LASTLEASE)
+			gotlease = get_old_lease(state);
+
+#ifdef ENABLE_IPV4LL
+		if (state->options & DHCPCD_IPV4LL && gotlease != 0) {
+			logger(LOG_INFO, "probing for an IPV4LL address");
+			free(state->offer);
+			state->offer = ipv4ll_get_dhcp(0);
+			gotlease = 0;
+		}
+#endif
+
+#ifdef ENABLE_ARP
+		if (gotlease == 0 &&
+		    state->offer->yiaddr != iface->addr.s_addr)
+		{
+			state->state = STATE_PROBING;
+			state->claims = 0;
+			state->probes = 0;
+			state->conflicts = 0;
+			return 0;
+		}
+#endif
+
+		if (gotlease == 0)
+			return bind_dhcp(state, options);
+
+		reason = "FAIL";
+		drop_config(state, reason, options);
+		if (!(state->options & DHCPCD_DAEMONISED) &&
+		    (state->options & DHCPCD_DAEMONISE))
+			return -1;
+		state->state = STATE_INIT;
+		break;
+	case STATE_RENEWING:
+		logger(LOG_ERR, "failed to renew, attempting to rebind");
+		lease->addr.s_addr = 0;
+		state->state = STATE_REBINDING;
+		tv.tv_sec = lease->rebindtime - lease->renewaltime;
+		break;
+	case STATE_REBINDING:
+		logger(LOG_ERR, "failed to rebind, attempting to discover");
+		reason = "EXPIRE";
+		drop_config(state, reason, options);
+		state->state = STATE_INIT;
+		break;
+	default:
+		logger(LOG_DEBUG, "handle_timeout_failed: invalid state %d",
+		       state->state);
+	}
+
+	get_time(&state->start);
+	if (timerisset(&tv))
+		timeradd(&state->start, &tv, &state->stop);
+
+	/* This effectively falls through into the handle_timeout funtion */
+	return 0;
+}
+
+static int
+handle_timeout(struct if_state *state, const struct options *options)
+{
+	struct dhcp_lease *lease = &state->lease;
+	struct interface *iface = state->interface;
+	int i;
+	struct timeval tv;
+	struct in_addr addr;
+
+#ifdef ENABLE_ARP
+	switch (state->state) {
+	case STATE_PROBING:
+		timerclear(&state->stop);
+		if (iface->arp_fd == -1)
+			open_socket(iface, ETHERTYPE_ARP);
+		if (state->probes < PROBE_NUM) {
+			if (state->probes == 0) {
+				addr.s_addr = state->offer->yiaddr;
+				logger(LOG_INFO, "checking %s is available"
+				       " on attached networks",
+				       inet_ntoa(addr));
+			}
+			state->probes++;
+			logger(LOG_DEBUG, "sending ARP probe #%d",
+			       state->probes);
+			if (state->probes < PROBE_NUM) 
+				state->timeout = (arc4random() %
+						  (PROBE_MAX - PROBE_MIN)) + PROBE_MIN;
+			else
+				state->timeout = ANNOUNCE_WAIT;
+			send_arp(iface, ARPOP_REQUEST, 0, state->offer->yiaddr);
+			return 0;
+		} else {
+			/* We've waited for ANNOUNCE_WAIT after the final probe
+			 * so the address is now ours */
+			i = bind_dhcp(state, options);
+			state->state = STATE_ANNOUNCING;
+			state->timeout = ANNOUNCE_INTERVAL;
+			return i;
+		}
+	case STATE_ANNOUNCING:
+		timerclear(&state->stop);
+		if (state->claims < ANNOUNCE_NUM) {
+			state->claims++;
+			logger(LOG_DEBUG, "sending ARP announce #%d",
+			       state->claims);
+			send_arp(iface, ARPOP_REQUEST,
+				 state->new->yiaddr, state->new->yiaddr);
+			if (state->claims < ANNOUNCE_NUM)
+				state->timeout = ANNOUNCE_INTERVAL;
+			else if (IN_LINKLOCAL(htonl(lease->addr.s_addr))) {
+				state->state = STATE_INIT;
+				state->timeout = 0;
+			} else {
+				state->state = STATE_BOUND;
+				state->timeout = lease->renewaltime * 1000 -
+					(ANNOUNCE_INTERVAL * ANNOUNCE_NUM);
+				close(iface->arp_fd);
+				iface->arp_fd = -1;
+			}
+		}
+		return 0;
+	}
+#endif
+
+	if (timerisset(&state->stop)) {
+		get_time(&tv);
+		if (timercmp(&tv, &state->stop, >))
+			return handle_timeout_fail(state, options);
+	}
+	timerclear(&tv);
+
+	switch (state->state) {
+	case STATE_INIT:  /* FALLTHROUGH */
+	case STATE_BOUND: /* FALLTHROUGH */
+	case STATE_RENEW_REQUESTED:
+		do_socket(state, SOCKET_OPEN);
+		state->xid = arc4random();
+		state->messages = 0;
+		state->nakoff = 1;
+		iface->start_uptime = uptime();
+		get_time(&state->start);
+		timerclear(&state->stop);
+	}
+
+	switch(state->state) {
+	case STATE_INIT:
+                /* 21Jul08 - was && DHCPCD_DAEMONISED */
+		if (!(state->state & DHCPCD_DAEMONISED) &&
+		    options->timeout &&		
+		    !IN_LINKLOCAL(htonl(iface->addr.s_addr)))
+		{
+			get_time(&state->start);
+			tv.tv_sec = options->timeout;
+			timeradd(&state->start, &tv, &state->stop);
+		}
+		if (lease->addr.s_addr == 0 ||
+		    IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+		{
+			logger(LOG_INFO, "broadcasting for a lease");
+			state->state = STATE_DISCOVERING;
+		} else if (state->options & DHCPCD_INFORM) {
+			logger(LOG_INFO, "broadcasting inform for %s",
+			       inet_ntoa(lease->addr));
+			state->state = STATE_REQUESTING;
+		} else {
+			logger(LOG_INFO, "broadcasting for a lease of %s",
+			       inet_ntoa(lease->addr));
+			state->state = STATE_REQUESTING;
+		}
+		break;
+	case STATE_RENEW_REQUESTED:
+	case STATE_BOUND:
+		if (IN_LINKLOCAL(ntohl(lease->addr.s_addr))) {
+			lease->addr.s_addr = 0;
+			state->state = STATE_INIT;
+			state->timeout = 0;
+			break;
+		}
+		logger(LOG_INFO, "renewing lease of %s",inet_ntoa(lease->addr));
+		state->state = STATE_RENEWING;
+		break;
+	}
+
+	switch (state->state) {
+	case STATE_DISCOVERING:
+		send_message(state, DHCP_DISCOVER, options);
+		break;
+	case STATE_REQUESTING:
+		if (state->options & DHCPCD_INFORM) {
+			send_message(state, DHCP_INFORM, options);
+			break;
+		}
+		/* FALLTHROUGH */
+	case STATE_RENEWING:   /* FALLTHROUGH */
+	case STATE_REBINDING:
+		send_message(state, DHCP_REQUEST, options);
+		break;
+	}
+
+	state->timeout = DHCP_BASE;
+	for (i = 1; i < state->messages; i++) {
+		state->timeout *= 2;
+		if (state->timeout > DHCP_MAX) {
+			state->timeout = DHCP_MAX;
+			break;
+		}
+	}
+	state->timeout += (arc4random() % (DHCP_RAND_MAX - DHCP_RAND_MIN)) +
+		DHCP_RAND_MIN;
+	return 0;
+}
+
+static int
+handle_dhcp(struct if_state *state, struct dhcp_message **dhcpp,
+	    const struct options *options)
+{
+	struct timespec ts;
+	struct dhcp_message *dhcp = *dhcpp;
+	struct interface *iface = state->interface;
+	struct dhcp_lease *lease = &state->lease;
+	char *addr;
+	struct in_addr saddr;
+	uint8_t type;
+	int r;
+
+	if (get_option_uint8(&type, dhcp, DHCP_MESSAGETYPE) == -1) {
+		logger(LOG_ERR, "no DHCP type in message");
+		return -1;
+	}
+
+	/* reset the message counter */
+	state->messages = 0;
+
+	/* We should restart on a NAK */
+	if (type == DHCP_NAK) {
+		addr = get_option_string(dhcp, DHCP_MESSAGE);
+		logger(LOG_WARNING, "received NAK: %s", addr);
+		free(addr);
+		state->state = STATE_INIT;
+		state->timeout = 0;
+		lease->addr.s_addr = 0;
+		timerclear(&state->stop);
+
+		/* If we constantly get NAKS then we should slowly back off */
+		if (state->nakoff > 0) {
+			logger(LOG_DEBUG, "sleeping for %lu seconds",
+			       (unsigned long)state->nakoff);
+			ts.tv_sec = state->nakoff;
+			ts.tv_nsec = 0;
+			state->nakoff *= 2;
+			if (state->nakoff > NAKOFF_MAX)
+				state->nakoff = NAKOFF_MAX;
+			nanosleep(&ts, NULL);
+		}
+
+		return 0;
+	}
+
+	/* No NAK, so reset the backoff */
+	state->nakoff = 1;
+
+	if (type == DHCP_OFFER && state->state == STATE_DISCOVERING) {
+		lease->addr.s_addr = dhcp->yiaddr;
+		addr = xstrdup(inet_ntoa(lease->addr));
+		r = get_option_addr(&lease->server.s_addr, dhcp, DHCP_SERVERID);
+		if (dhcp->servername[0] && r == 0)
+			logger(LOG_INFO, "offered %s from %s `%s'",
+			       addr, inet_ntoa(lease->server),
+			       dhcp->servername);
+		else if (r == 0)
+			logger(LOG_INFO, "offered %s from %s",
+			       addr, inet_ntoa(lease->server));
+		else
+			logger(LOG_INFO, "offered %s", addr);
+		free(addr);
+
+		if (state->options & DHCPCD_TEST) {
+			exec_script(options, iface->name, "TEST", dhcp, NULL);
+			free(dhcp);
+			return 0;
+		}
+
+		free(dhcp);
+		state->state = STATE_REQUESTING;
+		state->timeout = 0;
+		return 0;
+	}
+
+	if (type == DHCP_OFFER) {
+		saddr.s_addr = dhcp->yiaddr;
+		logger(LOG_INFO, "got subsequent offer of %s, ignoring ",
+		       inet_ntoa(saddr));
+		free(dhcp);
+		return 0;
+	}
+
+	/* We should only be dealing with acks */
+	if (type != DHCP_ACK) {
+		logger(LOG_ERR, "%d not an ACK or OFFER", type);
+		free(dhcp);
+		return 0;
+	}
+	    
+	switch (state->state) {
+	case STATE_RENEW_REQUESTED:
+	case STATE_REQUESTING:
+	case STATE_RENEWING:
+	case STATE_REBINDING:
+		if (!(state->options & DHCPCD_INFORM)) {
+			saddr.s_addr = dhcp->yiaddr;
+			logger(LOG_INFO, "lease of %s acknowledged",
+			       inet_ntoa(saddr));
+		}
+		break;
+	default:
+		logger(LOG_ERR, "wrong state %d", state->state);
+	}
+
+	do_socket(state, SOCKET_CLOSED);
+	free(state->offer);
+	state->offer = dhcp;
+	*dhcpp = NULL;
+
+#ifdef ENABLE_ARP
+	if (state->options & DHCPCD_ARP &&
+	    iface->addr.s_addr != state->offer->yiaddr)
+	{
+		state->state = STATE_PROBING;
+		state->timeout = 0;
+		state->claims = 0;
+		state->probes = 0;
+		state->conflicts = 0;
+		timerclear(&state->stop);
+		return 0;
+	}
+#endif
+
+	return bind_dhcp(state, options);		
+}
+
+static int
+handle_dhcp_packet(struct if_state *state, const struct options *options)
+{
+	uint8_t *packet;
+	struct interface *iface = state->interface;
+	struct dhcp_message *dhcp;
+	const uint8_t *pp;
+	uint8_t *p;
+	ssize_t bytes;
+	int retval = -1;
+
+	/* We loop through until our buffer is empty.
+	 * The benefit is that if we get >1 DHCP packet in our buffer and
+	 * the first one fails for any reason, we can use the next. */
+	packet = xmalloc(udp_dhcp_len);
+	dhcp = xmalloc(sizeof(*dhcp));
+	for(;;) {
+		bytes = get_raw_packet(iface, ETHERTYPE_IP,
+				       packet, udp_dhcp_len);
+		if (bytes == 0) {
+			retval = 0;
+			break;
+		}
+		if (bytes == -1)
+			break;
+		if (valid_udp_packet(packet) == -1)
+			continue;
+		bytes = get_udp_data(&pp, packet);
+		if ((size_t)bytes > sizeof(*dhcp)) {
+			logger(LOG_ERR, "packet greater than DHCP size");
+			continue;
+		}
+		memcpy(dhcp, pp, bytes);
+		if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
+			logger(LOG_DEBUG, "bogus cookie, ignoring");
+			continue;
+		}
+		if (state->xid != dhcp->xid) {
+			logger(LOG_DEBUG,
+			       "ignoring packet with xid 0x%x as"
+			       " it's not ours (0x%x)",
+			       dhcp->xid, state->xid);
+			continue;
+		}
+		/* We should ensure that the packet is terminated correctly
+		 * if we have space for the terminator */
+		if ((size_t)bytes < sizeof(struct dhcp_message)) {
+			p = (uint8_t *)dhcp + bytes - 1;
+			while (p > dhcp->options && *p == DHCP_PAD)
+				p--;
+			if (*p != DHCP_END)
+				*++p = DHCP_END;
+		}
+		free(packet);
+		if (handle_dhcp(state, &dhcp, options) == 0) {
+			/* Fake the fact we forked so we return 0 to userland */
+			if (state->options & DHCPCD_TEST)
+				state->options |= DHCPCD_FORKED;
+			else
+				return 0;
+		}
+		if (state->options & DHCPCD_FORKED)
+			return -1;
+	}
+
+	free(packet);
+	free(dhcp);
+	return retval;
+}
+
+#ifdef ENABLE_ARP
+static int
+handle_arp_packet(struct if_state *state)
+{
+	struct arphdr reply;
+	uint32_t reply_s;
+	uint32_t reply_t;
+	uint8_t arp_reply[sizeof(reply) + 2 * sizeof(reply_s) + 2 * HWADDR_LEN];
+	uint8_t *hw_s, *hw_t;
+	ssize_t bytes;
+	struct interface *iface = state->interface;
+
+	state->fail.s_addr = 0;
+
+	for(;;) {
+		bytes = get_raw_packet(iface, ETHERTYPE_ARP,
+				       arp_reply, sizeof(arp_reply));
+		if (bytes == 0 || bytes == -1)
+			return (int)bytes;
+		/* We must have a full ARP header */
+		if ((size_t)bytes < sizeof(reply))
+			continue;
+		memcpy(&reply, arp_reply, sizeof(reply));
+		/* Protocol must be IP. */
+		if (reply.ar_pro != htons(ETHERTYPE_IP))
+			continue;
+		if (reply.ar_pln != sizeof(reply_s))
+			continue;
+		/* Only these types are recognised */
+		if (reply.ar_op != htons(ARPOP_REPLY) &&
+		    reply.ar_op != htons(ARPOP_REQUEST))
+			continue;
+
+		/* Get pointers to the hardware addreses */
+		hw_s = arp_reply + sizeof(reply);
+		hw_t = hw_s + reply.ar_hln + reply.ar_pln;
+		/* Ensure we got all the data */
+		if ((hw_t + reply.ar_hln + reply.ar_pln) - arp_reply > bytes)
+			continue;
+		/* Copy out the IP addresses */
+		memcpy(&reply_s, hw_s + reply.ar_hln, reply.ar_pln);
+		memcpy(&reply_t, hw_t + reply.ar_hln, reply.ar_pln);
+
+		/* Check for conflict */
+		if (state->offer && 
+		    (reply_s == state->offer->yiaddr ||
+		     (reply_t == state->offer->yiaddr &&
+		      reply.ar_op == htons(ARPOP_REQUEST) &&
+		      (iface->hwlen != reply.ar_hln ||
+		       memcmp(hw_s, iface->hwaddr, iface->hwlen) != 0))))
+			state->fail.s_addr = state->offer->yiaddr;
+
+		/* Handle IPv4LL conflicts */
+		if (IN_LINKLOCAL(htonl(iface->addr.s_addr)) &&
+		    (reply_s == iface->addr.s_addr ||
+		     (reply_t == iface->addr.s_addr &&
+		      reply.ar_op == htons(ARPOP_REQUEST) &&
+		      (iface->hwlen != reply.ar_hln ||
+		       memcmp(hw_s, iface->hwaddr, iface->hwlen) != 0))))
+			state->fail.s_addr = iface->addr.s_addr;
+
+		if (state->fail.s_addr) {
+			logger(LOG_ERR, "hardware address %s claims %s",
+			       hwaddr_ntoa((unsigned char *)hw_s,
+					   (size_t)reply.ar_hln),
+			       inet_ntoa(state->fail));
+			errno = EEXIST;
+			return -1;
+		}
+	}
+}
+
+static int
+handle_arp_fail(struct if_state *state, const struct options *options)
+{
+	struct timespec ts;
+	time_t up;
+
+	if (IN_LINKLOCAL(htonl(state->fail.s_addr))) {
+		if (state->fail.s_addr == state->interface->addr.s_addr) {
+			up = uptime();
+			if (state->defend + DEFEND_INTERVAL > up) {
+				drop_config(state, "FAIL", options);
+				state->state = STATE_PROBING;
+				state->timeout = 0;
+				state->claims = 0;
+				state->probes = 0;
+				state->conflicts = 0;
+				timerclear(&state->stop);
+			} else
+				state->defend = up;
+			return 0;
+		}
+
+		timerclear(&state->stop);
+		state->conflicts++;
+		state->timeout = 0;
+		state->claims = 0;
+		state->probes = 0;
+		state->state = STATE_PROBING;
+		free(state->offer);
+		if (state->conflicts > MAX_CONFLICTS) {
+			/* RFC 3927 says we should rate limit */
+			logger(LOG_INFO, "sleeping for %d seconds",
+			       RATE_LIMIT_INTERVAL);
+			ts.tv_sec = RATE_LIMIT_INTERVAL;
+			ts.tv_nsec = 0;
+			nanosleep(&ts, NULL);
+		}
+		state->offer = ipv4ll_get_dhcp(0);
+		return 0;
+	}
+
+	do_socket(state, SOCKET_OPEN);
+	send_message(state, DHCP_DECLINE, options);
+	state->timeout = 0;
+	state->state = STATE_INIT;
+	/* RFC 2131 says that we should wait for 10 seconds
+	 * before doing anything else */
+	logger(LOG_INFO, "sleeping for 10 seconds");
+	ts.tv_sec = 10;
+	ts.tv_nsec = 0;
+	nanosleep(&ts, NULL);
+	return 0;
+}
+#endif
+
+int
+dhcp_run(const struct options *options, int *pid_fd)
+{
+	struct interface *iface;
+	struct if_state *state = NULL;
+	int retval = -1;
+	int sig;
+
+	iface = read_interface(options->interface, options->metric);
+	if (!iface) {
+		logger(LOG_ERR, "read_interface: %s", strerror(errno));
+		goto eexit;
+	}
+
+	logger(LOG_INFO, "hardware address = %s",
+	       hwaddr_ntoa(iface->hwaddr, iface->hwlen));
+
+	state = xzalloc(sizeof(*state));
+	state->pid_fd = pid_fd;
+	state->interface = iface;
+
+	if (client_setup(state, options) == -1)
+		goto eexit;
+	if (signal_init() == -1)
+		goto eexit;
+	if (signal_setup() == -1)
+		goto eexit;
+
+	state->signal_fd = signal_fd();
+
+	for (;;) {
+		retval = wait_for_packet(state);
+
+		/* We should always handle our signals first */
+		if ((sig = (signal_read(state->signal_fd))) != -1) {
+			retval = handle_signal(sig, state, options);
+		} else if (retval == 0)
+			retval = handle_timeout(state, options);
+		else if (retval == -1) {
+			if (errno == EINTR)
+				/* The interupt will be handled above */
+				retval = 0;
+		} else if (retval > 0) {
+			if (fd_hasdata(state->interface->fd) == 1)
+				retval = handle_dhcp_packet(state, options);
+#ifdef ENABLE_ARP
+			else if (fd_hasdata(state->interface->arp_fd) == 1) {
+				retval = handle_arp_packet(state);
+				if (retval == -1)
+					retval = handle_arp_fail(state, options);
+			}
+#endif
+			else
+				retval = 0;
+		}
+
+		if (retval != 0)
+			break;
+	}
+
+eexit:
+	if (iface) {
+		do_socket(state, SOCKET_CLOSED);
+		free_routes(iface->routes);
+		free(iface->clientid);
+		free(iface->buffer);
+		free(iface);
+	}
+
+	if (state) {
+		if (state->options & DHCPCD_FORKED)
+			retval = 0;
+		if (state->options & DHCPCD_DAEMONISED)
+			unlink(options->pidfile);
+		free(state->offer);
+		free(state->new);
+		free(state->old);
+		free(state);
+	}
+
+	return retval;
+}
diff --git a/client.h b/client.h
new file mode 100644
index 0000000..35a5e37
--- /dev/null
+++ b/client.h
@@ -0,0 +1,35 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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.
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "dhcpcd.h"
+
+int dhcp_run(const struct options *, int *);
+
+#endif
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..8bc2b78
--- /dev/null
+++ b/common.c
@@ -0,0 +1,304 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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/param.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef BSD
+#  include <paths.h>
+#endif
+#include <poll.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "logger.h"
+
+#ifndef _PATH_DEVNULL
+#  define _PATH_DEVNULL "/dev/null"
+#endif
+
+/* Handy routine to read very long lines in text files.
+ * This means we read the whole line and avoid any nasty buffer overflows. */
+ssize_t
+get_line(char **line, size_t *len, FILE *fp)
+{
+	char *p;
+	size_t last = 0;
+
+	while(!feof(fp)) {
+		if (*line == NULL || last != 0) {
+			*len += BUFSIZ;
+			*line = xrealloc(*line, *len);
+		}
+		p = *line + last;
+		memset(p, 0, BUFSIZ);
+		fgets(p, BUFSIZ, fp);
+		last += strlen(p);
+		if (last && (*line)[last - 1] == '\n') {
+			(*line)[last - 1] = '\0';
+			break;
+		}
+	}
+	return last;
+}
+
+/* Simple hack to return a random number without arc4random */
+#ifndef HAVE_ARC4RANDOM
+uint32_t arc4random(void)
+{
+	int fd;
+	static unsigned long seed = 0;
+
+	if (!seed) {
+		fd = open("/dev/urandom", 0);
+		if (fd == -1 || read(fd,  &seed, sizeof(seed)) == -1)
+			seed = time(0);
+		if (fd >= 0)
+			close(fd);
+		srandom(seed);
+	}
+
+	return (uint32_t)random();
+}
+#endif
+
+/* strlcpy is nice, shame glibc does not define it */
+#if HAVE_STRLCPY
+#else
+size_t
+strlcpy(char *dst, const char *src, size_t size)
+{
+	const char *s = src;
+	size_t n = size;
+
+	if (n && --n)
+		do {
+			if (!(*dst++ = *src++))
+				break;
+		} while (--n);
+
+	if (!n) {
+		if (size)
+			*dst = '\0';
+		while (*src++);
+	}
+
+	return src - s - 1;
+}
+#endif
+
+#if HAVE_CLOSEFROM
+#else
+int
+closefrom(int fd)
+{
+	int max = getdtablesize();
+	int i;
+	int r = 0;
+
+	for (i = fd; i < max; i++)
+		r += close(i);
+	return r;
+}
+#endif
+
+/* Close our fd's */
+int
+close_fds(void)
+{
+	int fd;
+
+	if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1)
+		return -1;
+
+	dup2(fd, fileno(stdin));
+	dup2(fd, fileno(stdout));
+	dup2(fd, fileno(stderr));
+	if (fd > 2)
+		close(fd);
+	return 0;
+}
+
+int
+fd_hasdata(int fd)
+{
+	struct pollfd fds;
+	int retval;
+
+	if (fd == -1)
+		return -1;
+	fds.fd = fd;
+	fds.events = POLLIN;
+	fds.revents = 0;
+	retval = poll(&fds, 1, 0);
+	if (retval == -1)
+		return -1;
+	if (retval > 0 && fds.revents & POLLIN)
+		return retval;
+	return 0;
+}
+
+int
+set_cloexec(int fd)
+{
+	int flags;
+
+	if ((flags = fcntl(fd, F_GETFD, 0)) == -1
+	    || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+	{
+		logger(LOG_ERR, "fcntl: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+int
+set_nonblock(int fd)
+{
+	int flags;
+
+	if ((flags = fcntl(fd, F_GETFL, 0)) == -1
+	    || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+	{
+		logger(LOG_ERR, "fcntl: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+/* Handy function to get the time.
+ * We only care about time advancements, not the actual time itself
+ * Which is why we use CLOCK_MONOTONIC, but it is not available on all
+ * platforms.
+ */
+int
+get_time(struct timeval *tp)
+{
+#if defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC)
+	struct timespec ts;
+	static clockid_t posix_clock;
+	static int posix_clock_set = 0;
+
+	if (!posix_clock_set) {
+		if (sysconf(_SC_MONOTONIC_CLOCK) >= 0)
+			posix_clock = CLOCK_MONOTONIC;
+		else
+			posix_clock = CLOCK_REALTIME;
+		posix_clock_set = 1;
+	}
+
+	if (clock_gettime(posix_clock, &ts) == -1)
+		return -1;
+
+	tp->tv_sec = ts.tv_sec;
+	tp->tv_usec = ts.tv_nsec / 1000;
+	return 0;
+#else
+	return gettimeofday(tp, NULL);
+#endif
+}
+
+time_t
+uptime(void)
+{
+	struct timeval tp;
+
+	if (get_time(&tp) == -1)
+		return -1;
+	return tp.tv_sec;
+}
+
+int
+writepid(int fd, pid_t pid)
+{
+	char spid[16];
+	ssize_t len;
+
+	if (ftruncate(fd, (off_t)0) == -1)
+		return -1;
+	snprintf(spid, sizeof(spid), "%u", pid);
+	len = pwrite(fd, spid, strlen(spid), (off_t)0);
+	if (len != (ssize_t)strlen(spid))
+		return -1;
+	return 0;
+}
+
+void *
+xmalloc(size_t s)
+{
+	void *value = malloc(s);
+
+	if (value)
+		return value;
+	logger(LOG_ERR, "memory exhausted");
+	exit (EXIT_FAILURE);
+	/* NOTREACHED */
+}
+
+void *
+xzalloc(size_t s)
+{
+	void *value = xmalloc(s);
+
+	memset(value, 0, s);
+	return value;
+}
+
+void *
+xrealloc(void *ptr, size_t s)
+{
+	void *value = realloc(ptr, s);
+
+	if (value)
+		return (value);
+	logger(LOG_ERR, "memory exhausted");
+	exit(EXIT_FAILURE);
+	/* NOTREACHED */
+}
+
+char *
+xstrdup(const char *str)
+{
+	char *value;
+
+	if (!str)
+		return NULL;
+
+	if ((value = strdup(str)))
+		return value;
+
+	logger(LOG_ERR, "memory exhausted");
+	exit(EXIT_FAILURE);
+	/* NOTREACHED */
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..94a1cc0
--- /dev/null
+++ b/common.h
@@ -0,0 +1,86 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+/* string.h pulls in features.h so the below define checks work */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#define UNCONST(a)		((void *)(unsigned long)(const void *)(a))
+
+#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
+# define _unused __attribute__((__unused__))
+#else
+# define _unused
+#endif
+
+#ifndef HAVE_ARC4RANDOM
+# ifdef __GLIBC__
+uint32_t arc4random(void);
+#else
+# define HAVE_ARC4RANDOM
+# endif
+#endif
+
+#ifndef HAVE_STRLCPY
+#  define HAVE_STRLCPY 1
+#endif
+/* Only GLIBC doesn't support strlcpy */
+#ifdef __GLIBC__
+#  if !defined(__UCLIBC__) && !defined (__dietlibc__)
+#    undef HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+#  endif
+#endif
+
+#ifndef HAVE_CLOSEFROM
+#define HAVE_CLOSEFROM 1
+#endif
+#if defined(__linux__) || defined(__FreeBSD__)
+#  undef HAVE_CLOSEFROM
+int closefrom(int);
+#endif
+
+int close_fds(void);
+int set_cloexec(int);
+int set_nonblock(int);
+int fd_hasdata(int);
+ssize_t get_line(char **, size_t *, FILE *);
+int get_time(struct timeval *);
+time_t uptime(void);
+int writepid(int, pid_t);
+void *xrealloc(void *, size_t);
+void *xmalloc(size_t);
+void *xzalloc(size_t);
+char *xstrdup(const char *);
+
+#endif
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..8539420
--- /dev/null
+++ b/config.h
@@ -0,0 +1,102 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * 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.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define PACKAGE			"dhcpcd"
+#define VERSION			"4.0.0-beta9"
+
+/* You can enable/disable various chunks of optional code here.
+ * You would only do this to try and shrink the end binary if dhcpcd
+ * was running on a low memory device */
+
+/* Disable everything we possibly can. */
+#ifdef MINIMAL
+# ifndef DISABLE_ARP
+#  define DISABLE_ARP
+# endif
+# ifndef DISABLE_IPV4LL
+#  define DISABLE_IPV4LL
+# endif
+#endif
+
+/* Enable ARP by default. */
+#ifndef DISABLE_ARP
+# define ENABLE_ARP
+#endif
+
+/* IPV4LL, aka ZeroConf, aka APIPA, aka RFC 3927.
+ * Needs ARP. */
+#ifndef DISABLE_IPV4LL
+# ifdef ENABLE_ARP
+#  define ENABLE_IPV4LL
+# endif
+#endif
+
+/*
+ * By default we don't add a local link route if we got a routeable address.
+ * This is because dhcpcd can't really decide which interface should allow
+ * link local routing when we have more than one interface.
+ * Ideally the host network scripts should add the link local route for us.
+ * If not, you can define this to get dhcpcd to always add the link local route.
+ */
+// #define ENABLE_IPV4LL_ALWAYSROUTE 
+
+/* Some systems do not have a working fork. */
+/* #define THERE_IS_NO_FORK */
+
+/* Paths to things */
+#ifndef SYSCONFDIR
+# define SYSCONFDIR		"/system/etc/dhcpcd"
+#endif
+#ifndef LIBEXECDIR
+# define LIBEXECDIR		"/system/etc/dhcpcd"
+#endif
+#ifndef RUNDIR
+# define RUNDIR			"/data/misc/dhcp"
+#endif
+#ifndef DBDIR
+# define DBDIR			"/data/misc/dhcp"
+#endif
+
+#ifndef CONFIG
+# define CONFIG			SYSCONFDIR "/" PACKAGE ".conf"
+#endif
+#ifndef SCRIPT
+# define SCRIPT			LIBEXECDIR "/" PACKAGE "-run-hooks"
+#endif
+#ifndef DUID
+# define DUID			SYSCONFDIR "/" PACKAGE ".duid"
+#endif
+#ifndef LEASEFILE
+# define LEASEFILE		DBDIR "/" PACKAGE "-%s.lease"
+#endif
+#ifndef PIDFILE
+# define PIDFILE		RUNDIR "/" PACKAGE "-%s.pid"
+#endif
+
+#endif
diff --git a/configure.c b/configure.c
new file mode 100644
index 0000000..8c024f8
--- /dev/null
+++ b/configure.c
@@ -0,0 +1,496 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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/stat.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "configure.h"
+#include "dhcp.h"
+#include "dhcpcd.h"
+#include "logger.h"
+#include "net.h"
+#include "signals.h"
+
+#define DEFAULT_PATH	"PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+
+int
+exec_script(const struct options *options, const char *iface,
+	    const char *reason,
+	    const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
+{
+	char *const argv[2] = { UNCONST(options->script), NULL };
+	char **env = NULL, **ep;
+	char *path;
+	ssize_t e, elen;
+	int ret = 0;
+	pid_t pid;
+	int status = 0;
+	sigset_t full;
+	sigset_t old;
+
+	logger(LOG_DEBUG, "executing `%s'", options->script);
+
+	/* Make our env */
+	elen = 5;
+	env = xmalloc(sizeof(char *) * (elen + 1));
+	path = getenv("PATH");
+	if (path) {
+		e = strlen("PATH") + strlen(path) + 2;
+		env[0] = xmalloc(e);
+		snprintf(env[0], e, "PATH=%s", path);
+	} else
+		env[0] = xstrdup(DEFAULT_PATH);
+	e = strlen("interface") + strlen(iface) + 2;
+	env[1] = xmalloc(e);
+	snprintf(env[1], e, "interface=%s", iface);
+	e = strlen("reason") + strlen(reason) + 2;
+	env[2] = xmalloc(e);
+	snprintf(env[2], e, "reason=%s", reason);
+	e = 20;
+	env[3] = xmalloc(e);
+	snprintf(env[3], e, "pid=%d", getpid());
+	env[4] = xmalloc(e);
+	snprintf(env[4], e, "metric=%d", options->metric);
+	if (dhcpo) {
+		e = configure_env(NULL, NULL, dhcpo, options);
+		if (e > 0) {
+			env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+			elen += configure_env(env + elen, "old", dhcpo, options);
+		}
+	}
+	if (dhcpn) {
+		e = configure_env(NULL, NULL, dhcpn, options);
+		if (e > 0) {
+			env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+			elen += configure_env(env + elen, "new", dhcpn, options);
+		}
+	}
+	/* Add our base environment */
+	if (options->environ) {
+		e = 0;
+		while (options->environ[e++])
+			;
+		env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+		e = 0;
+		while (options->environ[e]) {
+			env[elen + e] = xstrdup(options->environ[e]);
+			e++;
+		}
+		elen += e;
+	}
+	env[elen] = '\0';
+
+	/* OK, we need to block signals */
+	sigfillset(&full);
+	sigprocmask(SIG_SETMASK, &full, &old);
+
+#ifdef THERE_IS_NO_FORK
+	signal_reset();
+	pid = vfork();
+#else
+	pid = fork();
+#endif
+
+	switch (pid) {
+	case -1:
+#ifdef THERE_IS_NO_FORK
+		logger(LOG_ERR, "vfork: %s", strerror(errno));
+#else
+		logger(LOG_ERR, "fork: %s", strerror(errno));
+#endif
+		ret = -1;
+		break;
+	case 0:
+#ifndef THERE_IS_NO_FORK
+		signal_reset();
+#endif
+		sigprocmask(SIG_SETMASK, &old, NULL);
+		execve(options->script, argv, env);
+		logger(LOG_ERR, "%s: %s", options->script, strerror(errno));
+		_exit(111);
+		/* NOTREACHED */
+	}
+
+#ifdef THERE_IS_NO_FORK
+	signal_setup();
+#endif
+
+	/* Restore our signals */
+	sigprocmask(SIG_SETMASK, &old, NULL);
+
+	/* Wait for the script to finish */
+	while (waitpid(pid, &status, 0) == -1) {
+		if (errno != EINTR) {
+			logger(LOG_ERR, "waitpid: %s", strerror(errno));
+			status = -1;
+			break;
+		}
+	}
+
+	/* Cleanup */
+	ep = env;
+	while (*ep)
+		free(*ep++);
+	free(env);
+
+	return status;
+}
+
+static struct rt *
+reverse_routes(struct rt *routes)
+{
+	struct rt *rt;
+	struct rt *rtn = NULL;
+	
+	while (routes) {
+		rt = routes->next;
+		routes->next = rtn;
+		rtn = routes;
+		routes = rt;
+	}
+	return rtn;
+}
+
+static int
+delete_route(const char *iface, struct rt *rt, int metric)
+{
+	char *addr;
+	int retval;
+
+	addr = xstrdup(inet_ntoa(rt->dest));
+	logger(LOG_DEBUG, "removing route %s/%d via %s",
+			addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
+	free(addr);
+	retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric);
+	if (retval != 0)
+		logger(LOG_ERR," del_route: %s", strerror(errno));
+	return retval;
+
+}
+
+static int
+delete_routes(struct interface *iface, int metric)
+{
+	struct rt *rt;
+	struct rt *rtn;
+	int retval = 0;
+
+	rt = reverse_routes(iface->routes);
+	while (rt) {
+		rtn = rt->next;
+		retval += delete_route(iface->name, rt, metric);
+		free(rt);
+		rt = rtn;
+	}
+	iface->routes = NULL;
+
+	return retval;
+}
+
+static int
+in_routes(const struct rt *routes, const struct rt *rt)
+{
+	while (routes) {
+		if (routes->dest.s_addr == rt->dest.s_addr &&
+				routes->net.s_addr == rt->net.s_addr &&
+				routes->gate.s_addr == rt->gate.s_addr)
+			return 0;
+		routes = routes->next;
+	}
+	return -1;
+}
+
+static int
+configure_routes(struct interface *iface, const struct dhcp_message *dhcp,
+		 const struct options *options)
+{
+	struct rt *rt, *ort;
+	struct rt *rtn = NULL, *nr = NULL;
+	int remember;
+	int retval = 0;
+	char *addr;
+
+#ifdef THERE_IS_NO_FORK
+	char *skipp;
+	size_t skiplen;
+	int skip = 0;
+
+	free(dhcpcd_skiproutes);
+	/* We can never have more than 255 routes. So we need space
+	 * for 255 3 digit numbers and commas */
+	skiplen = 255 * 4 + 1;
+	skipp = dhcpcd_skiproutes = xmalloc(sizeof(char) * skiplen);
+	*skipp = '\0';
+#endif
+
+	ort = get_option_routes(dhcp);
+
+#ifdef ENABLE_IPV4LL_ALWAYSROUTE
+	if (options->options & DHCPCD_IPV4LL &&
+	    IN_PRIVATE(ntohl(dhcp->yiaddr)))
+	{
+		for (rt = ort; rt; rt = rt->next) {
+			/* Check if we have already got a link locale route
+			 * dished out by the DHCP server */
+			if (rt->dest.s_addr == htonl(LINKLOCAL_ADDR) &&
+			    rt->net.s_addr == htonl(LINKLOCAL_MASK))
+				break;
+			rtn = rt;
+		}
+
+		if (!rt) {
+			rt = xmalloc(sizeof(*rt));
+			rt->dest.s_addr = htonl(LINKLOCAL_ADDR);
+			rt->net.s_addr = htonl(LINKLOCAL_MASK);
+			rt->gate.s_addr = 0;
+			rt->next = NULL;
+			if (rtn)
+				rtn->next = rt;
+			else
+				ort = rt;
+		}
+	}
+#endif
+
+#ifdef THERE_IS_NO_FORK
+	if (dhcpcd_skiproutes) {
+		int i = -1;
+		char *sk, *skp, *token;
+		free_routes(iface->routes);
+		for (rt = ort; rt; rt = rt->next) {
+			i++;
+			/* Check that we did add this route or not */
+			sk = skp = xstrdup(dhcpcd_skiproutes);
+			while ((token = strsep(&skp, ","))) {
+				if (isdigit((unsigned char)*token) &&
+				    atoi(token) == i)
+					break;
+			}
+			free(sk);
+			if (token)
+				continue;
+			if (nr) {
+				rtn->next = xmalloc(sizeof(*rtn));
+				rtn = rtn->next;
+			} else {
+				nr = rtn = xmalloc(sizeof(*rtn));
+			}
+			rtn->dest.s_addr = rt->dest.s_addr;
+			rtn->net.s_addr = rt->net.s_addr;
+			rtn->gate.s_addr = rt->gate.s_addr;
+			rtn->next = NULL;
+		}
+		iface->routes = nr;
+		nr = NULL;
+
+		/* We no longer need this */
+		free(dhcpcd_skiproutes);
+		dhcpcd_skiproutes = NULL;
+	}
+#endif
+
+	/* Now remove old routes we no longer use.
+ 	 * We should do this in reverse order. */
+	iface->routes = reverse_routes(iface->routes);
+	for (rt = iface->routes; rt; rt = rt->next)
+		if (in_routes(ort, rt) != 0)
+			delete_route(iface->name, rt, options->metric);
+
+	for (rt = ort; rt; rt = rt->next) {
+		/* Don't set default routes if not asked to */
+		if (rt->dest.s_addr == 0 &&
+		    rt->net.s_addr == 0 &&
+		    !(options->options & DHCPCD_GATEWAY))
+			continue;
+
+		addr = xstrdup(inet_ntoa(rt->dest));
+		logger(LOG_DEBUG, "adding route to %s/%d via %s",
+			addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
+		free(addr);
+		remember = add_route(iface->name, &rt->dest,
+				     &rt->net, &rt->gate,
+				     options->metric);
+		retval += remember;
+
+		/* If we failed to add the route, we may have already added it
+		   ourselves. If so, remember it again. */
+		if (remember < 0) {
+			if (errno != EEXIST)
+				logger(LOG_ERR, "add_route: %s",
+				       strerror(errno));
+			if (in_routes(iface->routes, rt) == 0)
+				remember = 1;
+		}
+
+		/* This login is split from above due to the #ifdef below */
+		if (remember >= 0) {
+			if (nr) {
+				rtn->next = xmalloc(sizeof(*rtn));
+				rtn = rtn->next;
+			} else {
+				nr = rtn = xmalloc(sizeof(*rtn));
+			}
+			rtn->dest.s_addr = rt->dest.s_addr;
+			rtn->net.s_addr = rt->net.s_addr;
+			rtn->gate.s_addr = rt->gate.s_addr;
+			rtn->next = NULL;
+		}
+#ifdef THERE_IS_NO_FORK
+		/* If we have daemonised yet we need to record which routes
+		 * we failed to add so we can skip them */
+		else if (!(options->options & DHCPCD_DAEMONISED)) {
+			/* We can never have more than 255 / 4 routes,
+			 * so 3 chars is plently */
+			printf("foo\n");
+			if (*skipp)
+				*skipp++ = ',';
+			skipp += snprintf(skipp,
+					  dhcpcd_skiproutes + skiplen - skipp,
+					  "%d", skip);
+		}
+		skip++;
+#endif
+	}
+	free_routes(ort);
+	free_routes(iface->routes);
+	iface->routes = nr;
+
+#ifdef THERE_IS_NO_FORK
+	if (dhcpcd_skiproutes) {
+		if (*dhcpcd_skiproutes)
+			*skipp = '\0';
+		else {
+			free(dhcpcd_skiproutes);
+			dhcpcd_skiproutes = NULL;
+		}
+	}
+#endif
+
+	return retval;
+}
+
+int
+configure(struct interface *iface, const char *reason,
+	  const struct dhcp_message *dhcp, const struct dhcp_message *old,
+	  const struct dhcp_lease *lease, const struct options *options,
+	  int up)
+{
+	struct in_addr addr;
+	struct in_addr net;
+	struct in_addr brd;
+#ifdef __linux__
+	struct in_addr dest;
+	struct in_addr gate;
+#endif
+
+	/* Grab our IP config */
+	if (dhcp == NULL || dhcp->yiaddr == 0)
+		up = 0;
+	else {
+		addr.s_addr = dhcp->yiaddr;
+		/* Ensure we have all the needed values */
+		if (get_option_addr(&net.s_addr, dhcp, DHCP_SUBNETMASK) == -1)
+			net.s_addr = get_netmask(addr.s_addr);
+		if (get_option_addr(&brd.s_addr, dhcp, DHCP_BROADCAST) == -1)
+			brd.s_addr = addr.s_addr | ~net.s_addr;
+	}
+
+	/* If we aren't up, then reset the interface as much as we can */
+	if (!up) {
+		/* Only reset things if we had set them before */
+		if (iface->addr.s_addr != 0) {
+			delete_routes(iface, options->metric);
+			logger(LOG_DEBUG, "deleting IP address %s/%d",
+			       inet_ntoa(iface->addr),
+			       inet_ntocidr(iface->net));
+			if (del_address(iface->name, &iface->addr,
+					&iface->net) == -1 &&
+			    errno != ENOENT) 
+				logger(LOG_ERR, "del_address: %s",
+				       strerror(errno));
+			iface->addr.s_addr = 0;
+			iface->net.s_addr = 0;
+		}
+
+		exec_script(options, iface->name, reason, NULL, old);
+		return 0;
+	}
+
+	/* This also changes netmask */
+	if (!(options->options & DHCPCD_INFORM) ||
+	    !has_address(iface->name, &addr, &net)) {
+		logger(LOG_DEBUG, "adding IP address %s/%d",
+		       inet_ntoa(addr), inet_ntocidr(net));
+		if (add_address(iface->name, &addr, &net, &brd) == -1 &&
+		    errno != EEXIST)
+		{
+			logger(LOG_ERR, "add_address: %s", strerror(errno));
+			return -1;
+		}
+	}
+
+	/* Now delete the old address if different */
+	if (iface->addr.s_addr != addr.s_addr &&
+	    iface->addr.s_addr != 0) 
+		del_address(iface->name, &iface->addr, &iface->net);
+
+#ifdef __linux__
+	/* On linux, we need to change the subnet route to have our metric. */
+	if (iface->addr.s_addr != lease->addr.s_addr &&
+	    options->metric > 0 && net.s_addr != INADDR_BROADCAST)
+	{
+		dest.s_addr = addr.s_addr & net.s_addr;
+		gate.s_addr = 0;
+		add_route(iface->name, &dest, &net, &gate, options->metric);
+		del_route(iface->name, &dest, &net, &gate, 0);
+	}
+#endif
+
+	configure_routes(iface, dhcp, options);
+	up = (iface->addr.s_addr != addr.s_addr ||
+	      iface->net.s_addr != net.s_addr);
+	iface->addr.s_addr = addr.s_addr;
+	iface->net.s_addr = net.s_addr;
+
+	if (!lease->frominfo)
+		if (write_lease(iface, dhcp) == -1)
+			logger(LOG_ERR, "write_lease: %s", strerror(errno));
+
+	exec_script(options, iface->name, reason, dhcp, old);
+	return 0;
+}
diff --git a/configure.h b/configure.h
new file mode 100644
index 0000000..c4c731c
--- /dev/null
+++ b/configure.h
@@ -0,0 +1,41 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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.
+ */
+
+#ifndef DHCPCONFIG_H
+#define DHCPCONFIG_H
+
+#include "dhcpcd.h"
+#include "dhcp.h"
+#include "net.h"
+
+int exec_script(const struct options *, const char *, const char *,
+		const struct dhcp_message *, const struct dhcp_message *);
+int configure(struct interface *, const char *,
+	      const struct dhcp_message *, const struct dhcp_message *,
+	      const struct dhcp_lease *, const struct options *, int);
+
+#endif
diff --git a/dhcp.c b/dhcp.c
new file mode 100644
index 0000000..0dbc39e
--- /dev/null
+++ b/dhcp.c
@@ -0,0 +1,1218 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+
+#define REQUEST	(1 << 0)
+#define UINT8	(1 << 1)
+#define UINT16	(1 << 2)
+#define SINT16	(1 << 3)
+#define UINT32	(1 << 4)
+#define SINT32	(1 << 5)
+#define IPV4	(1 << 6)
+#define STRING	(1 << 7)
+#define PAIR	(1 << 8)
+#define ARRAY	(1 << 9)
+#define RFC3361	(1 << 10)
+#define RFC3397	(1 << 11)
+#define RFC3442 (1 << 12)
+
+#define IPV4R	IPV4 | REQUEST
+
+/* Our aggregate option buffer.
+ * We ONLY use this when options are split, which for most purposes is
+ * practically never. See RFC3396 for details. */
+static uint8_t *dhcp_opt_buffer = NULL;
+
+struct dhcp_opt {
+	uint8_t option;
+	int type;
+	const char *var;
+};
+
+static const struct dhcp_opt const dhcp_opts[] = {
+	{ 1,	IPV4 | REQUEST,	"subnet_mask" },
+	{ 2,	UINT32,		"time_offset" },
+	{ 3,	IPV4 | ARRAY | REQUEST,	"routers" },
+	{ 4,	IPV4 | ARRAY,	"time_servers" },
+	{ 5,	IPV4 | ARRAY,	"ien116_name_servers" },
+	{ 6,	IPV4 | ARRAY,	"domain_name_servers" },
+	{ 7,	IPV4 | ARRAY,	"log_servers" },
+	{ 8,	IPV4 | ARRAY,	"cookie_servers" },
+	{ 9, 	IPV4 | ARRAY,	"lpr_servers" },
+	{ 10,	IPV4 | ARRAY,	"impress_servers" },
+	{ 11,	IPV4 | ARRAY,	"resource_location_servers" },
+	{ 12,	STRING,		"host_name" },
+	{ 13,	UINT16,		"boot_size" },
+	{ 14,	STRING,		"merit_dump" },
+	{ 15,	STRING,		"domain_name" },
+	{ 16,	IPV4,		"swap_server" },
+	{ 17,	STRING,		"root_path" },
+	{ 18,	STRING,		"extensions_path" },
+	{ 19,	UINT8,		"ip_forwarding" },
+	{ 20,	UINT8,		"non_local_source_routing" },
+	{ 21,	IPV4 | ARRAY,	"policy_filter" },
+	{ 22,	SINT16,		"max_dgram_reassembly" },
+	{ 23,	UINT16,		"default_ip_ttl" },
+	{ 24,	UINT32,		"path_mtu_aging_timeout" },
+	{ 25,	UINT16 | ARRAY,	"path_mtu_plateau_table" },
+	{ 26,	UINT16,		"interface_mtu" },
+	{ 27,	UINT8,		"all_subnets_local" },
+	{ 28,	IPV4 | REQUEST,	"broadcast_address" },
+	{ 29,	UINT8,		"perform_mask_discovery" },
+	{ 30,	UINT8,		"mask_supplier" },
+	{ 31,	UINT8,		"router_discovery" },
+	{ 32,	IPV4,		"router_solicitation_address" },
+	{ 33,	IPV4 | ARRAY | REQUEST,	"static_routes" },
+	{ 34,	UINT8,		"trailer_encapsulation" },
+	{ 35, 	UINT32,		"arp_cache_timeout" },
+	{ 36,	UINT16,		"ieee802_3_encapsulation" },
+	{ 37,	UINT8,		"default_tcp_ttl" },
+	{ 38,	UINT32,		"tcp_keepalive_interval" },
+	{ 39,	UINT8,		"tcp_keepalive_garbage" },
+	{ 40,	STRING,		"nis_domain" },
+	{ 41,	IPV4 | ARRAY,	"nis_servers" },
+	{ 42,	IPV4 | ARRAY,	"ntp_servers" },
+	{ 43,	STRING,		"vendor_encapsulated_options" },
+	{ 44,	IPV4 | ARRAY,	"netbios_name_servers" },
+	{ 45,	IPV4,		"netbios_dd_server" },
+	{ 46,	UINT8,		"netbios_node_type" },
+	{ 47,	STRING,		"netbios_scope" },
+	{ 48,	IPV4 | ARRAY,	"font_servers" },
+	{ 49,	IPV4 | ARRAY,	"x_display_manager" },
+	{ 50, 	IPV4,		"dhcp_requested_address" },
+	{ 51,	UINT32 | REQUEST,	"dhcp_lease_time" },
+	{ 52,	UINT8,		"dhcp_option_overload" },
+	{ 53,	UINT8,		"dhcp_message_type" },
+	{ 54,	IPV4,		"dhcp_server_identifier" },
+	{ 55,	UINT8 | ARRAY,	"dhcp_parameter_request_list" },
+	{ 56,	STRING,		"dhcp_message" },
+	{ 57,	UINT16,		"dhcp_max_message_size" },
+	{ 58,	UINT32 | REQUEST,	"dhcp_renewal_time" },
+	{ 59,	UINT32 | REQUEST,	"dhcp_rebinding_time" },
+	{ 64,	STRING,		"nisplus_domain" },
+	{ 65,	IPV4 | ARRAY,	"nisplus_servers" },
+	{ 66,	STRING,		"tftp_server_name" },
+	{ 67,	STRING,		"bootfile_name" },
+	{ 68,	IPV4 | ARRAY,	"mobile_ip_home_agent" },
+	{ 69,	IPV4 | ARRAY,	"smtp_server" },
+	{ 70,	IPV4 | ARRAY,	"pop_server" },
+	{ 71,	IPV4 | ARRAY,	"nntp_server" },
+	{ 72,	IPV4 | ARRAY,	"www_server" },
+	{ 73,	IPV4 | ARRAY,	"finger_server" },
+	{ 74,	IPV4 | ARRAY,	"irc_server" },
+	{ 75,	IPV4 | ARRAY,	"streettalk_server" },
+	{ 76,	IPV4 | ARRAY,	"streettalk_directory_assistance_server" },
+	{ 77,	STRING,		"user_class" },
+	{ 85,	IPV4 | ARRAY,	"nds_servers" },
+	{ 86,	STRING,		"nds_tree_name" },
+	{ 87,	STRING,		"nds_context" },
+	{ 88,	STRING | RFC3397,	"bcms_controller_names" },
+	{ 89,	IPV4 | ARRAY,	"bcms_controller_address" },
+	{ 91,	UINT32,		"client_last_transaction_time" },
+	{ 92,	IPV4 | ARRAY,	"associated_ip" },
+	{ 98,	STRING,		"uap_servers" },
+	{ 112,	IPV4 | ARRAY,	"netinfo_server_address" },
+	{ 113,	STRING,		"netinfo_server_tag" },
+	{ 114,	STRING,		"default_url" },
+	{ 118,	IPV4,		"subnet_selection" },
+	{ 119,	STRING | RFC3397,	"domain_search" },
+	{ 121,  RFC3442 | REQUEST,	"classless_static_routes" },
+	{ 249,  RFC3442,	"ms-classless_static_routes" },
+	{ 0, 0, NULL }
+};
+
+void
+print_options(void)
+{
+	const struct dhcp_opt *opt;
+
+	for (opt = dhcp_opts; opt->option; opt++)
+		if (opt->var)
+			printf("%03d %s\n", opt->option, opt->var);
+}
+
+int make_reqmask(uint8_t *mask, char **opts, int add)
+{
+	char *token;
+	char *p = *opts;
+	const struct dhcp_opt *opt;
+
+	while ((token = strsep(&p, ", "))) {
+		if (*token == '\0')
+			continue;
+		for (opt = dhcp_opts; opt->option; opt++) {
+			if (!opt->var)
+				continue;
+			if (strcmp(opt->var, token) == 0) {
+				if (add == 1)
+					add_reqmask(mask,
+						    opt->option);
+				else
+					del_reqmask(mask,
+						    opt->option);
+				break;
+			}
+		}
+		if (!opt->option) {
+			*opts = token;
+			errno = ENOENT;
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int
+valid_length(uint8_t option, int dl, int *type)
+{
+	const struct dhcp_opt *opt;
+	ssize_t sz;
+
+	if (dl == 0)
+		return -1;
+
+	for (opt = dhcp_opts; opt->option; opt++) {
+		if (opt->option != option)
+			continue;
+
+		if (type)
+			*type = opt->type;
+
+		if (opt->type == 0 || opt->type & STRING || opt->type & RFC3442)
+			return 0;
+
+		sz = 0;
+		if (opt->type & UINT32 || opt->type & IPV4)
+			sz = sizeof(uint32_t);
+		if (opt->type & UINT16)
+			sz = sizeof(uint16_t);
+		if (opt->type & UINT8)
+			sz = sizeof(uint8_t);
+		if (opt->type & IPV4 || opt->type & ARRAY)
+			return dl % sz;
+		return (dl == sz ? 0 : -1);
+	}
+
+	/* unknown option, so let it pass */
+	return 0;
+}
+
+static void
+free_option_buffer(void)
+{
+	free(dhcp_opt_buffer);
+}
+
+#define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL, NULL)
+static const uint8_t *
+get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len, int *type)
+{
+	const uint8_t *p = dhcp->options;
+	const uint8_t *e = p + sizeof(dhcp->options);
+	uint8_t l, ol = 0;
+	uint8_t o = 0;
+	uint8_t overl = 0;
+	uint8_t *bp = NULL;
+	const uint8_t *op = NULL;
+	int bl = 0;
+
+	while (p < e) {
+		o = *p++;
+		if (o == opt) {
+			if (op) {
+				if (!dhcp_opt_buffer) {
+					dhcp_opt_buffer = xmalloc(sizeof(struct dhcp_message));
+					atexit(free_option_buffer);
+				}
+				if (!bp) 
+					bp = dhcp_opt_buffer;
+				memcpy(bp, op, ol);
+				bp += ol;
+			}
+			ol = *p;
+			op = p + 1;
+			bl += ol;
+		}
+		switch (o) {
+		case DHCP_PAD:
+			continue;
+		case DHCP_END:
+			if (overl & 1) {
+				/* bit 1 set means parse boot file */
+				overl &= ~1;
+				p = dhcp->bootfile;
+				e = p + sizeof(dhcp->bootfile);
+			} else if (overl & 2) {
+				/* bit 2 set means parse server name */
+				overl &= ~2;
+				p = dhcp->servername;
+				e = p + sizeof(dhcp->servername);
+			} else
+				goto exit;
+			break;
+		case DHCP_OPTIONSOVERLOADED:
+			/* Ensure we only get this option once */
+			if (!overl)
+				overl = p[1];
+			break;
+		}
+		l = *p++;
+		p += l;
+	}
+
+exit:
+	if (valid_length(o, bl, type) == -1) {
+		errno = EINVAL;
+		return NULL;
+	}
+	if (len)
+		*len = bl;
+	if (bp) {
+		memcpy(bp, op, ol);
+		return (const uint8_t *)&dhcp_opt_buffer;
+	}
+	if (op)
+		return op;
+	errno = ENOENT;
+	return NULL;
+}
+
+int
+get_option_addr(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option)
+{
+	const uint8_t *p = get_option_raw(dhcp, option);
+
+	if (!p)
+		return -1;
+	memcpy(a, p, sizeof(*a));
+	return 0;
+}
+
+int
+get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+	uint32_t a;
+
+	if (get_option_addr(&a, dhcp, option) == -1)
+		return -1;
+
+	*i = ntohl(a);
+	return 0;
+}
+
+int
+get_option_uint16(uint16_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+	const uint8_t *p = get_option_raw(dhcp, option);
+	uint16_t d;
+
+	if (!p)
+		return -1;
+	memcpy(&d, p, sizeof(d));
+	*i = ntohs(d);
+	return 0;
+}
+
+int
+get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option)
+{
+	const uint8_t *p = get_option_raw(dhcp, option);
+
+	if (!p)
+		return -1;
+	*i = *(p);
+	return 0;
+}
+
+/* Decode an RFC3397 DNS search order option into a space
+ * seperated string. Returns length of string (including 
+ * terminating zero) or zero on error. out may be NULL
+ * to just determine output length. */
+static ssize_t
+decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+	const uint8_t *r, *q = p;
+	int count = 0, l, hops;
+	uint8_t ltype;
+
+	while (q - p < pl) {
+		r = NULL;
+		hops = 0;
+		while ((l = *q++)) {
+			ltype = l & 0xc0;
+			if (ltype == 0x80 || ltype == 0x40)
+				return 0;
+			else if (ltype == 0xc0) { /* pointer */
+				l = (l & 0x3f) << 8;
+				l |= *q++;
+				/* save source of first jump. */
+				if (!r)
+					r = q;
+				hops++;
+				if (hops > 255)
+					return 0;
+				q = p + l;
+				if (q - p >= pl)
+					return 0;
+			} else {
+				/* straightforward name segment, add with '.' */
+				count += l + 1;
+				if (out) {
+					if ((ssize_t)l + 1 > len) {
+						errno = ENOBUFS;
+						return -1;
+					}
+					memcpy(out, q, l);
+					out += l;
+					*out++ = '.';
+					len -= l;
+					len--;
+				}
+				q += l;
+			}
+		}
+		/* change last dot to space */
+		if (out)
+			*(out - 1) = ' ';
+		if (r)
+			q = r;
+	}
+
+	/* change last space to zero terminator */
+	if (out)
+		*(out - 1) = 0;
+
+	return count;  
+}
+
+static ssize_t
+decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
+{
+	const uint8_t *e;
+	ssize_t bytes = 0;
+	ssize_t b;
+	uint8_t cidr;
+	uint8_t ocets;
+	struct in_addr addr;
+	char *o = out;
+
+	/* Minimum is 5 -first is CIDR and a router length of 4 */
+	if (pl < 5) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	e = p + pl;
+	while (p < e) {
+		cidr = *p++;
+		if (cidr > 32) {
+			errno = EINVAL;
+			return -1;
+		}
+		ocets = (cidr + 7) / 8;
+		if (!out) {
+			p += 4 + ocets;
+			bytes += ((4 * 4) * 2) + 4;
+			continue;
+		}
+		if ((((4 * 4) * 2) + 4) > len) {
+			errno = ENOBUFS;
+			return -1;
+		}
+		if (o != out) {
+			*o++ = ' ';
+			len--;
+		}
+		/* If we have ocets then we have a destination and netmask */
+		if (ocets > 0) {
+			addr.s_addr = 0;
+			memcpy(&addr.s_addr, p, (size_t)ocets);
+			b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr);
+			p += ocets;
+		} else
+			b = snprintf(o, len, "0.0.0.0/0");
+		o += b;
+		len -= b;
+
+		/* Finally, snag the router */
+		memcpy(&addr.s_addr, p, 4);
+		p += 4;
+		b = snprintf(o, len, " %s", inet_ntoa(addr));
+		o += b;
+		len -= b;
+	}
+
+	if (out)
+		return o - out;
+	return bytes;
+}
+
+static struct rt *
+decode_rfc3442_rt(int dl, const uint8_t *data)
+{
+	const uint8_t *p = data;
+	const uint8_t *e;
+	uint8_t cidr;
+	uint8_t ocets;
+	struct rt *routes = NULL;
+	struct rt *rt = NULL;
+
+	/* Minimum is 5 -first is CIDR and a router length of 4 */
+	if (dl < 5)
+		return NULL;
+
+	e = p + dl;
+	while (p < e) {
+		cidr = *p++;
+		if (cidr > 32) {
+			free_routes(routes);
+			errno = EINVAL;
+			return NULL;
+		}
+
+		if (rt) {
+			rt->next = xzalloc(sizeof(*rt));
+			rt = rt->next;
+		} else {
+			routes = rt = xzalloc(sizeof(*routes));
+		}
+		rt->next = NULL;
+
+		ocets = (cidr + 7) / 8;
+		/* If we have ocets then we have a destination and netmask */
+		if (ocets > 0) {
+			memcpy(&rt->dest.s_addr, p, (size_t)ocets);
+			memset(&rt->net.s_addr, 255, (size_t)ocets - 1);
+			memset((uint8_t *)&rt->net.s_addr +
+			       (ocets - 1),
+			       (256 - (1 << (32 - cidr) % 8)), 1);
+			p += ocets;
+		} else {
+			rt->dest.s_addr = 0;
+			rt->net.s_addr = 0;
+		}
+
+		/* Finally, snag the router */
+		memcpy(&rt->gate.s_addr, p, 4);
+		p += 4;
+	}
+	return routes;
+}
+
+static char *
+decode_rfc3361(int dl, const uint8_t *data)
+{
+	uint8_t enc;
+	unsigned int l;
+	char *sip = NULL;
+	struct in_addr addr;
+	char *p;
+
+	if (dl < 2) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	enc = *data++;
+	dl--;
+	switch (enc) {
+	case 0:
+		if ((l = decode_rfc3397(NULL, 0, dl, data)) > 0) {
+			sip = xmalloc(l);
+			decode_rfc3397(sip, l, dl, data);
+		}
+		break;
+	case 1:
+		if (dl == 0 || dl % 4 != 0) {
+			errno = EINVAL;
+			break;
+		}
+		addr.s_addr = INADDR_BROADCAST;
+		l = ((dl / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1;
+		sip = p = xmalloc(l);
+		while (l != 0) {
+			memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
+			data += sizeof(addr.s_addr);
+			p += snprintf(p, l - (p - sip), "%s ", inet_ntoa(addr));
+			l -= sizeof(addr.s_addr);
+		}
+		*--p = '\0';
+		break;
+	default:
+		errno = EINVAL;
+		return 0;
+	}
+
+	return sip;
+}
+
+char *
+get_option_string(const struct dhcp_message *dhcp, uint8_t option)
+{
+	int type;
+	int len;
+	const uint8_t *p;
+	char *s;
+
+	p = get_option(dhcp, option, &len, &type);
+	if (!p || *p == '\0')
+		return NULL;
+
+	if (type & RFC3397) {
+		type = decode_rfc3397(NULL, 0, len, p);
+		if (!type) {
+			errno = EINVAL;
+			return NULL;
+		}
+		s = xmalloc(sizeof(char) * type);
+		decode_rfc3397(s, type, len, p);
+		return s;
+	}
+
+	if (type & RFC3361)
+		return decode_rfc3361(len, p);
+
+	s = xmalloc(sizeof(char) * (len + 1));
+	memcpy(s, p, len);
+	s[len] = '\0';
+	return s;
+}
+
+/* This calculates the netmask that we should use for static routes.
+ * This IS different from the calculation used to calculate the netmask
+ * for an interface address. */
+static uint32_t
+route_netmask(uint32_t ip_in)
+{
+	/* used to be unsigned long - check if error */
+	uint32_t p = ntohl(ip_in);
+	uint32_t t;
+
+	if (IN_CLASSA(p))
+		t = ~IN_CLASSA_NET;
+	else {
+		if (IN_CLASSB(p))
+			t = ~IN_CLASSB_NET;
+		else {
+			if (IN_CLASSC(p))
+				t = ~IN_CLASSC_NET;
+			else
+				t = 0;
+		}
+	}
+
+	while (t & p)
+		t >>= 1;
+
+	return (htonl(~t));
+}
+
+/* We need to obey routing options.
+ * If we have a CSR then we only use that.
+ * Otherwise we add static routes and then routers. */
+struct rt *
+get_option_routes(const struct dhcp_message *dhcp)
+{
+	const uint8_t *p;
+	const uint8_t *e;
+	struct rt *routes = NULL;
+	struct rt *route = NULL;
+	int len;
+
+	/* If we have CSR's then we MUST use these only */
+	p = get_option(dhcp, DHCP_CSR, &len, NULL);
+	/* Check for crappy MS option */
+	if (!p)
+		p = get_option(dhcp, DHCP_MSCSR, &len, NULL);
+	if (p) {
+		routes = decode_rfc3442_rt(len, p);
+		if (routes)
+			return routes;
+	}
+
+	/* OK, get our static routes first. */
+	p = get_option(dhcp, DHCP_STATICROUTE, &len, NULL);
+	if (p) {
+		e = p + len;
+		while (p < e) {
+			if (route) {
+				route->next = xmalloc(sizeof(*route));
+				route = route->next;
+			} else
+				routes = route = xmalloc(sizeof(*routes));
+			route->next = NULL;
+			memcpy(&route->dest.s_addr, p, 4);
+			p += 4;
+			memcpy(&route->gate.s_addr, p, 4);
+			p += 4;
+			route->net.s_addr = route_netmask(route->dest.s_addr);
+		}
+	}
+
+	/* Now grab our routers */
+	p = get_option(dhcp, DHCP_ROUTER, &len, NULL);
+	if (p) {
+		e = p + len;
+		while (p < e) {
+			if (route) {
+				route->next = xzalloc(sizeof(*route));
+				route = route->next;
+			} else
+				routes = route = xzalloc(sizeof(*route));
+			memcpy(&route->gate.s_addr, p, 4);
+			p += 4;
+		}
+	}
+
+	return routes;
+}
+
+ssize_t
+make_message(struct dhcp_message **message,
+	     const struct interface *iface, const struct dhcp_lease *lease,
+	     uint32_t xid, uint8_t type, const struct options *options)
+{
+	struct dhcp_message *dhcp;
+	uint8_t *m, *p;
+	uint8_t *n_params = NULL;
+	time_t up = uptime() - iface->start_uptime;
+	uint32_t ul;
+	uint16_t sz;
+	const struct dhcp_opt *opt;
+#ifndef MINIMAL
+	uint8_t *d;
+	const char *c;
+#endif
+
+	dhcp = xzalloc(sizeof (*dhcp));
+	m = (uint8_t *)dhcp;
+	p = (uint8_t *)&dhcp->options;
+
+	if ((type == DHCP_INFORM ||
+	     type == DHCP_RELEASE ||
+	     type == DHCP_REQUEST) &&
+	    !IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+	{
+		dhcp->ciaddr = iface->addr.s_addr;
+		/* Just incase we haven't actually configured the address yet */
+		if (type == DHCP_INFORM && iface->addr.s_addr == 0)
+			dhcp->ciaddr = lease->addr.s_addr;
+		/* Zero the address if we're currently on a different subnet */
+		if (type == DHCP_REQUEST &&
+		    iface->net.s_addr != lease->net.s_addr)
+			dhcp->ciaddr = 0;
+	}
+
+	dhcp->op = DHCP_BOOTREQUEST;
+	dhcp->hwtype = iface->family;
+	switch (iface->family) {
+	case ARPHRD_ETHER:
+	case ARPHRD_IEEE802:
+		dhcp->hwlen = ETHER_ADDR_LEN;
+		memcpy(&dhcp->chaddr, &iface->hwaddr, ETHER_ADDR_LEN);
+		break;
+	case ARPHRD_IEEE1394:
+	case ARPHRD_INFINIBAND:
+		dhcp->hwlen = 0;
+		if (dhcp->ciaddr == 0)
+			dhcp->flags = htons(BROADCAST_FLAG);
+		break;
+	}
+
+	if (up < 0 || up > (time_t)UINT16_MAX)
+		dhcp->secs = htons((uint16_t)UINT16_MAX);
+	else
+		dhcp->secs = htons(up);
+	dhcp->xid = xid;
+	dhcp->cookie = htonl(MAGIC_COOKIE);
+
+	*p++ = DHCP_MESSAGETYPE; 
+	*p++ = 1;
+	*p++ = type;
+
+	if (type == DHCP_REQUEST) {
+		*p++ = DHCP_MAXMESSAGESIZE;
+		*p++ = 2;
+		sz = get_mtu(iface->name);
+		if (sz < MTU_MIN) {
+			if (set_mtu(iface->name, MTU_MIN) == 0)
+				sz = MTU_MIN;
+		}
+		sz = htons(sz);
+		memcpy(p, &sz, 2);
+		p += 2;
+	}
+
+#ifndef MINIMAL
+	if (iface->clientid) {
+		*p++ = DHCP_CLIENTID;
+		memcpy(p, iface->clientid, iface->clientid[0] + 1);
+		p += iface->clientid[0] + 1;
+	}
+
+	if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
+		if (options->userclass[0]) {
+			*p++ = DHCP_USERCLASS;
+			memcpy(p, options->userclass, options->userclass[0] + 1);
+			p += options->userclass[0] + 1;
+		}
+
+		if (options->classid[0]) {
+			*p++ = DHCP_CLASSID;
+			memcpy(p, options->classid, options->classid[0] + 1);
+			p += options->classid[0] + 1;
+		}
+	}
+#endif
+
+	if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
+#define PUTADDR(_type, _val) \
+		{ \
+			*p++ = _type; \
+			*p++ = 4; \
+			memcpy(p, &_val.s_addr, 4); \
+			p += 4; \
+		}
+		if (lease->addr.s_addr &&
+		    lease->addr.s_addr != iface->addr.s_addr &&
+		    !IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
+		{
+			PUTADDR(DHCP_IPADDRESS, lease->addr);
+			if (lease->server.s_addr)
+				PUTADDR(DHCP_SERVERID, lease->server);
+		}
+#undef PUTADDR
+
+		if (options->leasetime != 0) {
+			*p++ = DHCP_LEASETIME;
+			*p++ = 4;
+			ul = htonl(options->leasetime);
+			memcpy(p, &ul, 4);
+			p += 4;
+		}
+	}
+
+	if (type == DHCP_DISCOVER ||
+	    type == DHCP_INFORM ||
+	    type == DHCP_REQUEST)
+	{
+#ifndef MINIMAL
+		if (options->hostname[0]) {
+			if (options->fqdn == FQDN_DISABLE) {
+				*p++ = DHCP_HOSTNAME;
+				memcpy(p, options->hostname, options->hostname[0] + 1);
+				p += options->hostname[0] + 1;
+			} else {
+				/* Draft IETF DHC-FQDN option (81) */
+				*p++ = DHCP_FQDN;
+				*p++ = options->hostname[0] + 4;
+				/*
+				 * Flags: 0000NEOS
+				 * S: 1 => Client requests Server to update
+				 *         a RR in DNS as well as PTR
+				 * O: 1 => Server indicates to client that
+				 *         DNS has been updated
+				 * E: 1 => Name data is DNS format
+				 * N: 1 => Client requests Server to not
+				 *         update DNS
+				 */
+				*p++ = (options->fqdn & 0x9) | 0x4;
+				*p++ = 0; /* from server for PTR RR */
+				*p++ = 0; /* from server for A RR if S=1 */
+				c = options->hostname + 1;
+				d = p++;
+				while (*c) {
+					if (*c == '.') {
+						*d = p - d - 1;
+						d = p++;
+					} else
+						*p++ = (uint8_t) *c;
+					c++;
+				}
+				*p ++ = 0;
+			}
+		}
+
+		/* vendor is already encoded correctly, so just add it */
+		if (options->vendor[0]) {
+			*p++ = DHCP_VENDOR;
+			memcpy(p, options->vendor, options->vendor[0] + 1);
+			p += options->vendor[0] + 1;
+		}
+#endif
+
+		*p++ = DHCP_PARAMETERREQUESTLIST;
+		n_params = p;
+		*p++ = 0;
+		for (opt = dhcp_opts; opt->option; opt++) {
+			if (!(opt->type & REQUEST || 
+			      has_reqmask(options->reqmask, opt->option)))
+				continue;
+			switch (opt->option) {
+			case DHCP_RENEWALTIME:	/* FALLTHROUGH */
+			case DHCP_REBINDTIME:
+				if (type == DHCP_INFORM)
+					continue;
+				break;
+			}
+			*p++ = opt->option;
+		}
+		*n_params = p - n_params - 1;
+	}
+	*p++ = DHCP_END;
+
+#ifdef BOOTP_MESSAGE_LENTH_MIN
+	/* Some crappy DHCP servers think they have to obey the BOOTP minimum
+	 * message length.
+	 * They are wrong, but we should still cater for them. */
+	while (p - m < BOOTP_MESSAGE_LENTH_MIN)
+		*p++ = DHCP_PAD;
+#endif
+
+	*message = dhcp;
+	return p - m;
+}
+
+ssize_t
+write_lease(const struct interface *iface, const struct dhcp_message *dhcp)
+{
+	int fd;
+	ssize_t bytes = sizeof(*dhcp);
+	const uint8_t *p = dhcp->options;
+	const uint8_t *e = p + sizeof(dhcp->options);
+	uint8_t l;
+	uint8_t o = 0;
+
+	fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+#ifdef ANDROID
+	if (fd == -1 && errno == EACCES) {
+		/* the lease file might have been created when dhcpcd was running as root */
+		unlink(iface->leasefile);
+		fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+	}
+#endif
+	if (fd == -1)
+		return -1;
+
+	/* Only write as much as we need */
+	while (p < e) {
+		o = *p;
+		if (o == DHCP_END) {
+			bytes = p - (const uint8_t *)dhcp;
+			break;
+		}
+		p++;
+		if (o != DHCP_PAD) {
+			l = *p++;
+			p += l;
+		}
+	}
+	bytes = write(fd, dhcp, bytes);
+	close(fd);
+	return bytes;
+}
+
+struct dhcp_message *
+read_lease(const struct interface *iface)
+{
+	int fd;
+	struct dhcp_message *dhcp;
+	ssize_t bytes;
+
+	fd = open(iface->leasefile, O_RDONLY);
+	if (fd == -1)
+		return NULL;
+	dhcp = xmalloc(sizeof(*dhcp));
+	memset(dhcp, 0, sizeof(*dhcp));
+	bytes = read(fd, dhcp, sizeof(*dhcp));
+	close(fd);
+	if (bytes < 0) {
+		free(dhcp);
+		dhcp = NULL;
+	}
+	return dhcp;
+}
+
+static ssize_t
+print_string(char *s, ssize_t len, int dl, const uint8_t *data)
+{
+	uint8_t c;
+	const uint8_t *e;
+	ssize_t bytes = 0;
+	ssize_t r;
+
+	e = data + dl;
+	while (data < e) {
+		c = *data++;
+		if (!isascii(c) || !isprint(c)) {
+			if (s) {
+				if (len < 5) {
+					errno = ENOBUFS;
+					return -1;
+				}
+				r = snprintf(s, len, "\\%03o", c);
+				len -= r;
+				bytes += r;
+				s += r;
+			} else
+				bytes += 4;
+			continue;
+		}
+		switch (c) {
+			case '"':  /* FALLTHROUGH */
+			case '\'': /* FALLTHROUGH */
+			case '$':  /* FALLTHROUGH */
+			case '`':  /* FALLTHROUGH */
+			case '\\': /* FALLTHROUGH */
+				if (s) {
+					if (len < 3) {
+						errno = ENOBUFS;
+						return -1;
+					}
+					*s++ = '\\';
+					len--;
+				}
+				bytes++;
+				break;
+		}
+		if (s) {
+			*s++ = c;
+			len--;
+		}
+		bytes++;
+	}
+
+	/* NULL */
+	if (s)
+		*s = '\0';
+	bytes++;
+	return bytes;
+}
+
+static ssize_t
+print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
+{
+	const uint8_t *e, *t;
+	uint16_t u16;
+	int16_t s16;
+	uint32_t u32;
+	int32_t s32;
+	struct in_addr addr;
+	ssize_t bytes = 0;
+	ssize_t l;
+	char *tmp;
+
+	if (type & RFC3397) {
+		l = decode_rfc3397(NULL, 0, dl, data);
+		if (l < 1)
+			return l;
+		tmp = xmalloc(l);
+		decode_rfc3397(tmp, l, dl, data);
+		l = print_string(s, len, l - 1, (uint8_t *)tmp);
+		free(tmp);
+		return l;
+	}
+
+	if (type & RFC3442)
+		return decode_rfc3442(s, len, dl, data);
+
+	if (type & STRING) {
+		/* Some DHCP servers return NULL strings */
+		if (*data == '\0')
+			return 0;
+		return print_string(s, len, dl, data);
+	}
+
+	if (!s) {
+		if (type & UINT8)
+			l = 3;
+		else if (type & UINT16)
+			l = 5;
+		else if (type & SINT16)
+			l = 6;
+		else if (type & UINT32)
+			l = 10;
+		else if (type & SINT32)
+			l = 11;
+		else if (type & IPV4)
+			l = 16;
+		else {
+			errno = EINVAL;
+			return -1;
+		}
+		return (l + 1) * dl;
+	}
+
+	t = data;
+	e = data + dl;
+	while (data < e) {
+		if (data != t) {
+			*s++ = ' ';
+			bytes++;
+			len--;
+		}
+		if (type & UINT8) {
+			l = snprintf(s, len, "%d", *data);
+			data++;
+		} else if (type & UINT16) {
+			memcpy(&u16, data, sizeof(u16));
+			u16 = ntohs(u16);
+			l = snprintf(s, len, "%d", u16);
+			data += sizeof(u16);
+		} else if (type & SINT16) {
+			memcpy(&s16, data, sizeof(s16));
+			s16 = ntohs(s16);
+			l = snprintf(s, len, "%d", s16);
+			data += sizeof(s16);
+		} else if (type & UINT32) {
+			memcpy(&u32, data, sizeof(u32));
+			u32 = ntohl(u32);
+			l = snprintf(s, len, "%d", u32);
+			data += sizeof(u32);
+		} else if (type & SINT32) {
+			memcpy(&s32, data, sizeof(s32));
+			s32 = ntohl(s32);
+			l = snprintf(s, len, "%d", s32);
+			data += sizeof(s32);
+		} else if (type & IPV4) {
+			memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
+			l = snprintf(s, len, "%s", inet_ntoa(addr));
+			data += sizeof(addr.s_addr);
+		} else
+			l = 0;
+		len -= l;
+		bytes += l;
+		s += l;
+	}
+
+	return bytes;
+}
+
+static void
+setvar(char ***e, const char *prefix, const char *var, const char *value)
+{
+	size_t len = strlen(prefix) + strlen(var) + strlen(value) + 4;
+
+	**e = xmalloc(len);
+	snprintf(**e, len, "%s_%s=%s", prefix, var, value);
+	(*e)++;
+}
+
+ssize_t
+configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
+	      const struct options *options)
+{
+	unsigned int i;
+	const uint8_t *p;
+	int pl;
+	struct in_addr addr;
+	struct in_addr net;
+	struct in_addr brd;
+	char *val, *v;
+	const struct dhcp_opt *opt;
+	ssize_t len, e = 0;
+	char **ep;
+	char cidr[4];
+	uint8_t overl = 0;
+
+	get_option_uint8(&overl, dhcp, DHCP_OPTIONSOVERLOADED);
+
+	if (!env) {
+		for (opt = dhcp_opts; opt->option; opt++) {
+			if (!opt->var)
+				continue;
+			if (has_reqmask(options->nomask, opt->option))
+				continue;
+			if (get_option_raw(dhcp, opt->option))
+				e++;
+		}
+		if (dhcp->yiaddr)
+			e += 5;
+		if (*dhcp->bootfile && !(overl & 1))
+			e++;
+		if (*dhcp->servername && !(overl & 2))
+			e++;
+		return e;
+	}
+
+	ep = env;
+	if (dhcp->yiaddr) {
+		/* Set some useful variables that we derive from the DHCP
+		 * message but are not necessarily in the options */
+		addr.s_addr = dhcp->yiaddr;
+		setvar(&ep, prefix, "ip_address", inet_ntoa(addr));
+		if (get_option_addr(&net.s_addr, dhcp, DHCP_SUBNETMASK) == -1) {
+			net.s_addr = get_netmask(addr.s_addr);
+			setvar(&ep, prefix, "subnet_mask", inet_ntoa(net));
+		}
+		i = inet_ntocidr(net);
+		snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net));
+		setvar(&ep, prefix, "subnet_cidr", cidr);
+		if (get_option_addr(&brd.s_addr, dhcp, DHCP_BROADCAST) == -1) {
+			brd.s_addr = addr.s_addr | ~net.s_addr;
+			setvar(&ep, prefix, "broadcast_address", inet_ntoa(net));
+		}
+		addr.s_addr = dhcp->yiaddr & net.s_addr;
+		setvar(&ep, prefix, "network_number", inet_ntoa(addr));
+	}
+
+	if (*dhcp->bootfile && !(overl & 1))
+		setvar(&ep, prefix, "filename", (const char *)dhcp->bootfile);
+	if (*dhcp->servername && !(overl & 2))
+		setvar(&ep, prefix, "server_name", (const char *)dhcp->servername);
+
+	for (opt = dhcp_opts; opt->option; opt++) {
+		if (!opt->var)
+			continue;
+		if (has_reqmask(options->nomask, opt->option))
+			continue;
+		val = NULL;
+		p = get_option(dhcp, opt->option, &pl, NULL);
+		if (!p)
+			continue;
+		len = print_option(NULL, 0, opt->type, pl, p);
+		if (len < 0)
+			return -1;
+		e = strlen(prefix) + strlen(opt->var) + len + 4;
+		v = val = *ep++ = xmalloc(e);
+		v += snprintf(val, e, "%s_%s=", prefix, opt->var);
+		if (len != 0)
+			print_option(v, len, opt->type, pl, p);
+	}
+
+	return ep - env;
+}
diff --git a/dhcp.h b/dhcp.h
new file mode 100644
index 0000000..8b88c5e
--- /dev/null
+++ b/dhcp.h
@@ -0,0 +1,185 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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.
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#include <arpa/inet.h>
+
+#include <stdint.h>
+
+#include "config.h"
+#include "dhcpcd.h"
+#include "net.h"
+
+/* Max MTU - defines dhcp option length */
+#define MTU_MAX             1500
+#define MTU_MIN             576
+
+/* UDP port numbers for DHCP */
+#define DHCP_SERVER_PORT    67
+#define DHCP_CLIENT_PORT    68
+
+#define MAGIC_COOKIE        0x63825363
+#define BROADCAST_FLAG      0x8000
+
+/* DHCP message OP code */
+#define DHCP_BOOTREQUEST    1
+#define DHCP_BOOTREPLY      2
+
+/* DHCP message type */
+#define DHCP_DISCOVER       1
+#define DHCP_OFFER          2
+#define DHCP_REQUEST        3
+#define DHCP_DECLINE        4
+#define DHCP_ACK            5
+#define DHCP_NAK            6
+#define DHCP_RELEASE        7
+#define DHCP_INFORM         8
+
+/* DHCP options */
+enum DHCP_OPTIONS
+{
+	DHCP_PAD                    = 0,
+	DHCP_SUBNETMASK             = 1,
+	DHCP_ROUTER                 = 3,
+	DHCP_DNSSERVER              = 6,
+	DHCP_HOSTNAME               = 12,
+	DHCP_DNSDOMAIN              = 15,
+	DHCP_MTU                    = 26,
+	DHCP_BROADCAST              = 28,
+	DHCP_STATICROUTE            = 33,
+	DHCP_NISDOMAIN              = 40,
+	DHCP_NISSERVER              = 41,
+	DHCP_NTPSERVER              = 42,
+	DHCP_VENDOR                 = 43,
+	DHCP_IPADDRESS              = 50,
+	DHCP_LEASETIME              = 51,
+	DHCP_OPTIONSOVERLOADED      = 52,
+	DHCP_MESSAGETYPE            = 53,
+	DHCP_SERVERID               = 54,
+	DHCP_PARAMETERREQUESTLIST   = 55,
+	DHCP_MESSAGE                = 56,
+	DHCP_MAXMESSAGESIZE         = 57,
+	DHCP_RENEWALTIME            = 58,
+	DHCP_REBINDTIME             = 59,
+	DHCP_CLASSID                = 60,
+	DHCP_CLIENTID               = 61,
+	DHCP_USERCLASS              = 77,  /* RFC 3004 */
+	DHCP_FQDN                   = 81,
+	DHCP_DNSSEARCH              = 119, /* RFC 3397 */
+	DHCP_CSR                    = 121, /* RFC 3442 */
+	DHCP_MSCSR                  = 249, /* MS code for RFC 3442 */
+	DHCP_END                    = 255
+};
+
+/* SetFQDNHostName values - lsnybble used in flags
+ * byte (see buildmsg.c), hsnybble to create order
+ * and to allow 0x00 to mean disable
+ */
+enum FQQN {
+	FQDN_DISABLE    = 0x00,
+	FQDN_NONE       = 0x18,
+	FQDN_PTR        = 0x20,
+	FQDN_BOTH       = 0x31
+};
+
+struct fqdn
+{
+	uint8_t flags;
+	uint8_t r1;
+	uint8_t r2;
+	char *name;
+};
+
+/* Sizes for DHCP options */
+#define DHCP_CHADDR_LEN         16
+#define SERVERNAME_LEN          64
+#define BOOTFILE_LEN            128
+#define DHCP_UDP_LEN            (20 + 8)
+#define DHCP_BASE_LEN           (4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4)
+#define DHCP_RESERVE_LEN        (4 + 4 + 4 + 4 + 2)
+#define DHCP_FIXED_LEN          (DHCP_BASE_LEN + DHCP_CHADDR_LEN + \
+				 + SERVERNAME_LEN + BOOTFILE_LEN)
+#define DHCP_OPTION_LEN         (MTU_MAX - DHCP_FIXED_LEN - DHCP_UDP_LEN \
+				 - DHCP_RESERVE_LEN)
+
+/* Some crappy DHCP servers require the BOOTP minimum length */
+#define BOOTP_MESSAGE_LENTH_MIN 300
+
+struct dhcp_message {
+	uint8_t op;           /* message type */
+	uint8_t hwtype;       /* hardware address type */
+	uint8_t hwlen;        /* hardware address length */
+	uint8_t hwopcount;    /* should be zero in client message */
+	uint32_t xid;            /* transaction id */
+	uint16_t secs;           /* elapsed time in sec. from boot */
+	uint16_t flags;
+	uint32_t ciaddr;         /* (previously allocated) client IP */
+	uint32_t yiaddr;         /* 'your' client IP address */
+	uint32_t siaddr;         /* should be zero in client's messages */
+	uint32_t giaddr;         /* should be zero in client's messages */
+	uint8_t chaddr[DHCP_CHADDR_LEN];  /* client's hardware address */
+	uint8_t servername[SERVERNAME_LEN];    /* server host name */
+	uint8_t bootfile[BOOTFILE_LEN];    /* boot file name */
+	uint32_t cookie;
+	uint8_t options[DHCP_OPTION_LEN]; /* message options - cookie */
+};
+
+struct dhcp_lease {
+	struct in_addr addr;
+	struct in_addr net;
+	uint32_t leasetime;
+	uint32_t renewaltime;
+	uint32_t rebindtime;
+	struct in_addr server;
+	uint32_t leasedfrom;
+	uint8_t frominfo;
+};
+
+#define add_reqmask(var, val) (var[val >> 3] |= 1 << (val & 7))
+#define del_reqmask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
+#define has_reqmask(var, val) (var[val >> 3] & (1 << (val & 7)))
+int make_reqmask(uint8_t *, char **, int);
+void print_options(void);
+char *get_option_string(const struct dhcp_message *, uint8_t);
+int get_option_addr(uint32_t *, const struct dhcp_message *, uint8_t);
+int get_option_uint32(uint32_t *, const struct dhcp_message *, uint8_t);
+int get_option_uint16(uint16_t *, const struct dhcp_message *, uint8_t);
+int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
+struct rt *get_option_routes(const struct dhcp_message *);
+ssize_t configure_env(char **, const char *, const struct dhcp_message *,
+		      const struct options *);
+
+ssize_t make_message(struct dhcp_message **,
+			const struct interface *, const struct dhcp_lease *,
+	     		uint32_t, uint8_t, const struct options *);
+int valid_dhcp_packet(unsigned char *);
+
+ssize_t write_lease(const struct interface *, const struct dhcp_message *);
+struct dhcp_message *read_lease(const struct interface *iface);
+#endif
diff --git a/dhcpcd-hooks/01-test b/dhcpcd-hooks/01-test
new file mode 100644
index 0000000..d3ca40d
--- /dev/null
+++ b/dhcpcd-hooks/01-test
@@ -0,0 +1,7 @@
+# Just echo our DHCP options we have
+
+case ${reason} in
+TEST)   set | grep "^\(interface\|metric\|pid\|reason\|skip_hooks\)=" | sort
+	set | grep "^\(new_\|old_\)" | sort
+        ;;
+esac
diff --git a/dhcpcd-hooks/10-mtu b/dhcpcd-hooks/10-mtu
new file mode 100644
index 0000000..4265b48
--- /dev/null
+++ b/dhcpcd-hooks/10-mtu
@@ -0,0 +1,5 @@
+# Configure the MTU for the interface
+
+if [ -n "${new_interface_mtu}" ]; then
+	ifconfig "${interface}" mtu "${new_interface_mtu}"
+fi
diff --git a/dhcpcd-hooks/20-dns.conf b/dhcpcd-hooks/20-dns.conf
new file mode 100644
index 0000000..a92e91d
--- /dev/null
+++ b/dhcpcd-hooks/20-dns.conf
@@ -0,0 +1,32 @@
+# Set net.<iface>.dnsN properties that contain the
+# DNS server addresses given by the DHCP server.
+
+set_dns_props()
+{
+    case "${new_domain_name_servers}" in
+    "")   return 0;;
+    esac
+
+    count=1
+    for i in 1 2 3 4; do
+        setprop dhcp.${interface}.dns${i} ""
+    done
+    
+    count=1
+    for dnsaddr in ${new_domain_name_servers}; do
+        setprop dhcp.${interface}.dns${count} ${dnsaddr}
+        count=$(($count + 1))
+    done
+}
+
+unset_dns_props()
+{
+    for i in 1 2 3 4; do
+        setprop dhcp.${interface}.dns${i} ""
+    done
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)       set_dns_props;;
+EXPIRE|FAIL|IPV4LL|RELEASE|STOP)                unset_dns_props;;
+esac
diff --git a/dhcpcd-hooks/20-resolv.conf b/dhcpcd-hooks/20-resolv.conf
new file mode 100644
index 0000000..437c116
--- /dev/null
+++ b/dhcpcd-hooks/20-resolv.conf
@@ -0,0 +1,40 @@
+# Generate /etc/resolv.conf
+# Support resolvconf(8) if available
+
+make_resolv_conf()
+{
+	if [ -z "${new_domain_name_servers}" -a \
+		-z "${new_domain_name}" -a \
+		-z "${new_domain_search}" ]; then
+		return 0
+	fi
+	local x= conf="${signature}\n"
+	if [ -n "${new_domain_search}" ]; then
+		conf="${conf}search ${new_domain_search}\n"
+	elif [ -n "${new_domain_name}" ]; then
+		conf="${conf}search ${new_domain_name}\n"
+	fi
+	for x in ${new_domain_name_servers}; do
+		conf="${conf}nameserver ${x}\n"
+	done
+	if type resolvconf >/dev/null 2>&1; then
+		printf "${conf}" | resolvconf -a "${interface}"
+	else
+		save_conf /etc/resolv.conf
+		printf "${conf}" > /etc/resolv.conf
+	fi
+}
+
+restore_resolv_conf()
+{
+	if type resolvconf >/dev/null 2>&1; then
+		resolvconf -d "${interface}" -f
+	else
+		restore_conf /etc/resolv.conf || return 0
+	fi
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)	make_resolv_conf;;
+EXPIRE|FAIL|IPV4LL|RELEASE|STOP)		restore_resolv_conf;;
+esac
diff --git a/dhcpcd-hooks/29-lookup-hostname b/dhcpcd-hooks/29-lookup-hostname
new file mode 100644
index 0000000..b9ce458
--- /dev/null
+++ b/dhcpcd-hooks/29-lookup-hostname
@@ -0,0 +1,33 @@
+# Lookup the hostname in DNS if not set
+
+lookup_hostname()
+{
+	local h=
+	# Silly ISC programs love to send error text to stdout
+	if type dig >/dev/null 2>&1; then
+		h=`dig +short -x ${new_ip_address}`
+		if [ $? = 0 ]; then
+			echo "${h}" | sed 's/\.$//'
+			return 0
+		fi
+	elif type host >/dev/null 2>&1; then
+		h=`host ${new_ip_address}`
+		if [ $? = 0 ]; then 
+			echo "${h}" \
+			| sed 's/.* domain name pointer \(.*\)./\1/'
+			return 0
+		fi
+	fi
+	return 1
+}
+
+set_hostname()
+{
+	if [ -z "${new_host_name}" ]; then
+		export new_host_name="$(lookup_hostname)"
+	fi
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)	set_hostname;;
+esac
diff --git a/dhcpcd-hooks/30-hostname b/dhcpcd-hooks/30-hostname
new file mode 100644
index 0000000..500ec0f
--- /dev/null
+++ b/dhcpcd-hooks/30-hostname
@@ -0,0 +1,21 @@
+# Set the hostname from DHCP data if required
+
+need_hostname()
+{
+	case "$(hostname)" in
+		""|"(none)"|localhost)	[ -n "${new_host_name}" ];;
+		"${old_host_name}") 	true;;
+		*) 			false;;
+	esac
+}
+
+set_hostname()
+{
+	if need_hostname; then
+		hostname "${new_host_name}"
+	fi
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)	set_hostname;;
+esac
diff --git a/dhcpcd-hooks/50-dhcpcd-compat b/dhcpcd-hooks/50-dhcpcd-compat
new file mode 100644
index 0000000..cba40a4
--- /dev/null
+++ b/dhcpcd-hooks/50-dhcpcd-compat
@@ -0,0 +1,31 @@
+# Compat enter hook shim for older dhcpcd versions
+
+IPADDR=${new_ip_address}
+INTERFACE=${interface}
+NETMASK=${new_subnet_mask}
+BROADCAST=${new_broadcast_address}
+NETWORK=${new_network_number}
+DHCPSID=${new_dhcp_server_identifier}
+GATEWAYS=${new_routers}
+DNSSERVERS=${new_domain_name_servers}
+DNSDOMAIN=${new_domain_name}
+DNSSEARCH=${new_domain_search}
+NISDOMAIN=${new_nis_domain}
+NISSERVERS=${new_nis_servers}
+NTPSERVERS=${new_ntp_servers}
+
+GATEWAY=
+for x in ${new_routers}; do
+	GATEWAY="${GATEWAY}${GATEWAY:+,}${x}"
+done
+DNS=
+for x in ${new_domain_name_servers}; do
+	DNS="${DNS}${DNS:+,}${x}"
+done
+
+x="down"
+case "${reason}" in
+RENEW) x="up";;
+BOUND|INFORM|REBIND|REBOOT|TEST|TIMEOUT|IPV4LL) x="new";;
+esac
+set -- "" "${x}"
diff --git a/dhcpcd-hooks/50-ntp.conf b/dhcpcd-hooks/50-ntp.conf
new file mode 100644
index 0000000..3772215
--- /dev/null
+++ b/dhcpcd-hooks/50-ntp.conf
@@ -0,0 +1,51 @@
+# Sample dhcpcd hook script for ntp
+
+# Detect OpenRC or BSD rc
+# Distributions may want to just have their command here instead of this
+if type rc-service >/dev/null 2>&1 && rc-service --exists ntpd; then
+	ntpd_restart_cmd="rc-service ntpd -- --ifstarted --quiet restart"
+elif [ -x /etc/rc.d/ntpd ]; then
+	ntpd_restart_cmd="/etc/rc.d/ntpd restart"
+elif [ -x /usr/local/etc/rc.d/ntpd ]; then
+	ntpd_restart_cmd="/usr/local/etc/rc.d/ntpd restart"
+fi
+
+make_ntp_conf()
+{
+	[ -z "${new_ntp_servers}" ] && return 0
+	local cf=/etc/ntp.conf."${interface}" x=
+	echo "${signature}" > "${cf}"
+	echo "restrict default noquery notrust nomodify" >> "${cf}"
+	echo "restrict 127.0.0.1" >> "${cf}"
+	for x in ${new_ntp_servers}; do
+		echo "restrict ${x} nomodify notrap noquery" >> "${cf}"
+		echo "server ${x}" >> "${cf}"
+	done
+	if [ ! -e /etc/ntp.conf ]; then
+		false	
+	elif type cmp >/dev/null 2>&1; then
+		cmp -s /etc/ntp.conf "${cf}"
+	elif type diff >/dev/null 2>&1; then
+		diff -q /etc/ntp.conf "${cf}" >/dev/null
+	else
+		false
+	fi
+	if [ $? = 0 ]; then
+		rm -f "${cf}"
+	else
+		save_conf /etc/ntp.conf
+		mv -f "${cf}" /etc/ntp.conf
+		[ -n "${ntpd_restart_cmd}" ] && ${ntpd_restart_cmd}
+	fi
+}
+
+restore_ntp_conf()
+{
+	restore_conf /etc/ntp.conf || return 0
+	[ -n "${ntpd_restart_cmd}" ] && ${ntpd_restart_cmd}
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)	make_ntp_conf;;
+EXPIRE|FAIL|IPV4LL|RELEASE|STOP)		restore_ntp_conf;;
+esac
diff --git a/dhcpcd-hooks/50-yp.conf b/dhcpcd-hooks/50-yp.conf
new file mode 100644
index 0000000..603f267
--- /dev/null
+++ b/dhcpcd-hooks/50-yp.conf
@@ -0,0 +1,48 @@
+# Sample dhcpcd hook for ypbind
+# This script is only suitable for the Linux version.
+
+ypbind_pid()
+{
+	[ -s /var/run/ypbind.pid ] && cat /var/run/ypbind.pid
+}
+
+make_yp_conf()
+{
+	[ -z "${new_nis_domain}" -a -z "${new_nis_servers}" ] && return 0
+	local cf=/etc/yp.conf."${interface}" prefix= x= pid=
+	echo "${signature}" > "${cf}"
+	if [ -n "${new_nis_domain}" ]; then
+		domainname "${new_nis_domain}"
+		if [ -n "${new_nis_servers}" ]; then
+			prefix="domain ${new_nis_domain} server "
+		else
+			echo "domain ${new_nis_domain} broadcast" >> "${cf}"
+		fi
+	else
+		prefix="ypserver "
+	fi
+	for x in ${new_nis_servers}; do
+		echo "${prefix}${x}" >> "${cf}"
+	done
+	save_conf /etc/yp.conf
+	mv -f "${cf}" /etc/yp.conf
+	pid="$(ypbind_pid)"
+	if [ -n "${pid}" ]; then
+		kill -HUP "${pid}"
+	fi
+}
+
+restore_yp_conf()
+{
+	[ -n "${old_nis_domain}" ] && domainname ""
+	restore_conf /etc/yp.conf || return 0
+	local pid="$(ypbind_pid)"
+	if [ -n "${pid}" ]; then
+		kill -HUP "${pid}"
+	fi
+}
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)	make_yp_conf;;
+EXPIRE|FAIL|IPV4LL|RELEASE|STOP)		restore_yp_conf;;
+esac
diff --git a/dhcpcd-hooks/90-NetworkManager b/dhcpcd-hooks/90-NetworkManager
new file mode 100644
index 0000000..f05ccd7
--- /dev/null
+++ b/dhcpcd-hooks/90-NetworkManager
@@ -0,0 +1,7 @@
+# Hook for NetworkManager, relies on D-Bus
+
+if type dbus-send >/dev/null 2>&1; then
+	dbus-send --system --dest=com.redhat.dhcp \
+		--type=method_call /com/redhat/dhcp/"${interface}" \
+		com.redhat.dhcp.set 'string:'"`env`"
+fi
diff --git a/dhcpcd-hooks/95-configured b/dhcpcd-hooks/95-configured
new file mode 100644
index 0000000..1ff07cf
--- /dev/null
+++ b/dhcpcd-hooks/95-configured
@@ -0,0 +1,22 @@
+# This script runs last, after all network configuration
+# has completed. It sets a property to let the framework
+# know that setting up the interface is complete.
+
+# For debugging:
+setprop dhcp.${interface}.reason "${reason}"
+
+case "${reason}" in
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)
+    setprop dhcp.${interface}.ipaddress "${new_ip_address}"
+    setprop dhcp.${interface}.gateway   "${new_routers%% *}"
+    setprop dhcp.${interface}.mask      "${new_subnet_mask}"
+    setprop dhcp.${interface}.leasetime "${new_dhcp_lease_time}"
+    setprop dhcp.${interface}.server    "${new_dhcp_server_identifier}"
+
+    setprop dhcp.${interface}.result "ok"
+    ;;
+
+EXPIRE|FAIL|IPV4LL|RELEASE|STOP)
+    setprop dhcp.${interface}.result "failed"
+    ;;
+esac
diff --git a/dhcpcd-hooks/Makefile b/dhcpcd-hooks/Makefile
new file mode 100644
index 0000000..06660ac
--- /dev/null
+++ b/dhcpcd-hooks/Makefile
@@ -0,0 +1,11 @@
+LIBEXECDIR=	${PREFIX}/libexec
+HOOKDIR=	${LIBEXECDIR}/dhcpcd-hooks
+SYSTEMSCRIPTS=	01-test 10-mtu 20-resolv.conf 30-hostname
+FILES=		${SYSTEMSCRIPTS} ${HOOKSCRIPTS}
+FILESDIR=	${HOOKDIR}
+
+MK=		../mk
+include ${MK}/os.mk
+include ${MK}/sys.mk
+include ${MK}/files.mk
+install: _filesinstall
diff --git a/dhcpcd-run-hooks b/dhcpcd-run-hooks
new file mode 100755
index 0000000..83534be
--- /dev/null
+++ b/dhcpcd-run-hooks
@@ -0,0 +1,38 @@
+#!/system/bin/sh
+# dhcpcd client configuration script 
+
+# Handy functions for our hooks to use
+signature="# Generated by dhcpcd for ${interface}"
+save_conf()
+{
+	if ls "$1" >/dev/null 2>&1; then
+		rm -f "$1"-pre."${interface}"
+		mv -f "$1" "$1"-pre."${interface}"
+	fi
+}
+restore_conf()
+{
+	ls "$1"-pre."${interface}" >/dev/null 2>&1 || return 1
+	rm -f "$1"
+	mv -f "$1"-pre."${interface}" "$1"
+}
+
+# We source each script into this one so that scripts run earlier can
+# remove variables from the environment so later scripts don't see them.
+# Thus, the user can create their dhcpcd.hook script to configure
+# /etc/resolv.conf how they want and stop the system scripts ever updating it.
+for hook in \
+	/system/etc/dhcpcd/dhcpcd.hook \
+	/system/etc/dhcpcd/dhcpcd-hooks/*
+do
+	for skip in ${skip_hooks}; do
+		case "${hook}" in
+			"${skip}")			continue 2;;
+			*/[0-9][0-9]"-${skip}")		continue 2;;
+			*/[0-9][0-9]"-${skip}.sh")	continue 2;;
+		esac
+	done
+	if ls "${hook}" >/dev/null 2>&1; then
+		. "${hook}"
+	fi
+done
diff --git a/dhcpcd-run-hooks.8 b/dhcpcd-run-hooks.8
new file mode 100644
index 0000000..2ba9792
--- /dev/null
+++ b/dhcpcd-run-hooks.8
@@ -0,0 +1,105 @@
+.\" Copyright 2006-2008 Roy Marples
+.\" 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.
+.\"
+.Dd May 21, 2008
+.Dt DHCPCD.SH 8 SMM
+.Sh NAME
+.Nm dhcpcd-run-hooks
+.Nd DHCP client configuration script 
+.Sh DESCRIPTION
+.Nm
+is used by
+.Xr dhcpcd 8
+to run any system or user defined hook scripts.
+System hook scripts are found in
+.Pa /system/etc/dhcpcd/dhcpcd-hooks
+and the user defined hook is
+.Pa /system/etc/dhcpcd/dhcpcd.hook .
+The default install supplies hook scripts for configuring
+.Pa /etc/resolv.conf
+and the hostname.
+Your distribution may have included other hook scripts to say configure
+ntp or ypbind.
+A test hook is also supplied that simply echos the dhcp variables to the
+console from DISCOVER message.
+.Pp
+Each time
+.Nm
+is invoked,
+.Ev $interface
+is set to the interface that
+.Nm dhcpcd
+is run on and
+.Ev $reason
+is to the reason why
+.Nm
+was invoked.
+DHCP information to be configured is held in variables starting with the word
+new_ and old DHCP information to be removed is held in variables starting with
+the word old_.
+.Nm dhcpcd
+can display the full list of variables it knows how about by using the
+.Fl V , -variables
+argument.
+.Pp
+Here's a list of reasons why
+.Nm
+could be invoked:
+.Bl -tag -width indent
+.It Dv BOUND
+dhcpcd obtained a new lease from a DHCP server.
+.It Dv RENEW
+dhcpcd renewed it's lease.
+.It Dv REBIND
+dhcpcd has rebound to a new DHCP server.
+.It Dv REBOOT
+dhcpcd successfully requested a lease from a DHCP server.
+.It Dv EXPIRE
+dhcpcd's lease expired and it failed to obtain a new one.
+.It Dv IPV4LL
+dhcpcd failed to contact any DHCP servers but did obtain an IPV4LL address.
+.It Dv FAIL
+dhcpcd failed to contact any DHCP servers or use an old lease.
+.It Dv TIMEOUT
+dhcpcd failed to contact any DHCP servers but was able to use an old lease.
+.It Dv TEST
+dhcpcd received an OFFER from a DHCP server but will not configure the
+interface.
+This is primarily used to test the variables are filled correctly for the
+script to process them.
+.El
+.Sh FILES 
+When
+.Nm
+runs, it loads
+.Pa /system/etc/dhcpcd/dhcpcd.hook
+and any scripts found in
+.Pa /system/etc/dhcpcd/dhcpcd-hooks
+in a lexical order.
+.Sh SEE ALSO
+.Xr dhcpcd 8
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd-run-hooks.8.in b/dhcpcd-run-hooks.8.in
new file mode 100644
index 0000000..a545203
--- /dev/null
+++ b/dhcpcd-run-hooks.8.in
@@ -0,0 +1,105 @@
+.\" Copyright 2006-2008 Roy Marples
+.\" 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.
+.\"
+.Dd May 21, 2008
+.Dt DHCPCD.SH 8 SMM
+.Sh NAME
+.Nm dhcpcd-run-hooks
+.Nd DHCP client configuration script 
+.Sh DESCRIPTION
+.Nm
+is used by
+.Xr dhcpcd 8
+to run any system or user defined hook scripts.
+System hook scripts are found in
+.Pa @HOOKDIR@
+and the user defined hook is
+.Pa @SYSCONFDIR@/dhcpcd.hook .
+The default install supplies hook scripts for configuring
+.Pa /etc/resolv.conf
+and the hostname.
+Your distribution may have included other hook scripts to say configure
+ntp or ypbind.
+A test hook is also supplied that simply echos the dhcp variables to the
+console from DISCOVER message.
+.Pp
+Each time
+.Nm
+is invoked,
+.Ev $interface
+is set to the interface that
+.Nm dhcpcd
+is run on and
+.Ev $reason
+is to the reason why
+.Nm
+was invoked.
+DHCP information to be configured is held in variables starting with the word
+new_ and old DHCP information to be removed is held in variables starting with
+the word old_.
+.Nm dhcpcd
+can display the full list of variables it knows how about by using the
+.Fl V , -variables
+argument.
+.Pp
+Here's a list of reasons why
+.Nm
+could be invoked:
+.Bl -tag -width indent
+.It Dv BOUND
+dhcpcd obtained a new lease from a DHCP server.
+.It Dv RENEW
+dhcpcd renewed it's lease.
+.It Dv REBIND
+dhcpcd has rebound to a new DHCP server.
+.It Dv REBOOT
+dhcpcd successfully requested a lease from a DHCP server.
+.It Dv EXPIRE
+dhcpcd's lease expired and it failed to obtain a new one.
+.It Dv IPV4LL
+dhcpcd failed to contact any DHCP servers but did obtain an IPV4LL address.
+.It Dv FAIL
+dhcpcd failed to contact any DHCP servers or use an old lease.
+.It Dv TIMEOUT
+dhcpcd failed to contact any DHCP servers but was able to use an old lease.
+.It Dv TEST
+dhcpcd received an OFFER from a DHCP server but will not configure the
+interface.
+This is primarily used to test the variables are filled correctly for the
+script to process them.
+.El
+.Sh FILES 
+When
+.Nm
+runs, it loads
+.Pa @SYSCONFDIR@/dhcpcd.hook
+and any scripts found in
+.Pa @HOOKDIR@
+in a lexical order.
+.Sh SEE ALSO
+.Xr dhcpcd 8
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd-run-hooks.in b/dhcpcd-run-hooks.in
new file mode 100644
index 0000000..7fd8b09
--- /dev/null
+++ b/dhcpcd-run-hooks.in
@@ -0,0 +1,38 @@
+#!/bin/sh
+# dhcpcd client configuration script 
+
+# Handy functions for our hooks to use
+signature="# Generated by dhcpcd for ${interface}"
+save_conf()
+{
+	if [ -f "$1" ]; then
+		rm -f "$1"-pre."${interface}"
+		mv -f "$1" "$1"-pre."${interface}"
+	fi
+}
+restore_conf()
+{
+	[ -f "$1"-pre."${interface}" ] || return 1
+	rm -f "$1"
+	mv -f "$1"-pre."${interface}" "$1"
+}
+
+# We source each script into this one so that scripts run earlier can
+# remove variables from the environment so later scripts don't see them.
+# Thus, the user can create their dhcpcd.hook script to configure
+# /etc/resolv.conf how they want and stop the system scripts ever updating it.
+for hook in \
+	@SYSCONFDIR@/dhcpcd.hook \
+	@HOOKDIR@/*
+do
+	for skip in ${skip_hooks}; do
+		case "${hook}" in
+			"${skip}")			continue 2;;
+			*/[0-9][0-9]"-${skip}")		continue 2;;
+			*/[0-9][0-9]"-${skip}.sh")	continue 2;;
+		esac
+	done
+	if [ -f "${hook}" ]; then
+		. "${hook}"
+	fi
+done
diff --git a/dhcpcd.8 b/dhcpcd.8
new file mode 100644
index 0000000..fff2ca0
--- /dev/null
+++ b/dhcpcd.8
@@ -0,0 +1,384 @@
+.\" Copyright 2006-2008 Roy Marples
+.\" 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.
+.\"
+.Dd Jul 08, 2008
+.Dt DHCPCD 8 SMM
+.Sh NAME
+.Nm dhcpcd
+.Nd an RFC 2131 compliant DHCP client
+.Sh SYNOPSIS
+.Nm
+.Op Fl dknpqADEGLSTXV
+.Op Fl c , -script Ar script
+.Op Fl f , -config Ar file
+.Op Fl h , -hostname Ar hostname
+.Op Fl i , -classid Ar classid
+.Op Fl l , -leasetime Ar seconds
+.Op Fl m , -metric Ar metric
+.Op Fl o , -option Ar option
+.Op Fl r , -request Ar address
+.Op Fl s , -inform Ar address Ns Op Ar /cidr
+.Op Fl t , -timeout Ar seconds
+.Op Fl u , -userclass Ar class
+.Op Fl v , -vendor Ar code , Ar value
+.Op Fl C , -nohook Ar hook
+.Op Fl F , -fqdn Ar FQDN
+.Op Fl I , -clientid Ar clientid
+.Op Fl O , -nooption Ar option
+.Ar interface
+.Nm
+.Fl k , -release
+.Ar interface
+.Nm
+.Fl x , -exit
+.Ar interface
+.Sh DESCRIPTION
+.Nm
+is an implementation of the DHCP client specified in
+.Rs
+.%T "RFC 2131"
+.Re
+.Nm
+gets the host information
+.Po 
+IP address, routes, etc
+.Pc
+from a DHCP server and configures the network
+.Ar interface
+of the
+machine on which it is running. 
+.Nm
+will then write DNS information to
+.Xr resolvconf 8 ,
+if available, otherwise directly to
+.Pa /etc/resolv.conf .
+If the hostname is currenly blank, (null) or localhost then
+.Nm
+will set the hostname to the one supplied by the DHCP server.
+.Nm
+then daemonises and waits for the lease renewal time to lapse.
+Then it attempts to renew its lease and reconfigure if the new lease changes.
+.Ss Local Link configuration
+If
+.Nm
+failed to obtain a lease, it will probe for a valid IPv4LL address
+.Po
+aka Zeroconf, aka APIPA
+.Pc .
+Once obtained it will restart the process of looking for a DHCP server to get a
+proper address.
+.Pp
+When using IPv4LL,
+.Nm
+will always succeed and return a 0 exit code. To disable this behaviour, you
+can use the
+.Fl L , -noipv4ll
+option.
+.Ss Hooking into DHCP events
+.Nm
+will run
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks ,
+or the script specified by the
+.Fl c , -script
+option.
+This script will run each script found in
+.Pa /system/etc/dhcpcd/dhcpcd-hooks
+in a lexical order.
+The default installation supplies the scripts
+.Pa 01-test ,
+.Pa 10-mtu ,
+.Pa 20-resolv.conf
+and
+.Pa 30-hostname .
+You can disable each script by using the
+.Fl C , -nohook
+option.
+See
+.Xr dhcpcd-run-hooks 8
+for details on how these scripts work.
+.Nm
+currently ignores the exit code of the script.
+.Ss Fine tuning
+You can fine tune the behaviour of
+.Nm
+with the following options:
+.Bl -tag -width indent
+.It Fl c , -script Ar script
+Use this
+.Ar script
+instead of the default
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
+.It Fl d , -debug
+Echo debug and informational messages to the console.
+Subsequent debug options stop
+.Nm
+from daemonising.
+.It Fl f , -config Ar file
+Specify a config to load instead of
+.Pa /system/etc/dhcpcd/dhcpcd.conf .
+.Nm
+always processes the config file before any command line options.
+.It Fl h , -hostname Ar hostname
+By default,
+.Nm
+will send the current hostname to the DHCP server so it can register in DNS.
+You can use this option to specify the
+.Ar hostname
+sent, or an empty string to
+stop any
+.Ar hostname
+from being sent.
+.It Fl i , -classid Ar classid
+Override the
+.Ar classid
+field sent. The default is
+dhcpcd <version>.
+If not set then none is sent.
+.It Fl k , -release
+This causes an existing
+.Nm
+process running on the
+.Ar interface
+to release its lease, deconfigure the
+.Ar interface
+and then exit.
+.It Fl l , -leasetime Ar seconds
+Request a specific lease time in
+.Ar seconds .
+By default
+.Nm
+does not request any lease time and leaves the it in the hands of the
+DHCP server.
+.It Fl m , -metric Ar metric
+Added routes will use the
+.Ar metric
+on systems where this is supported
+.Po
+presently only Linux
+.Pc .
+Route metrics allow the addition of routes to the same destination across
+different interfaces, the lower the metric the more it is preferred.
+.It Fl o , -option Ar option
+Request the DHCP
+.Ar option
+variable for use in
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
+.It Fl n , -renew
+Notifies an existing
+.Nm
+process running on the
+.Ar interface
+to renew it's lease. If
+.Nm
+is not running, then it starts up as normal.
+.It Fl p , -persistent
+.Nm
+normally deconfigures the
+.Ar interface
+and configuration when it exits.
+Sometimes, this isn't desirable if for example you have root mounted over NFS.
+You can use this option to stop this from happening.
+.It Fl r , -request Op Ar address
+.Nm
+normally sends a DHCP Broadcast to find servers to offer an address.
+.Nm
+will then request the address used.
+You can use this option to skip the broadcast step and just request an
+.Ar address .
+The downside is if you request an
+.Ar address
+the DHCP server does not know about or the DHCP server is not
+authorative, it will remain silent.
+In this situation, we go back to the init state and broadcast again.
+If no
+.Ar address
+is given then the first address currently assigned to the
+.Ar interface
+is used.
+.It Fl s , -inform Op Ar address Ns Op Ar /cidr
+Behaves exactly like
+.Fl r , -request
+as above, but sends a DHCP INFORM instead of a REQUEST.
+This does not get a lease as such, just notifies the DHCP server of the
+.Ar address
+in use.
+.Nm
+remains running and pretends it has an infinite lease.
+.Nm
+will not de-configure the interface when it exits.
+If
+.Nm
+fails to contact a DHCP server then it returns a failure instead of falling
+back on IPv4LL.
+.It Fl t , -timeout Ar seconds
+Timeout after
+.Ar seconds ,
+instead of the default 30.
+A setting of 0
+.Ar seconds
+causes
+.Nm
+to wait forever to get a lease.
+.It Fl u , -userclass Ar class
+Tags the DHCP message with the userclass
+.Ar class .
+DHCP servers use this give memebers of the class DHCP options other than the
+default, without having to know things like hardware address or hostname.
+.It Fl v , -vendor Ar code , Ns Ar value
+Add an enscapulated vendor option.
+.Ar code
+should be between 1 and 254 inclusive.
+Examples.
+.Pp
+Set the vendor option 01 with an IP address.
+.D1 dhcpcd -v 01,192.168.0.2 eth0
+Set the vendor option 02 with a hex code.
+.D1 dhcpcd -v 02,01:02:03:04:05 eth0
+Do the above and set a third option with a string and not an IP address.
+.D1 dhcpcd -v 01,192.168.0.2 -v 02,01:02:03:04:05 -v 03,\e"192.168.0.2\e" eth0
+.It Fl x , -exit
+This causes an existing
+.Nm
+process running on the
+.Ar interface
+to deconfigure the
+.Ar interface
+and exit.
+.It Fl D , -duid 
+Generate an
+.Rs
+.%T "RFC 4361"
+.Re
+compliant clientid.
+This requires persistent storage and not all DHCP servers work with it so it's
+not enabled by default.
+The DUID generated will be held in
+.Pa /system/etc/dhcpcd/dhcpcd.duid
+and should not be copied to other hosts.
+.It Fl E , -lastlease
+If
+.Nm
+cannot obtain a lease, then try to use the last lease acquired for the
+interface.
+If the
+.Fl p, -persistent
+option is not given then the lease is used if it hasn't expired.
+.It Fl F , -fqdn Ar fqdn
+Requests that the DHCP server updates DNS using FQDN instead of just a
+hostname.
+Valid values for
+.Ar fqdn
+are none, ptr and both.
+The current hostname or the hostname specified using the
+.Fl h , -hostname
+option must be a FQDN.
+.Nm
+itself never does any DNS updates.
+.It Fl I , -clientid Ar clientid
+Change the default clientid sent from the interface hardware address.
+If the string is of the format 01:02:03 then it is encoded as hex.
+If not set then none is sent.
+.El
+.Ss Restriciting behaviour
+.Nm
+will try to do as much as it can by default.
+However, there are sometimes situations where you don't want the things to be
+configured exactly how the the DHCP server wants.
+Here are some options that deal with turning these bits off.
+.Bl -tag -width indent
+.It Fl q , -quiet
+Quiet
+.Nm
+on the command line, only warnings and errors will be displayed.
+The messagea are still logged though.
+.It Fl A , -noarp
+Don't request or claim the address by ARP.
+This also disables IPv4LL.
+.It Fl G , -nogateway
+Don't set any default routes.
+.It Fl C , -nohook Ar script
+Don't run this hook script.
+Matches full name, or prefixed with 2 numbers optionally ending with
+.Pa .sh .
+.Pp
+So to stop dhcpcd from touching your DNS or MTU settings you would do:-
+.D1 dhcpcd -C resolv.conf -C mtu eth0
+.It Fl X , -nodaemonise
+Don't daemonise when we acquire a lease.
+This disables the
+.Fl t, -timeout
+option.
+This is mainly useful for running under the control of another process, such
+as a debugger or a network manager.
+.It Fl L , -noipv4ll
+Don't use IPv4LL at all.
+.It Fl O , -nooption Ar option
+Don't request the specified option.
+If no option given, then don't request any options other than those to
+configure the interface and routing.
+.It Fl T, -test
+On receipt of OFFER messages just call
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks
+with the reason of TEST which echo's the DHCP variables found in the message
+to the console.
+The interface configuration isn't touched and neither are any configuration
+files.
+.It Fl V, -variables
+Display a list of option codes and the associated variable for use in
+.Xr dhcpcd-run-hooks 8 .
+.El
+.Sh NOTES
+.Nm
+requires a Berkley Packet Filter, or BPF device on BSD based systems and a
+Linux Socket Filter, or LPF device on Linux based systems.
+.Sh FILES
+.Bl -ohang
+.It Pa /system/etc/dhcpcd/dhcpcd.conf
+Configuration file for dhcpcd.
+If you always use the same options, put them here.
+.It Pa /system/etc/dhcpcd/dhcpcd.duid
+Text file that holds the DUID used to identify the host.
+.It Pa /system/etc/dhcpcd/dhcpcd-run-hooks
+Bourne shell script that is run to configure or deconfigure an interface.
+.It Pa /system/etc/dhcpcd/dhcpcd-hooks
+A directory containing bourne shell scripts that are run by the above script.
+Each script can be disabled by using the
+.Fl C , -nohook
+option described above.
+.It Pa /data/misc/dhcp/dhcpcd\- Ns Ar interface Ns .lease
+The actual DHCP message send by the server. We use this when reading the last
+lease and use the files mtime as when it was issued.
+.El
+.Sh SEE ALSO
+.Xr dhcpcd.conf 5 ,
+.Xr dhcpcd-run-hooks 8 ,
+.Xr resolv.conf 5 ,
+.Xr resolvconf 8 ,
+.Sh STANDARDS
+RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396, RFC 3397,
+RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd.8.in b/dhcpcd.8.in
new file mode 100644
index 0000000..c008a5a
--- /dev/null
+++ b/dhcpcd.8.in
@@ -0,0 +1,384 @@
+.\" Copyright 2006-2008 Roy Marples
+.\" 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.
+.\"
+.Dd Jul 08, 2008
+.Dt DHCPCD 8 SMM
+.Sh NAME
+.Nm dhcpcd
+.Nd an RFC 2131 compliant DHCP client
+.Sh SYNOPSIS
+.Nm
+.Op Fl dknpqADEGLSTXV
+.Op Fl c , -script Ar script
+.Op Fl f , -config Ar file
+.Op Fl h , -hostname Ar hostname
+.Op Fl i , -classid Ar classid
+.Op Fl l , -leasetime Ar seconds
+.Op Fl m , -metric Ar metric
+.Op Fl o , -option Ar option
+.Op Fl r , -request Ar address
+.Op Fl s , -inform Ar address Ns Op Ar /cidr
+.Op Fl t , -timeout Ar seconds
+.Op Fl u , -userclass Ar class
+.Op Fl v , -vendor Ar code , Ar value
+.Op Fl C , -nohook Ar hook
+.Op Fl F , -fqdn Ar FQDN
+.Op Fl I , -clientid Ar clientid
+.Op Fl O , -nooption Ar option
+.Ar interface
+.Nm
+.Fl k , -release
+.Ar interface
+.Nm
+.Fl x , -exit
+.Ar interface
+.Sh DESCRIPTION
+.Nm
+is an implementation of the DHCP client specified in
+.Rs
+.%T "RFC 2131"
+.Re
+.Nm
+gets the host information
+.Po 
+IP address, routes, etc
+.Pc
+from a DHCP server and configures the network
+.Ar interface
+of the
+machine on which it is running. 
+.Nm
+will then write DNS information to
+.Xr resolvconf 8 ,
+if available, otherwise directly to
+.Pa /etc/resolv.conf .
+If the hostname is currenly blank, (null) or localhost then
+.Nm
+will set the hostname to the one supplied by the DHCP server.
+.Nm
+then daemonises and waits for the lease renewal time to lapse.
+Then it attempts to renew its lease and reconfigure if the new lease changes.
+.Ss Local Link configuration
+If
+.Nm
+failed to obtain a lease, it will probe for a valid IPv4LL address
+.Po
+aka Zeroconf, aka APIPA
+.Pc .
+Once obtained it will restart the process of looking for a DHCP server to get a
+proper address.
+.Pp
+When using IPv4LL,
+.Nm
+will always succeed and return a 0 exit code. To disable this behaviour, you
+can use the
+.Fl L , -noipv4ll
+option.
+.Ss Hooking into DHCP events
+.Nm
+will run
+.Pa @SCRIPT@ ,
+or the script specified by the
+.Fl c , -script
+option.
+This script will run each script found in
+.Pa @HOOKDIR@
+in a lexical order.
+The default installation supplies the scripts
+.Pa 01-test ,
+.Pa 10-mtu ,
+.Pa 20-resolv.conf
+and
+.Pa 30-hostname .
+You can disable each script by using the
+.Fl C , -nohook
+option.
+See
+.Xr dhcpcd-run-hooks 8
+for details on how these scripts work.
+.Nm
+currently ignores the exit code of the script.
+.Ss Fine tuning
+You can fine tune the behaviour of
+.Nm
+with the following options:
+.Bl -tag -width indent
+.It Fl c , -script Ar script
+Use this
+.Ar script
+instead of the default
+.Pa @SCRIPT@ .
+.It Fl d , -debug
+Echo debug and informational messages to the console.
+Subsequent debug options stop
+.Nm
+from daemonising.
+.It Fl f , -config Ar file
+Specify a config to load instead of
+.Pa @SYSCONFDIR@/dhcpcd.conf .
+.Nm
+always processes the config file before any command line options.
+.It Fl h , -hostname Ar hostname
+By default,
+.Nm
+will send the current hostname to the DHCP server so it can register in DNS.
+You can use this option to specify the
+.Ar hostname
+sent, or an empty string to
+stop any
+.Ar hostname
+from being sent.
+.It Fl i , -classid Ar classid
+Override the
+.Ar classid
+field sent. The default is
+dhcpcd <version>.
+If not set then none is sent.
+.It Fl k , -release
+This causes an existing
+.Nm
+process running on the
+.Ar interface
+to release its lease, deconfigure the
+.Ar interface
+and then exit.
+.It Fl l , -leasetime Ar seconds
+Request a specific lease time in
+.Ar seconds .
+By default
+.Nm
+does not request any lease time and leaves the it in the hands of the
+DHCP server.
+.It Fl m , -metric Ar metric
+Added routes will use the
+.Ar metric
+on systems where this is supported
+.Po
+presently only Linux
+.Pc .
+Route metrics allow the addition of routes to the same destination across
+different interfaces, the lower the metric the more it is preferred.
+.It Fl o , -option Ar option
+Request the DHCP
+.Ar option
+variable for use in
+.Pa @SCRIPT@ .
+.It Fl n , -renew
+Notifies an existing
+.Nm
+process running on the
+.Ar interface
+to renew it's lease. If
+.Nm
+is not running, then it starts up as normal.
+.It Fl p , -persistent
+.Nm
+normally deconfigures the
+.Ar interface
+and configuration when it exits.
+Sometimes, this isn't desirable if for example you have root mounted over NFS.
+You can use this option to stop this from happening.
+.It Fl r , -request Op Ar address
+.Nm
+normally sends a DHCP Broadcast to find servers to offer an address.
+.Nm
+will then request the address used.
+You can use this option to skip the broadcast step and just request an
+.Ar address .
+The downside is if you request an
+.Ar address
+the DHCP server does not know about or the DHCP server is not
+authorative, it will remain silent.
+In this situation, we go back to the init state and broadcast again.
+If no
+.Ar address
+is given then the first address currently assigned to the
+.Ar interface
+is used.
+.It Fl s , -inform Op Ar address Ns Op Ar /cidr
+Behaves exactly like
+.Fl r , -request
+as above, but sends a DHCP INFORM instead of a REQUEST.
+This does not get a lease as such, just notifies the DHCP server of the
+.Ar address
+in use.
+.Nm
+remains running and pretends it has an infinite lease.
+.Nm
+will not de-configure the interface when it exits.
+If
+.Nm
+fails to contact a DHCP server then it returns a failure instead of falling
+back on IPv4LL.
+.It Fl t , -timeout Ar seconds
+Timeout after
+.Ar seconds ,
+instead of the default 30.
+A setting of 0
+.Ar seconds
+causes
+.Nm
+to wait forever to get a lease.
+.It Fl u , -userclass Ar class
+Tags the DHCP message with the userclass
+.Ar class .
+DHCP servers use this give memebers of the class DHCP options other than the
+default, without having to know things like hardware address or hostname.
+.It Fl v , -vendor Ar code , Ns Ar value
+Add an enscapulated vendor option.
+.Ar code
+should be between 1 and 254 inclusive.
+Examples.
+.Pp
+Set the vendor option 01 with an IP address.
+.D1 dhcpcd -v 01,192.168.0.2 eth0
+Set the vendor option 02 with a hex code.
+.D1 dhcpcd -v 02,01:02:03:04:05 eth0
+Do the above and set a third option with a string and not an IP address.
+.D1 dhcpcd -v 01,192.168.0.2 -v 02,01:02:03:04:05 -v 03,\e"192.168.0.2\e" eth0
+.It Fl x , -exit
+This causes an existing
+.Nm
+process running on the
+.Ar interface
+to deconfigure the
+.Ar interface
+and exit.
+.It Fl D , -duid 
+Generate an
+.Rs
+.%T "RFC 4361"
+.Re
+compliant clientid.
+This requires persistent storage and not all DHCP servers work with it so it's
+not enabled by default.
+The DUID generated will be held in
+.Pa @SYSCONFDIR@/dhcpcd.duid
+and should not be copied to other hosts.
+.It Fl E , -lastlease
+If
+.Nm
+cannot obtain a lease, then try to use the last lease acquired for the
+interface.
+If the
+.Fl p, -persistent
+option is not given then the lease is used if it hasn't expired.
+.It Fl F , -fqdn Ar fqdn
+Requests that the DHCP server updates DNS using FQDN instead of just a
+hostname.
+Valid values for
+.Ar fqdn
+are none, ptr and both.
+The current hostname or the hostname specified using the
+.Fl h , -hostname
+option must be a FQDN.
+.Nm
+itself never does any DNS updates.
+.It Fl I , -clientid Ar clientid
+Change the default clientid sent from the interface hardware address.
+If the string is of the format 01:02:03 then it is encoded as hex.
+If not set then none is sent.
+.El
+.Ss Restriciting behaviour
+.Nm
+will try to do as much as it can by default.
+However, there are sometimes situations where you don't want the things to be
+configured exactly how the the DHCP server wants.
+Here are some options that deal with turning these bits off.
+.Bl -tag -width indent
+.It Fl q , -quiet
+Quiet
+.Nm
+on the command line, only warnings and errors will be displayed.
+The messagea are still logged though.
+.It Fl A , -noarp
+Don't request or claim the address by ARP.
+This also disables IPv4LL.
+.It Fl G , -nogateway
+Don't set any default routes.
+.It Fl C , -nohook Ar script
+Don't run this hook script.
+Matches full name, or prefixed with 2 numbers optionally ending with
+.Pa .sh .
+.Pp
+So to stop dhcpcd from touching your DNS or MTU settings you would do:-
+.D1 dhcpcd -C resolv.conf -C mtu eth0
+.It Fl X , -nodaemonise
+Don't daemonise when we acquire a lease.
+This disables the
+.Fl t, -timeout
+option.
+This is mainly useful for running under the control of another process, such
+as a debugger or a network manager.
+.It Fl L , -noipv4ll
+Don't use IPv4LL at all.
+.It Fl O , -nooption Ar option
+Don't request the specified option.
+If no option given, then don't request any options other than those to
+configure the interface and routing.
+.It Fl T, -test
+On receipt of OFFER messages just call
+.Pa @SCRIPT@
+with the reason of TEST which echo's the DHCP variables found in the message
+to the console.
+The interface configuration isn't touched and neither are any configuration
+files.
+.It Fl V, -variables
+Display a list of option codes and the associated variable for use in
+.Xr dhcpcd-run-hooks 8 .
+.El
+.Sh NOTES
+.Nm
+requires a Berkley Packet Filter, or BPF device on BSD based systems and a
+Linux Socket Filter, or LPF device on Linux based systems.
+.Sh FILES
+.Bl -ohang
+.It Pa @SYSCONFDIR@/dhcpcd.conf
+Configuration file for dhcpcd.
+If you always use the same options, put them here.
+.It Pa @SYSCONFDIR@/dhcpcd.duid
+Text file that holds the DUID used to identify the host.
+.It Pa @SCRIPT@
+Bourne shell script that is run to configure or deconfigure an interface.
+.It Pa @HOOKDIR@
+A directory containing bourne shell scripts that are run by the above script.
+Each script can be disabled by using the
+.Fl C , -nohook
+option described above.
+.It Pa @DBDIR@/dhcpcd\- Ns Ar interface Ns .lease
+The actual DHCP message send by the server. We use this when reading the last
+lease and use the files mtime as when it was issued.
+.El
+.Sh SEE ALSO
+.Xr dhcpcd.conf 5 ,
+.Xr dhcpcd-run-hooks 8 ,
+.Xr resolv.conf 5 ,
+.Xr resolvconf 8 ,
+.Sh STANDARDS
+RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396, RFC 3397,
+RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702.
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd.c b/dhcpcd.c
new file mode 100644
index 0000000..4a6e4af
--- /dev/null
+++ b/dhcpcd.c
@@ -0,0 +1,1041 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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.
+ */
+
+const char copyright[] = "Copyright (c) 2006-2008 Roy Marples";
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "client.h"
+#include "dhcpcd.h"
+#include "dhcp.h"
+#include "net.h"
+#include "logger.h"
+
+#ifdef ANDROID
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#include <private/android_filesystem_config.h>
+#endif
+
+/* Don't set any optional arguments here so we retain POSIX
+ * compatibility with getopt */
+#define OPTS "c:df:h:i:kl:m:no:pqr:s:t:u:v:xAC:DEF:GI:LO:TVX"
+
+static int doversion = 0;
+static int dohelp = 0;
+static const struct option longopts[] = {
+	{"script",      required_argument,  NULL, 'c'},
+	{"debug",       no_argument,        NULL, 'd'},
+	{"config",	required_argument,  NULL, 'f'},
+	{"hostname",    optional_argument,  NULL, 'h'},
+	{"classid",     optional_argument,  NULL, 'i'},
+	{"release",     no_argument,        NULL, 'k'},
+	{"leasetime",   required_argument,  NULL, 'l'},
+	{"metric",      required_argument,  NULL, 'm'},
+	{"renew",       no_argument,        NULL, 'n'},
+	{"option",      required_argument,  NULL, 'o'},
+	{"persistent",  no_argument,        NULL, 'p'},
+	{"quiet",       no_argument,        NULL, 'q'},
+	{"inform",      optional_argument,  NULL, 's'},
+	{"request",     optional_argument,  NULL, 'r'},
+	{"timeout",     required_argument,  NULL, 't'},
+	{"userclass",   required_argument,  NULL, 'u'},
+	{"vendor",      required_argument,  NULL, 'v'},
+	{"exit",        no_argument,        NULL, 'x'},
+	{"noarp",       no_argument,        NULL, 'A'},
+	{"nohook",	required_argument,  NULL, 'C'},
+	{"duid",        no_argument,        NULL, 'D'},
+	{"lastlease",   no_argument,        NULL, 'E'},
+	{"fqdn",        optional_argument,  NULL, 'F'},
+	{"nogateway",   no_argument,        NULL, 'G'},
+	{"clientid",    optional_argument,  NULL, 'I'},
+	{"noipv4ll",    no_argument,        NULL, 'L'},
+	{"nooption",    optional_argument,  NULL, 'O'},
+	{"test",        no_argument,        NULL, 'T'},
+	{"variables",   no_argument,        NULL, 'V'},
+	{"nodaemonise", no_argument,        NULL, 'X'},
+	{"help",        no_argument,        &dohelp, 1},
+	{"version",     no_argument,        &doversion, 1},
+#ifdef THERE_IS_NO_FORK
+	{"daemonised",	no_argument,        NULL, 'z'},
+	{"skiproutes",  required_argument,  NULL, 'Z'},
+#endif
+#ifdef CMDLINE_COMPAT
+	{"nohostname",  no_argument,        NULL, 'H'},
+	{"nomtu",       no_argument,        NULL, 'M'},
+	{"nontp",       no_argument,        NULL, 'N'},
+	{"nodns",       no_argument,        NULL, 'R'},
+	{"msscr",       no_argument,        NULL, 'S'},
+	{"nonis",       no_argument,        NULL, 'Y'},
+#endif
+	{NULL,          0,                  NULL, '\0'}
+};
+
+#ifdef THERE_IS_NO_FORK
+char dhcpcd[PATH_MAX];
+char **dhcpcd_argv = NULL;
+int dhcpcd_argc = 0;
+char *dhcpcd_skiproutes = NULL;
+#define EXTRA_OPTS "zZ:"
+#endif
+
+#ifdef CMDLINE_COMPAT
+# define EXTRA_OPTS "HMNRSY"
+#endif
+
+#ifndef EXTRA_OPTS
+# define EXTRA_OPTS
+#endif
+
+static int
+atoint(const char *s)
+{
+	char *t;
+	long n;
+
+	errno = 0;
+	n = strtol(s, &t, 0);
+	if ((errno != 0 && n == 0) || s == t ||
+	    (errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)))
+	{
+		logger(LOG_ERR, "`%s' out of range", s);
+		return -1;
+	}
+
+	return (int)n;
+}
+
+static pid_t
+read_pid(const char *pidfile)
+{
+	FILE *fp;
+	pid_t pid = 0;
+
+	if ((fp = fopen(pidfile, "r")) == NULL) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	fscanf(fp, "%d", &pid);
+	fclose(fp);
+
+	return pid;
+}
+
+static void
+usage(void)
+{
+#ifndef MINIMAL
+	printf("usage: "PACKAGE" [-dknpqxADEGHKLOTV] [-c script] [-f file ] [-h hostname]\n"
+	       "              [-i classID ] [-l leasetime] [-m metric] [-o option] [-r ipaddr]\n"
+	       "              [-s ipaddr] [-t timeout] [-u userclass] [-F none|ptr|both]\n"
+	       "              [-I clientID] [-C hookscript] <interface>\n");
+#endif
+}
+
+static char * 
+add_environ(struct options *options, const char *value, int uniq)
+{
+	char **newlist;
+	char **lst = options->environ;
+	size_t i = 0, l, lv;
+	char *match = NULL, *p;
+
+	match = xstrdup(value);
+	p = strchr(match, '=');
+	if (p)
+		*p++ = '\0';
+	l = strlen(match);
+
+	while (lst && lst[i]) {
+		if (match && strncmp(lst[i], match, l) == 0) {
+			if (uniq) {
+				free(lst[i]);
+				lst[i] = xstrdup(value);
+			} else {
+				/* Append a space and the value to it */
+				l = strlen(lst[i]);
+				lv = strlen(p);
+				lst[i] = xrealloc(lst[i], l + lv + 2);
+				lst[i][l] = ' ';
+				memcpy(lst[i] + l + 1, p, lv);
+				lst[i][l + lv + 1] = '\0';
+			}
+			free(match);
+			return lst[i];
+		}
+		i++;
+	}
+
+	newlist = xrealloc(lst, sizeof(char *) * (i + 2));
+	newlist[i] = xstrdup(value);
+	newlist[i + 1] = NULL;
+	options->environ = newlist;
+	free(match);
+	return newlist[i];
+}
+
+#ifndef MINIMAL
+#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
+static ssize_t
+parse_string_hwaddr(char *sbuf, ssize_t slen, char *str, int clid)
+{
+	ssize_t l;
+	char *p;
+	int i;
+	char c[4];
+
+	/* If surrounded by quotes then it's a string */
+	if (*str == '"') {
+		str++;
+		l = strlen(str);
+		p = str + l - 1;
+		if (*p == '"')
+			*p = '\0';
+	} else {
+		l = hwaddr_aton(NULL, str);
+		if (l > 1) {
+			if (l > slen) {
+				errno = ENOBUFS;
+				return -1;
+			}
+			hwaddr_aton((uint8_t *)sbuf, str);
+			return l;
+		}
+	}
+
+	/* Process escapes */
+	l = 0;
+	/* If processing a string on the clientid, first byte should be
+	 * 0 to indicate a non hardware type */
+	if (clid) {
+		*sbuf++ = 0;
+		l++;
+	}
+	c[3] = '\0';
+	while (*str) {
+		if (++l > slen) {
+			errno = ENOBUFS;
+			return -1;
+		}
+		if (*str == '\\') {
+			str++;
+			switch(*str++) {
+			case '\0':
+				break;
+			case 'b':
+				*sbuf++ = '\b';
+				break;
+			case 'n':
+				*sbuf++ = '\n';
+				break;
+			case 'r':
+				*sbuf++ = '\r';
+				break;
+			case 't':
+				*sbuf++ = '\t';
+				break;
+			case 'x':
+				/* Grab a hex code */
+				c[1] = '\0';
+				for (i = 0; i < 2; i++) {
+					if (isxdigit((unsigned char)*str) == 0)
+						break;
+					c[i] = *str++;
+				}
+				if (c[1] != '\0') {
+					c[2] = '\0';
+					*sbuf++ = strtol(c, NULL, 16);
+				} else
+					l--;
+				break;
+			case '0':
+				/* Grab an octal code */
+				c[2] = '\0';
+				for (i = 0; i < 3; i++) {
+					if (*str < '0' || *str > '7')
+						break;
+					c[i] = *str++;
+				}
+				if (c[2] != '\0') {
+					i = strtol(c, NULL, 8);
+					if (i > 255)
+						i = 255;
+					*sbuf ++= i;
+				} else
+					l--;
+				break;
+			default:
+				*sbuf++ = *str++;
+			}
+		} else
+			*sbuf++ = *str++;
+	}
+	return l;
+}
+#endif
+
+static int
+parse_option(int opt, char *oarg, struct options *options)
+{
+	int i;
+	char *p;
+	ssize_t s;
+#ifndef MINIMAL
+	struct in_addr addr;
+#endif
+
+	switch(opt) {
+	case 'c':
+		strlcpy(options->script, oarg, sizeof(options->script));
+		break;
+	case 'h':
+#ifndef MINIMAL
+		if (oarg)
+			s = parse_string(options->hostname + 1,
+					 MAXHOSTNAMELEN, oarg);
+		else
+			s = 0;
+		if (s == -1) {
+			logger(LOG_ERR, "hostname: %s", strerror(errno));
+			return -1;
+		}
+		options->hostname[0] = (uint8_t)s;
+#endif
+		break;
+	case 'i':
+#ifndef MINIMAL
+		if (oarg)
+			s = parse_string((char *)options->classid + 1,
+					 CLASSID_MAX_LEN, oarg);
+		else
+			s = 0;
+		if (s == -1) {
+			logger(LOG_ERR, "classid: %s", strerror(errno));
+			return -1;
+		}
+		*options->classid = (uint8_t)s;
+#endif
+		break;
+	case 'l':
+#ifndef MINIMAL
+		if (*oarg == '-') {
+			logger(LOG_ERR,
+			       "leasetime must be a positive value");
+			return -1;
+		}
+		errno = 0;
+		options->leasetime = (uint32_t)strtol(oarg, NULL, 0);
+		if (errno == EINVAL || errno == ERANGE) {
+			logger(LOG_ERR, "`%s' out of range", oarg);
+			return -1;
+		}
+#endif
+		break;
+	case 'm':
+		options->metric = atoint(oarg);
+		if (options->metric < 0) {
+			logger(LOG_ERR, "metric must be a positive value");
+			return -1;
+		}
+		break;
+	case 'o':
+		if (make_reqmask(options->reqmask, &oarg, 1) != 0) {
+			logger(LOG_ERR, "unknown option `%s'", oarg);
+			return -1;
+		}
+		break;
+	case 'p':
+		options->options |= DHCPCD_PERSISTENT;
+		break;
+	case 'q':
+		setloglevel(LOG_WARNING);
+		break;
+	case 's':
+		options->options |= DHCPCD_INFORM;
+		options->options |= DHCPCD_PERSISTENT;
+		options->options &= ~DHCPCD_ARP;
+		if (!oarg || *oarg == '\0') {
+			options->request_address.s_addr = 0;
+			break;
+		} else {
+			if ((p = strchr(oarg, '/'))) {
+				/* nullify the slash, so the -r option
+				 * can read the address */
+				*p++ = '\0';
+				if (sscanf(p, "%d", &i) != 1 ||
+				    inet_cidrtoaddr(i, &options->request_netmask) != 0)
+				{
+					logger(LOG_ERR,
+					       "`%s' is not a valid CIDR",
+					       p);
+					return -1;
+				}
+			}
+		}
+		/* FALLTHROUGH */
+	case 'r':
+		if (!(options->options & DHCPCD_INFORM))
+			options->options |= DHCPCD_REQUEST;
+		if (*oarg && !inet_aton(oarg, &options->request_address)) {
+			logger(LOG_ERR, "`%s' is not a valid IP address",
+			       oarg);
+			return -1;
+		}
+		break;
+	case 't':
+		options->timeout = atoint(oarg);
+		if (options->timeout < 0) {
+			logger (LOG_ERR, "timeout must be a positive value");
+			return -1;
+		}
+		break;
+	case 'u':
+#ifndef MINIMAL
+		s = USERCLASS_MAX_LEN - options->userclass[0] - 1;
+		s = parse_string((char *)options->userclass + options->userclass[0] + 2,
+				 s, oarg);
+		if (s == -1) {
+			logger(LOG_ERR, "userclass: %s", strerror(errno));
+			return -1;
+		}
+		if (s != 0) {
+			options->userclass[options->userclass[0] + 1] = s;
+			options->userclass[0] += s + 1;
+		}
+#endif
+		break;
+	case 'v':
+#ifndef MINIMAL
+		p = strchr(oarg, ',');
+		if (!p || !p[1]) {
+			logger(LOG_ERR, "invalid vendor format");
+			return -1;
+		}
+		*p = '\0';
+		i = atoint(oarg);
+		oarg = p + 1;
+		if (i < 1 || i > 254) {
+			logger(LOG_ERR, "vendor option should be between"
+					" 1 and 254 inclusive");
+			return -1;
+		}
+		s = VENDOR_MAX_LEN - options->vendor[0] - 2;
+		if (inet_aton(oarg, &addr) == 1) {
+			if (s < 6) {
+				s = -1;
+				errno = ENOBUFS;
+			} else
+				memcpy(options->vendor + options->vendor[0] + 3,
+				       &addr.s_addr, sizeof(addr.s_addr));
+		} else {
+			s = parse_string((char *)options->vendor + options->vendor[0] + 3,
+					 s, oarg);
+		}
+		if (s == -1) {
+			logger(LOG_ERR, "vendor: %s", strerror(errno));
+			return -1;
+		}
+		if (s != 0) {
+			options->vendor[options->vendor[0] + 1] = i;
+			options->vendor[options->vendor[0] + 2] = s;
+			options->vendor[0] += s + 2;
+		}
+#endif
+		break;
+	case 'A':
+		options->options &= ~DHCPCD_ARP;
+		/* IPv4LL requires ARP */
+		options->options &= ~DHCPCD_IPV4LL;
+		break;
+	case 'C':
+		/* Commas to spaces for shell */
+		while ((p = strchr(oarg, ',')))
+			*p = ' ';
+		s = strlen("skip_hooks=") + strlen(oarg) + 1;
+		p = xmalloc(sizeof(char) * s);
+		snprintf(p, s, "skip_hooks=%s", oarg);
+		add_environ(options, p, 0);
+		free(p);
+		break;
+	case 'D':
+		options->options |= DHCPCD_DUID;
+		break;
+	case 'E':
+		options->options |= DHCPCD_LASTLEASE;
+		break;
+	case 'F':
+#ifndef MINIMAL
+		if (!oarg) {
+			options->fqdn = FQDN_BOTH;
+			break;
+		}
+		if (strcmp(oarg, "none") == 0)
+			options->fqdn = FQDN_NONE;
+		else if (strcmp(oarg, "ptr") == 0)
+			options->fqdn = FQDN_PTR;
+		else if (strcmp(oarg, "both") == 0)
+			options->fqdn = FQDN_BOTH;
+		else {
+			logger(LOG_ERR, "invalid value `%s' for FQDN",
+			       oarg);
+			return -1;
+		}
+#endif
+		break;
+	case 'G':
+		options->options &= ~DHCPCD_GATEWAY;
+		break;
+	case 'I':
+#ifndef MINIMAL
+		/* Strings have a type of 0 */;
+		options->classid[1] = 0;
+		if (oarg)
+			s = parse_string_hwaddr((char *)options->clientid + 1,
+						CLIENTID_MAX_LEN, oarg, 1);
+		else
+			s = 0;
+		if (s == -1) {
+			logger(LOG_ERR, "clientid: %s", strerror(errno));
+			return -1;
+		}
+		options->clientid[0] = (uint8_t)s;
+		if (s == 0) {
+			options->options &= ~DHCPCD_DUID;
+			options->options &= ~DHCPCD_CLIENTID;
+		}
+#endif
+		break;
+	case 'L':
+		options->options &= ~DHCPCD_IPV4LL;
+		break;
+	case 'O':
+		if (make_reqmask(options->reqmask, &optarg, -1) != 0 ||
+		    make_reqmask(options->nomask, &optarg, 1) != 0)
+		{
+			logger(LOG_ERR, "unknown option `%s'", optarg);
+			return -1;
+		}
+		break;
+	case 'X':
+		options->options &= ~DHCPCD_DAEMONISE;
+		break;
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
+static int
+parse_config_line(const char *opt, char *line, struct options *options)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(longopts) / sizeof(longopts[0]); i++) {
+		if (!longopts[i].name ||
+		    strcmp(longopts[i].name, opt) != 0)
+			continue;
+
+		if (longopts[i].has_arg == required_argument && !line) {
+			fprintf(stderr,
+				PACKAGE ": option requires an argument -- %s\n",
+				opt);
+			return -1;
+		}
+
+		return parse_option(longopts[i].val, line, options);
+	}
+
+	fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
+	return -1;
+}
+
+#ifdef ANDROID
+void switchUser() {
+	gid_t groups[] = { AID_INET, AID_SHELL };
+	setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+
+	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+	setgid(AID_DHCP);
+	setuid(AID_DHCP);
+
+	struct __user_cap_header_struct header;
+	struct __user_cap_data_struct cap;
+	header.version = _LINUX_CAPABILITY_VERSION;
+	header.pid = 0;
+	cap.effective = cap.permitted =
+		(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
+                (1 << CAP_NET_BROADCAST) | (1 << CAP_NET_BIND_SERVICE);
+	cap.inheritable = 0;
+	capset(&header, &cap);
+}
+#endif /* ANDROID */
+
+int
+main(int argc, char **argv)
+{
+	struct options *options;
+	int opt;
+	int option_index = 0;
+	char *prefix;
+	pid_t pid;
+	int debug = 0;
+	int i, r;
+	int pid_fd = -1;
+	int sig = 0;
+	int retval = EXIT_FAILURE;
+	char *line, *option, *p, *buffer = NULL;
+	size_t len = 0;
+	FILE *f;
+	char *cf = NULL;
+	char *intf = NULL;
+
+#ifdef ANDROID
+	switchUser();
+#endif
+
+	closefrom(3);
+	openlog(PACKAGE, LOG_PID, LOG_LOCAL0);
+
+	options = xzalloc(sizeof(*options));
+	options->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
+	options->timeout = DEFAULT_TIMEOUT;
+	strlcpy(options->script, SCRIPT, sizeof(options->script));
+
+#ifndef MINIMAL
+	options->options |= DHCPCD_CLIENTID;
+	options->classid[0] = snprintf((char *)options->classid + 1, CLASSID_MAX_LEN,
+				       "%s %s", PACKAGE, VERSION);
+#endif
+#ifdef ENABLE_ARP
+	options->options |= DHCPCD_ARP;
+ #ifdef ENABLE_IPV4LL
+	options->options |= DHCPCD_IPV4LL;
+ #endif
+#endif
+
+#ifdef CMDLINE_COMPAT
+	add_reqmask(options->reqmask, DHCP_DNSSERVER);
+	add_reqmask(options->reqmask, DHCP_DNSDOMAIN);
+	add_reqmask(options->reqmask, DHCP_DNSSEARCH);
+	add_reqmask(options->reqmask, DHCP_NISSERVER);
+	add_reqmask(options->reqmask, DHCP_NISDOMAIN);
+	add_reqmask(options->reqmask, DHCP_NTPSERVER);
+
+	/* If the duid file exists, then enable duid by default
+	 * This means we don't break existing clients that easily :) */
+	if ((f = fopen(DUID, "r"))) {
+		options->options |= DHCPCD_DUID;
+		fclose(f);
+	}
+#endif
+
+#ifdef THERE_IS_NO_FORK
+	dhcpcd_argv = argv;
+	dhcpcd_argc = argc;
+	if (!realpath(argv[0], dhcpcd)) {
+		fprintf(stderr, "unable to resolve the path `%s': %s",
+			argv[0], strerror(errno));
+		goto abort;
+	}
+#endif
+
+#ifndef MINIMAL
+	gethostname(options->hostname + 1, sizeof(options->hostname));
+	if (strcmp(options->hostname + 1, "(none)") == 0 ||
+	    strcmp(options->hostname + 1, "localhost") == 0)
+		options->hostname[1] = '\0';
+	*options->hostname = strlen(options->hostname + 1);
+#endif
+
+	while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
+				  longopts, &option_index)) != -1)
+	{
+		switch (opt) {
+		case 0:
+			if (longopts[option_index].flag)
+				break;
+			logger(LOG_ERR,	"option `%s' should set a flag",
+			       longopts[option_index].name);
+			goto abort;
+		case 'f':
+			cf = optarg;
+			break;
+		case 'V':
+			print_options();
+			goto abort;
+		case '?':
+			usage();
+			goto abort;
+		}
+	}
+
+	if (doversion) {
+		printf(""PACKAGE" "VERSION"\n%s\n", copyright);
+		printf("Compile time options:"
+#ifdef ENABLE_ARP
+		       " ARP"
+#endif
+#ifdef ENABLE_IPV4LL
+		       " IPV4LL"
+#endif
+#ifdef MINIMAL
+		       " MINIMAL"
+#endif
+#ifdef THERE_IS_NO_FORK
+		       " THERE_IS_NO_FORK"
+#endif
+		       "\n");
+	}
+
+	if (dohelp)
+		usage();
+
+	if (optind < argc) {
+		if (strlen(argv[optind]) >= IF_NAMESIZE) {
+			logger(LOG_ERR,
+			       "`%s' too long for an interface name (max=%d)",
+			       argv[optind], IF_NAMESIZE);
+			goto abort;
+		}
+		strlcpy(options->interface, argv[optind],
+			sizeof(options->interface));
+	} else {
+		/* If only version was requested then exit now */
+		if (doversion || dohelp) {
+			retval = 0;
+			goto abort;
+		}
+
+		logger(LOG_ERR, "no interface specified");
+		goto abort;
+	}
+
+	/* Parse our options file */
+	f = fopen(cf ? cf : CONFIG, "r");
+	if (f) {
+		r = 1;
+		while ((get_line(&buffer, &len, f))) {
+			line = buffer;
+			while ((option = strsep(&line, " \t")))
+				if (*option != '\0')
+					break;
+			if (!option || *option == '\0' || *option == '#')
+				continue;
+			/* Trim leading whitespace */
+			if (line) {
+				while (*line != '\0' && (*line == ' ' || *line == '\t'))
+					line++;
+			}
+			/* Trim trailing whitespace */
+			if (line && *line) {
+				p = line + strlen(line) - 1;
+				while (p != line &&
+				       (*p == ' ' || *p == '\t') &&
+				       *(p - 1) != '\\')
+					*p-- = '\0';
+			}
+			if (strcmp(option, "interface") == 0) {
+				free(intf);
+				intf = xstrdup(line);
+				continue;
+			}
+			/* If we're in an interface block don't use these
+			 * options unless it's for us */
+			if (intf && strcmp(intf, options->interface) != 0)
+				continue;
+			r = parse_config_line(option, line, options);
+			if (r != 1)
+				break;
+		}
+		free(buffer);
+		free(intf);
+		fclose(f);
+		if (r == 0)
+			usage();
+		if (r != 1)
+			goto abort;
+	} else {
+		if (errno != ENOENT || cf) {
+			logger(LOG_ERR, "fopen `%s': %s", cf ? cf : CONFIG,
+			       strerror(errno));
+			goto abort;
+		}
+	}
+
+	optind = 0;
+	while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
+				  longopts, &option_index)) != -1)
+	{
+		switch (opt) {
+		case 'd':
+			debug++;
+			switch (debug) {
+			case 1:
+				setloglevel(LOG_DEBUG);
+				break;
+			case 2:
+				options->options &= ~DHCPCD_DAEMONISE;
+				break;
+			}
+			break;
+		case 'f':
+			break;
+#ifdef THERE_IS_NO_FORK
+		case 'z':
+			options->options |= DHCPCD_DAEMONISED;
+			close_fds();
+			break;
+		case 'Z':
+			dhcpcd_skiproutes = xstrdup(optarg);
+			break;
+#endif
+		case 'k':
+			sig = SIGHUP;
+			break;
+		case 'n':
+			sig = SIGALRM;
+			break;
+		case 'x':
+			sig = SIGTERM;
+			break;
+		case 'T':
+			options->options |= DHCPCD_TEST | DHCPCD_PERSISTENT;
+			break;
+#ifdef CMDLINE_COMPAT
+		case 'H': /* FALLTHROUGH */
+		case 'M':
+			del_reqmask(options->reqmask, DHCP_MTU);
+			break;
+		case 'N':
+			del_reqmask(options->reqmask, DHCP_NTPSERVER);
+			break;
+		case 'R':
+			del_reqmask(options->reqmask, DHCP_DNSSERVER);
+			del_reqmask(options->reqmask, DHCP_DNSDOMAIN);
+			del_reqmask(options->reqmask, DHCP_DNSSEARCH);
+			break;
+		case 'S':
+			add_reqmask(options->reqmask, DHCP_MSCSR);
+			break;
+		case 'Y':
+			del_reqmask(options->reqmask, DHCP_NISSERVER);
+			del_reqmask(options->reqmask, DHCP_NISDOMAIN);
+			break;
+#endif
+		default:
+			i = parse_option(opt, optarg, options);
+			if (i == 1)
+				break;
+			if (i == 0)
+				usage();
+			goto abort;
+		}
+	}
+
+#ifndef MINIMAL
+	if ((p = strchr(options->hostname, '.'))) {
+		if (options->fqdn == FQDN_DISABLE)
+			*p = '\0';
+	} else {
+		if (options->fqdn != FQDN_DISABLE) {
+			logger(LOG_WARNING, "hostname `%s' is not a FQDN",
+			       options->hostname);
+			options->fqdn = FQDN_DISABLE;
+		}
+	}
+	if (options->fqdn != FQDN_DISABLE)
+		del_reqmask(options->reqmask, DHCP_HOSTNAME);
+#endif
+
+	if (options->request_address.s_addr == 0 &&
+	    (options->options & DHCPCD_INFORM ||
+	     options->options & DHCPCD_REQUEST))
+	{
+		if (get_address(options->interface,
+				&options->request_address,
+				&options->request_netmask) != 1)
+		{
+			logger(LOG_ERR, "no existing address");
+			goto abort;
+		}
+	}
+
+	if (!(options->options & DHCPCD_DAEMONISE))
+		options->timeout = 0;
+
+	if (IN_LINKLOCAL(ntohl(options->request_address.s_addr))) {
+		logger(LOG_ERR,
+		       "you are not allowed to request a link local address");
+		goto abort;
+	}
+
+/* android runs us as user "dhcp" */
+#ifndef ANDROID
+	if (geteuid())
+		logger(LOG_WARNING, PACKAGE " will not work correctly unless"
+		       " run as root");
+#endif
+
+	prefix = xmalloc(sizeof(char) * (IF_NAMESIZE + 3));
+	snprintf(prefix, IF_NAMESIZE, "%s: ", options->interface);
+	setlogprefix(prefix);
+	snprintf(options->pidfile, sizeof(options->pidfile), PIDFILE,
+		 options->interface);
+	free(prefix);
+
+	chdir("/");
+	umask(022);
+
+	if (options->options & DHCPCD_TEST) {
+		if (options->options & DHCPCD_REQUEST ||
+		    options->options & DHCPCD_INFORM) {
+			logger(LOG_ERR,
+			       "cannot test with --inform or --request");
+			goto abort;
+		}
+
+		if (options->options & DHCPCD_LASTLEASE) {
+			logger(LOG_ERR, "cannot test with --lastlease");
+			goto abort;
+		}
+
+		if (sig != 0) {
+			logger(LOG_ERR,
+			       "cannot test with --release or --renew");
+			goto abort;
+		}
+	}
+
+	if (sig != 0 && !(options->options & DHCPCD_DAEMONISED)) {
+		i = -1;
+		pid = read_pid(options->pidfile);
+		if (pid != 0)
+			logger(LOG_INFO, "sending signal %d to pid %d",
+			       sig, pid);
+
+		if (!pid || (i = kill(pid, sig)))
+			logger(sig == SIGALRM ? LOG_INFO : LOG_ERR,
+			       ""PACKAGE" not running");
+
+		if (pid != 0 && (sig != SIGALRM || i != 0))
+			unlink(options->pidfile);
+
+		if (i == 0) {
+			retval = EXIT_SUCCESS;
+			goto abort;
+		}
+
+		if (sig != SIGALRM)
+			goto abort;	
+	}
+#ifndef ANDROID
+	if (!(options->options & DHCPCD_TEST) &&
+	    !(options->options & DHCPCD_DAEMONISED))
+	{
+		if ((pid = read_pid(options->pidfile)) > 0 &&
+		    kill(pid, 0) == 0)
+		{
+			logger(LOG_ERR, ""PACKAGE
+			       " already running on pid %d (%s)",
+			       pid, options->pidfile);
+			goto abort;
+		}
+
+		pid_fd = open(options->pidfile,
+			     O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+		if (pid_fd == -1) {
+			logger(LOG_ERR, "open `%s': %s",
+			       options->pidfile, strerror(errno));
+			goto abort;
+		}
+
+		/* Lock the file so that only one instance of dhcpcd runs
+		 * on an interface */
+		if (flock(pid_fd, LOCK_EX | LOCK_NB) == -1) {
+			logger(LOG_ERR, "flock `%s': %s",
+			       options->pidfile, strerror(errno));
+			goto abort;
+		}
+
+		if (set_cloexec(pid_fd) == -1)
+			goto abort;
+		writepid(pid_fd, getpid());
+		logger(LOG_INFO, PACKAGE " " VERSION " starting");
+	}
+#endif /* ANDROID */
+#ifndef MINIMAL
+	/* Terminate the encapsulated options */
+	if (options->vendor[0]) {
+		options->vendor[0]++;
+		options->vendor[options->vendor[0]] = DHCP_END;
+	}
+#endif
+
+	if (dhcp_run(options, &pid_fd) == 0)
+		retval = EXIT_SUCCESS;
+
+abort:
+	/* If we didn't daemonise then we need to punt the pidfile now */
+	if (pid_fd > -1) {
+		close(pid_fd);
+		unlink(options->pidfile);
+	}
+	if (options->environ) {
+		len = 0;
+		while (options->environ[len])
+			free(options->environ[len++]);
+		free(options->environ);
+	}
+	free(options);
+
+#ifdef THERE_IS_NO_FORK
+	/* There may have been an error before the dhcp_run function
+	 * clears this, so just do it here to be safe */
+	free(dhcpcd_skiproutes);
+#endif
+
+	exit(retval);
+	/* NOTREACHED */
+}
diff --git a/dhcpcd.conf b/dhcpcd.conf
new file mode 100644
index 0000000..cce1795
--- /dev/null
+++ b/dhcpcd.conf
@@ -0,0 +1,13 @@
+# A sample configuration for dhcpcd.
+# See dhcpcd.conf(5) for details.
+
+# dhcpcd-run-hooks uses these options.
+option domain_name_servers, domain_name, domain_search, host_name
+
+# Most distros have ntp support.
+option ntp_servers
+
+# We should behave nicely on networks and respect their MTU.
+# However, a lot of buggy DHCP servers set invalid MTUs so this is not
+# enabled by default.
+#option interface_mtu
diff --git a/dhcpcd.conf.5 b/dhcpcd.conf.5
new file mode 100644
index 0000000..8fd4b90
--- /dev/null
+++ b/dhcpcd.conf.5
@@ -0,0 +1,137 @@
+.\" Copyright 2006-2008 Roy Marples
+.\" 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.
+.\"
+.Dd Jun 30, 2008
+.Dt DHCPCD.CONF 5 SMM
+.Sh NAME
+.Nm dhcpcd.conf
+.Nd dhcpcd configuration file 
+.Sh DESCRIPTION
+Although
+.Nm dhcpcd
+can do everything from the command line, there are cases where it's just easier
+to do it once in a configuration file.
+Most of the options found in
+.Xr dhcpcd 8
+can be used here.
+The first word on the line is the option and the rest of the line is the value.
+Leading and trailing whitespace for the option and value are trimmed.
+You can escape characters in the value using the \\ character.
+.Pp
+Blank lines and lines starting with # are ignored.
+.Pp
+Here's a list of available options:
+.Bl -tag -width indent
+.It Ic classid Ar string
+Change the default classid sent from dhcpcd-version.
+If not set then none is sent.
+.It Ic clientid Ar string
+Change the default clientid sent from the interface hardware address.
+If the string is of the format 01:02:03 then it is encoded as hex.
+If not set then none is sent.
+.It Ic duid
+Generate an
+.Rs
+.%T "RFC 4361"
+.Re
+compliant clientid.
+This requires persistent storage and not all DHCP servers work with it so it's
+not enabled by default.
+The duid generated will be held in
+.Pa /system/etc/dhcpcd/dhcpcd.duid
+and should not be copied to other hosts.
+.It Ic hostname Ar name
+Sends specified
+.Ar hostname 
+to the DHCP server so it can be registered in DNS. If
+.Ar hostname
+if a FQDN (ie, contains a .) then it will be encoded as such.
+.It Ic fqdn Op none | ptr | both
+none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
+record of the host in DNS whereas both also updates the A record.
+The current hostname or the hostname specified using the
+.Fl h , -hostname
+option must be a FQDN.
+.Nm dhcpcd
+itself never does any DNS updates.
+.It Ic interface Ar interface
+Subsequent options are only parsed for this
+.Ar interface .
+.It Ic leasetime Ar seconds
+Request a leasetime of
+.Ar seconds .
+.It Ic noarp
+Don't send any ARP requests.
+This also disables IPv4LL.
+.It Ic nogateway
+Don't install any default routes.
+.It Ic nohook Ar script
+Don't run this hook script.
+Matches full name, or prefixed with 2 numbers optionally ending with
+.Pa .sh .
+.It Ic noipv4ll
+Don't attempt to obtain an IPv4LL address if we failed to get one via DHCP.
+See
+.Rs
+.%T "RFC 3927"
+.Re
+.It Ic option Ar dhcp-option
+Requests the
+.Ar dhcp-option
+from the server.
+It can be a variable to be used in
+.Xr dhcpcd-run-hooks 8
+or the numerical value.
+You can specify more seperated by commas, spaces or more option lines.
+.It Ic script Ar script
+Use
+.Ar script
+instead of the default
+.Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
+.It Ic timeout Ar seconds
+The default timeout for waiting for a DHCP response is 30 seconds which may
+be too long or too short and can be changed here.
+.It Ic userclass Ar string
+Tag the DHCP messages with the userclass.
+You can specify more than one.
+.It vendor Ar code , Ns Ar value
+Add an enscapulated vendor option.
+.Ar code
+should be between 1 and 254 inclusive.
+Examples.
+.Pp
+Set the vendor option 01 with an IP address.
+.D1 vendor 01,192.168.0.2
+Set the vendor option 02 with a hex code.
+.D1 vendor 02,01:02:03:04:05
+Set the vendor option 03 with an IP address as a string.
+.D1 vendor 03,\e"192.168.0.2\e"
+.El
+.Sh SEE ALSO
+.Xr dhcpcd-run-hooks 8 ,
+.Xr dhcpcd 8
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in
new file mode 100644
index 0000000..5ba825f
--- /dev/null
+++ b/dhcpcd.conf.5.in
@@ -0,0 +1,137 @@
+.\" Copyright 2006-2008 Roy Marples
+.\" 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.
+.\"
+.Dd Jun 30, 2008
+.Dt DHCPCD.CONF 5 SMM
+.Sh NAME
+.Nm dhcpcd.conf
+.Nd dhcpcd configuration file 
+.Sh DESCRIPTION
+Although
+.Nm dhcpcd
+can do everything from the command line, there are cases where it's just easier
+to do it once in a configuration file.
+Most of the options found in
+.Xr dhcpcd 8
+can be used here.
+The first word on the line is the option and the rest of the line is the value.
+Leading and trailing whitespace for the option and value are trimmed.
+You can escape characters in the value using the \\ character.
+.Pp
+Blank lines and lines starting with # are ignored.
+.Pp
+Here's a list of available options:
+.Bl -tag -width indent
+.It Ic classid Ar string
+Change the default classid sent from dhcpcd-version.
+If not set then none is sent.
+.It Ic clientid Ar string
+Change the default clientid sent from the interface hardware address.
+If the string is of the format 01:02:03 then it is encoded as hex.
+If not set then none is sent.
+.It Ic duid
+Generate an
+.Rs
+.%T "RFC 4361"
+.Re
+compliant clientid.
+This requires persistent storage and not all DHCP servers work with it so it's
+not enabled by default.
+The duid generated will be held in
+.Pa @SYSCONFDIR@/dhcpcd.duid
+and should not be copied to other hosts.
+.It Ic hostname Ar name
+Sends specified
+.Ar hostname 
+to the DHCP server so it can be registered in DNS. If
+.Ar hostname
+if a FQDN (ie, contains a .) then it will be encoded as such.
+.It Ic fqdn Op none | ptr | both
+none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
+record of the host in DNS whereas both also updates the A record.
+The current hostname or the hostname specified using the
+.Fl h , -hostname
+option must be a FQDN.
+.Nm dhcpcd
+itself never does any DNS updates.
+.It Ic interface Ar interface
+Subsequent options are only parsed for this
+.Ar interface .
+.It Ic leasetime Ar seconds
+Request a leasetime of
+.Ar seconds .
+.It Ic noarp
+Don't send any ARP requests.
+This also disables IPv4LL.
+.It Ic nogateway
+Don't install any default routes.
+.It Ic nohook Ar script
+Don't run this hook script.
+Matches full name, or prefixed with 2 numbers optionally ending with
+.Pa .sh .
+.It Ic noipv4ll
+Don't attempt to obtain an IPv4LL address if we failed to get one via DHCP.
+See
+.Rs
+.%T "RFC 3927"
+.Re
+.It Ic option Ar dhcp-option
+Requests the
+.Ar dhcp-option
+from the server.
+It can be a variable to be used in
+.Xr dhcpcd-run-hooks 8
+or the numerical value.
+You can specify more seperated by commas, spaces or more option lines.
+.It Ic script Ar script
+Use
+.Ar script
+instead of the default
+.Pa @SCRIPT@ .
+.It Ic timeout Ar seconds
+The default timeout for waiting for a DHCP response is 30 seconds which may
+be too long or too short and can be changed here.
+.It Ic userclass Ar string
+Tag the DHCP messages with the userclass.
+You can specify more than one.
+.It vendor Ar code , Ns Ar value
+Add an enscapulated vendor option.
+.Ar code
+should be between 1 and 254 inclusive.
+Examples.
+.Pp
+Set the vendor option 01 with an IP address.
+.D1 vendor 01,192.168.0.2
+Set the vendor option 02 with a hex code.
+.D1 vendor 02,01:02:03:04:05
+Set the vendor option 03 with an IP address as a string.
+.D1 vendor 03,\e"192.168.0.2\e"
+.El
+.Sh SEE ALSO
+.Xr dhcpcd-run-hooks 8 ,
+.Xr dhcpcd 8
+.Sh AUTHORS
+.An Roy Marples <roy@marples.name>
+.Sh BUGS
+Please report them to http://bugs.marples.name
diff --git a/dhcpcd.h b/dhcpcd.h
new file mode 100644
index 0000000..10d23ff
--- /dev/null
+++ b/dhcpcd.h
@@ -0,0 +1,98 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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.
+ */
+
+#ifndef DHCPCD_H
+#define DHCPCD_H
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <limits.h>
+
+#include "common.h"
+
+#define DEFAULT_TIMEOUT		30
+#define DEFAULT_LEASETIME	3600	/* 1 hour */
+
+#define CLASSID_MAX_LEN		48
+#define CLIENTID_MAX_LEN	48
+#define USERCLASS_MAX_LEN	255
+#define VENDOR_MAX_LEN		255
+
+#ifdef THERE_IS_NO_FORK 
+extern char dhcpcd[PATH_MAX];
+extern char **dhcpcd_argv;
+extern int dhcpcd_argc;
+extern char *dhcpcd_skiproutes;
+#endif
+
+#define DHCPCD_ARP		(1 << 0)
+#define DHCPCD_DOMAIN		(1 << 2)
+#define DHCPCD_GATEWAY		(1 << 3)
+#define DHCPCD_LASTLEASE	(1 << 7)
+#define DHCPCD_INFORM		(1 << 8)
+#define DHCPCD_REQUEST		(1 << 9)
+#define DHCPCD_IPV4LL		(1 << 10)
+#define DHCPCD_DUID		(1 << 11)
+#define DHCPCD_PERSISTENT	(1 << 12)
+#define DHCPCD_DAEMONISE	(1 << 14)
+#define DHCPCD_DAEMONISED	(1 << 15)
+#define DHCPCD_TEST		(1 << 16)
+#define DHCPCD_FORKED		(1 << 17)
+#define DHCPCD_HOSTNAME		(1 << 18)
+#define DHCPCD_CLIENTID		(1 << 19)
+
+struct options {
+	char interface[IF_NAMESIZE];
+	int metric;
+	uint8_t reqmask[256 / 8];
+	uint8_t nomask[256 / 8];
+	uint32_t leasetime;
+	time_t timeout;
+	int options;
+
+	struct in_addr request_address;
+	struct in_addr request_netmask;
+
+	char **environ;
+	char script[PATH_MAX];
+	char pidfile[PATH_MAX];
+
+#ifndef MINIMAL
+	char hostname[MAXHOSTNAMELEN];
+	int fqdn;
+	uint8_t classid[CLASSID_MAX_LEN + 1];
+	char clientid[CLIENTID_MAX_LEN + 1];
+	uint8_t userclass[USERCLASS_MAX_LEN + 1];
+	uint8_t vendor[VENDOR_MAX_LEN + 1];
+#endif
+};
+
+#endif
diff --git a/if-bsd.c b/if-bsd.c
new file mode 100644
index 0000000..2cc0c2f
--- /dev/null
+++ b/if-bsd.c
@@ -0,0 +1,187 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "net.h"
+
+/* Darwin doesn't define this for some very odd reason */
+#ifndef SA_SIZE
+# define SA_SIZE(sa)						\
+	(  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?	\
+	   sizeof(long)		:				\
+	   1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
+#endif
+
+int
+if_address(const char *ifname, const struct in_addr *address,
+	   const struct in_addr *netmask, const struct in_addr *broadcast,
+	   int action)
+{
+	int s;
+	int retval;
+	struct ifaliasreq ifa;
+	union {
+		struct sockaddr *sa;
+		struct sockaddr_in *sin;
+	} _s;
+
+	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+		return -1;
+
+	memset(&ifa, 0, sizeof(ifa));
+	strlcpy(ifa.ifra_name, ifname, sizeof(ifa.ifra_name));
+
+#define ADDADDR(_var, _addr) \
+	_s.sa = &_var; \
+	_s.sin->sin_family = AF_INET; \
+	_s.sin->sin_len = sizeof(*_s.sin); \
+	memcpy(&_s.sin->sin_addr, _addr, sizeof(_s.sin->sin_addr));
+
+	ADDADDR(ifa.ifra_addr, address);
+	ADDADDR(ifa.ifra_mask, netmask);
+	if (action >= 0) {
+		ADDADDR(ifa.ifra_broadaddr, broadcast);
+	}
+#undef ADDADDR
+
+	if (action < 0)
+		retval = ioctl(s, SIOCDIFADDR, &ifa);
+	else
+		retval = ioctl(s, SIOCAIFADDR, &ifa);
+	close(s);
+	return retval;
+}
+
+int
+if_route(const char *ifname, const struct in_addr *destination,
+	 const struct in_addr *netmask, const struct in_addr *gateway,
+	 _unused int metric, int action)
+{
+	int s;
+	static int seq;
+	union sockunion {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+#ifdef INET6
+		struct sockaddr_in6 sin6;
+#endif
+		struct sockaddr_dl sdl;
+		struct sockaddr_storage ss;
+	} su;
+	struct rtm 
+	{
+		struct rt_msghdr hdr;
+		char buffer[sizeof(su) * 3];
+	} rtm;
+	char *bp = rtm.buffer;
+	size_t l;
+	unsigned char *hwaddr;
+	size_t hwlen = 0;
+	int retval = 0;
+
+	if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
+		return -1;
+
+	memset(&rtm, 0, sizeof(rtm));
+	rtm.hdr.rtm_version = RTM_VERSION;
+	rtm.hdr.rtm_seq = ++seq;
+	if (action == 0)
+		rtm.hdr.rtm_type = RTM_CHANGE;
+	else if (action > 0)
+		rtm.hdr.rtm_type = RTM_ADD;
+	else
+		rtm.hdr.rtm_type = RTM_DELETE;
+	rtm.hdr.rtm_flags = RTF_UP | RTF_STATIC;
+	if (netmask->s_addr == INADDR_BROADCAST)
+		rtm.hdr.rtm_flags |= RTF_HOST;
+
+	/* This order is important */
+	rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+
+#define ADDADDR(_addr) \
+	memset (&su, 0, sizeof(su)); \
+	su.sin.sin_family = AF_INET; \
+	su.sin.sin_len = sizeof(su.sin); \
+	memcpy (&su.sin.sin_addr, _addr, sizeof(su.sin.sin_addr)); \
+	l = SA_SIZE (&(su.sa)); \
+	memcpy (bp, &(su), l); \
+	bp += l;
+
+	ADDADDR(destination);
+
+	if (gateway->s_addr == INADDR_ANY) {
+		/* Make us a link layer socket */
+		hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
+		do_interface(ifname, hwaddr, &hwlen, NULL, 0, 0);
+		memset(&su, 0, sizeof(su));
+		su.sdl.sdl_len = sizeof(su.sdl);
+		su.sdl.sdl_family = AF_LINK;
+		su.sdl.sdl_nlen = strlen(ifname);
+		memcpy(&su.sdl.sdl_data, ifname, (size_t)su.sdl.sdl_nlen);
+		su.sdl.sdl_alen = hwlen;
+		memcpy(((unsigned char *)&su.sdl.sdl_data) + su.sdl.sdl_nlen,
+		       hwaddr, (size_t)su.sdl.sdl_alen);
+
+		l = SA_SIZE(&(su.sa));
+		memcpy(bp, &su, l);
+		bp += l;
+		free(hwaddr);
+	} else {
+		rtm.hdr.rtm_flags |= RTF_GATEWAY;
+		ADDADDR(gateway);
+	}
+
+	ADDADDR(netmask);
+#undef ADDADDR
+
+	rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
+	if (write(s, &rtm, l) == -1)
+		retval = -1;
+	close(s);
+	return retval;
+}
diff --git a/if-linux.c b/if-linux.c
new file mode 100644
index 0000000..68af3f6
--- /dev/null
+++ b/if-linux.c
@@ -0,0 +1,328 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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 <asm/types.h> /* Needed for 2.4 kernels */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "net.h"
+
+/* This netlink stuff is overly compex IMO.
+ * The BSD implementation is much cleaner and a lot less code.
+ * send_netlink handles the actual transmission so we can work out
+ * if there was an error or not. */
+#define BUFFERLEN 256
+static int
+send_netlink(struct nlmsghdr *hdr)
+{
+	int s;
+	pid_t mypid = getpid ();
+	struct sockaddr_nl nl;
+	struct iovec iov;
+	struct msghdr msg;
+	static unsigned int seq;
+	char *buffer = NULL;
+	ssize_t bytes;
+	union
+	{
+		char *buffer;
+		struct nlmsghdr *nlm;
+	} h;
+	int len, l;
+	struct nlmsgerr *err;
+
+	if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
+		return -1;
+
+	memset(&nl, 0, sizeof(nl));
+	nl.nl_family = AF_NETLINK;
+	if (bind(s, (struct sockaddr *)&nl, sizeof(nl)) == -1)
+		goto eexit;
+
+	memset(&iov, 0, sizeof(iov));
+	iov.iov_base = hdr;
+	iov.iov_len = hdr->nlmsg_len;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_name = &nl;
+	msg.msg_namelen = sizeof(nl);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	/* Request a reply */
+	hdr->nlmsg_flags |= NLM_F_ACK;
+	hdr->nlmsg_seq = ++seq;
+
+	if (sendmsg(s, &msg, 0) == -1)
+		goto eexit;
+
+	buffer = xzalloc(sizeof(char) * BUFFERLEN);
+	iov.iov_base = buffer;
+
+	for (;;) {
+		iov.iov_len = BUFFERLEN;
+		bytes = recvmsg(s, &msg, 0);
+
+		if (bytes == -1) {
+			if (errno == EINTR)
+				continue;
+			goto eexit;
+		}
+
+		if (bytes == 0) {
+			errno = ENODATA;
+			goto eexit;
+		}
+
+		if (msg.msg_namelen != sizeof(nl)) {
+			errno = EBADMSG;
+			goto eexit;
+		}
+
+		for (h.buffer = buffer; bytes >= (signed) sizeof(*h.nlm); ) {
+			len = h.nlm->nlmsg_len;
+			l = len - sizeof(*h.nlm);
+			err = (struct nlmsgerr *)NLMSG_DATA(h.nlm);
+
+			if (l < 0 || len > bytes) {
+				errno = EBADMSG;
+				goto eexit;
+			}
+
+			/* Ensure it's our message */
+			if (nl.nl_pid != 0 ||
+			    (pid_t)h.nlm->nlmsg_pid != mypid ||
+			    h.nlm->nlmsg_seq != seq)
+			{
+				/* Next Message */
+				bytes -= NLMSG_ALIGN(len);
+				h.buffer += NLMSG_ALIGN(len);
+				continue;
+			}
+
+			/* We get an NLMSG_ERROR back with a code of zero for success */
+			if (h.nlm->nlmsg_type != NLMSG_ERROR)
+				continue;
+
+			if ((unsigned)l < sizeof(*err)) {
+				errno = EBADMSG;
+				goto eexit;
+			}
+
+			if (err->error == 0) {
+				close(s);
+				free(buffer);
+				return l;
+			}
+
+			errno = -err->error;
+			goto eexit;
+		}
+	}
+
+eexit:
+	close(s);
+	free(buffer);
+	return -1;
+}
+
+#define NLMSG_TAIL(nmsg) \
+	((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+static int
+add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type,
+	   const void *data, int alen)
+{
+	int len = RTA_LENGTH(alen);
+	struct rtattr *rta;
+
+	if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+		errno = ENOBUFS;
+		return -1;
+	}
+
+	rta = NLMSG_TAIL(n);
+	rta->rta_type = type;
+	rta->rta_len = len;
+	memcpy(RTA_DATA(rta), data, alen);
+	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+
+	return 0;
+}
+
+static int
+add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data)
+{
+	int len = RTA_LENGTH(sizeof(data));
+	struct rtattr *rta;
+
+	if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+		errno = ENOBUFS;
+		return -1;
+	}
+
+	rta = NLMSG_TAIL(n);
+	rta->rta_type = type;
+	rta->rta_len = len;
+	memcpy(RTA_DATA(rta), &data, sizeof(data));
+	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+
+	return 0;
+}
+
+struct nlma
+{
+	struct nlmsghdr hdr;
+	struct ifaddrmsg ifa; 
+	char buffer[64];
+};
+
+struct nlmr
+{
+	struct nlmsghdr hdr;
+	struct rtmsg rt;
+	char buffer[256];
+};
+
+int
+if_address(const char *ifname,
+	   const struct in_addr *address, const struct in_addr *netmask,
+	   const struct in_addr *broadcast, int action)
+{
+	struct nlma *nlm;
+	int retval = 0;
+
+	nlm = xzalloc(sizeof(*nlm));
+	nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+	nlm->hdr.nlmsg_flags = NLM_F_REQUEST;
+	if (action >= 0) {
+		nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+		nlm->hdr.nlmsg_type = RTM_NEWADDR;
+	} else
+		nlm->hdr.nlmsg_type = RTM_DELADDR;
+	if (!(nlm->ifa.ifa_index = if_nametoindex(ifname))) {
+		free(nlm);
+		errno = ENODEV;
+		return -1;
+	}
+	nlm->ifa.ifa_family = AF_INET;
+	nlm->ifa.ifa_prefixlen = inet_ntocidr(*netmask);
+	/* This creates the aliased interface */
+	add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LABEL,
+		   ifname, strlen(ifname) + 1);
+	add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_LOCAL,
+		   &address->s_addr, sizeof(address->s_addr));
+	if (action >= 0)
+		add_attr_l(&nlm->hdr, sizeof(*nlm), IFA_BROADCAST,
+			   &broadcast->s_addr, sizeof(broadcast->s_addr));
+
+	if (send_netlink(&nlm->hdr) == -1)
+		retval = -1;
+	free(nlm);
+	return retval;
+}
+
+int
+if_route(const char *ifname,
+	 const struct in_addr *destination, const struct in_addr *netmask,
+	 const struct in_addr *gateway, int metric, int action)
+{
+	struct nlmr *nlm;
+	unsigned int ifindex;
+	int retval = 0;
+
+
+	if (!(ifindex = if_nametoindex(ifname))) {
+		errno = ENODEV;
+		return -1;
+	}
+
+	nlm = xzalloc(sizeof(*nlm));
+	nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	nlm->hdr.nlmsg_type = RTM_NEWROUTE;
+	if (action == 0)
+		nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
+	else if (action > 0)
+		/*
+		 * commented out NLM_F_EXCL here and
+		 * below. We sometimes keep one interface up while
+		 * we are configuring the other one, and this flag
+		 * causes route addition to fail.
+		 */
+		nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL*/;
+	else
+		nlm->hdr.nlmsg_type = RTM_DELROUTE;
+	nlm->hdr.nlmsg_flags |= NLM_F_REQUEST;
+	nlm->rt.rtm_family = AF_INET;
+	nlm->rt.rtm_table = RT_TABLE_MAIN;
+
+	if (action < 0)
+		nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
+	else {
+		nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/;
+		nlm->rt.rtm_protocol = RTPROT_BOOT;
+		if (gateway->s_addr == INADDR_ANY)
+			nlm->rt.rtm_scope = RT_SCOPE_LINK;
+		else
+			nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
+		nlm->rt.rtm_type = RTN_UNICAST;
+	}
+
+	nlm->rt.rtm_dst_len = inet_ntocidr(*netmask);
+	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
+		   &destination->s_addr, sizeof(destination->s_addr));
+	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
+		   &gateway->s_addr, sizeof(gateway->s_addr));
+
+	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
+	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
+
+	if (send_netlink(&nlm->hdr) == -1)
+		retval = -1;
+	free(nlm);
+	return retval;
+}
diff --git a/logger.c b/logger.c
new file mode 100644
index 0000000..6eb0c29
--- /dev/null
+++ b/logger.c
@@ -0,0 +1,118 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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 <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "common.h"
+#include "logger.h"
+
+static int loglevel = LOG_INFO;
+static char logprefix[12] = {0};
+
+struct logname {
+	int level;
+	const char *name;
+};
+static const struct logname const lognames[] = {
+	{ LOG_DEBUG,	"debug" },
+	{ LOG_INFO,	"info" },
+	{ LOG_WARNING,	"warning" },
+	{ LOG_ERR,	"error" },
+	{ -1,		NULL }
+};
+
+int
+logtolevel(const char *priority)
+{
+	const struct logname *lt;
+
+	if (isdigit((unsigned char)*priority))
+		return atoi(priority);
+	for (lt = lognames; lt->name; lt++)
+		if (!strcasecmp(priority, lt->name))
+			return lt->level;
+	return -1;
+}
+
+void
+setloglevel(int level)
+{
+	loglevel = level;
+}
+
+void
+setlogprefix(const char *prefix)
+{
+	strlcpy(logprefix, prefix, sizeof(logprefix));
+}
+
+void
+logger(int level, const char *fmt, ...)
+{
+	va_list p, p2;
+	FILE *f = stderr;
+	size_t len, fmt2len;
+	char *fmt2, *pf;
+
+	va_start(p, fmt);
+	va_copy(p2, p);
+
+	if (level <= LOG_ERR || level <= loglevel) {
+		fprintf(f, "%s", logprefix);
+		vfprintf(f, fmt, p);
+		fputc('\n', f);
+
+		/* stdout, stderr may be re-directed to some kind of buffer.
+		 * So we always flush to ensure it's written. */
+		fflush(f);
+	}
+
+	if (level < LOG_DEBUG || level <= loglevel) {
+		len = strlen(logprefix);
+		fmt2len = strlen(fmt) + len + 1;
+		fmt2 = pf = malloc(sizeof(char) * fmt2len);
+		if (fmt2) {
+			strlcpy(pf, logprefix, fmt2len);
+			pf += len;
+			strlcpy(pf, fmt, fmt2len - len);
+			vsyslog(level, fmt2, p2);
+			free(fmt2);
+		} else {
+			vsyslog(level, fmt, p2);
+			syslog(LOG_ERR, "logger: memory exhausted");
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	va_end(p2);
+	va_end(p);
+}
diff --git a/logger.h b/logger.h
new file mode 100644
index 0000000..cde4864
--- /dev/null
+++ b/logger.h
@@ -0,0 +1,44 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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.
+ */
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#if defined(__GNUC__)
+#  define _PRINTF_LIKE(_one, _two)  __attribute__ ((__format__ (__printf__, _one, _two)))
+#else
+#  define _PRINTF_LIKE(_one, _two)
+#endif
+
+#include <syslog.h>
+
+int logtolevel(const char *);
+void setloglevel(int);
+void setlogprefix(const char *);
+void logger(int, const char *, ...) _PRINTF_LIKE (2, 3);
+
+#endif
diff --git a/lpf.c b/lpf.c
new file mode 100644
index 0000000..daa8a4c
--- /dev/null
+++ b/lpf.c
@@ -0,0 +1,177 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ *
+ * 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/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef __linux__
+# include <asm/types.h> /* needed for 2.4 kernels for the below header */
+# include <linux/filter.h>
+# include <netpacket/packet.h>
+# define bpf_insn sock_filter
+# define BPF_SKIPTYPE
+# define BPF_ETHCOOK		-ETH_HLEN
+# define BPF_WHOLEPACKET	0x0fffffff /* work around buggy LPF filters */
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "net.h"
+#include "bpf-filter.h"
+
+/* Broadcast address for IPoIB */
+static const uint8_t ipv4_bcast_addr[] = {
+	0x00, 0xff, 0xff, 0xff,
+	0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+int
+open_socket(struct interface *iface, int protocol)
+{
+	int s;
+	union sockunion {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+		struct sockaddr_ll sll;
+		struct sockaddr_storage ss;
+	} su;
+	struct sock_fprog pf;
+	int *fd;
+
+	if ((s = socket(PF_PACKET, SOCK_DGRAM, htons(protocol))) == -1)
+		return -1;
+
+	memset(&su, 0, sizeof(su));
+	su.sll.sll_family = PF_PACKET;
+	su.sll.sll_protocol = htons(protocol);
+	if (!(su.sll.sll_ifindex = if_nametoindex(iface->name))) {
+		errno = ENOENT;
+		goto eexit;
+	}
+	/* Install the DHCP filter */
+	memset(&pf, 0, sizeof(pf));
+#ifdef ENABLE_ARP
+	if (protocol == ETHERTYPE_ARP) {
+		pf.filter = UNCONST(arp_bpf_filter);
+		pf.len = arp_bpf_filter_len;
+	} else
+#endif
+	{
+		pf.filter = UNCONST(dhcp_bpf_filter);
+		pf.len = dhcp_bpf_filter_len;
+	}
+	if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) != 0)
+		goto eexit;
+	if (set_cloexec(s) == -1)
+		goto eexit;
+	if (set_nonblock(s) == -1)
+		goto eexit;
+	if (bind(s, &su.sa, sizeof(su)) == -1)
+		goto eexit;
+#ifdef ENABLE_ARP
+	if (protocol == ETHERTYPE_ARP)
+		fd = &iface->arp_fd;
+	else
+#endif
+		fd = &iface->fd;
+	if (*fd != -1)
+		close(*fd);
+	*fd = s;
+	return s;
+
+eexit:
+	close(s);
+	return -1;
+}
+
+ssize_t
+send_raw_packet(const struct interface *iface, int protocol,
+		const void *data, ssize_t len)
+{
+	union sockunion {
+		struct sockaddr sa;
+		struct sockaddr_ll sll;
+		struct sockaddr_storage ss;
+	} su;
+	int fd;
+
+	memset(&su, 0, sizeof(su));
+	su.sll.sll_family = AF_PACKET;
+	su.sll.sll_protocol = htons(protocol);
+	if (!(su.sll.sll_ifindex = if_nametoindex(iface->name))) {
+		errno = ENOENT;
+		return -1;
+	}
+	su.sll.sll_hatype = htons(iface->family);
+	su.sll.sll_halen = iface->hwlen;
+	if (iface->family == ARPHRD_INFINIBAND)
+		memcpy(&su.sll.sll_addr,
+		       &ipv4_bcast_addr, sizeof(ipv4_bcast_addr));
+	else
+		memset(&su.sll.sll_addr, 0xff, iface->hwlen);
+#ifdef ENABLE_ARP
+	if (protocol == ETHERTYPE_ARP)
+		fd = iface->arp_fd;
+	else
+#endif
+		fd = iface->fd;
+
+	return sendto(fd, data, len, 0, &su.sa, sizeof(su));
+}
+
+ssize_t
+get_raw_packet(struct interface *iface, int protocol, void *data, ssize_t len)
+{
+	ssize_t bytes;
+	int fd = -1;
+
+	if (protocol == ETHERTYPE_ARP) {
+#ifdef ENABLE_ARP
+		fd = iface->arp_fd;
+#endif
+	} else
+		fd = iface->fd;
+	bytes = read(fd, data, len);
+	if (bytes == -1)
+		return errno == EAGAIN ? 0 : -1;
+	return bytes;
+}
diff --git a/mk/cc.mk b/mk/cc.mk
new file mode 100644
index 0000000..fcf0535
--- /dev/null
+++ b/mk/cc.mk
@@ -0,0 +1,28 @@
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+# Setup some good default CFLAGS
+CFLAGS?=	-Os
+
+# Default to using the C99 standard
+CSTD?=		c99
+_CSTD_SH=	if test -n "${CSTD}"; then echo "-std=${CSTD}"; else echo ""; fi
+_CSTD!=		${_CSTD_SH}
+CFLAGS+=	${_CSTD}$(shell ${_CSTD_SH})
+
+# Try and use some good cc flags if we're building from git
+_CCFLAGS=	-pedantic -Wall -Wunused -Wimplicit -Wshadow -Wformat=2 \
+		-Wmissing-declarations -Wno-missing-prototypes -Wwrite-strings \
+		-Wbad-function-cast -Wnested-externs -Wcomment -Winline \
+		-Wchar-subscripts -Wcast-align -Wno-format-nonliteral \
+		-Wdeclaration-after-statement -Wsequence-point -Wextra
+_CC_FLAGS_SH=	if ! test -d .git; then echo ""; else for f in ${_CCFLAGS}; do \
+		if ${CC} $$f -S -o /dev/null -xc /dev/null >/dev/null 2>&1; \
+		then printf "%s" "$$f "; fi \
+		done; fi
+_CC_FLAGS!=	${_CC_FLAGS_SH}
+CFLAGS+=	${_CC_FLAGS}$(shell ${_CC_FLAGS_SH})
+
+_GGDB_SH=	if test "${DEBUG}" = "yes"; then echo "-ggdb -DDEBUG"; else echo ""; fi
+_GGDB!=		${_GGDB_SH}
+GGDB=		${_GGDB}$(shell ${_GGDB_SH})
+CFLAGS+=	${GGDB}
diff --git a/mk/depend.mk b/mk/depend.mk
new file mode 100644
index 0000000..a4d717a
--- /dev/null
+++ b/mk/depend.mk
@@ -0,0 +1,11 @@
+# This only works for make implementations that always include a .depend if
+# it exists. Only GNU make does not do this.
+
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+CLEANFILES+=	.depend
+
+.depend: ${SRCS}
+	${CC} ${CFLAGS} -MM ${SRCS} > .depend
+
+depend: .depend
diff --git a/mk/dist.mk b/mk/dist.mk
new file mode 100644
index 0000000..3d9385b
--- /dev/null
+++ b/mk/dist.mk
@@ -0,0 +1,31 @@
+# rules to make a distribution tarball from a git repo
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+GITREF?=	HEAD
+DISTPREFIX?=	${PROG}-${VERSION}
+DISTFILE?=	${DISTPREFIX}.tar.bz2
+
+CLEANFILES+=	*.tar.bz2
+
+_VERSION_SH=	sed -n 's/\#define VERSION[[:space:]]*"\(.*\)".*/\1/p' config.h
+_VERSION!=	${_VERSION_SH}
+VERSION=	${_VERSION}$(shell ${_VERSION_SH})
+
+_SNAP_SH=	date -u +%Y%m%d%H%M
+_SNAP!=		${_SNAP_SH}
+SNAP=		${_SNAP}$(shell ${_SNAP_SH})
+SNAPDIR=	${DISTPREFIX}-${SNAP}
+SNAPFILE=	${SNAPDIR}.tar.bz2
+
+dist:
+	git archive --prefix=${DISTPREFIX}/ ${GITREF} | bzip2 > ${DISTFILE}
+
+snapshot:
+	mkdir /tmp/${SNAPDIR}
+	cp -RPp * /tmp/${SNAPDIR}
+	(cd /tmp/${SNAPDIR}; make clean)
+	tar -cvjpf ${SNAPFILE} -C /tmp ${SNAPDIR}
+	rm -rf /tmp/${SNAPDIR}
+	ls -l ${SNAPFILE}
+
+snap: snapshot
diff --git a/mk/files.mk b/mk/files.mk
new file mode 100644
index 0000000..0682b03
--- /dev/null
+++ b/mk/files.mk
@@ -0,0 +1,9 @@
+# Quick and dirty files
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+FILESDIR?=	${BINDIR}
+FILESMODE?=	${NONBINMODE}
+
+_filesinstall:
+	${INSTALL} -d ${DESTDIR}${FILESDIR}
+	${INSTALL} -m ${FILESMODE} ${FILES} ${DESTDIR}${FILESDIR}
diff --git a/mk/man.mk b/mk/man.mk
new file mode 100644
index 0000000..f1570bb
--- /dev/null
+++ b/mk/man.mk
@@ -0,0 +1,25 @@
+# rules to install manpages
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+_MANPREFIX_SH=	if [ -n "${PREFIX}" ]; then echo "${PREFIX}"; else echo "/usr/share"; fi
+_MANPREFIX!=	${_MANPREFIX_SH}
+MANPREFIX?=	${_MANPREFIX}$(shell ${_MANPREFIX_SH})
+
+MANDIR?=	${MANPREFIX}/man/man
+MANMODE?=	0444
+
+_MAN5_SH=	for man in ${MAN}; do case $$man in *.5) echo $$man;; esac; done
+_MAN5!=		${_MAN5_SH}
+MAN5=		${_MAN5}$(shell ${_MAN5_SH})
+
+_MAN8_SH=	for man in ${MAN}; do case $$man in *.8) echo $$man;; esac; done
+_MAN8!=		${_MAN8_SH}
+MAN8=		${_MAN8}$(shell ${_MAN8_SH})
+
+_man: ${MAN}
+
+_maninstall: _man
+	${INSTALL} -d ${DESTDIR}${MANDIR}5
+	${INSTALL} -m ${MANMODE} ${MAN5} ${DESTDIR}${MANDIR}5
+	${INSTALL} -d ${DESTDIR}${MANDIR}8
+	${INSTALL} -m ${MANMODE} ${MAN8} ${DESTDIR}${MANDIR}8
diff --git a/mk/os-BSD.mk b/mk/os-BSD.mk
new file mode 100644
index 0000000..f9d3397
--- /dev/null
+++ b/mk/os-BSD.mk
@@ -0,0 +1,5 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+SRC_PF=		bpf.c
+SRC_IF=		if-bsd.c
diff --git a/mk/os-Linux.mk b/mk/os-Linux.mk
new file mode 100644
index 0000000..16f0f60
--- /dev/null
+++ b/mk/os-Linux.mk
@@ -0,0 +1,28 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+SRC_PF=		lpf.c
+SRC_IF=		if-linux.c
+
+CPPFLAGS+=	-D_BSD_SOURCE -D_XOPEN_SOURCE=600
+LIBRT=		-lrt
+
+# Work out if our fork() works or not.
+# If cross-compiling, you'll need to set HAVE_FORK to yes or no depending
+# on your target arch.
+_HAVE_FORK_SH= if test "${HAVE_FORK}" = "yes"; then \
+		echo ""; \
+	elif test -n "${HAVE_FORK}"; then \
+		echo "-DTHERE_IS_NO_FORK"; \
+	else \
+		printf '\#include <stdlib.h>\n\#include <unistd.h>\nint main (void) { pid_t pid = fork(); if (pid == -1) exit (-1); exit (0); }\n' > .fork.c; \
+		${CC} .fork.c -o .fork >/dev/null 2>&1; \
+		if ./.fork; then \
+			echo ""; \
+		else \
+			echo "-DTHERE_IS_NO_FORK"; \
+		fi; \
+		rm -f .fork.c .fork; \
+	fi;
+_HAVE_FORK!=	${_HAVE_FORK_SH}
+CPPFLAGS+=	${_HAVE_FORK}$(shell ${_HAVE_FORK_SH})
diff --git a/mk/os.mk b/mk/os.mk
new file mode 100644
index 0000000..f2a83f4
--- /dev/null
+++ b/mk/os.mk
@@ -0,0 +1,7 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+_OS_SH=	case `uname -s` in Linux) echo "Linux";; *) echo "BSD";; esac
+_OS!=		${_OS_SH}
+OS=		${_OS}$(shell ${_OS_SH})
+include ${MK}/os-${OS}.mk
diff --git a/mk/prog.mk b/mk/prog.mk
new file mode 100644
index 0000000..3f763b8
--- /dev/null
+++ b/mk/prog.mk
@@ -0,0 +1,67 @@
+# rules to build a program 
+# based on FreeBSD's bsd.prog.mk
+
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+include ${MK}/cc.mk
+
+OBJS+=		${SRCS:.c=.o}
+
+# This is for NetBSD which has a different libc in /lib which we need
+# to link to if installing in /
+_RPATH_SH=		if test "${PREFIX}" = "" -o "${PREIX}" = "/"; then \
+				echo "-Wl,-rpath=${PREFIX}/${LIBNAME}"; \
+			else \
+				echo ""; \
+			fi
+_RPATH!=		${_RPATH_SH}
+LDFLAGS+=		${_RPATH}$(shell ${_RPATH_SH})
+
+# This is for NetBSD which has different dynamic linker in /lib which we need
+# to use to if installing in /
+_DYNLINK_SH=		if test "${PREFIX}" = "" -o "${PREFIX}" = "/" && test -e /libexec/ld.elf_so; then \
+				echo "-Wl,-dynamic-linker=/libexec/ld.elf_so"; \
+			else \
+				echo ""; \
+			fi
+_DYNLINK!=		${_DYNLINK_SH}
+LDFLAGS+=		${_DYNLINK}$(shell ${_DYNLINK_SH})
+
+all: ${PROG} ${SCRIPTS} _man
+
+.c.o:
+	${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
+
+${PROG}: ${OBJS}
+	${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
+
+# We could save about 600 bytes by building it like this
+# instead of the more traditional method above
+small: ${SRCS}
+	echo "" > _${PROG}.c
+	for src in ${SRCS}; do echo "#include \"$$src\"" >> _${PROG}.c; done
+	${CC} ${CFLAGS} ${CPPFLAGS} -c _${PROG}.c -o _${PROG}.o
+	${CC} ${LDFLAGS} -o ${PROG} _${PROG}.o ${LDADD}
+
+_proginstall: ${PROG}
+	${INSTALL} -d ${DESTDIR}${BINDIR}
+	${INSTALL} -m ${BINMODE} ${PROG} ${DESTDIR}${BINDIR}
+	${INSTALL} -d ${DESTDIR}${DBDIR}
+
+include ${MK}/depend.mk
+include ${MK}/files.mk
+include ${MK}/scripts.mk
+include ${MK}/man.mk
+include ${MK}/dist.mk
+
+install: _proginstall _scriptsinstall _filesinstall _maninstall
+	for x in ${SUBDIRS}; do cd $$x; ${MAKE} $@; cd ..; done
+
+clean:
+	rm -f ${OBJS} ${PROG} _${PROG}.c _${PROG}.o ${PROG}.core ${CLEANFILES}
+
+LINTFLAGS?=	-hx
+LINTFLAGS+=	-X 159,247,352
+
+lint: ${SRCS:.c=.c}
+	${LINT} ${LINTFLAGS} ${CFLAGS:M-[DIU]*} $^ ${.ALLSRC}
diff --git a/mk/scripts.mk b/mk/scripts.mk
new file mode 100644
index 0000000..d295163
--- /dev/null
+++ b/mk/scripts.mk
@@ -0,0 +1,9 @@
+# Quick and dirty scripts
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+SCRIPTSDIR?=	${BINDIR}
+SCRIPTSMODE?=	${BINMODE}
+
+_scriptsinstall: ${SCRIPTS}
+	${INSTALL} -d ${DESTDIR}${SCRIPTSDIR}
+	${INSTALL} -m ${SCRIPTSMODE} ${SCRIPTS} ${DESTDIR}${SCRIPTSDIR}
diff --git a/mk/sys.mk b/mk/sys.mk
new file mode 100644
index 0000000..81882ab
--- /dev/null
+++ b/mk/sys.mk
@@ -0,0 +1,14 @@
+# Simple defaults
+
+BINDIR?=	${PREFIX}/usr/bin
+BINMODE?=	0755
+NONBINMODE?=	0644
+
+SYSCONFDIR?=	${PREFIX}/etc
+
+INSTALL?=	install
+SED?=		sed
+
+_LIBNAME_SH=		case `readlink /lib` in "") echo "lib";; *) basename `readlink /lib`;; esac
+_LIBNAME!=		${_LIBNAME_SH}
+LIBNAME?=		${_LIBNAME}$(shell ${_LIBNAME_SH})
diff --git a/net.c b/net.c
new file mode 100644
index 0000000..f8aad4c
--- /dev/null
+++ b/net.c
@@ -0,0 +1,633 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#ifdef __linux__
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
+#include <netinet/udp.h>
+#undef __FAVOR_BSD
+#include <arpa/inet.h>
+#ifdef AF_LINK
+# include <net/if_dl.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "dhcp.h"
+#include "logger.h"
+#include "net.h"
+#include "signals.h"
+
+int
+inet_ntocidr(struct in_addr address)
+{
+	int cidr = 0;
+	uint32_t mask = htonl(address.s_addr);
+
+	while (mask) {
+		cidr++;
+		mask <<= 1;
+	}
+
+	return cidr;
+}
+
+int
+inet_cidrtoaddr (int cidr, struct in_addr *addr)
+{
+	int ocets;
+
+	if (cidr < 0 || cidr > 32) {
+		errno = EINVAL;
+		return -1;
+	}
+	ocets = (cidr + 7) / 8;
+
+	addr->s_addr = 0;
+	if (ocets > 0) {
+		memset(&addr->s_addr, 255, (size_t)ocets - 1);
+		memset((unsigned char *)&addr->s_addr + (ocets - 1),
+		       (256 - (1 << (32 - cidr) % 8)), 1);
+	}
+
+	return 0;
+}
+
+uint32_t
+get_netmask(uint32_t addr)
+{
+	uint32_t dst;
+
+	if (addr == 0)
+		return 0;
+
+	dst = htonl(addr);
+	if (IN_CLASSA(dst))
+		return ntohl(IN_CLASSA_NET);
+	if (IN_CLASSB (dst))
+		return ntohl(IN_CLASSB_NET);
+	if (IN_CLASSC (dst))
+		return ntohl(IN_CLASSC_NET);
+
+	return 0;
+}
+
+char *
+hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
+{
+	static char buffer[(HWADDR_LEN * 3) + 1];
+	char *p = buffer;
+	size_t i;
+
+	for (i = 0; i < hwlen && i < HWADDR_LEN; i++) {
+		if (i > 0)
+			*p ++= ':';
+		p += snprintf(p, 3, "%.2x", hwaddr[i]);
+	}
+
+	*p ++= '\0';
+
+	return buffer;
+}
+
+size_t
+hwaddr_aton(unsigned char *buffer, const char *addr)
+{
+	char c[3];
+	const char *p = addr;
+	unsigned char *bp = buffer;
+	size_t len = 0;
+
+	c[2] = '\0';
+	while (*p) {
+		c[0] = *p++;
+		c[1] = *p++;
+		/* Ensure that digits are hex */
+		if (isxdigit((unsigned char)c[0]) == 0 ||
+		    isxdigit((unsigned char)c[1]) == 0)
+		{
+			errno = EINVAL;
+			return 0;
+		}
+		/* We should have at least two entries 00:01 */
+		if (len == 0 && *p == '\0') {
+			errno = EINVAL;
+			return 0;
+		}
+		/* Ensure that next data is EOL or a seperator with data */
+		if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) {
+			errno = EINVAL;
+			return 0;
+		}
+		if (*p)
+			p++;
+		if (bp)
+			*bp++ = (unsigned char)strtol(c, NULL, 16);
+		len++;
+	}
+	return len;
+}
+
+int
+do_interface(const char *ifname,
+	     _unused unsigned char *hwaddr, _unused size_t *hwlen,
+	     struct in_addr *addr, struct in_addr *net, int get)
+{
+	int s;
+	struct ifconf ifc;
+	int retval = 0;
+	int len = 10 * sizeof(struct ifreq);
+	int lastlen = 0;
+	char *p;
+	union {
+		char *buffer;
+		struct ifreq *ifr;
+	} ifreqs;
+	struct sockaddr_in address;
+	struct ifreq *ifr;
+	struct sockaddr_in netmask;
+
+#ifdef AF_LINK
+	struct sockaddr_dl sdl;
+#endif
+
+	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+		return -1;
+
+	/* Not all implementations return the needed buffer size for
+	 * SIOGIFCONF so we loop like so for all until it works */
+	memset(&ifc, 0, sizeof(ifc));
+	for (;;) {
+		ifc.ifc_len = len;
+		ifc.ifc_buf = xmalloc((size_t)len);
+		if (ioctl(s, SIOCGIFCONF, &ifc) == -1) {
+			if (errno != EINVAL || lastlen != 0) {
+				close(s);
+				free(ifc.ifc_buf);	
+				return -1;
+			}
+		} else {
+			if (ifc.ifc_len == lastlen)
+				break;
+			lastlen = ifc.ifc_len;
+		}
+
+		free(ifc.ifc_buf);
+		ifc.ifc_buf = NULL;
+		len *= 2;
+	}
+
+	for (p = (char *)ifc.ifc_buf; p < (char *)ifc.ifc_buf + ifc.ifc_len;) {
+		/* Cast the ifc buffer to an ifreq cleanly */
+		ifreqs.buffer = p;
+		ifr = ifreqs.ifr;
+
+#ifndef __linux__
+		if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
+			p += offsetof(struct ifreq, ifr_ifru) +
+				ifr->ifr_addr.sa_len;
+		else
+#endif
+			p += sizeof(*ifr);
+
+		if (strcmp(ifname, ifr->ifr_name) != 0)
+			continue;
+
+#ifdef AF_LINK
+		if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) {
+			memcpy(&sdl, &ifr->ifr_addr, sizeof(sdl));
+			*hwlen = sdl.sdl_alen;
+			memcpy(hwaddr, sdl.sdl_data + sdl.sdl_nlen,
+			       (size_t)sdl.sdl_alen);
+			retval = 1;
+			break;
+		}
+#endif
+
+		if (ifr->ifr_addr.sa_family == AF_INET)	{
+			memcpy(&address, &ifr->ifr_addr, sizeof(address));
+			if (ioctl(s, SIOCGIFNETMASK, ifr) == -1)
+				continue;
+			memcpy(&netmask, &ifr->ifr_addr, sizeof(netmask));
+			if (get) {
+				addr->s_addr = address.sin_addr.s_addr;
+				net->s_addr = netmask.sin_addr.s_addr;
+				retval = 1;
+				break;
+			} else {
+				if (address.sin_addr.s_addr == addr->s_addr &&
+				    (!net ||
+				     netmask.sin_addr.s_addr == net->s_addr))
+				{
+					retval = 1;
+					break;
+				}
+			}
+		}
+
+	}
+
+	close(s);
+	free(ifc.ifc_buf);
+	return retval;
+}
+
+struct interface *
+read_interface(const char *ifname, _unused int metric)
+{
+	int s;
+	struct ifreq ifr;
+	struct interface *iface = NULL;
+	unsigned char *hwaddr = NULL;
+	size_t hwlen = 0;
+	sa_family_t family = 0;
+#ifdef __linux__
+	char *p;
+#endif
+
+	memset(&ifr, 0, sizeof(ifr));
+	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+		return NULL;
+
+#ifdef __linux__
+	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	if (ioctl(s, SIOCGIFHWADDR, &ifr) == -1)
+		goto eexit;
+
+	switch (ifr.ifr_hwaddr.sa_family) {
+	case ARPHRD_ETHER:
+	case ARPHRD_IEEE802:
+		hwlen = ETHER_ADDR_LEN;
+		break;
+	case ARPHRD_IEEE1394:
+		hwlen = EUI64_ADDR_LEN;
+	case ARPHRD_INFINIBAND:
+		hwlen = INFINIBAND_ADDR_LEN;
+		break;
+	}
+
+	hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
+	memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, hwlen);
+	family = ifr.ifr_hwaddr.sa_family;
+#else
+	ifr.ifr_metric = metric;
+	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	if (ioctl(s, SIOCSIFMETRIC, &ifr) == -1)
+		goto eexit;
+
+	hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
+	if (do_interface(ifname, hwaddr, &hwlen, NULL, NULL, 0) != 1)
+		goto eexit;
+
+	family = ARPHRD_ETHER;
+#endif
+
+	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	if (ioctl(s, SIOCGIFMTU, &ifr) == -1)
+		goto eexit;
+
+	/* Ensure that the MTU is big enough for DHCP */
+	if (ifr.ifr_mtu < MTU_MIN) {
+		ifr.ifr_mtu = MTU_MIN;
+		strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+		if (ioctl(s, SIOCSIFMTU, &ifr) == -1)
+			goto eexit;
+	}
+
+	/* Bring the interface up if it's down */
+	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+#ifdef __linux__
+	/* We can only bring the real interface up */
+	if ((p = strchr(ifr.ifr_name, ':')))
+		*p = '\0';
+#endif
+	if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1)
+		goto eexit;
+	if (!(ifr.ifr_flags & IFF_UP)) {
+		ifr.ifr_flags |= IFF_UP;
+		if (ioctl(s, SIOCSIFFLAGS, &ifr) != 0)
+			goto eexit;
+	}
+
+	iface = xzalloc(sizeof(*iface));
+	strlcpy(iface->name, ifname, IF_NAMESIZE);
+	snprintf(iface->leasefile, PATH_MAX, LEASEFILE, ifname);
+	memcpy(&iface->hwaddr, hwaddr, hwlen);
+	iface->hwlen = hwlen;
+
+	iface->family = family;
+	iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK));
+
+	/* 0 is a valid fd, so init to -1 */
+	iface->fd = -1;
+	iface->udp_fd = -1;
+#ifdef ENABLE_ARP
+	iface->arp_fd = -1;
+#endif
+
+eexit:
+	close(s);
+	free(hwaddr);
+	return iface;
+}
+
+int
+do_mtu(const char *ifname, short int mtu)
+{
+	struct ifreq ifr;
+	int r;
+	int s;
+
+	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+		return -1;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_mtu = mtu;
+	r = ioctl(s, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
+	close(s);
+	if (r == -1)
+		return -1;
+	return ifr.ifr_mtu;
+}
+
+void
+free_routes(struct rt *routes)
+{
+	struct rt *r;
+
+	while (routes) {
+		r = routes->next;
+		free(routes);
+		routes = r;
+	}
+}
+
+int
+open_udp_socket(struct interface *iface)
+{
+	int s;
+	union sockunion {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+	} su;
+	int n = 1;
+
+	if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+		return -1;
+
+	memset(&su, 0, sizeof(su));
+	su.sin.sin_family = AF_INET;
+	su.sin.sin_port = htons(DHCP_CLIENT_PORT);
+	su.sin.sin_addr.s_addr = iface->addr.s_addr;
+	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
+		goto eexit;
+	/* As we don't actually use this socket for anything, set
+	 * the receiver buffer to 1 */
+	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
+		goto eexit;
+	if (bind(s, &su.sa, sizeof(su)) == -1)
+		goto eexit;
+
+	iface->udp_fd = s;
+	set_cloexec(s);
+	return 0;
+
+eexit:
+	close(s);
+	return -1;
+}
+
+ssize_t
+send_packet(const struct interface *iface, struct in_addr to,
+	    const uint8_t *data, ssize_t len)
+{
+	union sockunion {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+	} su;
+
+	memset(&su, 0, sizeof(su));
+	su.sin.sin_family = AF_INET;
+	su.sin.sin_addr.s_addr = to.s_addr;
+	su.sin.sin_port = htons(DHCP_SERVER_PORT);
+
+	return sendto(iface->udp_fd, data, len, 0, &su.sa, sizeof(su));
+}
+
+struct udp_dhcp_packet
+{
+	struct ip ip;
+	struct udphdr udp;
+	struct dhcp_message dhcp;
+};
+const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet);
+
+static uint16_t
+checksum(const void *data, uint16_t len)
+{
+	const uint8_t *addr = data;
+	uint32_t sum = 0;
+
+	while (len > 1) {
+		sum += addr[0] * 256 + addr[1];
+		addr += 2;
+		len -= 2;
+	}
+
+	if (len == 1)
+		sum += *addr * 256;
+
+	sum = (sum >> 16) + (sum & 0xffff);
+	sum += (sum >> 16);
+
+	sum = htons(sum);
+
+	return ~sum;
+}
+
+ssize_t
+make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length,
+		struct in_addr source, struct in_addr dest)
+{
+	struct udp_dhcp_packet *udpp;
+	struct ip *ip;
+	struct udphdr *udp;
+
+	udpp = xzalloc(sizeof(*udpp));
+	ip = &udpp->ip;
+	udp = &udpp->udp;
+
+	/* OK, this is important :)
+	 * We copy the data to our packet and then create a small part of the
+	 * ip structure and an invalid ip_len (basically udp length).
+	 * We then fill the udp structure and put the checksum
+	 * of the whole packet into the udp checksum.
+	 * Finally we complete the ip structure and ip checksum.
+	 * If we don't do the ordering like so then the udp checksum will be
+	 * broken, so find another way of doing it! */
+
+	memcpy(&udpp->dhcp, data, length);
+
+	ip->ip_p = IPPROTO_UDP;
+	ip->ip_src.s_addr = source.s_addr;
+	if (dest.s_addr == 0)
+		ip->ip_dst.s_addr = INADDR_BROADCAST;
+	else
+		ip->ip_dst.s_addr = dest.s_addr;
+
+	udp->uh_sport = htons(DHCP_CLIENT_PORT);
+	udp->uh_dport = htons(DHCP_SERVER_PORT);
+	udp->uh_ulen = htons(sizeof(*udp) + length);
+	ip->ip_len = udp->uh_ulen;
+	udp->uh_sum = checksum(udpp, sizeof(*udpp));
+
+	ip->ip_v = IPVERSION;
+	ip->ip_hl = 5;
+	ip->ip_id = 0;
+	ip->ip_tos = IPTOS_LOWDELAY;
+	ip->ip_len = htons (sizeof(*ip) + sizeof(*udp) + length);
+	ip->ip_id = 0;
+	ip->ip_off = htons(IP_DF); /* Don't fragment */
+	ip->ip_ttl = IPDEFTTL;
+
+	ip->ip_sum = checksum(ip, sizeof(*ip));
+
+	*packet = (uint8_t *)udpp;
+	return sizeof(*ip) + sizeof(*udp) + length;
+}
+
+ssize_t
+get_udp_data(const uint8_t **data, const uint8_t *udp)
+{
+	struct udp_dhcp_packet packet;
+
+	memcpy(&packet, udp, sizeof(packet));
+	*data = udp + offsetof(struct udp_dhcp_packet, dhcp);
+	return ntohs(packet.ip.ip_len) - sizeof(packet.ip) - sizeof(packet.udp);
+}
+
+int
+valid_udp_packet(const uint8_t *data)
+{
+	struct udp_dhcp_packet packet;
+	uint16_t bytes;
+	uint16_t ipsum;
+	uint16_t iplen;
+	uint16_t udpsum;
+	struct in_addr source;
+	struct in_addr dest;
+	int retval = 0;
+
+	memcpy(&packet, data, sizeof(packet));
+	bytes = ntohs(packet.ip.ip_len);
+	ipsum = packet.ip.ip_sum;
+	iplen = packet.ip.ip_len;
+	udpsum = packet.udp.uh_sum;
+
+	if (0 != checksum(&packet.ip, sizeof(packet.ip))) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	packet.ip.ip_sum = 0;
+	memcpy(&source, &packet.ip.ip_src, sizeof(packet.ip.ip_src));
+	memcpy(&dest, &packet.ip.ip_dst, sizeof(packet.ip.ip_dst));
+	memset(&packet.ip, 0, sizeof(packet.ip));
+	packet.udp.uh_sum = 0;
+
+	packet.ip.ip_p = IPPROTO_UDP;
+	memcpy(&packet.ip.ip_src, &source, sizeof(packet.ip.ip_src));
+	memcpy(&packet.ip.ip_dst, &dest, sizeof(packet.ip.ip_dst));
+	packet.ip.ip_len = packet.udp.uh_ulen;
+	if (udpsum && udpsum != checksum(&packet, bytes)) {
+		errno = EINVAL;
+		retval = -1;
+	}
+
+	return retval;
+}
+
+#ifdef ENABLE_ARP
+int
+send_arp(const struct interface *iface, int op, in_addr_t sip, in_addr_t tip)
+{
+	struct arphdr *arp;
+	size_t arpsize;
+	unsigned char *p;
+	int retval;
+
+	arpsize = sizeof(*arp) + 2 * iface->hwlen + 2 *sizeof(sip);
+
+	arp = xmalloc(arpsize);
+	arp->ar_hrd = htons(iface->family);
+	arp->ar_pro = htons(ETHERTYPE_IP);
+	arp->ar_hln = iface->hwlen;
+	arp->ar_pln = sizeof(sip);
+	arp->ar_op = htons(op);
+	p = (unsigned char *)arp;
+	p += sizeof(*arp);
+	memcpy(p, iface->hwaddr, iface->hwlen);
+	p += iface->hwlen;
+	memcpy(p, &sip, sizeof(sip));
+	p += sizeof(sip);
+	/* ARP requests should ignore this, but we fill with 0xff
+	 * for broadcast. */
+	memset(p, 0xff, iface->hwlen);
+	p += iface->hwlen;
+	memcpy(p, &tip, sizeof(tip));
+
+	retval = send_raw_packet(iface, ETHERTYPE_ARP, arp, arpsize);
+	free(arp);
+	return retval;
+}
+#endif
+
diff --git a/net.h b/net.h
new file mode 100644
index 0000000..0be56cf
--- /dev/null
+++ b/net.h
@@ -0,0 +1,172 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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.
+ */
+
+#ifndef INTERFACE_H
+#define INTERFACE_H
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <limits.h>
+
+#include "config.h"
+
+#ifndef DUID_LEN
+#  define DUID_LEN			128 + 2
+#endif
+
+#define EUI64_ADDR_LEN			8
+#define INFINIBAND_ADDR_LEN		20
+
+/* Linux 2.4 doesn't define this */
+#ifndef ARPHRD_IEEE1394
+#  define ARPHRD_IEEE1394		24
+#endif
+
+/* The BSD's don't define this yet */
+#ifndef ARPHRD_INFINIBAND
+#  define ARPHRD_INFINIBAND		32
+#endif
+
+#define HWADDR_LEN			20
+
+/* Work out if we have a private address or not
+ * 10/8
+ * 172.16/12
+ * 192.168/16
+ */
+#ifndef IN_PRIVATE
+# define IN_PRIVATE(addr) (((addr & IN_CLASSA_NET) == 0x0a000000) || \
+			   ((addr & 0xfff00000)    == 0xac100000) || \
+			   ((addr & IN_CLASSB_NET) == 0xc0a80000))
+#endif
+
+#define LINKLOCAL_ADDR	0xa9fe0000
+#define LINKLOCAL_MASK	0xffff0000
+#define LINKLOCAL_BRDC	0xa9feffff
+
+#ifndef IN_LINKLOCAL
+# define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
+#endif
+
+/* There is an argument that this should be converted to an STAIL using
+ * queue(3). However, that isn't readily available on all libc's that
+ * dhcpcd works on. The only benefit of STAILQ over this is the ability to
+ * quickly loop backwards through the list - currently we reverse the list
+ * and then move through it forwards. This isn't that much of a big deal
+ * though as the norm is to just have one default route, and an IPV4LL route.
+ * You can (and do) get more routes in the DHCP message, but not enough to
+ * really warrant a change to STAIL queue for performance reasons. */
+struct rt {
+	struct in_addr dest;
+	struct in_addr net;
+	struct in_addr gate;
+	struct rt *next;
+};
+
+struct interface
+{
+	char name[IF_NAMESIZE];
+	sa_family_t family;
+	unsigned char hwaddr[HWADDR_LEN];
+	size_t hwlen;
+	int arpable;
+
+	int fd;
+	int udp_fd;
+	size_t buffer_size, buffer_len, buffer_pos;
+	unsigned char *buffer;
+#ifdef ENABLE_ARP
+	int arp_fd;
+#endif
+
+	struct in_addr addr;
+	struct in_addr net;
+	struct rt *routes;
+
+	char leasefile[PATH_MAX];
+	time_t start_uptime;
+
+	unsigned char *clientid;
+};
+
+uint32_t get_netmask(uint32_t);
+char *hwaddr_ntoa(const unsigned char *, size_t);
+size_t hwaddr_aton(unsigned char *, const char *);
+
+struct interface *read_interface(const char *, int);
+int do_mtu(const char *, short int);
+#define get_mtu(iface) do_mtu(iface, 0)
+#define set_mtu(iface, mtu) do_mtu(iface, mtu)
+
+int inet_ntocidr(struct in_addr);
+int inet_cidrtoaddr(int, struct in_addr *);
+
+int do_interface(const char *, unsigned char *, size_t *,
+		 struct in_addr *, struct in_addr *, int);
+int if_address(const char *, const struct in_addr *, const struct in_addr *,
+	       const struct in_addr *, int);
+#define add_address(ifname, addr, net, brd) \
+	if_address(ifname, addr, net, brd, 1)
+#define del_address(ifname, addr, net) \
+	if_address(ifname, addr, net, NULL, -1)
+#define has_address(ifname, addr, net) \
+	do_interface(ifname, NULL, NULL, addr, net, 0)
+#define get_address(ifname, addr, net) \
+	do_interface(ifname, NULL, NULL, addr, net, 1)
+
+int if_route(const char *, const struct in_addr *, const struct in_addr *,
+	     const struct in_addr *, int, int);
+#define add_route(ifname, dest, mask, gate, metric) \
+	if_route(ifname, dest, mask, gate, metric, 1)
+#define del_route(ifname, dest, mask, gate, metric) \
+	if_route(ifname, dest, mask, gate, metric, -1)
+void free_routes(struct rt *);
+
+int open_udp_socket(struct interface *);
+const size_t udp_dhcp_len;
+ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t,
+			struct in_addr, struct in_addr);
+ssize_t get_udp_data(const uint8_t **, const uint8_t *);
+int valid_udp_packet(const uint8_t *);
+
+int open_socket(struct interface *, int);
+ssize_t send_packet(const struct interface *, struct in_addr, 
+		    const uint8_t *, ssize_t);
+ssize_t send_raw_packet(const struct interface *, int,
+			const void *, ssize_t);
+ssize_t get_raw_packet(struct interface *, int, void *, ssize_t);
+
+#ifdef ENABLE_ARP
+int send_arp(const struct interface *, int, in_addr_t, in_addr_t);
+#endif
+#endif
diff --git a/signals.c b/signals.c
new file mode 100644
index 0000000..fb986d7
--- /dev/null
+++ b/signals.c
@@ -0,0 +1,137 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "signals.h"
+
+static int signal_pipe[2];
+
+static const int handle_sigs[] = {
+	SIGHUP,
+	SIGALRM,
+	SIGTERM,
+	SIGINT
+};
+
+static void
+signal_handler(int sig)
+{
+	int serrno = errno;
+
+	write(signal_pipe[1], &sig, sizeof(sig));
+	/* Restore errno */
+	errno = serrno;
+}
+
+int
+signal_fd(void)
+{
+	return (signal_pipe[0]);
+}
+
+/* Check if we have a signal or not */
+int
+signal_exists(int fd)
+{
+	if (fd_hasdata(fd) == 1)
+		return 0;
+	return -1;
+}
+
+/* Read a signal from the signal pipe. Returns 0 if there is
+ * no signal, -1 on error (and sets errno appropriately), and
+ * your signal on success */
+int
+signal_read(int fd)
+{
+	int sig = -1;
+	char buf[16];
+	size_t bytes;
+
+	if (fd_hasdata(fd) == 1) {
+		memset(buf, 0, sizeof(buf));
+		bytes = read(signal_pipe[0], buf, sizeof(buf));
+		if (bytes >= sizeof(sig))
+			memcpy(&sig, buf, sizeof(sig));
+	}
+	return sig;
+}
+
+/* Call this before doing anything else. Sets up the socket pair
+ * and installs the signal handler */
+int
+signal_init(void)
+{
+	if (pipe(signal_pipe) == -1)
+		return -1;
+	/* Don't block on read */
+	if (set_nonblock(signal_pipe[0]) == -1)
+		return -1;
+	/* Stop any scripts from inheriting us */
+	if (set_cloexec(signal_pipe[0]) == -1)
+		return -1;
+	if (set_cloexec(signal_pipe[1]) == -1)
+		return -1;
+	return 0;
+}
+
+static int
+signal_handle(void (*func)(int))
+{
+	unsigned int i;
+	struct sigaction sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = func;
+	sigemptyset(&sa.sa_mask);
+
+	for (i = 0; i < sizeof(handle_sigs) / sizeof(handle_sigs[0]); i++)
+		if (sigaction(handle_sigs[i], &sa, NULL) == -1)
+			return -1;
+	return 0;
+}
+
+int
+signal_setup(void)
+{
+	return signal_handle(signal_handler);
+}
+
+int
+signal_reset(void)
+{
+	return signal_handle(SIG_DFL);
+}
diff --git a/signals.h b/signals.h
new file mode 100644
index 0000000..5972b31
--- /dev/null
+++ b/signals.h
@@ -0,0 +1,40 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 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.
+ */
+
+#ifndef SIGNAL_H
+#define SIGNAL_H
+
+#include <poll.h>
+
+int signal_init(void);
+int signal_setup(void);
+int signal_reset(void);
+int signal_fd(void);
+int signal_exists(int fd);
+int signal_read(int fd);
+
+#endif