Code drop from //branches/cupcake/...@124589
diff --git a/Android.mk b/Android.mk
index b1e0e55..5cac7ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -10,13 +10,20 @@
 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_SHARED_LIBRARIES := libc libcutils
 LOCAL_MODULE = dhcpcd
 LOCAL_MODULE_TAGS := user development
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
+LOCAL_SRC_FILES := showlease.c
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_SHARED_LIBRARIES := libc
+LOCAL_MODULE = showlease
+LOCAL_MODULE_TAGS := user development
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
 LOCAL_MODULE := dhcpcd.conf
 LOCAL_MODULE_TAGS := user development
 LOCAL_MODULE_CLASS := ETC
diff --git a/Makefile b/Makefile
index f93d256..a6b066b 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,6 @@
 	${SED} ${SED_HOOKDIR} ${SED_SCRIPT} ${SED_SYS} $< > $@
 
 MK=		mk
-include ${MK}/os.mk
 include ${MK}/sys.mk
+include ${MK}/os.mk
 include ${MK}/prog.mk
diff --git a/README b/README
index 27aadf0..50294fe 100644
--- a/README
+++ b/README
@@ -15,12 +15,10 @@
 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.
+If you're building for an MMU-less system where fork() does not work, you
+should add -DTHERE_IS_NO_FORK to your CPPFLAGS.
+This also puts the --no-background flag on and stops the --background flag
+from working.
 
 You can change the default dir with these knobs.
 For example, to satisfy FHS compliance you would do this:-
@@ -66,4 +64,4 @@
 ---------
 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
+http://git.marples.name/?p=dhcpcd.git;a=summary
diff --git a/bpf-filter.h b/bpf-filter.h
index adcc8bb..881f678 100644
--- a/bpf-filter.h
+++ b/bpf-filter.h
@@ -36,14 +36,14 @@
 	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... */
+	/* Make sure this is an ARP REQUEST... */
+	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 + BPF_ETHCOOK),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
+	/* or 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),
 };
@@ -81,25 +81,19 @@
 	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),
 };
diff --git a/bpf.c b/bpf.c
index f015344..96e53a1 100644
--- a/bpf.c
+++ b/bpf.c
@@ -108,15 +108,13 @@
 
 	/* 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;
+		fdp = &iface->raw_fd;
 	}
 	if (ioctl(fd, BIOCSETF, &pf) == -1)
 		goto eexit;
@@ -142,6 +140,7 @@
 {
 	struct iovec iov[2];
 	struct ether_header hw;
+	int fd;
 
 	memset(&hw, 0, ETHER_HDR_LEN);
 	memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
@@ -150,7 +149,11 @@
 	iov[0].iov_len = ETHER_HDR_LEN;
 	iov[1].iov_base = UNCONST(data);
 	iov[1].iov_len = len;
-	return writev(iface->fd, iov, 2);
+	if (protocol == ETHERTYPE_ARP)
+		fd = iface->arp_fd;
+	else
+		fd = iface->raw_fd;
+	return writev(fd, iov, 2);
 }
 
 /* BPF requires that we read the entire buffer.
@@ -164,12 +167,10 @@
 	ssize_t bytes;
 	const unsigned char *payload;
 
-	if (protocol == ETHERTYPE_ARP) {
-#ifdef ENABLE_ARP
+	if (protocol == ETHERTYPE_ARP)
 		fd = iface->arp_fd;
-#endif
-	} else
-		fd = iface->fd;
+	else
+		fd = iface->raw_fd;
 
 	for (;;) {
 		if (iface->buffer_len == 0) {
diff --git a/client.c b/client.c
index 99d0fa8..c34d318 100644
--- a/client.c
+++ b/client.c
@@ -53,12 +53,7 @@
 #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
+#define IPV4LL_LEASETIME 	2
 
 /* Some platforms don't define INFTIM */
 #ifndef INFTIM
@@ -73,17 +68,18 @@
 #define STATE_REBINDING         5
 #define STATE_REBOOT            6
 #define STATE_RENEW_REQUESTED   7
-#define STATE_PROBING		8
-#define STATE_ANNOUNCING	9
+#define STATE_INIT_IPV4LL	8
+#define STATE_PROBING		9
+#define STATE_ANNOUNCING	10
 
-/* Constants taken from RFC 2131. 
- * We multiply some numbers by 1000 so they are suitable for use in poll(). */
+/* Constants taken from RFC 2131. */
 #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
+#define DHCP_BASE		4
+#define DHCP_MAX		64
+#define DHCP_RAND_MIN		-1
+#define DHCP_RAND_MAX		1
+#define DHCP_ARP_FAIL		10
 
 /* We should define a maximum for the NAK exponential backoff */ 
 #define NAKOFF_MAX              60
@@ -91,24 +87,44 @@
 #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
+/* These are for IPV4LL, RFC 3927. */
+#define PROBE_WAIT		 1
 #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 PROBE_MIN		 1
+#define PROBE_MAX		 2
+#define ANNOUNCE_WAIT		 2
+/* BSD systems always do a grauitous ARP when assigning an address,
+ * so we can do one less announce. */
+#ifdef BSD
+# define ANNOUNCE_NUM		 1
+#else
+# define ANNOUNCE_NUM		 2
+#endif
+#define ANNOUNCE_INTERVAL	 2
 #define MAX_CONFLICTS		10
 #define RATE_LIMIT_INTERVAL	60
 #define DEFEND_INTERVAL		10
 
+
+/* number of usecs in a second. */
+#define USECS_SECOND		1000000
+/* As we use timevals, we should use the usec part for
+ * greater randomisation. */
+#define DHCP_RAND_MIN_U		DHCP_RAND_MIN * USECS_SECOND
+#define DHCP_RAND_MAX_U		DHCP_RAND_MAX * USECS_SECOND
+#define PROBE_MIN_U		PROBE_MIN * USECS_SECOND
+#define PROBE_MAX_U		PROBE_MAX * USECS_SECOND
+
+#define timernorm(tvp)						\
+	do {							\
+		while ((tvp)->tv_usec >= 1000000) {		\
+			(tvp)->tv_sec++;			\
+			(tvp)->tv_usec -= 1000000;		\
+		}						\
+	} while (0 /* CONSTCOND */);
+
+#define timerneg(tvp)	((tvp)->tv_sec < 0 || (tvp)->tv_usec < 0)
+
 struct if_state {
 	int options;
 	struct interface *interface;
@@ -116,25 +132,28 @@
 	struct dhcp_message *new;
 	struct dhcp_message *old;
 	struct dhcp_lease lease;
-	struct timeval start;
+	struct timeval timeout;
 	struct timeval stop;
+	struct timeval exit;
 	int state;
 	int messages;
-	long timeout;
 	time_t nakoff;
 	uint32_t xid;
 	int socket;
 	int *pid_fd;
 	int signal_fd;
-#ifdef ENABLE_ARP
+	int carrier;
 	int probes;
 	int claims;
 	int conflicts;
 	time_t defend;
 	struct in_addr fail;
-#endif
 };
 
+#define LINK_UP 	1
+#define LINK_UNKNOWN	0
+#define LINK_DOWN 	-1
+
 struct dhcp_op {
         uint8_t value;
         const char *name;
@@ -163,19 +182,17 @@
 	return NULL;
 }
 
+#ifdef THERE_IS_NO_FORK
+#define daemonise(a,b) 0
+#else
 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))
@@ -184,7 +201,6 @@
 	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));
@@ -214,36 +230,6 @@
 			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) {
@@ -253,18 +239,16 @@
 	}
 
 	sigprocmask(SIG_SETMASK, &old, NULL);
-
-	state->state = STATE_BOUND;
 	if (pid == 0) {
 		state->options |= DHCPCD_DAEMONISED;
+		timerclear(&state->exit);
 		return 0;
 	}
-
 	state->options |= DHCPCD_PERSISTENT | DHCPCD_FORKED;
 	return -1;
 }
+#endif
 
-#ifndef MINIMAL
 #define THIRTY_YEARS_IN_SECONDS    946707779
 static size_t
 get_duid(unsigned char *duid, const struct interface *iface)
@@ -333,9 +317,7 @@
 	}
 	return len;
 }
-#endif
 
-#ifdef ENABLE_IPV4LL
 static struct dhcp_message*
 ipv4ll_get_dhcp(uint32_t old_addr)
 {
@@ -346,42 +328,58 @@
 	dhcp = xzalloc(sizeof(*dhcp));
 	/* Put some LL options in */
 	p = dhcp->options;
-	*p++ = DHCP_SUBNETMASK;
-	*p += sizeof(u32);
-	u32 = LINKLOCAL_MASK;
+	*p++ = DHO_SUBNETMASK;
+	*p++ = sizeof(u32);
+	u32 = htonl(LINKLOCAL_MASK);
 	memcpy(p, &u32, sizeof(u32));
 	p += sizeof(u32);
-	*p++ = DHCP_BROADCAST;
-	*p += sizeof(u32);
-	u32 = LINKLOCAL_BRDC;
+	*p++ = DHO_BROADCAST;
+	*p++ = sizeof(u32);
+	u32 = htonl(LINKLOCAL_BRDC);
 	memcpy(p, &u32, sizeof(u32));
 	p += sizeof(u32);
-	*p++ = DHCP_END;
+	*p++ = DHO_END;
 
 	for (;;) {
 		dhcp->yiaddr = htonl(LINKLOCAL_ADDR |
 				     (((uint32_t)abs((int)arc4random())
 				       % 0xFD00) + 0x0100));
-		if (dhcp->yiaddr != old_addr)
+		if (dhcp->yiaddr != old_addr &&
+		    IN_LINKLOCAL(ntohl(dhcp->yiaddr)))
 			break;
 	}
 	return dhcp;
 }
-#endif
+
+static double
+timeval_to_double(struct timeval *tv)
+{
+	return tv->tv_sec * 1.0 + tv->tv_usec * 1.0e-6;
+}
 
 static void
 get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
 {
+	time_t t;
+
 	lease->frominfo = 0;
 	lease->addr.s_addr = dhcp->yiaddr;
 
-	if (get_option_addr(&lease->net.s_addr, dhcp, DHCP_SUBNETMASK) == -1)
+	if (get_option_addr(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
 		lease->net.s_addr = get_netmask(dhcp->yiaddr);
-	if (get_option_uint32(&lease->leasetime, dhcp, DHCP_LEASETIME) != 0)
+	if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) {
+		/* Ensure that we can use the lease */
+		t = 0;
+		if (t + (time_t)lease->leasetime < t) {
+			logger(LOG_WARNING, "lease of %u would overflow, "
+			       "treating as infinite", lease->leasetime);
+			lease->leasetime = ~0U; /* Infinite lease */
+		}
+	} else
 		lease->leasetime = DEFAULT_LEASETIME;
-	if (get_option_uint32(&lease->renewaltime, dhcp, DHCP_RENEWALTIME) != 0)
+	if (get_option_uint32(&lease->renewaltime, dhcp, DHO_RENEWALTIME) != 0)
 		lease->renewaltime = 0;
-	if (get_option_uint32(&lease->rebindtime, dhcp, DHCP_REBINDTIME) != 0)
+	if (get_option_uint32(&lease->rebindtime, dhcp, DHO_REBINDTIME) != 0)
 		lease->rebindtime = 0;
 }
 
@@ -390,21 +388,21 @@
 {
 	struct interface *iface = state->interface;
 	struct dhcp_lease *lease = &state->lease;
-	struct dhcp_message *dhcp;
+	struct dhcp_message *dhcp = NULL;
 	struct timeval tv;
 	unsigned int offset = 0;
 	struct stat sb;
 
+	if (stat(iface->leasefile, &sb) == -1) {
+		if (errno != ENOENT)
+			logger(LOG_ERR, "stat: %s", strerror(errno));
+		goto eexit;
+	}
 	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));
+		logger(LOG_INFO, "read_lease: %s", strerror(errno));
 		goto eexit;
 	}
 	get_lease(&state->lease, dhcp);
@@ -416,10 +414,8 @@
 	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) {
@@ -429,7 +425,7 @@
 
 		offset = tv.tv_sec - lease->leasedfrom;
 		if (lease->leasedfrom &&
-		    tv.tv_sec - lease->leasedfrom > lease->leasetime)
+		    tv.tv_sec - lease->leasedfrom > (time_t)lease->leasetime)
 		{
 			logger(LOG_ERR, "lease expired %u seconds ago",
 			       offset + lease->leasetime);
@@ -445,8 +441,8 @@
 
 	if (lease->leasedfrom == 0)
 		offset = 0;
-	state->timeout = lease->renewaltime - offset;
 	iface->start_uptime = uptime();
+	state->timeout.tv_sec = lease->renewaltime - offset;
 	free(state->old);
 	state->old = state->new;
 	state->new = NULL;
@@ -465,24 +461,25 @@
 	struct interface *iface = state->interface;
 	struct dhcp_lease *lease = &state->lease;
 	struct in_addr addr;
-#ifndef MINIMAL
+	struct timeval tv;
 	size_t len = 0;
 	unsigned char *duid = NULL;
 	uint32_t ul;
-#endif
 
 	state->state = STATE_INIT;
 	state->nakoff = 1;
 	state->options = options->options;
+	timerclear(&tv);
 
 	if (options->request_address.s_addr == 0 &&
 	    (options->options & DHCPCD_INFORM ||
 	     options->options & DHCPCD_REQUEST ||
-	     options->options & DHCPCD_DAEMONISED))
+	     (options->options & DHCPCD_DAEMONISED &&
+	      !(options->options & DHCPCD_BACKGROUND))))
 	{
 		if (get_old_lease(state) != 0)
 			return -1;
-		state->timeout = 0;
+		timerclear(&state->timeout);
 
 		if (!(options->options & DHCPCD_DAEMONISED) &&
 		    IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
@@ -490,21 +487,21 @@
 			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 (options->options & DHCPCD_REQUEST &&
+	    state->options & DHCPCD_ARP &&
+	    !state->offer)
+	{
+		state->offer = xzalloc(sizeof(*state->offer));
+		state->offer->yiaddr = options->request_address.s_addr;
+		state->state = STATE_PROBING;
+		state->xid = arc4random();
+	}
+
 	/* If INFORMing, ensure the interface has the address */
 	if (state->options & DHCPCD_INFORM &&
 	    has_address(iface->name, &lease->addr, &lease->net) < 1)
@@ -522,7 +519,6 @@
 		iface->net.s_addr = lease->net.s_addr;
 	}
 
-#ifndef MINIMAL
 	if (*options->clientid) {
 		iface->clientid = xmalloc(options->clientid[0] + 1);
 		memcpy(iface->clientid,
@@ -536,7 +532,7 @@
 		}
 
 		if (len > 0) {
-			logger(LOG_INFO, "DUID = %s",
+			logger(LOG_DEBUG, "DUID = %s",
 			       hwaddr_ntoa(duid, len));
 
 			iface->clientid = xmalloc(len + 6);
@@ -568,34 +564,64 @@
 			memcpy(iface->clientid + 2, iface->hwaddr, iface->hwlen);
 		}
 	}
-#endif
 
+	if (state->options & DHCPCD_LINK) {
+		open_link_socket(iface);
+		switch (carrier_status(iface->name)) {
+		case 0:
+			state->carrier = LINK_DOWN;
+			break;
+		case 1:
+			state->carrier = LINK_UP;
+			break;
+		default:
+			state->carrier = LINK_UNKNOWN;
+		}
+	}
+
+	if (options->timeout > 0 &&
+	    !(state->options & DHCPCD_DAEMONISED))
+	{
+		if (state->options & DHCPCD_IPV4LL) {
+			state->stop.tv_sec = options->timeout;
+			if (!(state->options & DHCPCD_BACKGROUND))
+				state->exit.tv_sec = state->stop.tv_sec + 10;
+		} else if (!(state->options & DHCPCD_BACKGROUND))
+			state->exit.tv_sec = options->timeout;
+	}
 	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 (state->interface->raw_fd != -1) {
+		close(state->interface->raw_fd);
+		state->interface->raw_fd = -1;
 	}
-	if (mode == SOCKET_CLOSED && state->interface->udp_fd >= 0) {
-		close(state->interface->udp_fd);
-		state->interface->udp_fd = -1;
+	if (mode == SOCKET_CLOSED) {
+		if (state->interface->udp_fd != -1) {
+			close(state->interface->udp_fd);
+			state->interface->udp_fd = -1;
+		}
+		if (state->interface->arp_fd != -1) {
+			close(state->interface->arp_fd);
+			state->interface->arp_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. */
+	/* Always have the UDP socket open to avoid the kernel sending
+	 * ICMP unreachable messages. */
+	/* For systems without SO_BINDTODEVICE, (ie BSD ones) we may get an
+	 * error or EADDRINUSE when binding to INADDR_ANY as another dhcpcd
+	 * instance could be running.
+	 * Oddly enough, we don't care about this as the socket is there
+	 * just to please the kernel - we don't care for reading from it. */
 	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;
-		}
+	    open_udp_socket(state->interface) == -1 &&
+	    (errno != EADDRINUSE || state->interface->addr.s_addr != 0))
+		logger(LOG_ERR, "open_udp_socket: %s", strerror(errno));
 
 	if (mode == SOCKET_OPEN) 
 		if (open_socket(state->interface, ETHERTYPE_IP) == -1) {
@@ -611,107 +637,211 @@
 {
 	struct dhcp_message *dhcp;
 	uint8_t *udp;
-	ssize_t len;
-	ssize_t r;
-	struct in_addr from;
-	struct in_addr to;
+	ssize_t len, r;
+	struct in_addr from, to;
+	in_addr_t a = 0;
 
-	logger(LOG_DEBUG, "sending %s with xid 0x%x",
-	       get_dhcp_op(type), state->xid);
+	if (state->carrier == LINK_DOWN)
+		return 0;
+	if (type == DHCP_RELEASE)
+		logger(LOG_DEBUG, "sending %s with xid 0x%x",
+		       get_dhcp_op(type), state->xid);
+	else
+		logger(LOG_DEBUG,
+		       "sending %s with xid 0x%x, next in %0.2f seconds",
+		       get_dhcp_op(type), state->xid,
+		       timeval_to_double(&state->timeout));
 	state->messages++;
+	if (state->messages < 0)
+		state->messages = INT_MAX;
+	/* If we couldn't open a UDP port for our IP address
+	 * then we cannot renew.
+	 * This could happen if our IP was pulled out from underneath us. */
+	if (state->interface->udp_fd == -1) {
+		a = state->interface->addr.s_addr;
+		state->interface->addr.s_addr = 0;
+	}
 	len = make_message(&dhcp, state->interface, &state->lease, state->xid,
 			   type, options);
+	if (state->interface->udp_fd == -1)
+		state->interface->addr.s_addr = a;
 	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) {
+	if (to.s_addr && to.s_addr != INADDR_BROADCAST) {
 		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);
+		free(udp);
 		if (r == -1)
 			logger(LOG_ERR, "send_raw_packet: %s", strerror(errno));
-		free(udp);
+	}
+	free(dhcp);
+	/* Failed to send the packet? Return to the init state */
+	if (r == -1) {
+		state->state = STATE_INIT;
+		timerclear(&state->timeout);
+		timerclear(&state->stop);
+		do_socket(state, SOCKET_CLOSED);
 	}
 	return r;
 }
 
 static void
-drop_config(struct if_state *state, const char *reason, const struct options *options)
+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;
-
+	if (state->new || strcmp(reason, "FAIL") == 0) {
+		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)
+static void
+reduce_timers(struct if_state *state, const struct timeval *tv)
 {
-	struct pollfd fds[3]; /* iface, arp, signal */
-	int retval, timeout, nfds = 0;
-	time_t start;
-	struct timeval now, d;
+	if (timerisset(&state->exit)) {
+		timersub(&state->exit, tv, &state->exit);
+		if (!timerisset(&state->exit))
+			state->exit.tv_sec = -1;
+	}
+	if (timerisset(&state->stop)) {
+		timersub(&state->stop, tv, &state->stop);
+		if (!timerisset(&state->stop))
+			state->stop.tv_sec = -1;
+	}
+	if (timerisset(&state->timeout)) {
+		timersub(&state->timeout, tv, &state->timeout);
+		if (!timerisset(&state->timeout))
+			state->timeout.tv_sec = -1;
+	}
+}
+
+static struct timeval *
+get_lowest_timer(struct if_state *state)
+{
+	struct timeval *ref = NULL;
+	
+	if (timerisset(&state->exit))
+		ref = &state->exit;
+	if (timerisset(&state->stop)) {
+		if (!ref || timercmp(&state->stop, ref, <))
+			ref = &state->stop;
+	}
+	if (timerisset(&state->timeout)) {
+		if (!ref || timercmp(&state->timeout, ref, <))
+			ref = &state->timeout;
+	}
+	return ref;
+}
+
+static int
+wait_for_fd(struct if_state *state, int *fd)
+{
+	struct pollfd fds[4]; /* signal, link, raw, arp */
+	struct interface *iface = state->interface;
+	int i, r, nfds = 0, msecs = -1;
+	struct timeval start, stop, diff, *ref;
+	static int lastinf = 0;
+
+	/* Ensure that we haven't already timed out */
+	ref = get_lowest_timer(state);
+	if (ref && timerneg(ref))
+		return 0;
 
 	/* We always listen to signals */
 	fds[nfds].fd = state->signal_fd;
 	fds[nfds].events = POLLIN;
 	nfds++;
+	/* And links */
+	if (iface->link_fd != -1) {
+		fds[nfds].fd = iface->link_fd;
+		fds[nfds].events = POLLIN;
+		nfds++;
+	}
 
-	if (state->lease.leasetime == ~0U && state->state == STATE_BOUND) {
-		logger(LOG_DEBUG, "waiting for infinity");
-		timeout = INFTIM;
+	if (state->lease.leasetime == ~0U &&
+	    state->state == STATE_BOUND)
+	{
+		if (!lastinf) {
+			logger(LOG_DEBUG, "waiting for infinity");
+			lastinf = 1;
+		}
+		ref = NULL;
+	} else if (state->carrier == LINK_DOWN && !ref) {
+		if (!lastinf) {
+			logger(LOG_DEBUG, "waiting for carrier");
+			lastinf = 1;
+		}
+		if (timerisset(&state->exit))
+			ref = &state->exit;
+		else
+			ref = NULL;
 	} 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;
+		if (iface->raw_fd != -1) {
+			fds[nfds].fd = iface->raw_fd;
 			fds[nfds].events = POLLIN;
 			nfds++;
 		}
-#ifdef ENABLE_ARP
-		if (state->interface->arp_fd != -1) {
-			fds[nfds].fd = state->interface->arp_fd;
+		if (iface->arp_fd != -1) {
+			fds[nfds].fd = iface->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)
+	/* Wait and then reduce the timers.
+	 * If we reduce a timer to zero, set it negative to indicate timeout.
+	 * We cannot reliably use select as there is no guarantee we will
+	 * actually wait the whole time if greater than 31 days according
+	 * to POSIX. So we loop on poll if needed as it's limitation of
+	 * INT_MAX milliseconds is known. */
+	for (;;) {
+		get_monotonic(&start);
+		if (ref) {
+			lastinf = 0;
+			if (ref->tv_sec > INT_MAX / 1000 ||
+			    (ref->tv_sec == INT_MAX / 1000 &&
+			     (ref->tv_usec + 999) / 1000 > INT_MAX % 1000))
+				msecs = INT_MAX;
+			else
+				msecs = ref->tv_sec * 1000 +
+					(ref->tv_usec + 999) / 1000;
+		} else
+			msecs = -1;
+		r = poll(fds, nfds, msecs);
+		get_monotonic(&stop);
+		timersub(&stop, &start, &diff);
+		reduce_timers(state, &diff);
+		if (r == -1) {
+			if (errno != EINTR)
+				logger(LOG_ERR, "poll: %s", strerror(errno));
+			return -1;
+		}
+		if (r)
+			break;
+		/* We should not have an infinite timeout if we get here */
+		if (timerneg(ref))
 			return 0;
-		logger(LOG_ERR, "poll: %s", strerror(errno));
 	}
-	return retval;
+
+	/* We configured our array in the order we should deal with them */
+	for (i = 0; i < nfds; i++)
+		if (fds[i].revents & POLLIN) {
+			*fd = fds[i].fd;
+			return r;
+		}
+	return r;
 }
 
 static int
@@ -730,37 +860,18 @@
 		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;
-		}
+		logger(LOG_INFO, "received SIGALRM, renewing lease");
+		do_socket(state, SOCKET_CLOSED);
+		state->state = STATE_RENEW_REQUESTED;
+		timerclear(&state->timeout);
 		timerclear(&state->stop);
-		state->timeout = 0;
-		return 0;
-
+		return 1;
 	case SIGHUP:
-		if (state->state != STATE_BOUND &&
-		    state->state != STATE_RENEWING &&
-		    state->state != STATE_REBINDING)
+		logger(LOG_INFO, "received SIGHUP, releasing lease");
+		if (lease->addr.s_addr &&
+		    !IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
 		{
-			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);
@@ -768,14 +879,13 @@
 		}
 		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;
+	return 0;
 }
 
 static int bind_dhcp(struct if_state *state, const struct options *options)
@@ -783,17 +893,19 @@
 	struct interface *iface = state->interface;
 	struct dhcp_lease *lease = &state->lease;
 	const char *reason = NULL;
-	struct timeval tv;
+	struct timeval start, stop, diff;
 	int retval;
 
 	free(state->old);
 	state->old = state->new;
 	state->new = state->offer;
 	state->offer = NULL;
-#ifdef ENABLE_ARP
+	state->messages = 0;
 	state->conflicts = 0;
 	state->defend = 0;
-#endif
+	timerclear(&state->exit);
+	if (clock_monotonic)
+		get_monotonic(&lease->boundtime);
 
 	if (options->options & DHCPCD_INFORM) {
 		if (options->request_address.s_addr != 0)
@@ -804,17 +916,18 @@
 		       inet_ntoa(lease->addr));
 		state->state = STATE_BOUND;
 		state->lease.leasetime = ~0U;
+		timerclear(&state->stop);
 		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;
+		timerclear(&state->timeout);
 		reason = "IPV4LL";
 	} else {
-		if (gettimeofday(&tv, NULL) == 0)
-			lease->leasedfrom = tv.tv_sec;
+		if (gettimeofday(&start, NULL) == 0)
+			lease->leasedfrom = start.tv_sec;
 
 		get_lease(lease, state->new);
 		if (lease->frominfo)
@@ -822,55 +935,40 @@
 
 		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;
+			timerclear(&state->stop);
 		} 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);
+				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);
+				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;
+			if (!lease->renewaltime)
+				lease->renewaltime = lease->leasetime * T1;
+			if (!lease->rebindtime)
+				lease->rebindtime = lease->leasetime * T2;
+			logger(LOG_INFO,
+			       "leased %s for %u seconds",
+			       inet_ntoa(lease->addr), lease->leasetime);
+			state->stop.tv_sec = lease->renewaltime;
+			state->stop.tv_usec = 0;
 		}
 		state->state = STATE_BOUND;
 	}
 
 	state->xid = 0;
-	timerclear(&state->stop);
+	timerclear(&state->timeout);
 	if (!reason) {
 		if (state->old) {
 			if (state->old->yiaddr == state->new->yiaddr &&
@@ -881,8 +979,18 @@
 		} else
 			reason = "BOUND";
 	}
+	/* If we have a monotonic clock we can safely substract the
+	 * script execution time from our timers.
+	 * Otherwise we can't as the script may update the real time. */
+	if (clock_monotonic)
+		get_monotonic(&start);
 	retval = configure(iface, reason, state->new, state->old,
 			   &state->lease, options, 1);
+	if (clock_monotonic) {
+		get_monotonic(&stop);
+		timersub(&stop, &start, &diff);
+		reduce_timers(state, &diff);
+	}
 	if (retval != 0)
 		return -1;
 	return daemonise(state, options);
@@ -895,18 +1003,15 @@
 	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;
+	timerclear(&state->exit);
+	if (state->state != STATE_DISCOVERING)
+		state->messages = 0;
 
 	switch (state->state) {
-	case STATE_DISCOVERING:
-		/* FALLTHROUGH */
+	case STATE_INIT:	/* FALLTHROUGH */
+	case STATE_DISCOVERING: /* FALLTHROUGH */
 	case STATE_REQUESTING:
 		if (IN_LINKLOCAL(ntohl(iface->addr.s_addr))) {
 			if (!(state->options & DHCPCD_DAEMONISED))
@@ -915,7 +1020,8 @@
 			if (iface->addr.s_addr != 0 &&
 			    !(state->options & DHCPCD_INFORM))
 				logger(LOG_ERR, "lost lease");
-			else
+			else if (state->carrier != LINK_DOWN || 
+				!(state->options & DHCPCD_DAEMONISED)) 
 				logger(LOG_ERR, "timed out");
 		}
 		do_socket(state, SOCKET_CLOSED);
@@ -923,64 +1029,81 @@
 		    state->options & DHCPCD_TEST)
 			return -1;
 
-		if (state->options & DHCPCD_IPV4LL ||
-		    state->options & DHCPCD_LASTLEASE)
+		if (state->carrier != LINK_DOWN &&
+		    (state->options & DHCPCD_IPV4LL ||
+		     state->options & DHCPCD_LASTLEASE))
 			gotlease = get_old_lease(state);
 
-#ifdef ENABLE_IPV4LL
-		if (state->options & DHCPCD_IPV4LL && gotlease != 0) {
+		if (state->carrier != LINK_DOWN &&
+		    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;
+			if (iface->addr.s_addr)
+				state->conflicts = 0;
+			return 1;
 		}
-#endif
 
 		if (gotlease == 0)
 			return bind_dhcp(state, options);
 
-		reason = "FAIL";
+		if (iface->addr.s_addr)
+			reason = "EXPIRE";
+		else
+			reason = "FAIL";
 		drop_config(state, reason, options);
 		if (!(state->options & DHCPCD_DAEMONISED) &&
 		    (state->options & DHCPCD_DAEMONISE))
 			return -1;
-		state->state = STATE_INIT;
+		state->state = STATE_RENEW_REQUESTED;
+		return 1;
+	case STATE_BOUND:
+		logger(LOG_INFO, "renewing lease of %s",inet_ntoa(lease->addr));
+		if (state->carrier != LINK_DOWN)
+			do_socket(state, SOCKET_OPEN);
+		state->xid = arc4random();
+		state->state = STATE_RENEWING;
+		state->stop.tv_sec = lease->rebindtime - lease->renewaltime;
 		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;
+		if (lease->server.s_addr == 0)
+			state->stop.tv_sec = options->timeout;
+		else
+			state->stop.tv_sec = lease->rebindtime - \
+					     lease->renewaltime;
+		lease->server.s_addr = 0;
 		break;
 	case STATE_REBINDING:
-		logger(LOG_ERR, "failed to rebind, attempting to discover");
+		logger(LOG_ERR, "failed to rebind");
 		reason = "EXPIRE";
 		drop_config(state, reason, options);
 		state->state = STATE_INIT;
 		break;
+	case STATE_PROBING:    /* FALLTHROUGH */
+	case STATE_ANNOUNCING:
+		/* We should have lost carrier here and exit timer went */
+		logger(LOG_ERR, "timed out");
+		return -1;
 	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;
+	return 1;
 }
 
 static int
@@ -988,14 +1111,32 @@
 {
 	struct dhcp_lease *lease = &state->lease;
 	struct interface *iface = state->interface;
-	int i;
-	struct timeval tv;
+	int i = 0;
 	struct in_addr addr;
+	struct timeval tv;
 
-#ifdef ENABLE_ARP
+	timerclear(&state->timeout);
+	if (timerneg(&state->exit))
+		return handle_timeout_fail(state, options);
+
+	if (state->state == STATE_RENEW_REQUESTED &&
+	    IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
+	{
+		state->state = STATE_PROBING;
+		free(state->offer);
+		state->offer = read_lease(state->interface);
+		state->probes = 0;
+		state->claims = 0;
+	}
 	switch (state->state) {
+	case STATE_INIT_IPV4LL:
+		state->state = STATE_PROBING;
+		free(state->offer);
+		state->offer = ipv4ll_get_dhcp(0);
+		state->probes = 0;
+		state->claims = 0;
+		/* FALLTHROUGH */
 	case STATE_PROBING:
-		timerclear(&state->stop);
 		if (iface->arp_fd == -1)
 			open_socket(iface, ETHERTYPE_ARP);
 		if (state->probes < PROBE_NUM) {
@@ -1006,79 +1147,130 @@
 				       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);
+			if (state->probes < PROBE_NUM) {
+				state->timeout.tv_sec = PROBE_MIN;
+				state->timeout.tv_usec = arc4random() %
+					(PROBE_MAX_U - PROBE_MIN_U);
+				timernorm(&state->timeout);
+			} else {
+				state->timeout.tv_sec = ANNOUNCE_WAIT;
+				state->timeout.tv_usec = 0;
+			}
+			logger(LOG_DEBUG,
+			       "sending ARP probe (%d of %d), next in %0.2f seconds",
+			       state->probes, PROBE_NUM,
+			       timeval_to_double(&state->timeout));
+			if (send_arp(iface, ARPOP_REQUEST, 0,
+				     state->offer->yiaddr) == -1)
+			{
+				logger(LOG_ERR, "send_arp: %s", strerror(errno));
+				return -1;
+			}
 			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;
+			if (IN_LINKLOCAL(htonl(state->offer->yiaddr))) {
+				i = bind_dhcp(state, options);
+				state->state = STATE_ANNOUNCING;
+				state->timeout.tv_sec = ANNOUNCE_INTERVAL;
+				state->timeout.tv_usec = 0;
+				return i;
+			}
+			state->state = STATE_REQUESTING;
 		}
+		break;
 	case STATE_ANNOUNCING:
-		timerclear(&state->stop);
+		if (iface->arp_fd == -1)
+			open_socket(iface, ETHERTYPE_ARP);
 		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;
+			if (state->claims < ANNOUNCE_NUM) {
+				state->timeout.tv_sec = ANNOUNCE_INTERVAL;
+				state->timeout.tv_usec = 0;
+				logger(LOG_DEBUG,
+				       "sending ARP announce (%d of %d),"
+				       " next in %0.2f seconds",
+				       state->claims, ANNOUNCE_NUM,
+				       timeval_to_double(&state->timeout));
+			} else
+				logger(LOG_DEBUG,
+				       "sending ARP announce (%d of %d)",
+				       state->claims, ANNOUNCE_NUM);
+			i = send_arp(iface, ARPOP_REQUEST,
+				     state->new->yiaddr, state->new->yiaddr);
+			if (i == -1) {
+				logger(LOG_ERR, "send_arp: %s", strerror(errno));
+				return -1;
+			}
+		}
+		if (state->claims < ANNOUNCE_NUM)
+			return 0;
+		if (IN_LINKLOCAL(htonl(state->new->yiaddr))) {
+			/* We should pretend to be at the end
+			 * of the DHCP negotation cycle */
+			state->state = STATE_INIT;
+			state->messages = DHCP_MAX / DHCP_BASE;
+			state->probes = 0;
+			state->claims = 0;
+			timerclear(&state->stop);
+			goto dhcp_timeout;
+		} else {
+			state->state = STATE_BOUND;
+			close(iface->arp_fd);
+			iface->arp_fd = -1;
+			if (lease->leasetime != ~0U) {
+				state->stop.tv_sec = lease->renewaltime;
+				state->stop.tv_usec = 0;
+				if (clock_monotonic) {
+					get_monotonic(&tv);
+					timersub(&tv, &lease->boundtime, &tv);
+					timersub(&state->stop, &tv, &state->stop);
+				} else {
+					state->stop.tv_sec -=
+						(ANNOUNCE_INTERVAL * ANNOUNCE_NUM);
+				}
+				logger(LOG_DEBUG, "renew in %ld seconds",
+				       (long int)state->stop.tv_sec);
 			}
 		}
 		return 0;
 	}
-#endif
 
-	if (timerisset(&state->stop)) {
-		get_time(&tv);
-		if (timercmp(&tv, &state->stop, >))
-			return handle_timeout_fail(state, options);
-	}
-	timerclear(&tv);
+	if (timerneg(&state->stop))
+		return handle_timeout_fail(state, options);
 
 	switch (state->state) {
-	case STATE_INIT:  /* FALLTHROUGH */
 	case STATE_BOUND: /* FALLTHROUGH */
 	case STATE_RENEW_REQUESTED:
+		timerclear(&state->stop);
+		/* FALLTHROUGH */
+	case STATE_INIT:
 		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);
+		break;
 	}
 
 	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);
+	case STATE_RENEW_REQUESTED:
+		/* If a renew was requested (ie, didn't timeout) we actually
+		 * enter the REBIND state so that we broadcast to all servers.
+		 * We need to do this for when we change networks. */
+		lease->server.s_addr = 0;
+		state->messages = 0;
+		if (lease->addr.s_addr && !(state->options & DHCPCD_INFORM)) {
+			logger(LOG_INFO, "rebinding lease of %s",
+			       inet_ntoa(lease->addr));
+			state->state = STATE_REBINDING;
+			state->stop.tv_sec = options->timeout;
+			state->stop.tv_usec = 0;
+			break;
 		}
+		/* FALLTHROUGH */
+	case STATE_INIT:
+		if (state->carrier == LINK_DOWN)
+			return 0;
 		if (lease->addr.s_addr == 0 ||
 		    IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
 		{
@@ -1093,20 +1285,33 @@
 			       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;
+		if (!lease->addr.s_addr && !timerisset(&state->stop)) {
+			state->stop.tv_sec = DHCP_MAX + DHCP_RAND_MIN;
+			state->stop.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+			timernorm(&state->stop);
 		}
-		logger(LOG_INFO, "renewing lease of %s",inet_ntoa(lease->addr));
-		state->state = STATE_RENEWING;
 		break;
 	}
 
+dhcp_timeout:
+	if (state->carrier == LINK_DOWN) {
+		timerclear(&state->timeout);
+		return 0;
+	}
+	state->timeout.tv_sec = DHCP_BASE;
+	for (i = 0; i < state->messages; i++) {
+		state->timeout.tv_sec *= 2;
+		if (state->timeout.tv_sec > DHCP_MAX) {
+			state->timeout.tv_sec = DHCP_MAX;
+			break;
+		}
+	}
+	state->timeout.tv_sec += DHCP_RAND_MIN;
+	state->timeout.tv_usec = arc4random() %
+		(DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+	timernorm(&state->timeout);
+
+	/* We send the message here so that the timeout is reported */
 	switch (state->state) {
 	case STATE_DISCOVERING:
 		send_message(state, DHCP_DISCOVER, options);
@@ -1119,111 +1324,155 @@
 		/* FALLTHROUGH */
 	case STATE_RENEWING:   /* FALLTHROUGH */
 	case STATE_REBINDING:
+		if (iface->raw_fd == -1)
+			do_socket(state, SOCKET_OPEN);
 		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 void
+log_dhcp(int lvl, const char *msg, const struct dhcp_message *dhcp)
+{
+	char *a;
+	struct in_addr addr;
+	int r;
+
+	if (strcmp(msg, "NAK:") == 0)
+		a = get_option_string(dhcp, DHO_MESSAGE);
+	else {
+		addr.s_addr = dhcp->yiaddr;
+		a = xstrdup(inet_ntoa(addr));
+	}
+	r = get_option_addr(&addr.s_addr, dhcp, DHO_SERVERID);
+	if (dhcp->servername[0] && r == 0)
+		logger(lvl, "%s %s from %s `%s'", msg, a,
+		       inet_ntoa(addr), dhcp->servername);
+	else if (r == 0)
+		logger(lvl, "%s %s from %s", msg, a, inet_ntoa(addr));
+	else
+		logger(lvl, "%s %s", msg, a);
+	free(a);
+}
+
 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;
+	uint8_t type, tmp;
+	struct in_addr addr;
+	size_t i;
 	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 have to have DHCP type to work */
+	if (get_option_uint8(&type, dhcp, DHO_MESSAGETYPE) == -1) {
+		log_dhcp(LOG_ERR, "no DHCP type in", dhcp);
+		return 0;
+	}
+
+	/* Ensure that it's not from a blacklisted server.
+	 * We should expand this to check IP and/or hardware address
+	 * at the packet level. */
+	if (options->blacklist_len != 0 &&
+	    get_option_addr(&addr.s_addr, dhcp, DHO_SERVERID) == 0)
+	{
+		for (i = 0; i < options->blacklist_len; i++) {
+			if (options->blacklist[i] != addr.s_addr)
+				continue;
+			if (dhcp->servername[0])
+				logger(LOG_WARNING,
+				       "ignoring blacklisted server %s `%s'",
+					inet_ntoa(addr), dhcp->servername);
+			else
+				logger(LOG_WARNING,
+				       "ignoring blacklisted server %s",
+				       inet_ntoa(addr));
+			return 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);
+		log_dhcp(LOG_WARNING, "NAK:", dhcp);
+		drop_config(state, "EXPIRE", options);
+		do_socket(state, SOCKET_CLOSED);
 		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;
+		if (state->nakoff == 0) {
+			state->nakoff = 1;
+			timerclear(&state->timeout);
+		} else {
+			state->timeout.tv_sec = state->nakoff;
+			state->timeout.tv_usec = 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);
+	/* Ensure that all required options are present */
+	for (i = 1; i < 255; i++) {
+		if (has_option_mask(options->requiremask, i) &&
+		    get_option_uint8(&tmp, dhcp, i) != 0)
+		{
+			log_dhcp(LOG_WARNING, "reject", dhcp);
 			return 0;
 		}
+	}
 
-		free(dhcp);
+	if (type == DHCP_OFFER && state->state == STATE_DISCOVERING) {
+		lease->addr.s_addr = dhcp->yiaddr;
+		get_option_addr(&lease->server.s_addr, dhcp, DHO_SERVERID);
+		log_dhcp(LOG_INFO, "offered", dhcp);
+		if (state->options & DHCPCD_TEST) {
+			run_script(options, iface->name, "TEST", dhcp, NULL);
+			/* Fake the fact we forked so we return 0 to userland */
+			state->options |= DHCPCD_FORKED;
+			return -1;
+		}
+		free(state->offer);
+		state->offer = dhcp;
+		*dhcpp = NULL;
+		timerclear(&state->timeout);
+		if (state->options & DHCPCD_ARP &&
+		    iface->addr.s_addr != state->offer->yiaddr)
+		{
+			/* If the interface already has the address configured
+			 * then we can't ARP for duplicate detection. */
+			addr.s_addr = state->offer->yiaddr;
+			if (!has_address(iface->name, &addr, NULL)) {
+				state->state = STATE_PROBING;
+				state->claims = 0;
+				state->probes = 0;
+				state->conflicts = 0;
+				timerclear(&state->stop);
+				return 1;
+			}
+		}
 		state->state = STATE_REQUESTING;
-		state->timeout = 0;
-		return 0;
+		return 1;
 	}
 
 	if (type == DHCP_OFFER) {
-		saddr.s_addr = dhcp->yiaddr;
-		logger(LOG_INFO, "got subsequent offer of %s, ignoring ",
-		       inet_ntoa(saddr));
-		free(dhcp);
+		log_dhcp(LOG_INFO, "ignoring offer of", 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);
+		log_dhcp(LOG_ERR, "not ACK or OFFER", dhcp);
 		return 0;
 	}
 	    
@@ -1233,35 +1482,30 @@
 	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));
+			get_option_addr(&lease->server.s_addr,
+					dhcp, DHO_SERVERID);
+			log_dhcp(LOG_INFO, "acknowledged", dhcp);
 		}
+		free(state->offer);
+		state->offer = dhcp;
+		*dhcpp = NULL;
 		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;
+	r = bind_dhcp(state, options);
+	if (!(state->options & DHCPCD_ARP)) {
+		if (!(state->options & DHCPCD_INFORM))
+			logger(LOG_DEBUG, "renew in %ld seconds",
+			       (long int)state->stop.tv_sec);
+		return r;
 	}
-#endif
-
-	return bind_dhcp(state, options);		
+	state->state = STATE_ANNOUNCING;
+	if (state->options & DHCPCD_FORKED)
+		return r;
+	return 1;
 }
 
 static int
@@ -1269,7 +1513,7 @@
 {
 	uint8_t *packet;
 	struct interface *iface = state->interface;
-	struct dhcp_message *dhcp;
+	struct dhcp_message *dhcp = NULL;
 	const uint8_t *pp;
 	uint8_t *p;
 	ssize_t bytes;
@@ -1279,7 +1523,6 @@
 	 * 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);
@@ -1296,11 +1539,14 @@
 			logger(LOG_ERR, "packet greater than DHCP size");
 			continue;
 		}
+		if (!dhcp)
+			dhcp = xmalloc(sizeof(*dhcp));
 		memcpy(dhcp, pp, bytes);
 		if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
 			logger(LOG_DEBUG, "bogus cookie, ignoring");
 			continue;
 		}
+		/* Ensure it's the right transaction */
 		if (state->xid != dhcp->xid) {
 			logger(LOG_DEBUG,
 			       "ignoring packet with xid 0x%x as"
@@ -1308,25 +1554,28 @@
 			       dhcp->xid, state->xid);
 			continue;
 		}
+		/* Ensure packet is for us */
+		if (iface->hwlen <= sizeof(dhcp->chaddr) &&
+		    memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
+		{
+			logger(LOG_DEBUG, "xid 0x%x is not for our hwaddr %s",
+			       dhcp->xid,
+			       hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr)));
+			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)
+			while (p > dhcp->options && *p == DHO_PAD)
 				p--;
-			if (*p != DHCP_END)
-				*++p = DHCP_END;
+			if (*p != DHO_END)
+				*++p = DHO_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;
+		retval = handle_dhcp(state, &dhcp, options);
+		if (retval == 0 && state->options & DHCPCD_TEST)
+			state->options |= DHCPCD_FORKED;
+		break;
 	}
 
 	free(packet);
@@ -1334,7 +1583,6 @@
 	return retval;
 }
 
-#ifdef ENABLE_ARP
 static int
 handle_arp_packet(struct if_state *state)
 {
@@ -1347,7 +1595,6 @@
 	struct interface *iface = state->interface;
 
 	state->fail.s_addr = 0;
-
 	for(;;) {
 		bytes = get_raw_packet(iface, ETHERTYPE_ARP,
 				       arp_reply, sizeof(arp_reply));
@@ -1373,6 +1620,10 @@
 		/* Ensure we got all the data */
 		if ((hw_t + reply.ar_hln + reply.ar_pln) - arp_reply > bytes)
 			continue;
+		/* Ignore messages from ourself */
+		if (reply.ar_hln == iface->hwlen &&
+		    memcmp(hw_s, iface->hwaddr, iface->hwlen) == 0)
+			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);
@@ -1380,19 +1631,13 @@
 		/* 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))))
+		     (reply_s == 0 && reply_t == state->offer->yiaddr)))
 			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))))
+		     (reply_s == 0 && reply_t == iface->addr.s_addr)))
 			state->fail.s_addr = iface->addr.s_addr;
 
 		if (state->fail.s_addr) {
@@ -1409,78 +1654,118 @@
 static int
 handle_arp_fail(struct if_state *state, const struct options *options)
 {
-	struct timespec ts;
 	time_t up;
+	int cookie = state->offer->cookie;
 
-	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;
+	if (!IN_LINKLOCAL(htonl(state->fail.s_addr))) {
+		state->state = STATE_INIT;
 		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);
+		state->offer = NULL;
+		state->lease.addr.s_addr = 0;
+		if (!cookie)
+			return 1;
+		state->timeout.tv_sec = DHCP_ARP_FAIL;
+		state->timeout.tv_usec = 0;
+		do_socket(state, SOCKET_OPEN);
+		send_message(state, DHCP_DECLINE, options);
+		do_socket(state, SOCKET_CLOSED);
 		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);
+	if (state->fail.s_addr == state->interface->addr.s_addr) {
+		if (state->state == STATE_PROBING)
+			/* This should only happen when SIGALRM or
+			 * link when down/up and we have a conflict. */
+			drop_config(state, "EXPIRE", options);
+		else {
+			up = uptime();
+			if (state->defend + DEFEND_INTERVAL > up) {
+				drop_config(state, "EXPIRE", options);
+				state->conflicts = -1;
+				/* drop through to set conflicts to 0 */
+			} else {
+				state->defend = up;
+				return 0;
+			}
+		}
+	}
+	do_socket(state, SOCKET_CLOSED);
+	state->conflicts++;
+	timerclear(&state->stop);
+	if (state->conflicts > MAX_CONFLICTS) {
+		logger(LOG_ERR, "failed to obtain an IPv4LL address");
+		state->state = STATE_INIT;
+		timerclear(&state->timeout);
+		if (!(state->options & DHCPCD_DAEMONISED) &&
+		    (state->options & DHCPCD_DAEMONISE))
+			return -1;
+		return 1;
+	}
+	state->state = STATE_INIT_IPV4LL;
+	state->timeout.tv_sec = PROBE_WAIT;
+	state->timeout.tv_usec = 0;
 	return 0;
 }
-#endif
+
+static int
+handle_link(struct if_state *state)
+{
+	int retval;
+
+	retval = link_changed(state->interface);
+	if (retval == -1) {
+		logger(LOG_ERR, "link_changed: %s", strerror(errno));
+		return -1;
+	}
+	if (retval == 0)
+		return 0;
+
+	timerclear(&state->timeout);
+	switch (carrier_status(state->interface->name)) {
+	case -1:
+		logger(LOG_ERR, "carrier_status: %s", strerror(errno));
+		return -1;
+	case 0:
+		if (state->carrier != LINK_DOWN) {
+			logger(LOG_INFO, "carrier lost");
+			state->carrier = LINK_DOWN;
+			do_socket(state, SOCKET_CLOSED);
+			if (state->state != STATE_BOUND)
+				timerclear(&state->stop);
+		}
+		break;
+	default:
+		if (state->carrier != LINK_UP) {
+			logger(LOG_INFO, "carrier acquired");
+			state->state = STATE_RENEW_REQUESTED;
+			state->carrier = LINK_UP;
+			timerclear(&state->stop);
+			return 1;
+		}
+		break;
+	}
+	return 0;
+}
 
 int
 dhcp_run(const struct options *options, int *pid_fd)
 {
 	struct interface *iface;
 	struct if_state *state = NULL;
-	int retval = -1;
-	int sig;
+	int fd = -1, r = 0, 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",
+	logger(LOG_DEBUG, "hardware address = %s",
 	       hwaddr_ntoa(iface->hwaddr, iface->hwlen));
-
 	state = xzalloc(sizeof(*state));
 	state->pid_fd = pid_fd;
 	state->interface = iface;
+	if (!(options->options & DHCPCD_TEST))
+		run_script(options, iface->name, "PREINIT", NULL, NULL);
 
 	if (client_setup(state, options) == -1)
 		goto eexit;
@@ -1488,42 +1773,51 @@
 		goto eexit;
 	if (signal_setup() == -1)
 		goto eexit;
-
 	state->signal_fd = signal_fd();
 
+	if (state->options & DHCPCD_BACKGROUND &&
+	    !(state->options & DHCPCD_DAEMONISED))
+		if (daemonise(state, options) == -1)
+			goto eexit;
+
+	if (state->carrier == LINK_DOWN)
+		logger(LOG_INFO, "waiting for carrier");
+
 	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 (r == 0)
+			r = handle_timeout(state, options);
+		else if (r > 0) {
+			if (fd == state->signal_fd) {
+			    	if ((sig = signal_read()) != -1)
+					r = handle_signal(sig, state, options);
+			} else if (fd == iface->link_fd)
+				r = handle_link(state);
+			else if (fd == iface->raw_fd)
+				r = handle_dhcp_packet(state, options);
+			else if (fd == iface->arp_fd) {
+				if ((r = handle_arp_packet(state)) == -1)
+					r = handle_arp_fail(state, options);
+			} else
+				r = 0;
 		}
-
-		if (retval != 0)
+		if (r == -1)
 			break;
+		if (r == 0) {
+			fd = -1;
+			r = wait_for_fd(state, &fd);
+			if (r == -1 && errno == EINTR) {
+				r = 1;
+				fd = state->signal_fd;
+			}
+		} else
+			r = 0;
 	}
 
 eexit:
 	if (iface) {
 		do_socket(state, SOCKET_CLOSED);
+		if (iface->link_fd != -1)
+		    close(iface->link_fd);
 		free_routes(iface->routes);
 		free(iface->clientid);
 		free(iface->buffer);
@@ -1532,7 +1826,7 @@
 
 	if (state) {
 		if (state->options & DHCPCD_FORKED)
-			retval = 0;
+			r = 0;
 		if (state->options & DHCPCD_DAEMONISED)
 			unlink(options->pidfile);
 		free(state->offer);
@@ -1541,5 +1835,5 @@
 		free(state);
 	}
 
-	return retval;
+	return r;
 }
diff --git a/common.c b/common.c
index 8bc2b78..d90c7d2 100644
--- a/common.c
+++ b/common.c
@@ -25,6 +25,11 @@
  * SUCH DAMAGE.
  */
 
+#ifdef __APPLE__
+#  include <mach/mach_time.h>
+#  include <mach/kern_return.h>
+#endif
+
 #include <sys/param.h>
 #include <sys/time.h>
 
@@ -33,7 +38,6 @@
 #ifdef BSD
 #  include <paths.h>
 #endif
-#include <poll.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -48,6 +52,8 @@
 #  define _PATH_DEVNULL "/dev/null"
 #endif
 
+int clock_monotonic = 0;
+
 /* 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
@@ -151,25 +157,6 @@
 }
 
 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;
@@ -202,41 +189,77 @@
  * Which is why we use CLOCK_MONOTONIC, but it is not available on all
  * platforms.
  */
+#define NO_MONOTONIC "host does not support a monotonic clock - timing can skew"
 int
-get_time(struct timeval *tp)
+get_monotonic(struct timeval *tp)
 {
+	static int posix_clock_set = 0;
 #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)
+	if (posix_clock_set == 0) {
+		if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
 			posix_clock = CLOCK_MONOTONIC;
-		else
-			posix_clock = CLOCK_REALTIME;
+			clock_monotonic = 1;
+		}
 		posix_clock_set = 1;
 	}
 
-	if (clock_gettime(posix_clock, &ts) == -1)
-		return -1;
+	if (clock_monotonic) {
+		if (clock_gettime(posix_clock, &ts) == 0) {
+			tp->tv_sec = ts.tv_sec;
+			tp->tv_usec = ts.tv_nsec / 1000;
+			return 0;
+		}
+	}
+#elif defined(__APPLE__)
+#define NSEC_PER_SEC 1000000000
+	/* We can use mach kernel functions here.
+	 * This is crap though - why can't they implement clock_gettime?*/
+	static struct mach_timebase_info info = { 0, 0 };
+	static double factor = 0.0;
+	uint64_t nano;
+	long rem;
 
-	tp->tv_sec = ts.tv_sec;
-	tp->tv_usec = ts.tv_nsec / 1000;
-	return 0;
-#else
-	return gettimeofday(tp, NULL);
+	if (posix_clock_set == 0) {
+		if (mach_timebase_info(&info) == KERN_SUCCESS) {
+			factor = (double)info.numer / (double)info.denom;
+			clock_monotonic = 1;	
+		}
+		posix_clock_set = 1;
+	}
+	if (clock_monotonic) {
+		nano = mach_absolute_time();
+		if ((info.denom != 1 || info.numer != 1) && factor != 0.0)
+			nano *= factor;
+		tp->tv_sec = nano / NSEC_PER_SEC;
+		rem = nano % NSEC_PER_SEC;
+		if (rem < 0) {
+			tp->tv_sec--;
+			rem += NSEC_PER_SEC;
+		}
+		tp->tv_usec = rem / 1000;
+		return 0;
+	}
 #endif
+
+	/* Something above failed, so fall back to gettimeofday */
+	if (!posix_clock_set) {
+		logger(LOG_WARNING, NO_MONOTONIC);
+		posix_clock_set = 1;
+	}
+	return gettimeofday(tp, NULL);
 }
 
 time_t
 uptime(void)
 {
-	struct timeval tp;
+	struct timeval tv;
 
-	if (get_time(&tp) == -1)
+	if (get_monotonic(&tv) == -1)
 		return -1;
-	return tp.tv_sec;
+	return tv.tv_sec;
 }
 
 int
@@ -247,7 +270,7 @@
 
 	if (ftruncate(fd, (off_t)0) == -1)
 		return -1;
-	snprintf(spid, sizeof(spid), "%u", pid);
+	snprintf(spid, sizeof(spid), "%u\n", pid);
 	len = pwrite(fd, spid, strlen(spid), (off_t)0);
 	if (len != (ssize_t)strlen(spid))
 		return -1;
diff --git a/common.h b/common.h
index 94a1cc0..2522663 100644
--- a/common.h
+++ b/common.h
@@ -63,19 +63,20 @@
 #endif
 
 #ifndef HAVE_CLOSEFROM
-#define HAVE_CLOSEFROM 1
+# if defined(__NetBSD__) || defined(__OpenBSD__)
+#  define HAVE_CLOSEFROM 1
+# endif
 #endif
-#if defined(__linux__) || defined(__FreeBSD__)
-#  undef HAVE_CLOSEFROM
+#ifndef 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 *);
+extern int clock_monotonic;
+int get_monotonic(struct timeval *);
 time_t uptime(void);
 int writepid(int, pid_t);
 void *xrealloc(void *, size_t);
diff --git a/config.h b/config.h
index 8539420..cff7079 100644
--- a/config.h
+++ b/config.h
@@ -28,34 +28,7 @@
 #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
+#define VERSION			"4.0.1"
 
 /*
  * By default we don't add a local link route if we got a routeable address.
@@ -64,7 +37,7 @@
  * 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 
+// #define IPV4LL_ALWAYSROUTE 
 
 /* Some systems do not have a working fork. */
 /* #define THERE_IS_NO_FORK */
diff --git a/configure.c b/configure.c
index 8c024f8..dad3bce 100644
--- a/configure.c
+++ b/configure.c
@@ -48,22 +48,50 @@
 
 #define DEFAULT_PATH	"PATH=/usr/bin:/usr/sbin:/bin:/sbin"
 
+
+static int
+exec_script(char *const *argv, char *const *env)
+{
+	pid_t pid;
+	sigset_t full;
+	sigset_t old;
+
+	/* OK, we need to block signals */
+	sigfillset(&full);
+	sigprocmask(SIG_SETMASK, &full, &old);
+	signal_reset();
+
+	switch (pid = vfork()) {
+	case -1:
+		logger(LOG_ERR, "vfork: %s", strerror(errno));
+		break;
+	case 0:
+		sigprocmask(SIG_SETMASK, &old, NULL);
+		execve(argv[0], argv, env);
+		logger(LOG_ERR, "%s: %s", argv[0], strerror(errno));
+		_exit(127);
+		/* NOTREACHED */
+	}
+
+	/* Restore our signals */
+	signal_setup();
+	sigprocmask(SIG_SETMASK, &old, NULL);
+	return pid;
+}
+
 int
-exec_script(const struct options *options, const char *iface,
-	    const char *reason,
-	    const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
+run_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);
+	logger(LOG_DEBUG, "executing `%s', reason %s", options->script, reason);
 
 	/* Make our env */
 	elen = 5;
@@ -115,50 +143,17 @@
 	}
 	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;
+	pid = exec_script(argv, env);
+	if (pid == -1)
+		status = -1;
+	else if (pid != 0) {
+		/* 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;
+			}
 		}
 	}
 
@@ -167,7 +162,6 @@
 	while (*ep)
 		free(*ep++);
 	free(env);
-
 	return status;
 }
 
@@ -193,14 +187,13 @@
 	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));
+	logger(LOG_DEBUG, "deleting 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)
+	if (retval != 0 && errno != ENOENT && errno != ESRCH)
 		logger(LOG_ERR," del_route: %s", strerror(errno));
 	return retval;
-
 }
 
 static int
@@ -245,22 +238,9 @@
 	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
+#ifdef IPV4LL_ALWAYSROUTE
 	if (options->options & DHCPCD_IPV4LL &&
 	    IN_PRIVATE(ntohl(dhcp->yiaddr)))
 	{
@@ -287,43 +267,6 @@
 	}
 #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);
@@ -370,37 +313,25 @@
 			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;
+	return retval;
+}
 
-#ifdef THERE_IS_NO_FORK
-	if (dhcpcd_skiproutes) {
-		if (*dhcpcd_skiproutes)
-			*skipp = '\0';
-		else {
-			free(dhcpcd_skiproutes);
-			dhcpcd_skiproutes = NULL;
-		}
-	}
-#endif
-
+static int
+delete_address(struct interface *iface)
+{
+	int retval;
+	logger(LOG_DEBUG, "deleting IP address %s/%d",
+	       inet_ntoa(iface->addr),
+	       inet_ntocidr(iface->net));
+	retval = del_address(iface->name, &iface->addr, &iface->net);
+	if (retval == -1 && errno != EADDRNOTAVAIL) 
+		logger(LOG_ERR, "del_address: %s", strerror(errno));
+	iface->addr.s_addr = 0;
+	iface->net.s_addr = 0;
 	return retval;
 }
 
@@ -419,14 +350,16 @@
 #endif
 
 	/* Grab our IP config */
-	if (dhcp == NULL || dhcp->yiaddr == 0)
+	if (dhcp == NULL)
 		up = 0;
 	else {
 		addr.s_addr = dhcp->yiaddr;
+		if (addr.s_addr == 0)
+			addr.s_addr = lease->addr.s_addr;
 		/* Ensure we have all the needed values */
-		if (get_option_addr(&net.s_addr, dhcp, DHCP_SUBNETMASK) == -1)
+		if (get_option_addr(&net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
 			net.s_addr = get_netmask(addr.s_addr);
-		if (get_option_addr(&brd.s_addr, dhcp, DHCP_BROADCAST) == -1)
+		if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1)
 			brd.s_addr = addr.s_addr | ~net.s_addr;
 	}
 
@@ -435,19 +368,10 @@
 		/* 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;
+			delete_address(iface);
 		}
 
-		exec_script(options, iface->name, reason, NULL, old);
+		run_script(options, iface->name, reason, NULL, old);
 		return 0;
 	}
 
@@ -466,8 +390,8 @@
 
 	/* 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);
+	    iface->addr.s_addr != 0)
+		delete_address(iface);
 
 #ifdef __linux__
 	/* On linux, we need to change the subnet route to have our metric. */
@@ -491,6 +415,6 @@
 		if (write_lease(iface, dhcp) == -1)
 			logger(LOG_ERR, "write_lease: %s", strerror(errno));
 
-	exec_script(options, iface->name, reason, dhcp, old);
+	run_script(options, iface->name, reason, dhcp, old);
 	return 0;
 }
diff --git a/configure.h b/configure.h
index c4c731c..fe065db 100644
--- a/configure.h
+++ b/configure.h
@@ -32,8 +32,8 @@
 #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 run_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);
diff --git a/dhcp.c b/dhcp.c
index 0dbc39e..2469429 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -137,6 +137,7 @@
 	{ 75,	IPV4 | ARRAY,	"streettalk_server" },
 	{ 76,	IPV4 | ARRAY,	"streettalk_directory_assistance_server" },
 	{ 77,	STRING,		"user_class" },
+	{ 81,	STRING | RFC3397,	"fqdn_name" },
 	{ 85,	IPV4 | ARRAY,	"nds_servers" },
 	{ 86,	STRING,		"nds_tree_name" },
 	{ 87,	STRING,		"nds_context" },
@@ -151,7 +152,7 @@
 	{ 118,	IPV4,		"subnet_selection" },
 	{ 119,	STRING | RFC3397,	"domain_search" },
 	{ 121,  RFC3442 | REQUEST,	"classless_static_routes" },
-	{ 249,  RFC3442,	"ms-classless_static_routes" },
+	{ 249,  RFC3442,	"ms_classless_static_routes" },
 	{ 0, 0, NULL }
 };
 
@@ -165,11 +166,11 @@
 			printf("%03d %s\n", opt->option, opt->var);
 }
 
-int make_reqmask(uint8_t *mask, char **opts, int add)
+int make_option_mask(uint8_t *mask, char **opts, int add)
 {
-	char *token;
-	char *p = *opts;
+	char *token, *p = *opts, *t;
 	const struct dhcp_opt *opt;
+	int match, n;
 
 	while ((token = strsep(&p, ", "))) {
 		if (*token == '\0')
@@ -177,13 +178,23 @@
 		for (opt = dhcp_opts; opt->option; opt++) {
 			if (!opt->var)
 				continue;
-			if (strcmp(opt->var, token) == 0) {
+			match = 0;
+			if (strcmp(opt->var, token) == 0)
+				match = 1;
+			else {
+				errno = 0;
+				n = strtol(token, &t, 0);
+				if (errno == 0 && !*t)
+					if (opt->option == n)
+						match = 1;
+			}
+			if (match) {	
 				if (add == 1)
-					add_reqmask(mask,
-						    opt->option);
+					add_option_mask(mask,
+							opt->option);
 				else
-					del_reqmask(mask,
-						    opt->option);
+					del_option_mask(mask,
+							opt->option);
 				break;
 			}
 		}
@@ -268,9 +279,9 @@
 			bl += ol;
 		}
 		switch (o) {
-		case DHCP_PAD:
+		case DHO_PAD:
 			continue;
-		case DHCP_END:
+		case DHO_END:
 			if (overl & 1) {
 				/* bit 1 set means parse boot file */
 				overl &= ~1;
@@ -284,7 +295,7 @@
 			} else
 				goto exit;
 			break;
-		case DHCP_OPTIONSOVERLOADED:
+		case DHO_OPTIONSOVERLOADED:
 			/* Ensure we only get this option once */
 			if (!overl)
 				overl = p[1];
@@ -295,7 +306,7 @@
 	}
 
 exit:
-	if (valid_length(o, bl, type) == -1) {
+	if (valid_length(opt, bl, type) == -1) {
 		errno = EINVAL;
 		return NULL;
 	}
@@ -372,7 +383,9 @@
 	while (q - p < pl) {
 		r = NULL;
 		hops = 0;
-		while ((l = *q++)) {
+		/* We check we are inside our length again incase
+		 * the data is NOT terminated correctly. */
+		while ((l = *q++) && q - p < pl) {
 			ltype = l & 0xc0;
 			if (ltype == 0x80 || ltype == 0x40)
 				return 0;
@@ -583,7 +596,7 @@
 char *
 get_option_string(const struct dhcp_message *dhcp, uint8_t option)
 {
-	int type;
+	int type = 0;
 	int len;
 	const uint8_t *p;
 	char *s;
@@ -654,10 +667,10 @@
 	int len;
 
 	/* If we have CSR's then we MUST use these only */
-	p = get_option(dhcp, DHCP_CSR, &len, NULL);
+	p = get_option(dhcp, DHO_CSR, &len, NULL);
 	/* Check for crappy MS option */
 	if (!p)
-		p = get_option(dhcp, DHCP_MSCSR, &len, NULL);
+		p = get_option(dhcp, DHO_MSCSR, &len, NULL);
 	if (p) {
 		routes = decode_rfc3442_rt(len, p);
 		if (routes)
@@ -665,7 +678,7 @@
 	}
 
 	/* OK, get our static routes first. */
-	p = get_option(dhcp, DHCP_STATICROUTE, &len, NULL);
+	p = get_option(dhcp, DHO_STATICROUTE, &len, NULL);
 	if (p) {
 		e = p + len;
 		while (p < e) {
@@ -684,7 +697,7 @@
 	}
 
 	/* Now grab our routers */
-	p = get_option(dhcp, DHCP_ROUTER, &len, NULL);
+	p = get_option(dhcp, DHO_ROUTER, &len, NULL);
 	if (p) {
 		e = p + len;
 		while (p < e) {
@@ -701,26 +714,51 @@
 	return routes;
 }
 
+static size_t
+encode_rfc1035(const char *src, uint8_t *dst, size_t len)
+{
+	const char *c = src;
+	uint8_t *p = dst;
+	uint8_t *lp = p++;
+
+	if (len == 0)
+		return 0;
+	while (c < src + len) {
+		if (*c == '\0')
+			break;
+		if (*c == '.') {
+			/* Skip the trailing . */
+			if (c == src + len - 1)
+				break;
+			*lp = p - lp - 1;
+			if (*lp == '\0')
+				return p - dst;
+			lp = p++;
+		} else
+			*p++ = (uint8_t) *c;
+		c++;
+	}
+	*lp = p - lp - 1;
+	*p++ = '\0';
+	return p - dst;
+}
+
 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 *m, *lp, *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;
+	p = dhcp->options;
 
 	if ((type == DHCP_INFORM ||
 	     type == DHCP_RELEASE ||
@@ -760,12 +798,12 @@
 	dhcp->xid = xid;
 	dhcp->cookie = htonl(MAGIC_COOKIE);
 
-	*p++ = DHCP_MESSAGETYPE; 
+	*p++ = DHO_MESSAGETYPE; 
 	*p++ = 1;
 	*p++ = type;
 
 	if (type == DHCP_REQUEST) {
-		*p++ = DHCP_MAXMESSAGESIZE;
+		*p++ = DHO_MAXMESSAGESIZE;
 		*p++ = 2;
 		sz = get_mtu(iface->name);
 		if (sz < MTU_MIN) {
@@ -777,27 +815,26 @@
 		p += 2;
 	}
 
-#ifndef MINIMAL
 	if (iface->clientid) {
-		*p++ = DHCP_CLIENTID;
+		*p++ = DHO_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;
+			*p++ = DHO_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;
+		if (options->vendorclassid[0]) {
+			*p++ = DHO_VENDORCLASSID;
+			memcpy(p, options->vendorclassid,
+			       options->vendorclassid[0] + 1);
+			p += options->vendorclassid[0] + 1;
 		}
 	}
-#endif
 
 	if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
 #define PUTADDR(_type, _val) \
@@ -811,14 +848,14 @@
 		    lease->addr.s_addr != iface->addr.s_addr &&
 		    !IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
 		{
-			PUTADDR(DHCP_IPADDRESS, lease->addr);
+			PUTADDR(DHO_IPADDRESS, lease->addr);
 			if (lease->server.s_addr)
-				PUTADDR(DHCP_SERVERID, lease->server);
+				PUTADDR(DHO_SERVERID, lease->server);
 		}
 #undef PUTADDR
 
 		if (options->leasetime != 0) {
-			*p++ = DHCP_LEASETIME;
+			*p++ = DHO_LEASETIME;
 			*p++ = 4;
 			ul = htonl(options->leasetime);
 			memcpy(p, &ul, 4);
@@ -830,61 +867,52 @@
 	    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;
-			}
+			*p++ = DHO_HOSTNAME;
+			memcpy(p, options->hostname, options->hostname[0] + 1);
+			p += options->hostname[0] + 1;
+		}
+		if (options->fqdn != FQDN_DISABLE) {
+			/* IETF DHC-FQDN option (81), RFC4702 */
+			*p++ = DHO_FQDN;
+			lp = p;
+			*p++ = 3;
+			/*
+			 * 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 & 0x09) | 0x04;
+			*p++ = 0; /* from server for PTR RR */
+			*p++ = 0; /* from server for A RR if S=1 */
+			ul = encode_rfc1035(options->hostname + 1, p,
+					options->hostname[0]);
+			*lp += ul;
+			p += ul;
 		}
 
 		/* vendor is already encoded correctly, so just add it */
 		if (options->vendor[0]) {
-			*p++ = DHCP_VENDOR;
+			*p++ = DHO_VENDOR;
 			memcpy(p, options->vendor, options->vendor[0] + 1);
 			p += options->vendor[0] + 1;
 		}
-#endif
 
-		*p++ = DHCP_PARAMETERREQUESTLIST;
+		*p++ = DHO_PARAMETERREQUESTLIST;
 		n_params = p;
 		*p++ = 0;
 		for (opt = dhcp_opts; opt->option; opt++) {
 			if (!(opt->type & REQUEST || 
-			      has_reqmask(options->reqmask, opt->option)))
+			      has_option_mask(options->requestmask, opt->option)))
 				continue;
 			switch (opt->option) {
-			case DHCP_RENEWALTIME:	/* FALLTHROUGH */
-			case DHCP_REBINDTIME:
+			case DHO_RENEWALTIME:	/* FALLTHROUGH */
+			case DHO_REBINDTIME:
 				if (type == DHCP_INFORM)
 					continue;
 				break;
@@ -893,14 +921,14 @@
 		}
 		*n_params = p - n_params - 1;
 	}
-	*p++ = DHCP_END;
+	*p++ = DHO_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;
+		*p++ = DHO_PAD;
 #endif
 
 	*message = dhcp;
@@ -931,12 +959,12 @@
 	/* Only write as much as we need */
 	while (p < e) {
 		o = *p;
-		if (o == DHCP_END) {
+		if (o == DHO_END) {
 			bytes = p - (const uint8_t *)dhcp;
 			break;
 		}
 		p++;
-		if (o != DHCP_PAD) {
+		if (o != DHO_PAD) {
 			l = *p++;
 			p += l;
 		}
@@ -1149,13 +1177,13 @@
 	char cidr[4];
 	uint8_t overl = 0;
 
-	get_option_uint8(&overl, dhcp, DHCP_OPTIONSOVERLOADED);
+	get_option_uint8(&overl, dhcp, DHO_OPTIONSOVERLOADED);
 
 	if (!env) {
 		for (opt = dhcp_opts; opt->option; opt++) {
 			if (!opt->var)
 				continue;
-			if (has_reqmask(options->nomask, opt->option))
+			if (has_option_mask(options->nomask, opt->option))
 				continue;
 			if (get_option_raw(dhcp, opt->option))
 				e++;
@@ -1175,14 +1203,14 @@
 		 * 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) {
+		if (get_option_addr(&net.s_addr, dhcp, DHO_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) {
+		if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1) {
 			brd.s_addr = addr.s_addr | ~net.s_addr;
 			setvar(&ep, prefix, "broadcast_address", inet_ntoa(net));
 		}
@@ -1198,12 +1226,17 @@
 	for (opt = dhcp_opts; opt->option; opt++) {
 		if (!opt->var)
 			continue;
-		if (has_reqmask(options->nomask, opt->option))
+		if (has_option_mask(options->nomask, opt->option))
 			continue;
 		val = NULL;
 		p = get_option(dhcp, opt->option, &pl, NULL);
 		if (!p)
 			continue;
+		/* We only want the FQDN name */
+		if (opt->option == DHO_FQDN) {
+			p += 3;
+			pl -= 3;
+		}
 		len = print_option(NULL, 0, opt->type, pl, p);
 		if (len < 0)
 			return -1;
diff --git a/dhcp.h b/dhcp.h
index 8b88c5e..e584452 100644
--- a/dhcp.h
+++ b/dhcp.h
@@ -62,60 +62,52 @@
 #define DHCP_INFORM         8
 
 /* DHCP options */
-enum DHCP_OPTIONS
+enum DHO
 {
-	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
+	DHO_PAD                    = 0,
+	DHO_SUBNETMASK             = 1,
+	DHO_ROUTER                 = 3,
+	DHO_DNSSERVER              = 6,
+	DHO_HOSTNAME               = 12,
+	DHO_DNSDOMAIN              = 15,
+	DHO_MTU                    = 26,
+	DHO_BROADCAST              = 28,
+	DHO_STATICROUTE            = 33,
+	DHO_NISDOMAIN              = 40,
+	DHO_NISSERVER              = 41,
+	DHO_NTPSERVER              = 42,
+	DHO_VENDOR                 = 43,
+	DHO_IPADDRESS              = 50,
+	DHO_LEASETIME              = 51,
+	DHO_OPTIONSOVERLOADED      = 52,
+	DHO_MESSAGETYPE            = 53,
+	DHO_SERVERID               = 54,
+	DHO_PARAMETERREQUESTLIST   = 55,
+	DHO_MESSAGE                = 56,
+	DHO_MAXMESSAGESIZE         = 57,
+	DHO_RENEWALTIME            = 58,
+	DHO_REBINDTIME             = 59,
+	DHO_VENDORCLASSID          = 60,
+	DHO_CLIENTID               = 61,
+	DHO_USERCLASS              = 77,  /* RFC 3004 */
+	DHO_FQDN                   = 81,
+	DHO_DNSSEARCH              = 119, /* RFC 3397 */
+	DHO_CSR                    = 121, /* RFC 3442 */
+	DHO_MSCSR                  = 249, /* MS code for RFC 3442 */
+	DHO_END                    = 255
 };
 
-/* SetFQDNHostName values - lsnybble used in flags
- * byte (see buildmsg.c), hsnybble to create order
+/* FQDN values - lsnybble used in flags
+ * hsnybble to create order
  * and to allow 0x00 to mean disable
  */
-enum FQQN {
+enum FQDN {
 	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
@@ -157,14 +149,15 @@
 	uint32_t renewaltime;
 	uint32_t rebindtime;
 	struct in_addr server;
-	uint32_t leasedfrom;
+	time_t leasedfrom;
+	struct timeval boundtime;
 	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);
+#define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
+#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
+#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
+int make_option_mask(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);
diff --git a/dhcpcd-hooks/01-test b/dhcpcd-hooks/01-test
index d3ca40d..609b3a1 100644
--- a/dhcpcd-hooks/01-test
+++ b/dhcpcd-hooks/01-test
@@ -3,5 +3,5 @@
 case ${reason} in
 TEST)   set | grep "^\(interface\|metric\|pid\|reason\|skip_hooks\)=" | sort
 	set | grep "^\(new_\|old_\)" | sort
-        ;;
+	;;
 esac
diff --git a/dhcpcd-hooks/20-resolv.conf b/dhcpcd-hooks/20-resolv.conf
index 437c116..e757ddf 100644
--- a/dhcpcd-hooks/20-resolv.conf
+++ b/dhcpcd-hooks/20-resolv.conf
@@ -1,14 +1,71 @@
 # Generate /etc/resolv.conf
 # Support resolvconf(8) if available
+# We can merge other dhcpcd resolv.conf files into one like resolvconf,
+# but resolvconf is preferred as other applications like VPN clients
+# can readily hook into it.
+# Also, resolvconf can configure local nameservers such as bind
+# or dnsmasq. This is important as the libc resolver isn't that powerful.
 
-make_resolv_conf()
+resolv_conf_dir="${state_dir}/resolv.conf"
+
+build_resolv_conf()
 {
+	local cf="/etc/resolv.conf.${interface}"
+	local interfaces= header= search= srvs= servers= x=
+
+	# Build a list of interfaces
+	interfaces=$(list_interfaces "${resolv_conf_dir}")
+
+	# Build the resolv.conf
+	if [ -n "${interfaces}" ]; then
+		# Build the header
+		for x in ${interfaces}; do
+			header="${header}${header:+, }${x}"
+		done
+
+		# Build the search list
+		search=$(cd "${resolv_conf_dir}"; \
+			key_get_value "search " ${interfaces})
+		[ -n "${search}" ] && search="search $(uniqify ${search})\n"
+
+		# Build the nameserver list
+		srvs=$(cd "${resolv_conf_dir}"; \
+			key_get_value "nameserver " ${interfaces})
+		for x in $(uniqify ${srvs}); do
+			servers="${servers}nameserver ${x}\n"
+		done
+	fi
+	header="${signature_base}${header:+ ${from} }${header}"
+
+	# Assemble resolv.conf using our head and tail files
+	[ -f "${cf}" ] && rm -f "${cf}"
+	echo "${header}" > "${cf}"
+	if [ -f /etc/resolv.conf.head ]; then
+		cat /etc/resolv.conf.head >> "${cf}"
+	else
+		echo "# /etc/resolv.conf.head can replace this line" >> "${cf}"
+	fi
+	printf "${search}${servers}" >> "${cf}"
+	if [ -f /etc/resolv.conf.tail ]; then
+		cat /etc/resolv.conf.tail >> "${cf}"
+	else
+		echo "# /etc/resolv.conf.tail can replace this line" >> "${cf}"
+	fi
+	mv -f "${cf}" /etc/resolv.conf
+}
+
+add_resolv_conf()
+{
+	local x= conf="${signature}\n"
+
+	# If we don't have any configuration, remove it
 	if [ -z "${new_domain_name_servers}" -a \
 		-z "${new_domain_name}" -a \
 		-z "${new_domain_search}" ]; then
-		return 0
+		remove_resolv_conf
+		return $?
 	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
@@ -19,22 +76,32 @@
 	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
+		return $?
 	fi
+
+	if [ -e "${resolv_conf_dir}/${interface}" ]; then
+		rm -f "${resolv_conf_dir}/${interface}"
+	fi
+	if [ ! -d "${resolv_conf_dir}" ]; then
+		mkdir -p "${resolv_conf_dir}"
+	fi
+	printf "${conf}" > "${resolv_conf_dir}/${interface}"
+	build_resolv_conf
 }
 
-restore_resolv_conf()
+remove_resolv_conf()
 {
 	if type resolvconf >/dev/null 2>&1; then
 		resolvconf -d "${interface}" -f
 	else
-		restore_conf /etc/resolv.conf || return 0
+		if [ -e "${resolv_conf_dir}/${interface}" ]; then
+			rm -f "${resolv_conf_dir}/${interface}"
+		fi
+		build_resolv_conf
 	fi
 }
 
 case "${reason}" in
-BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)	make_resolv_conf;;
-EXPIRE|FAIL|IPV4LL|RELEASE|STOP)		restore_resolv_conf;;
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)	add_resolv_conf;;
+PREINIT|EXPIRE|FAIL|IPV4LL|RELEASE|STOP)	remove_resolv_conf;;
 esac
diff --git a/dhcpcd-hooks/29-lookup-hostname b/dhcpcd-hooks/29-lookup-hostname
index b9ce458..3dfade3 100644
--- a/dhcpcd-hooks/29-lookup-hostname
+++ b/dhcpcd-hooks/29-lookup-hostname
@@ -2,6 +2,7 @@
 
 lookup_hostname()
 {
+	[ -z "${new_ip_address}" ] && return 1
 	local h=
 	# Silly ISC programs love to send error text to stdout
 	if type dig >/dev/null 2>&1; then
@@ -23,7 +24,7 @@
 
 set_hostname()
 {
-	if [ -z "${new_host_name}" ]; then
+	if [ -z "${new_host_name}" -a -z "${new_fqdn_name}" ]; then
 		export new_host_name="$(lookup_hostname)"
 	fi
 }
diff --git a/dhcpcd-hooks/30-hostname b/dhcpcd-hooks/30-hostname
index 500ec0f..b2e5fc8 100644
--- a/dhcpcd-hooks/30-hostname
+++ b/dhcpcd-hooks/30-hostname
@@ -3,16 +3,23 @@
 need_hostname()
 {
 	case "$(hostname)" in
-		""|"(none)"|localhost)	[ -n "${new_host_name}" ];;
-		"${old_host_name}") 	true;;
-		*) 			false;;
+	""|"(none)"|localhost|localhost.localdomain)
+		[ -n "${new_host_name}" -o -n "${new_fqdn_name}" ];;
+	"${old_host_name}"|"${old_fqdn_name}")
+		true;;
+	*)
+		false;;
 	esac
 }
 
 set_hostname()
 {
 	if need_hostname; then
-		hostname "${new_host_name}"
+		if [ -n "${new_host_name}" ]; then
+			hostname "${new_host_name}"
+		else
+			hostname "${new_fqdn_name}"
+		fi
 	fi
 }
 
diff --git a/dhcpcd-hooks/50-ntp.conf b/dhcpcd-hooks/50-ntp.conf
index 3772215..536f14e 100644
--- a/dhcpcd-hooks/50-ntp.conf
+++ b/dhcpcd-hooks/50-ntp.conf
@@ -1,4 +1,6 @@
 # Sample dhcpcd hook script for ntp
+# Like our resolv.conf hook script, we store a database of ntp.conf files
+# and merge into /etc/ntp.conf
 
 # Detect OpenRC or BSD rc
 # Distributions may want to just have their command here instead of this
@@ -10,42 +12,71 @@
 	ntpd_restart_cmd="/usr/local/etc/rc.d/ntpd restart"
 fi
 
-make_ntp_conf()
+ntp_conf_dir="${state_dir}/ntp.conf"
+
+build_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
+	local cf="/etc/ntp.conf.${interface}"
+	local interfaces= header= srvs= servers= x=
+
+	# Build a list of interfaces
+	interfaces=$(list_interfaces "${ntp_conf_dir}")
+
+	if [ -n "${interfaces}" ]; then
+		# Build the header
+		for x in ${interfaces}; do
+			header="${header}${header:+, }${x}"
+		done
+
+		# Build a server list
+		srvs=$(cd "${ntp_conf_dir}";
+			key_get_value "server " ${interfaces})
+		if [ -n "${srvs}" ]; then
+			for x in $(uniqify ${srvs}); do
+				servers="${servers}server ${x}\n"
+			done
+		fi
 	fi
-	if [ $? = 0 ]; then
-		rm -f "${cf}"
-	else
-		save_conf /etc/ntp.conf
-		mv -f "${cf}" /etc/ntp.conf
+
+	# Merge our config into ntp.conf
+	[ -e "${cf}" ] && rm -f "${cf}"
+	remove_markers "${signature_base}" "${signature_base_end}" \
+		/etc/ntp.conf > "${cf}"
+	if [ -n "${servers}" ]; then
+		echo "${signature_base}${header:+ ${from} }${header}" >> "${cf}"
+		printf "${search}${servers}" >> "${cf}"
+		echo "${signature_base_end}${header:+ ${from} }${header}" >> "${cf}"
+	fi
+
+	# If we changed anything, restart ntpd
+	if change_file /etc/ntp.conf "${cf}"; then
 		[ -n "${ntpd_restart_cmd}" ] && ${ntpd_restart_cmd}
 	fi
 }
 
-restore_ntp_conf()
+add_ntp_conf()
 {
-	restore_conf /etc/ntp.conf || return 0
-	[ -n "${ntpd_restart_cmd}" ] && ${ntpd_restart_cmd}
+	local cf="${ntp_conf_dir}/${interface}" x=
+
+	[ -e "${cf}" ] && rm "${cf}"
+	[ -d "${ntp_conf_dir}" ] || mkdir -p "${ntp_conf_dir}"
+	if [ -n "${new_ntp_servers}" ]; then
+		for x in ${new_ntp_servers}; do
+			echo "server ${x}" >> "${cf}"
+		done
+	fi
+	build_ntp_conf
+}
+
+remove_ntp_conf()
+{
+	if [ -e "${ntp_conf_dir}/${interface}" ]; then
+		rm "${ntp_conf_dir}/${interface}"
+	fi
+	build_ntp_conf
 }
 
 case "${reason}" in
-BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT)	make_ntp_conf;;
-EXPIRE|FAIL|IPV4LL|RELEASE|STOP)		restore_ntp_conf;;
+BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) 	add_ntp_conf add;;
+PREINIT|EXPIRE|FAIL|IPV4LL|RELEASE|STOP)	remove_ntp_conf del;;
 esac
diff --git a/dhcpcd-hooks/50-yp.conf b/dhcpcd-hooks/50-yp.conf
index 603f267..a2296eb 100644
--- a/dhcpcd-hooks/50-yp.conf
+++ b/dhcpcd-hooks/50-yp.conf
@@ -10,6 +10,7 @@
 {
 	[ -z "${new_nis_domain}" -a -z "${new_nis_servers}" ] && return 0
 	local cf=/etc/yp.conf."${interface}" prefix= x= pid=
+	rm -f "${cf}"
 	echo "${signature}" > "${cf}"
 	if [ -n "${new_nis_domain}" ]; then
 		domainname "${new_nis_domain}"
diff --git a/dhcpcd-hooks/90-NetworkManager b/dhcpcd-hooks/90-NetworkManager
index f05ccd7..c4d69fe 100644
--- a/dhcpcd-hooks/90-NetworkManager
+++ b/dhcpcd-hooks/90-NetworkManager
@@ -1,7 +1,8 @@
-# Hook for NetworkManager, relies on D-Bus
+# Hook for NetworkManager-0.7.0
+# NOTE: NetworkManager will override the script dhcpcd calls, so this hook
+# only makes sense if NetworkManager is patched NOT to override the
+# script dhcpcd would call.
 
-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`"
+if [ -x /usr/libexec/nm-dhcp-client.action ]; then
+	/usr/libexec/nm-dhcp-client.action
 fi
diff --git a/dhcpcd-hooks/95-configured b/dhcpcd-hooks/95-configured
index 1ff07cf..93f1c43 100644
--- a/dhcpcd-hooks/95-configured
+++ b/dhcpcd-hooks/95-configured
@@ -16,7 +16,11 @@
     setprop dhcp.${interface}.result "ok"
     ;;
 
-EXPIRE|FAIL|IPV4LL|RELEASE|STOP)
+EXPIRE|FAIL|IPV4LL|STOP)
     setprop dhcp.${interface}.result "failed"
     ;;
+
+RELEASE)
+    setprop dhcp.${interface}.result "released"
+    ;;
 esac
diff --git a/dhcpcd-hooks/Makefile b/dhcpcd-hooks/Makefile
index 06660ac..cfb19f7 100644
--- a/dhcpcd-hooks/Makefile
+++ b/dhcpcd-hooks/Makefile
@@ -1,9 +1,11 @@
-LIBEXECDIR=	${PREFIX}/libexec
+LIBEXECDIR?=	${PREFIX}/libexec
 HOOKDIR=	${LIBEXECDIR}/dhcpcd-hooks
 SYSTEMSCRIPTS=	01-test 10-mtu 20-resolv.conf 30-hostname
 FILES=		${SYSTEMSCRIPTS} ${HOOKSCRIPTS}
 FILESDIR=	${HOOKDIR}
 
+all:
+
 MK=		../mk
 include ${MK}/os.mk
 include ${MK}/sys.mk
diff --git a/dhcpcd-run-hooks b/dhcpcd-run-hooks
index 83534be..db9c4f8 100755
--- a/dhcpcd-run-hooks
+++ b/dhcpcd-run-hooks
@@ -1,29 +1,22 @@
 #!/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"
-}
+# Handy variables and functions for our hooks to use
+from="from"
+signature_base="# Generated by dhcpcd"
+signature="${signature_base} ${from} ${interface}"
+signature_base_end="# End of dhcpcd"
+signature_end="${signature_base_end} ${from} ${interface}"
+state_dir="/data/misc/dhcpcd"
 
 # 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
+# Thus, the user can create their dhcpcd.enter/exit-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/*
+	/system/etc/dhcpcd/dhcpcd.enter-hook \
+	/system/etc/dhcpcd/dhcpcd-hooks/* \
+	/system/etc/dhcpcd/dhcpcd.exit-hook
 do
 	for skip in ${skip_hooks}; do
 		case "${hook}" in
diff --git a/dhcpcd-run-hooks.8 b/dhcpcd-run-hooks.8
index 2ba9792..dca5378 100644
--- a/dhcpcd-run-hooks.8
+++ b/dhcpcd-run-hooks.8
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 21, 2008
+.Dd August 14, 2008
 .Dt DHCPCD.SH 8 SMM
 .Sh NAME
 .Nm dhcpcd-run-hooks
@@ -31,11 +31,13 @@
 .Nm
 is used by
 .Xr dhcpcd 8
-to run any system or user defined hook scripts.
+to run any system and 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 .
+and the user defined hooks are 
+.Pa /system/etc/dhcpcd/dhcpcd.enter-hook .
+and
+.Pa /system/etc/dhcpcd/dhcpcd.exit-hook .
 The default install supplies hook scripts for configuring
 .Pa /etc/resolv.conf
 and the hostname.
@@ -67,6 +69,11 @@
 .Nm
 could be invoked:
 .Bl -tag -width indent
+.It Dv PREINIT
+dhcpcd is starting up and any pre-initialisation should be done.
+.It Dv INFORM
+dhcpcd informed a DHCP server about it's address and obtained other
+configuration details.
 .It Dv BOUND
 dhcpcd obtained a new lease from a DHCP server.
 .It Dv RENEW
@@ -93,10 +100,11 @@
 When
 .Nm
 runs, it loads
-.Pa /system/etc/dhcpcd/dhcpcd.hook
+.Pa /system/etc/dhcpcd/dhcpcd.enter-hook
 and any scripts found in
 .Pa /system/etc/dhcpcd/dhcpcd-hooks
-in a lexical order.
+in a lexical order and then finally
+.Pa /system/etc/dhcpcd/dhcpcd.exit-hook
 .Sh SEE ALSO
 .Xr dhcpcd 8
 .Sh AUTHORS
diff --git a/dhcpcd-run-hooks.8.in b/dhcpcd-run-hooks.8.in
index a545203..72669f5 100644
--- a/dhcpcd-run-hooks.8.in
+++ b/dhcpcd-run-hooks.8.in
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 21, 2008
+.Dd August 14, 2008
 .Dt DHCPCD.SH 8 SMM
 .Sh NAME
 .Nm dhcpcd-run-hooks
@@ -31,11 +31,13 @@
 .Nm
 is used by
 .Xr dhcpcd 8
-to run any system or user defined hook scripts.
+to run any system and user defined hook scripts.
 System hook scripts are found in
 .Pa @HOOKDIR@
-and the user defined hook is
-.Pa @SYSCONFDIR@/dhcpcd.hook .
+and the user defined hooks are 
+.Pa @SYSCONFDIR@/dhcpcd.enter-hook .
+and
+.Pa @SYSCONFDIR@/dhcpcd.exit-hook .
 The default install supplies hook scripts for configuring
 .Pa /etc/resolv.conf
 and the hostname.
@@ -67,6 +69,11 @@
 .Nm
 could be invoked:
 .Bl -tag -width indent
+.It Dv PREINIT
+dhcpcd is starting up and any pre-initialisation should be done.
+.It Dv INFORM
+dhcpcd informed a DHCP server about it's address and obtained other
+configuration details.
 .It Dv BOUND
 dhcpcd obtained a new lease from a DHCP server.
 .It Dv RENEW
@@ -93,10 +100,11 @@
 When
 .Nm
 runs, it loads
-.Pa @SYSCONFDIR@/dhcpcd.hook
+.Pa @SYSCONFDIR@/dhcpcd.enter-hook
 and any scripts found in
 .Pa @HOOKDIR@
-in a lexical order.
+in a lexical order and then finally
+.Pa @SYSCONFDIR@/dhcpcd.exit-hook
 .Sh SEE ALSO
 .Xr dhcpcd 8
 .Sh AUTHORS
diff --git a/dhcpcd-run-hooks.in b/dhcpcd-run-hooks.in
index 7fd8b09..1e5d5b3 100644
--- a/dhcpcd-run-hooks.in
+++ b/dhcpcd-run-hooks.in
@@ -1,8 +1,104 @@
 #!/bin/sh
 # dhcpcd client configuration script 
 
-# Handy functions for our hooks to use
-signature="# Generated by dhcpcd for ${interface}"
+# Handy variables and functions for our hooks to use
+from="from"
+signature_base="# Generated by dhcpcd"
+signature="${signature_base} ${from} ${interface}"
+signature_base_end="# End of dhcpcd"
+signature_end="${signature_base_end} ${from} ${interface}"
+state_dir="/var/run/dhcpcd"
+
+# Ensure that all arguments are unique
+uniqify()
+{
+	local result=
+
+	while [ -n "$1" ]; do
+		case " ${result} " in
+		*" $1 "*);;
+		*) result="${result}${result:+ }$1";;
+		esac
+		shift
+	done
+	echo "${result}"
+}
+
+# List interface config files in a dir
+# We may wish to control the order at some point rather than just lexical
+list_interfaces()
+{
+	local x= interfaces=
+	for x in "$1"/*; do
+		[ -e "${x}" ] || continue
+		interfaces="${interfaces}${interfaces:+ }${x##*/}"
+	done
+	echo "${interfaces}"
+}
+
+# We normally use sed to extract values using a key from a list of files
+# but sed may not always be available at the time.
+key_get_value()
+{
+	local key="$1" value= x= line=
+
+	shift
+	if type sed >/dev/null 2>&1; then
+		sed -n "s/^${key}//p" $@
+	else
+		for x; do
+			while read line; do
+				case "${line}" in
+				"${key}"*) echo "${line##${key}}";;
+				esac
+			done < "${x}"
+		done
+	fi
+}
+
+# We normally use sed to remove markers from a configuration file
+# but sed may not always be available at the time.
+remove_markers()
+{
+	local m1="$1" m2="$2" x= line= in_marker=0
+
+	shift; shift
+	if type sed >/dev/null 2>&1; then
+		sed "/^${m1}/,/^${m2}/d" $@
+	else
+		for x; do
+			while read line; do
+				case "${line}" in
+				"${m1}"*) in_marker=1;;
+				"${m2}"*) in_marker=0;;
+				*) [ ${in_marker} = 0 ] && echo "${line}";;
+				esac
+			done < "${x}"
+		done
+	fi
+}
+
+# Compare two files
+# It different, replace first with second otherwise remove second
+change_file()
+{
+	if type cmp >/dev/null 2>&1; then
+		cmp -s "$1" "$2"
+	elif type diff >/dev/null 2>&1; then
+		diff -q "$1" "$2" >/dev/null
+	else
+		# Hopefully we're only working on small text files ...
+		[ "$(cat "$1")" = "$(cat "$2")" ]
+	fi
+	if [ $? -eq 0 ]; then
+		rm -f "$2"
+		return 1
+	fi
+	mv -f "$2" "$1"
+	return 0
+}
+
+# Save a config file
 save_conf()
 {
 	if [ -f "$1" ]; then
@@ -10,6 +106,8 @@
 		mv -f "$1" "$1"-pre."${interface}"
 	fi
 }
+
+# Restore a config file
 restore_conf()
 {
 	[ -f "$1"-pre."${interface}" ] || return 1
@@ -17,13 +115,15 @@
 	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
+# Thus, the user can create their dhcpcd.enter/exit-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@/*
+	@SYSCONFDIR@/dhcpcd.enter-hook \
+	@HOOKDIR@/* \
+	@SYSCONFDIR@/dhcpcd.exit-hook
 do
 	for skip in ${skip_hooks}; do
 		case "${hook}" in
diff --git a/dhcpcd.8 b/dhcpcd.8
index fff2ca0..ac6150c 100644
--- a/dhcpcd.8
+++ b/dhcpcd.8
@@ -22,18 +22,18 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd Jul 08, 2008
+.Dd August 20, 2008
 .Dt DHCPCD 8 SMM
 .Sh NAME
 .Nm dhcpcd
 .Nd an RFC 2131 compliant DHCP client
 .Sh SYNOPSIS
 .Nm
-.Op Fl dknpqADEGLSTXV
+.Op Fl bdknpqABDEGKLSTV
 .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 i , -vendorclassid Ar vendorclassid
 .Op Fl l , -leasetime Ar seconds
 .Op Fl m , -metric Ar metric
 .Op Fl o , -option Ar option
@@ -46,6 +46,8 @@
 .Op Fl F , -fqdn Ar FQDN
 .Op Fl I , -clientid Ar clientid
 .Op Fl O , -nooption Ar option
+.Op Fl Q , -require Ar option
+.Op Fl X , -blacklist Ar address
 .Ar interface
 .Nm
 .Fl k , -release
@@ -56,9 +58,7 @@
 .Sh DESCRIPTION
 .Nm
 is an implementation of the DHCP client specified in
-.Rs
-.%T "RFC 2131"
-.Re
+.Li RFC 2131 .
 .Nm
 gets the host information
 .Po 
@@ -69,40 +69,42 @@
 of the
 machine on which it is running. 
 .Nm
-will then write DNS information to
+then runs the configuration script which writes 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.
+sets 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
+failed to obtain a lease, it probes for a valid IPv4LL address
 .Po
-aka Zeroconf, aka APIPA
+aka ZeroConf, aka APIPA
 .Pc .
-Once obtained it will restart the process of looking for a DHCP server to get a
+Once obtained it restarts 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
+nearly always succeeds and returns an exit code of 0.
+In the rare case it fails, it normally means that there is a reverse ARP proxy
+installed which always defeats IPv4LL probing.
+To disable this behaviour, you can use the
 .Fl L , -noipv4ll
 option.
 .Ss Hooking into DHCP events
 .Nm
-will run
+runs
 .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
+This script runs each script found in
 .Pa /system/etc/dhcpcd/dhcpcd-hooks
 in a lexical order.
 The default installation supplies the scripts
@@ -124,6 +126,10 @@
 .Nm
 with the following options:
 .Bl -tag -width indent
+.It Fl b , -background
+Background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
 .It Fl c , -script Ar script
 Use this
 .Ar script
@@ -142,16 +148,16 @@
 .It Fl h , -hostname Ar hostname
 By default,
 .Nm
-will send the current hostname to the DHCP server so it can register in DNS.
+sends 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
+.It Fl i , -vendorclassid Ar vendorclassid
 Override the
-.Ar classid
+.Ar vendorclassid
 field sent. The default is
 dhcpcd <version>.
 If not set then none is sent.
@@ -163,6 +169,8 @@
 to release its lease, deconfigure the
 .Ar interface
 and then exit.
+.Nm
+then waits until this process has exited.
 .It Fl l , -leasetime Ar seconds
 Request a specific lease time in
 .Ar seconds .
@@ -184,14 +192,27 @@
 .Ar option
 variable for use in
 .Pa /system/etc/dhcpcd/dhcpcd-run-hooks .
-.It Fl n , -renew
+.It Fl n , -rebind
 Notifies an existing
 .Nm
 process running on the
 .Ar interface
-to renew it's lease. If
+to rebind it's lease.
+.Nm
+will not re-configure itself or use any other command line arguments.
+.Nm
+will timeout the rebind after 30 seconds at which point the lease will be
+expired and
+.Nm
+will enter the discovery state to obtain a new lease.
+Use the
+.Fl t , -timeout
+option to change this.
+If
 .Nm
 is not running, then it starts up as normal.
+This option used to be renew, but rebind is more accurate as we need to
+broadcast the request instead of unicasting.
 .It Fl p , -persistent
 .Nm
 normally deconfigures the
@@ -201,28 +222,31 @@
 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.
+normally sends a DHCP DISCOVER 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
+then requests the address used.
+You can use this option to skip the BROADCAST step and just request the
 .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.
+In this situation, we go back to the init state and DISCOVER 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
+Behaves 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.
+You should also include the optional
+.Ar cidr
+network number in-case the address is not already configured on the interface.
 .Nm
 remains running and pretends it has an infinite lease.
 .Nm
@@ -243,7 +267,7 @@
 .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
+DHCP servers use this give members 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.
@@ -252,30 +276,31 @@
 Examples.
 .Pp
 Set the vendor option 01 with an IP address.
-.D1 dhcpcd -v 01,192.168.0.2 eth0
+.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
+.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
+.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
+This will signal an existing
 .Nm
 process running on the
 .Ar interface
 to deconfigure the
 .Ar interface
 and exit.
+.Nm
+then waits until this process has exited.
 .It Fl D , -duid 
 Generate an
-.Rs
-.%T "RFC 4361"
-.Re
+.Li RFC 4361
 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
+.Nm
+generates the DUID and stores in it
 .Pa /system/etc/dhcpcd/dhcpcd.duid
-and should not be copied to other hosts.
+This file should not be copied to other hosts.
 .It Fl E , -lastlease
 If
 .Nm
@@ -289,12 +314,15 @@
 hostname.
 Valid values for
 .Ar fqdn
-are none, ptr and both.
+are disable, 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.
+.Nm
+encodes the FQDN hostname as specified in
+.Li RFC1035 .
 .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.
@@ -311,12 +339,14 @@
 Quiet
 .Nm
 on the command line, only warnings and errors will be displayed.
-The messagea are still logged though.
+The messages 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 B , -nobackground
+Don't run in the background when we acquire a lease.
+This is mainly useful for running under the control of another process, such
+as a debugger or a network manager.
 .It Fl C , -nohook Ar script
 Don't run this hook script.
 Matches full name, or prefixed with 2 numbers optionally ending with
@@ -324,19 +354,23 @@
 .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 G , -nogateway
+Don't set any default routes.
+.It Fl K , -nolink
+Don't receive link messages for carrier status.
+You should only have to use this with buggy device drivers or running
+.Nm
+through a network manager.
 .It Fl L , -noipv4ll
-Don't use IPv4LL at all.
+Don't use IPv4LL (aka APIPA, aka Bonjour, aka ZeroConf).
 .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 Q , -require Ar option
+Requires the
+.Ar option
+to be present in all DHCP messages, otherwise the message is ignored.
 .It Fl T, -test
 On receipt of OFFER messages just call
 .Pa /system/etc/dhcpcd/dhcpcd-run-hooks
@@ -347,6 +381,13 @@
 .It Fl V, -variables
 Display a list of option codes and the associated variable for use in
 .Xr dhcpcd-run-hooks 8 .
+.It Fl X, -blacklist Ar address
+Ignores all DHCP messages which have this
+.Ar address
+as the server ID.
+This may be expanded in future releases to ignore all packets
+matching either the IP or hardware
+.Ar address .
 .El
 .Sh NOTES
 .Nm
@@ -369,6 +410,11 @@
 .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.
+.It Pa /var/run/dhcpcd\- Ns Ar interface Ns .pid
+Stores the PID of
+.Nm
+running on the
+.Ar interface .
 .El
 .Sh SEE ALSO
 .Xr dhcpcd.conf 5 ,
diff --git a/dhcpcd.8.in b/dhcpcd.8.in
index c008a5a..6c82d3f 100644
--- a/dhcpcd.8.in
+++ b/dhcpcd.8.in
@@ -22,18 +22,18 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd Jul 08, 2008
+.Dd August 20, 2008
 .Dt DHCPCD 8 SMM
 .Sh NAME
 .Nm dhcpcd
 .Nd an RFC 2131 compliant DHCP client
 .Sh SYNOPSIS
 .Nm
-.Op Fl dknpqADEGLSTXV
+.Op Fl bdknpqABDEGKLSTV
 .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 i , -vendorclassid Ar vendorclassid
 .Op Fl l , -leasetime Ar seconds
 .Op Fl m , -metric Ar metric
 .Op Fl o , -option Ar option
@@ -46,6 +46,8 @@
 .Op Fl F , -fqdn Ar FQDN
 .Op Fl I , -clientid Ar clientid
 .Op Fl O , -nooption Ar option
+.Op Fl Q , -require Ar option
+.Op Fl X , -blacklist Ar address
 .Ar interface
 .Nm
 .Fl k , -release
@@ -56,9 +58,7 @@
 .Sh DESCRIPTION
 .Nm
 is an implementation of the DHCP client specified in
-.Rs
-.%T "RFC 2131"
-.Re
+.Li RFC 2131 .
 .Nm
 gets the host information
 .Po 
@@ -69,40 +69,42 @@
 of the
 machine on which it is running. 
 .Nm
-will then write DNS information to
+then runs the configuration script which writes 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.
+sets 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
+failed to obtain a lease, it probes for a valid IPv4LL address
 .Po
-aka Zeroconf, aka APIPA
+aka ZeroConf, aka APIPA
 .Pc .
-Once obtained it will restart the process of looking for a DHCP server to get a
+Once obtained it restarts 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
+nearly always succeeds and returns an exit code of 0.
+In the rare case it fails, it normally means that there is a reverse ARP proxy
+installed which always defeats IPv4LL probing.
+To disable this behaviour, you can use the
 .Fl L , -noipv4ll
 option.
 .Ss Hooking into DHCP events
 .Nm
-will run
+runs
 .Pa @SCRIPT@ ,
 or the script specified by the
 .Fl c , -script
 option.
-This script will run each script found in
+This script runs each script found in
 .Pa @HOOKDIR@
 in a lexical order.
 The default installation supplies the scripts
@@ -124,6 +126,10 @@
 .Nm
 with the following options:
 .Bl -tag -width indent
+.It Fl b , -background
+Background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
 .It Fl c , -script Ar script
 Use this
 .Ar script
@@ -142,16 +148,16 @@
 .It Fl h , -hostname Ar hostname
 By default,
 .Nm
-will send the current hostname to the DHCP server so it can register in DNS.
+sends 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
+.It Fl i , -vendorclassid Ar vendorclassid
 Override the
-.Ar classid
+.Ar vendorclassid
 field sent. The default is
 dhcpcd <version>.
 If not set then none is sent.
@@ -163,6 +169,8 @@
 to release its lease, deconfigure the
 .Ar interface
 and then exit.
+.Nm
+then waits until this process has exited.
 .It Fl l , -leasetime Ar seconds
 Request a specific lease time in
 .Ar seconds .
@@ -184,14 +192,27 @@
 .Ar option
 variable for use in
 .Pa @SCRIPT@ .
-.It Fl n , -renew
+.It Fl n , -rebind
 Notifies an existing
 .Nm
 process running on the
 .Ar interface
-to renew it's lease. If
+to rebind it's lease.
+.Nm
+will not re-configure itself or use any other command line arguments.
+.Nm
+will timeout the rebind after 30 seconds at which point the lease will be
+expired and
+.Nm
+will enter the discovery state to obtain a new lease.
+Use the
+.Fl t , -timeout
+option to change this.
+If
 .Nm
 is not running, then it starts up as normal.
+This option used to be renew, but rebind is more accurate as we need to
+broadcast the request instead of unicasting.
 .It Fl p , -persistent
 .Nm
 normally deconfigures the
@@ -201,28 +222,31 @@
 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.
+normally sends a DHCP DISCOVER 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
+then requests the address used.
+You can use this option to skip the BROADCAST step and just request the
 .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.
+In this situation, we go back to the init state and DISCOVER 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
+Behaves 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.
+You should also include the optional
+.Ar cidr
+network number in-case the address is not already configured on the interface.
 .Nm
 remains running and pretends it has an infinite lease.
 .Nm
@@ -243,7 +267,7 @@
 .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
+DHCP servers use this give members 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.
@@ -252,30 +276,31 @@
 Examples.
 .Pp
 Set the vendor option 01 with an IP address.
-.D1 dhcpcd -v 01,192.168.0.2 eth0
+.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
+.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
+.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
+This will signal an existing
 .Nm
 process running on the
 .Ar interface
 to deconfigure the
 .Ar interface
 and exit.
+.Nm
+then waits until this process has exited.
 .It Fl D , -duid 
 Generate an
-.Rs
-.%T "RFC 4361"
-.Re
+.Li RFC 4361
 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
+.Nm
+generates the DUID and stores in it
 .Pa @SYSCONFDIR@/dhcpcd.duid
-and should not be copied to other hosts.
+This file should not be copied to other hosts.
 .It Fl E , -lastlease
 If
 .Nm
@@ -289,12 +314,15 @@
 hostname.
 Valid values for
 .Ar fqdn
-are none, ptr and both.
+are disable, 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.
+.Nm
+encodes the FQDN hostname as specified in
+.Li RFC1035 .
 .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.
@@ -311,12 +339,14 @@
 Quiet
 .Nm
 on the command line, only warnings and errors will be displayed.
-The messagea are still logged though.
+The messages 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 B , -nobackground
+Don't run in the background when we acquire a lease.
+This is mainly useful for running under the control of another process, such
+as a debugger or a network manager.
 .It Fl C , -nohook Ar script
 Don't run this hook script.
 Matches full name, or prefixed with 2 numbers optionally ending with
@@ -324,19 +354,23 @@
 .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 G , -nogateway
+Don't set any default routes.
+.It Fl K , -nolink
+Don't receive link messages for carrier status.
+You should only have to use this with buggy device drivers or running
+.Nm
+through a network manager.
 .It Fl L , -noipv4ll
-Don't use IPv4LL at all.
+Don't use IPv4LL (aka APIPA, aka Bonjour, aka ZeroConf).
 .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 Q , -require Ar option
+Requires the
+.Ar option
+to be present in all DHCP messages, otherwise the message is ignored.
 .It Fl T, -test
 On receipt of OFFER messages just call
 .Pa @SCRIPT@
@@ -347,6 +381,13 @@
 .It Fl V, -variables
 Display a list of option codes and the associated variable for use in
 .Xr dhcpcd-run-hooks 8 .
+.It Fl X, -blacklist Ar address
+Ignores all DHCP messages which have this
+.Ar address
+as the server ID.
+This may be expanded in future releases to ignore all packets
+matching either the IP or hardware
+.Ar address .
 .El
 .Sh NOTES
 .Nm
@@ -369,6 +410,11 @@
 .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.
+.It Pa /var/run/dhcpcd\- Ns Ar interface Ns .pid
+Stores the PID of
+.Nm
+running on the
+.Ar interface .
 .El
 .Sh SEE ALSO
 .Xr dhcpcd.conf 5 ,
diff --git a/dhcpcd.c b/dhcpcd.c
index 4a6e4af..e674bd2 100644
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -42,6 +42,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <time.h>
 
 #include "config.h"
 #include "client.h"
@@ -53,71 +54,66 @@
 #ifdef ANDROID
 #include <linux/capability.h>
 #include <linux/prctl.h>
+#include <cutils/properties.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"
+#define OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xABC:DEF:GI:KLO:Q: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
+	{"background",    no_argument,        NULL, 'b'},
+	{"script",        required_argument,  NULL, 'c'},
+	{"debug",         no_argument,        NULL, 'd'},
+	{"config",        required_argument,  NULL, 'f'},
+	{"hostname",      optional_argument,  NULL, 'h'},
+	{"vendorclassid", optional_argument,  NULL, 'i'},
+	{"release",       no_argument,        NULL, 'k'},
+	{"leasetime",     required_argument,  NULL, 'l'},
+	{"metric",        required_argument,  NULL, 'm'},
+	{"rebind",        no_argument,        NULL, 'n'},
+	{"option",        required_argument,  NULL, 'o'},
+	{"persistent",    no_argument,        NULL, 'p'},
+	{"quiet",         no_argument,        NULL, 'q'},
+	{"request",       optional_argument,  NULL, 'r'},
+	{"inform",        optional_argument,  NULL, 's'},
+	{"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'},
+	{"nobackground",  no_argument,        NULL, 'B'},
+	{"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'},
+	{"nolink",        no_argument,        NULL, 'K'},
+	{"noipv4ll",      no_argument,        NULL, 'L'},
+	{"nooption",      optional_argument,  NULL, 'O'},
+	{"require",       required_argument,  NULL, 'Q'},
+	{"test",          no_argument,        NULL, 'T'},
+	{"variables",     no_argument,        NULL, 'V'},
+	{"blacklist",     required_argument,  NULL, 'X'},
+	{"help",          no_argument,        &dohelp, 1},
+	{"version",       no_argument,        &doversion, 1},
 #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'},
+	{"classid",       optional_argument,  NULL, 'i'},
+	{"renew",         no_argument,        NULL, 'n'},
+	{"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
@@ -164,12 +160,10 @@
 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
+	       "              [-I clientID] [-C hookscript] [-Q option] [-X ipaddr] <interface>\n");
 }
 
 static char * 
@@ -214,7 +208,6 @@
 	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)
@@ -312,7 +305,6 @@
 	}
 	return l;
 }
-#endif
 
 static int
 parse_option(int opt, char *oarg, struct options *options)
@@ -320,44 +312,44 @@
 	int i;
 	char *p;
 	ssize_t s;
-#ifndef MINIMAL
 	struct in_addr addr;
-#endif
 
 	switch(opt) {
+	case 'b':
+		options->options |= DHCPCD_BACKGROUND;
+		break;
 	case 'c':
 		strlcpy(options->script, oarg, sizeof(options->script));
 		break;
 	case 'h':
-#ifndef MINIMAL
 		if (oarg)
 			s = parse_string(options->hostname + 1,
-					 MAXHOSTNAMELEN, oarg);
+					 HOSTNAME_MAX_LEN, oarg);
 		else
 			s = 0;
 		if (s == -1) {
 			logger(LOG_ERR, "hostname: %s", strerror(errno));
 			return -1;
 		}
+		if (s != 0 && options->hostname[1] == '.') {
+			logger(LOG_ERR, "hostname cannot begin with a .");
+			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);
+			s = parse_string((char *)options->vendorclassid + 1,
+					 VENDORCLASSID_MAX_LEN, oarg);
 		else
 			s = 0;
 		if (s == -1) {
-			logger(LOG_ERR, "classid: %s", strerror(errno));
+			logger(LOG_ERR, "vendorclassid: %s", strerror(errno));
 			return -1;
 		}
-		*options->classid = (uint8_t)s;
-#endif
+		*options->vendorclassid = (uint8_t)s;
 		break;
 	case 'l':
-#ifndef MINIMAL
 		if (*oarg == '-') {
 			logger(LOG_ERR,
 			       "leasetime must be a positive value");
@@ -369,7 +361,6 @@
 			logger(LOG_ERR, "`%s' out of range", oarg);
 			return -1;
 		}
-#endif
 		break;
 	case 'm':
 		options->metric = atoint(oarg);
@@ -379,7 +370,7 @@
 		}
 		break;
 	case 'o':
-		if (make_reqmask(options->reqmask, &oarg, 1) != 0) {
+		if (make_option_mask(options->requestmask, &oarg, 1) != 0) {
 			logger(LOG_ERR, "unknown option `%s'", oarg);
 			return -1;
 		}
@@ -416,7 +407,7 @@
 	case 'r':
 		if (!(options->options & DHCPCD_INFORM))
 			options->options |= DHCPCD_REQUEST;
-		if (*oarg && !inet_aton(oarg, &options->request_address)) {
+		if (oarg && !inet_aton(oarg, &options->request_address)) {
 			logger(LOG_ERR, "`%s' is not a valid IP address",
 			       oarg);
 			return -1;
@@ -430,7 +421,6 @@
 		}
 		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);
@@ -442,10 +432,8 @@
 			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");
@@ -480,13 +468,15 @@
 			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 'B':
+		options->options &= ~DHCPCD_DAEMONISE;
+		break;
 	case 'C':
 		/* Commas to spaces for shell */
 		while ((p = strchr(oarg, ',')))
@@ -504,7 +494,6 @@
 		options->options |= DHCPCD_LASTLEASE;
 		break;
 	case 'F':
-#ifndef MINIMAL
 		if (!oarg) {
 			options->fqdn = FQDN_BOTH;
 			break;
@@ -515,20 +504,20 @@
 			options->fqdn = FQDN_PTR;
 		else if (strcmp(oarg, "both") == 0)
 			options->fqdn = FQDN_BOTH;
+		else if (strcmp(oarg, "disable") == 0)
+			options->fqdn = FQDN_DISABLE;
 		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;
+		options->clientid[1] = 0;
 		if (oarg)
 			s = parse_string_hwaddr((char *)options->clientid + 1,
 						CLIENTID_MAX_LEN, oarg, 1);
@@ -543,21 +532,40 @@
 			options->options &= ~DHCPCD_DUID;
 			options->options &= ~DHCPCD_CLIENTID;
 		}
-#endif
+		break;
+	case 'K':
+		options->options &= ~DHCPCD_LINK;
 		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)
+		if (make_option_mask(options->requestmask, &oarg, -1) != 0 ||
+		    make_option_mask(options->requiremask, &oarg, -1) != 0 ||
+		    make_option_mask(options->nomask, &oarg, 1) != 0)
 		{
-			logger(LOG_ERR, "unknown option `%s'", optarg);
+			logger(LOG_ERR, "unknown option `%s'", oarg);
+			return -1;
+		}
+		break;
+	case 'Q':
+		if (make_option_mask(options->requiremask, &oarg, 1) != 0 ||
+		    make_option_mask(options->requestmask, &oarg, 1) != 0)
+		{
+			logger(LOG_ERR, "unknown option `%s'", oarg);
 			return -1;
 		}
 		break;
 	case 'X':
-		options->options &= ~DHCPCD_DAEMONISE;
+		if (!inet_aton(oarg, &addr)) {
+			logger(LOG_ERR, "`%s' is not a valid IP address",
+			       oarg);
+			return -1;
+		}
+		options->blacklist = xrealloc(options->blacklist,
+		    sizeof(in_addr_t) * (options->blacklist_len + 1));
+		options->blacklist[options->blacklist_len] = addr.s_addr;
+		options->blacklist_len++;
 		break;
 	default:
 		return 0;
@@ -630,38 +638,34 @@
 	FILE *f;
 	char *cf = NULL;
 	char *intf = NULL;
+	struct timespec ts;
 
 #ifdef ANDROID
 	switchUser();
 #endif
-
 	closefrom(3);
+	/* Saves calling fflush(stream) in the logger */
+	setlinebuf(stdout);
 	openlog(PACKAGE, LOG_PID, LOG_LOCAL0);
+	setlogprefix(PACKAGE ": ");
 
 	options = xzalloc(sizeof(*options));
-	options->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
+	options->options |= DHCPCD_CLIENTID | DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
+	options->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
 	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
+	options->vendorclassid[0] = snprintf((char *)options->vendorclassid + 1,
+					     VENDORCLASSID_MAX_LEN,
+					     "%s %s", PACKAGE, VERSION);
 
 #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);
+	add_option_mask(options->requestmask, DHO_DNSSERVER);
+	add_option_mask(options->requestmask, DHO_DNSDOMAIN);
+	add_option_mask(options->requestmask, DHO_DNSSEARCH);
+	add_option_mask(options->requestmask, DHO_NISSERVER);
+	add_option_mask(options->requestmask, DHO_NISDOMAIN);
+	add_option_mask(options->requestmask, DHO_NTPSERVER);
 
 	/* If the duid file exists, then enable duid by default
 	 * This means we don't break existing clients that easily :) */
@@ -671,23 +675,11 @@
 	}
 #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)
@@ -711,23 +703,8 @@
 		}
 	}
 
-	if (doversion) {
+	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();
@@ -822,15 +799,6 @@
 			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;
@@ -846,22 +814,22 @@
 #ifdef CMDLINE_COMPAT
 		case 'H': /* FALLTHROUGH */
 		case 'M':
-			del_reqmask(options->reqmask, DHCP_MTU);
+			del_option_mask(options->requestmask, DHO_MTU);
 			break;
 		case 'N':
-			del_reqmask(options->reqmask, DHCP_NTPSERVER);
+			del_option_mask(options->requestmask, DHO_NTPSERVER);
 			break;
 		case 'R':
-			del_reqmask(options->reqmask, DHCP_DNSSERVER);
-			del_reqmask(options->reqmask, DHCP_DNSDOMAIN);
-			del_reqmask(options->reqmask, DHCP_DNSSEARCH);
+			del_option_mask(options->requestmask, DHO_DNSSERVER);
+			del_option_mask(options->requestmask, DHO_DNSDOMAIN);
+			del_option_mask(options->requestmask, DHO_DNSSEARCH);
 			break;
 		case 'S':
-			add_reqmask(options->reqmask, DHCP_MSCSR);
+			add_option_mask(options->requestmask, DHO_MSCSR);
 			break;
 		case 'Y':
-			del_reqmask(options->reqmask, DHCP_NISSERVER);
-			del_reqmask(options->reqmask, DHCP_NISDOMAIN);
+			del_option_mask(options->requestmask, DHO_NISSERVER);
+			del_option_mask(options->requestmask, DHO_NISDOMAIN);
 			break;
 #endif
 		default:
@@ -874,60 +842,17 @@
 		}
 	}
 
-#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);
+#ifdef THERE_IS_NO_FORK
+	options->options &= ~DHCPCD_DAEMONISE;
 #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
+        /* android runs us as user "dhcp" */
 	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) {
@@ -948,32 +873,100 @@
 		}
 	}
 
+	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);
+
+	if (options->request_address.s_addr == 0 &&
+	    (options->options & DHCPCD_INFORM ||
+	     options->options & DHCPCD_REQUEST))
+	{
+		errno = 0;
+		if (get_address(options->interface,
+				&options->request_address,
+				&options->request_netmask) != 1)
+		{
+			if (errno)
+				logger(LOG_ERR, "get_address: %s",
+				       strerror(errno));
+			else
+				logger(LOG_ERR, "no existing address");
+			goto abort;
+		}
+	}
+	if (IN_LINKLOCAL(ntohl(options->request_address.s_addr))) {
+		logger(LOG_ERR,
+		       "you are not allowed to request a link local address");
+		goto abort;
+	}
+
+	chdir("/");
+	umask(022);
+
 	if (sig != 0 && !(options->options & DHCPCD_DAEMONISED)) {
+#ifdef ANDROID
+                char pidpropname[PROPERTY_KEY_MAX];
+                char pidpropval[PROPERTY_VALUE_MAX];
+
+		i = -1;
+                if (snprintf(pidpropname,
+                             sizeof(pidpropname),
+                             "dhcp.%s.pid", options->interface) >= PROPERTY_KEY_MAX) {
+                    goto abort;
+                }
+                property_get(pidpropname, pidpropval, NULL);
+                if (strlen(pidpropval) == 0) {
+                    goto abort;
+                }
+                pid = atoi(pidpropval);
+#else
 		i = -1;
 		pid = read_pid(options->pidfile);
+#endif
 		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))
+		if (!pid || (i = kill(pid, sig))) {
+			if (sig != SIGALRM)
+				logger(LOG_ERR, ""PACKAGE" not running");
 			unlink(options->pidfile);
-
+		}
 		if (i == 0) {
-			retval = EXIT_SUCCESS;
+			if (sig == SIGALRM) {
+				retval = EXIT_SUCCESS;
+				goto abort;
+			}
+			/* Spin until it exits */
+			logger(LOG_INFO, "waiting for pid %d to exit", pid);
+			ts.tv_sec = 0;
+			ts.tv_nsec = 100000000; /* 10th of a second */
+			for(i = 0; i < 100; i++) {
+				nanosleep(&ts, NULL);
+				if (read_pid(options->pidfile) == 0) {
+					retval = EXIT_SUCCESS;
+					break;
+				}
+			}
+			if (retval != EXIT_SUCCESS)
+				logger(LOG_ERR, "pid %d failed to exit", pid);
 			goto abort;
 		}
-
 		if (sig != SIGALRM)
 			goto abort;	
 	}
-#ifndef ANDROID
+
 	if (!(options->options & DHCPCD_TEST) &&
 	    !(options->options & DHCPCD_DAEMONISED))
 	{
+#ifdef ANDROID
+                char pidpropname[PROPERTY_KEY_MAX];
+                char pidpropval[PROPERTY_VALUE_MAX];
+#endif
+#ifndef ANDROID
 		if ((pid = read_pid(options->pidfile)) > 0 &&
 		    kill(pid, 0) == 0)
 		{
@@ -982,7 +975,7 @@
 			       pid, options->pidfile);
 			goto abort;
 		}
-
+#endif
 		pid_fd = open(options->pidfile,
 			     O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
 		if (pid_fd == -1) {
@@ -1001,17 +994,27 @@
 
 		if (set_cloexec(pid_fd) == -1)
 			goto abort;
+#ifdef ANDROID
+                if (snprintf(pidpropname,
+                             sizeof(pidpropname),
+                             "dhcp.%s.pid", options->interface) >= PROPERTY_KEY_MAX) {
+                    goto abort;
+                }
+                if (snprintf(pidpropval, sizeof(pidpropval), "%d", getpid()) >= PROPERTY_VALUE_MAX) {
+                    goto abort;
+                }
+		property_set(pidpropname, pidpropval);
+#else
 		writepid(pid_fd, getpid());
+#endif
 		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;
+		options->vendor[options->vendor[0]] = DHO_END;
 	}
-#endif
 
 	if (dhcp_run(options, &pid_fd) == 0)
 		retval = EXIT_SUCCESS;
@@ -1028,14 +1031,8 @@
 			free(options->environ[len++]);
 		free(options->environ);
 	}
+	free(options->blacklist);
 	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.5 b/dhcpcd.conf.5
index 8fd4b90..217ecba 100644
--- a/dhcpcd.conf.5
+++ b/dhcpcd.conf.5
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd Jun 30, 2008
+.Dd August 18, 2008
 .Dt DHCPCD.CONF 5 SMM
 .Sh NAME
 .Nm dhcpcd.conf
@@ -43,9 +43,10 @@
 .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 background
+Background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
 .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.
@@ -71,10 +72,13 @@
 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
+.Ic hostname
 option must be a FQDN.
 .Nm dhcpcd
 itself never does any DNS updates.
+.Nm dhcpcd
+encodes the FQDN hostname as specified in
+.Li RFC1035 .
 .It Ic interface Ar interface
 Subsequent options are only parsed for this
 .Ar interface .
@@ -96,14 +100,25 @@
 .Rs
 .%T "RFC 3927"
 .Re
-.It Ic option Ar dhcp-option
+.It Ic nolink
+Don't receive link messages about carrier status.
+You should only set this for buggy interface drivers.
+.It Ic option Ar option
 Requests the
-.Ar dhcp-option
+.Ar 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.
+You can specify more options seperated by commas, spaces or more option lines.
+.It Ic require Ar option
+Requires the
+.Ar option
+to be present in all DHCP messages, otherwise the message is ignored.
+It can be a variable to be used in
+.Xr dhcpcd-run-hooks 8
+or the numerical value.
+You can specify more options seperated by commas, spaces or more require lines.
 .It Ic script Ar script
 Use
 .Ar script
@@ -115,7 +130,7 @@
 .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
+.It Ic vendor Ar code , Ns Ar value
 Add an enscapulated vendor option.
 .Ar code
 should be between 1 and 254 inclusive.
@@ -127,6 +142,9 @@
 .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"
+.It Ic vendorclassid Ar string
+Change the default vendorclassid sent from dhcpcd-version.
+If not set then none is sent.
 .El
 .Sh SEE ALSO
 .Xr dhcpcd-run-hooks 8 ,
diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in
index 5ba825f..899aea3 100644
--- a/dhcpcd.conf.5.in
+++ b/dhcpcd.conf.5.in
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd Jun 30, 2008
+.Dd August 18, 2008
 .Dt DHCPCD.CONF 5 SMM
 .Sh NAME
 .Nm dhcpcd.conf
@@ -43,9 +43,10 @@
 .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 background
+Background immediately.
+This is useful for startup scripts which don't disable link messages for
+carrier status.
 .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.
@@ -71,10 +72,13 @@
 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
+.Ic hostname
 option must be a FQDN.
 .Nm dhcpcd
 itself never does any DNS updates.
+.Nm dhcpcd
+encodes the FQDN hostname as specified in
+.Li RFC1035 .
 .It Ic interface Ar interface
 Subsequent options are only parsed for this
 .Ar interface .
@@ -96,14 +100,25 @@
 .Rs
 .%T "RFC 3927"
 .Re
-.It Ic option Ar dhcp-option
+.It Ic nolink
+Don't receive link messages about carrier status.
+You should only set this for buggy interface drivers.
+.It Ic option Ar option
 Requests the
-.Ar dhcp-option
+.Ar 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.
+You can specify more options seperated by commas, spaces or more option lines.
+.It Ic require Ar option
+Requires the
+.Ar option
+to be present in all DHCP messages, otherwise the message is ignored.
+It can be a variable to be used in
+.Xr dhcpcd-run-hooks 8
+or the numerical value.
+You can specify more options seperated by commas, spaces or more require lines.
 .It Ic script Ar script
 Use
 .Ar script
@@ -115,7 +130,7 @@
 .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
+.It Ic vendor Ar code , Ns Ar value
 Add an enscapulated vendor option.
 .Ar code
 should be between 1 and 254 inclusive.
@@ -127,6 +142,9 @@
 .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"
+.It Ic vendorclassid Ar string
+Change the default vendorclassid sent from dhcpcd-version.
+If not set then none is sent.
 .El
 .Sh SEE ALSO
 .Xr dhcpcd-run-hooks 8 ,
diff --git a/dhcpcd.h b/dhcpcd.h
index 10d23ff..1cd2b5d 100644
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -41,18 +41,12 @@
 #define DEFAULT_TIMEOUT		30
 #define DEFAULT_LEASETIME	3600	/* 1 hour */
 
-#define CLASSID_MAX_LEN		48
+#define HOSTNAME_MAX_LEN	250	/* 255 - 3 (FQDN) - 2 (DNS enc) */
+#define VENDORCLASSID_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)
@@ -68,11 +62,14 @@
 #define DHCPCD_FORKED		(1 << 17)
 #define DHCPCD_HOSTNAME		(1 << 18)
 #define DHCPCD_CLIENTID		(1 << 19)
+#define DHCPCD_LINK		(1 << 20)
+#define DHCPCD_BACKGROUND	(1 << 21)
 
 struct options {
 	char interface[IF_NAMESIZE];
 	int metric;
-	uint8_t reqmask[256 / 8];
+	uint8_t requestmask[256 / 8];
+	uint8_t requiremask[256 / 8];
 	uint8_t nomask[256 / 8];
 	uint32_t leasetime;
 	time_t timeout;
@@ -85,14 +82,14 @@
 	char script[PATH_MAX];
 	char pidfile[PATH_MAX];
 
-#ifndef MINIMAL
-	char hostname[MAXHOSTNAMELEN];
+	char hostname[HOSTNAME_MAX_LEN + 1];
 	int fqdn;
-	uint8_t classid[CLASSID_MAX_LEN + 1];
+	uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 1];
 	char clientid[CLIENTID_MAX_LEN + 1];
 	uint8_t userclass[USERCLASS_MAX_LEN + 1];
 	uint8_t vendor[VENDOR_MAX_LEN + 1];
-#endif
-};
 
+	size_t blacklist_len;
+	in_addr_t *blacklist;
+};
 #endif
diff --git a/if-bsd.c b/if-bsd.c
index 2cc0c2f..bbf1a95 100644
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -185,3 +185,57 @@
 	close(s);
 	return retval;
 }
+
+int
+open_link_socket(struct interface *iface)
+{
+	int fd;
+
+	fd = socket(PF_ROUTE, SOCK_RAW, 0);
+	if (fd == -1)
+		return -1;
+	set_cloexec(fd);
+	if (iface->link_fd != -1)
+		close(iface->link_fd);
+	iface->link_fd = fd;
+	return 0;
+}
+
+#define BUFFER_LEN	2048
+int
+link_changed(struct interface *iface)
+{
+	char buffer[2048], *p;
+	ssize_t bytes;
+	struct rt_msghdr *rtm;
+	struct if_msghdr *ifm;
+	int i;
+
+	if ((i = if_nametoindex(iface->name)) == -1)
+		return -1;
+	for (;;) {
+		bytes = recv(iface->link_fd, buffer, BUFFER_LEN, MSG_DONTWAIT);
+		if (bytes == -1) {
+			if (errno == EAGAIN)
+				return 0;
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		for (p = buffer; bytes > 0;
+		     bytes -= ((struct rt_msghdr *)p)->rtm_msglen,
+		     p += ((struct rt_msghdr *)p)->rtm_msglen)
+		{
+			rtm = (struct rt_msghdr *)p;
+			if (rtm->rtm_type != RTM_IFINFO)
+				continue;
+			ifm = (struct if_msghdr *)p;
+			if (ifm->ifm_index != i)
+				continue;
+
+			/* Link changed */
+			return 1;
+		}
+	}
+	return 0;
+}
diff --git a/if-linux.c b/if-linux.c
index 68af3f6..1009270 100644
--- a/if-linux.c
+++ b/if-linux.c
@@ -46,128 +46,178 @@
 #include <string.h>
 #include <unistd.h>
 
+/* Support older kernels */
+#ifndef IFLA_WIRELESS
+# define IFLA_WIRELSSS (IFLFA_MASTER + 1)
+#endif
+
 #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
+
+int
+open_link_socket(struct interface *iface)
+{
+	int fd;
+	struct sockaddr_nl nl;
+
+	if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
+		return -1;
+	memset(&nl, 0, sizeof(nl));
+	nl.nl_family = AF_NETLINK;
+	nl.nl_groups = RTMGRP_LINK;
+	if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1)
+		return -1;
+	set_cloexec(fd);
+	if (iface->link_fd != -1)
+		close(iface->link_fd);
+	iface->link_fd = fd;
+	return 0;
+}
+
+static int
+get_netlink(int fd, int flags,
+	    int (*callback)(struct nlmsghdr *, const char *),
+	    const char *ifname)
+{
+	char *buffer = NULL;
+	ssize_t bytes;
+	struct nlmsghdr *nlm;
+	int r = -1;
+
+	buffer = xzalloc(sizeof(char) * BUFFERLEN);
+	for (;;) {
+		bytes = recv(fd, buffer, BUFFERLEN, flags);
+		if (bytes == -1) {
+			if (errno == EAGAIN) {
+				r = 0;
+				goto eexit;
+			}
+			if (errno == EINTR)
+				continue;
+			goto eexit;
+		}
+		for (nlm = (struct nlmsghdr *)buffer;
+		     NLMSG_OK(nlm, (size_t)bytes);
+		     nlm = NLMSG_NEXT(nlm, bytes))
+		{
+			r = callback(nlm, ifname);
+			if (r != 0)
+				goto eexit;
+		}
+	}
+
+eexit:
+	free(buffer);
+	return r;
+}
+
+static int
+err_netlink(struct nlmsghdr *nlm, _unused const char *ifname)
+{
+	struct nlmsgerr *err;
+	int l;
+
+	if (nlm->nlmsg_type != NLMSG_ERROR)
+		return 0;
+	l = nlm->nlmsg_len - sizeof(*nlm);
+	if ((size_t)l < sizeof(*err)) {
+		errno = EBADMSG;
+		return -1;
+	}
+	err = (struct nlmsgerr *)NLMSG_DATA(nlm);
+	if (err->error == 0)
+		return l;
+	errno = -err->error;
+	return -1;
+}
+
+static int
+link_netlink(struct nlmsghdr *nlm, const char *ifname)
+{
+	int len;
+	struct rtattr *rta;
+	struct ifinfomsg *ifi;
+	char ifn[IF_NAMESIZE + 1];
+
+	if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK)
+		return 0;
+	len = nlm->nlmsg_len - sizeof(*nlm);
+	if ((size_t)len < sizeof(*ifi)) {
+		errno = EBADMSG;
+		return -1;
+	}
+	ifi = NLMSG_DATA(nlm);
+	if (ifi->ifi_flags & IFF_LOOPBACK)
+		return 0;
+	rta = (struct rtattr *) ((char *)ifi + NLMSG_ALIGN(sizeof(*ifi)));
+	len = NLMSG_PAYLOAD(nlm, sizeof(*ifi));
+	*ifn = '\0';
+	while (RTA_OK(rta, len)) {
+		switch (rta->rta_type) {
+		case IFLA_WIRELESS:
+			/* Ignore wireless messages */
+			if (nlm->nlmsg_type == RTM_NEWLINK &&
+			    ifi->ifi_change  == 0)
+				return 0;
+			break;
+		case IFLA_IFNAME:
+			strlcpy(ifn, RTA_DATA(rta), sizeof(ifn));
+			break;
+		}
+		rta = RTA_NEXT(rta, len);
+	}
+
+	if (strncmp(ifname, ifn, sizeof(ifn)) == 0)
+		return 1;
+	return 0;
+}
+
+int
+link_changed(struct interface *iface)
+{
+	return get_netlink(iface->link_fd, MSG_DONTWAIT,
+			   &link_netlink, iface->name);
+}
+
 static int
 send_netlink(struct nlmsghdr *hdr)
 {
-	int s;
-	pid_t mypid = getpid ();
+	int fd, r;
 	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)
+	if ((fd = 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;
-
+	if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
+		close(fd);
+		return -1;
+	}
 	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;
+	if (sendmsg(fd, &msg, 0) != -1)
+		r = get_netlink(fd, 0, &err_netlink, NULL);
+	else
+		r = -1;
+	close(fd);
+	return r;
 }
 
 #define NLMSG_TAIL(nmsg) \
@@ -285,15 +335,16 @@
 	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;
+            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
+		 * ers@google:
+		 * 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*/;
+		nlm->hdr.nlmsg_flags = NLM_F_CREATE /* | NLM_F_EXCL */;
 	else
 		nlm->hdr.nlmsg_type = RTM_DELROUTE;
 	nlm->hdr.nlmsg_flags |= NLM_F_REQUEST;
diff --git a/logger.c b/logger.c
index 6eb0c29..15c6cf7 100644
--- a/logger.c
+++ b/logger.c
@@ -38,31 +38,6 @@
 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)
 {
@@ -90,10 +65,6 @@
 		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) {
diff --git a/logger.h b/logger.h
index cde4864..8ac76b6 100644
--- a/logger.h
+++ b/logger.h
@@ -36,7 +36,6 @@
 
 #include <syslog.h>
 
-int logtolevel(const char *);
 void setloglevel(int);
 void setlogprefix(const char *);
 void logger(int, const char *, ...) _PRINTF_LIKE (2, 3);
diff --git a/lpf.c b/lpf.c
index daa8a4c..ae5dd03 100644
--- a/lpf.c
+++ b/lpf.c
@@ -89,13 +89,10 @@
 	}
 	/* 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
-	{
+	} else {
 		pf.filter = UNCONST(dhcp_bpf_filter);
 		pf.len = dhcp_bpf_filter_len;
 	}
@@ -107,12 +104,10 @@
 		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;
+		fd = &iface->raw_fd;
 	if (*fd != -1)
 		close(*fd);
 	*fd = s;
@@ -148,12 +143,10 @@
 		       &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;
+		fd = iface->raw_fd;
 
 	return sendto(fd, data, len, 0, &su.sa, sizeof(su));
 }
@@ -164,12 +157,10 @@
 	ssize_t bytes;
 	int fd = -1;
 
-	if (protocol == ETHERTYPE_ARP) {
-#ifdef ENABLE_ARP
+	if (protocol == ETHERTYPE_ARP)
 		fd = iface->arp_fd;
-#endif
-	} else
-		fd = iface->fd;
+	else
+		fd = iface->raw_fd;
 	bytes = read(fd, data, len);
 	if (bytes == -1)
 		return errno == EAGAIN ? 0 : -1;
diff --git a/mk/os-Darwin.mk b/mk/os-Darwin.mk
new file mode 100644
index 0000000..f2c3104
--- /dev/null
+++ b/mk/os-Darwin.mk
@@ -0,0 +1,5 @@
+# Setup OS specific variables
+# Copyright 2008 Roy Marples <roy@marples.name>
+
+include ${MK}/os-BSD.mk
+LINK_RPATH=	--rpath
diff --git a/mk/os-Linux.mk b/mk/os-Linux.mk
index 16f0f60..2d316b1 100644
--- a/mk/os-Linux.mk
+++ b/mk/os-Linux.mk
@@ -6,23 +6,3 @@
 
 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
index f2a83f4..f3426e4 100644
--- a/mk/os.mk
+++ b/mk/os.mk
@@ -1,7 +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_SH=	case `uname -s` in Linux) echo "Linux";; Darwin) echo "Darwin";;  *) 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
index 3f763b8..d970b2d 100644
--- a/mk/prog.mk
+++ b/mk/prog.mk
@@ -7,18 +7,19 @@
 
 OBJS+=		${SRCS:.c=.o}
 
-# This is for NetBSD which has a different libc in /lib which we need
-# to link to if installing in /
+# If building for /, ensure we use the libc in / if different from
+# the default one in /usr/lib
+LINK_RPATH?=		-Wl,-rpath
 _RPATH_SH=		if test "${PREFIX}" = "" -o "${PREIX}" = "/"; then \
-				echo "-Wl,-rpath=${PREFIX}/${LIBNAME}"; \
+				echo "${LINK_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 /
+# If building for /, ensure we use the linker in /libexec if different from
+# the default one in /usr/libexec
 _DYNLINK_SH=		if test "${PREFIX}" = "" -o "${PREFIX}" = "/" && test -e /libexec/ld.elf_so; then \
 				echo "-Wl,-dynamic-linker=/libexec/ld.elf_so"; \
 			else \
diff --git a/net.c b/net.c
index f8aad4c..b905573 100644
--- a/net.c
+++ b/net.c
@@ -43,6 +43,9 @@
 #define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
 #include <netinet/udp.h>
 #undef __FAVOR_BSD
+#ifdef SIOCGIFMEDIA
+#include <net/if_media.h>
+#endif
 #include <arpa/inet.h>
 #ifdef AF_LINK
 # include <net/if_dl.h>
@@ -50,7 +53,6 @@
 
 #include <ctype.h>
 #include <errno.h>
-#include <poll.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -79,7 +81,7 @@
 }
 
 int
-inet_cidrtoaddr (int cidr, struct in_addr *addr)
+inet_cidrtoaddr(int cidr, struct in_addr *addr)
 {
 	int ocets;
 
@@ -181,7 +183,7 @@
 {
 	int s;
 	struct ifconf ifc;
-	int retval = 0;
+	int retval = 0, found = 0;
 	int len = 10 * sizeof(struct ifreq);
 	int lastlen = 0;
 	char *p;
@@ -192,9 +194,8 @@
 	struct sockaddr_in address;
 	struct ifreq *ifr;
 	struct sockaddr_in netmask;
-
 #ifdef AF_LINK
-	struct sockaddr_dl sdl;
+	struct sockaddr_dl *sdl;
 #endif
 
 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
@@ -239,12 +240,15 @@
 		if (strcmp(ifname, ifr->ifr_name) != 0)
 			continue;
 
+		found = 1;
+
 #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);
+			sdl = xmalloc(ifr->ifr_addr.sa_len);
+			memcpy(sdl, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
+			*hwlen = sdl->sdl_alen;
+			memcpy(hwaddr, LLADDR(sdl), *hwlen);
+			free(sdl);
 			retval = 1;
 			break;
 		}
@@ -273,11 +277,90 @@
 
 	}
 
+	if (!found)
+		errno = ENXIO;
 	close(s);
 	free(ifc.ifc_buf);
 	return retval;
 }
 
+int
+up_interface(const char *ifname)
+{
+	int s;
+	struct ifreq ifr;
+	int retval = -1;
+#ifdef __linux__
+	char *p;
+#endif
+
+	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));
+#ifdef __linux__
+	/* We can only bring the real interface up */
+	if ((p = strchr(ifr.ifr_name, ':')))
+		*p = '\0';
+#endif
+	if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) {
+		if ((ifr.ifr_flags & IFF_UP))
+			retval = 0;
+		else {
+			ifr.ifr_flags |= IFF_UP;
+			if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0)
+				retval = 0;
+		}
+	}
+	close(s);
+	return retval;
+}
+
+int
+carrier_status(const char *ifname)
+{
+	int s;
+	struct ifreq ifr;
+	int retval = -1;
+#ifdef SIOCGIFMEDIA
+	struct ifmediareq ifmr;
+#endif
+#ifdef __linux__
+	char *p;
+#endif
+
+	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));
+#ifdef __linux__
+	/* We can only test the real interface up */
+	if ((p = strchr(ifr.ifr_name, ':')))
+		*p = '\0';
+#endif
+	if ((retval = ioctl(s, SIOCGIFFLAGS, &ifr)) == 0) {
+		if (ifr.ifr_flags & IFF_UP && ifr.ifr_flags & IFF_RUNNING)
+			retval = 1;
+		else
+			retval = 0;
+	}
+
+#ifdef SIOCGIFMEDIA
+	if (retval == 1) {
+		memset(&ifmr, 0, sizeof(ifmr));
+		strncpy(ifmr.ifm_name, ifr.ifr_name, sizeof(ifmr.ifm_name));
+		if (ioctl(s, SIOCGIFMEDIA, &ifmr) != -1 &&
+		    ifmr.ifm_status & IFM_AVALID)
+		{
+			if (!(ifmr.ifm_status & IFM_ACTIVE))
+				retval = 0;
+		}
+	}
+#endif
+	close(s);
+	return retval;
+}
+
 struct interface *
 read_interface(const char *ifname, _unused int metric)
 {
@@ -287,9 +370,6 @@
 	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));
@@ -342,20 +422,8 @@
 			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)
+	if (up_interface(ifname) != 0)
 		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);
@@ -367,11 +435,10 @@
 	iface->arpable = !(ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK));
 
 	/* 0 is a valid fd, so init to -1 */
-	iface->fd = -1;
+	iface->raw_fd = -1;
 	iface->udp_fd = -1;
-#ifdef ENABLE_ARP
 	iface->arp_fd = -1;
-#endif
+	iface->link_fd = -1;
 
 eexit:
 	close(s);
@@ -419,21 +486,32 @@
 		struct sockaddr sa;
 		struct sockaddr_in sin;
 	} su;
-	int n = 1;
+	int n;
+#ifdef SO_BINDTODEVICE
+	struct ifreq ifr;
+#endif
 
 	if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
 		return -1;
 
+	n = 1;
+	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
+		goto eexit;
+#ifdef SO_BINDTODEVICE
+	memset(&ifr, 0, sizeof(ifr));
+	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
+	if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
+		goto eexit;
+#endif
+	/* As we don't use this socket for receiving, set the
+	 * receive buffer to 1 */
+	n = 1;
+	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
+		goto eexit;
 	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;
 
@@ -596,38 +674,34 @@
 	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;
+	uint8_t *p;
 	int retval;
 
-	arpsize = sizeof(*arp) + 2 * iface->hwlen + 2 *sizeof(sip);
-
+	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 = (uint8_t *)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;
+	/* ARP requests should ignore this */
+	retval = iface->hwlen;
+	while (retval--)
+		*p++ = '\0';
 	memcpy(p, &tip, sizeof(tip));
-
+	p += sizeof(tip);
 	retval = send_raw_packet(iface, ETHERTYPE_ARP, arp, arpsize);
 	free(arp);
 	return retval;
 }
-#endif
-
diff --git a/net.h b/net.h
index 0be56cf..3ade025 100644
--- a/net.h
+++ b/net.h
@@ -71,8 +71,8 @@
 #endif
 
 #define LINKLOCAL_ADDR	0xa9fe0000
-#define LINKLOCAL_MASK	0xffff0000
-#define LINKLOCAL_BRDC	0xa9feffff
+#define LINKLOCAL_MASK	IN_CLASSB_NET
+#define LINKLOCAL_BRDC	(LINKLOCAL_ADDR | ~LINKLOCAL_MASK)
 
 #ifndef IN_LINKLOCAL
 # define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
@@ -101,13 +101,12 @@
 	size_t hwlen;
 	int arpable;
 
-	int fd;
+	int raw_fd;
 	int udp_fd;
+	int arp_fd;
+	int link_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;
@@ -131,6 +130,7 @@
 int inet_ntocidr(struct in_addr);
 int inet_cidrtoaddr(int, struct in_addr *);
 
+int up_interface(const char *);
 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 *,
@@ -148,6 +148,8 @@
 	     const struct in_addr *, int, int);
 #define add_route(ifname, dest, mask, gate, metric) \
 	if_route(ifname, dest, mask, gate, metric, 1)
+#define change_route(ifname, dest, mask, gate, metric) \
+	if_route(ifname, dest, mask, gate, metric, 0)
 #define del_route(ifname, dest, mask, gate, metric) \
 	if_route(ifname, dest, mask, gate, metric, -1)
 void free_routes(struct rt *);
@@ -166,7 +168,9 @@
 			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
+
+int open_link_socket(struct interface *);
+int link_changed(struct interface *);
+int carrier_status(const char *);
 #endif
diff --git a/showlease.c b/showlease.c
new file mode 100644
index 0000000..9bee4ab
--- /dev/null
+++ b/showlease.c
@@ -0,0 +1,349 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "dhcp.h"
+#include "config.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)
+
+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 }
+};
+
+struct dhcp_message *
+get_lease(const char *leasefile)
+{
+	int fd;
+	struct dhcp_message *dhcp;
+	ssize_t bytes;
+
+	fd = open(leasefile, O_RDONLY);
+	if (fd == -1)
+		return NULL;
+	dhcp = malloc(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 uint8_t *dhcp_opt_buffer = NULL;
+
+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 = malloc(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 DHO_PAD:
+			continue;
+		case DHO_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 DHO_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;
+}
+
+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;
+}
+
+void showlease(struct dhcp_lease *lease)
+{
+    printf("addr:      %s\n", inet_ntoa(lease->addr));
+    printf("net:       %s\n", inet_ntoa(lease->net));
+    printf("leasetime: %d\n", lease->leasetime);
+    printf("renew:     %d\n", lease->renewaltime);
+    printf("rebind:    %d\n", lease->rebindtime);
+    printf("server:    %s\n", inet_ntoa(lease->server));
+}
+#define MAX_LEASETIME 2147460
+
+int
+main(int argc, char *argv[])
+{
+    struct dhcp_message *dhcp;
+    struct dhcp_lease *lease;
+    char leasefile[PATH_MAX];
+
+    if (argc < 2) {
+        fprintf(stderr, "Usage: %s <interface>\n", argv[0]);
+        exit(1);
+    }
+    snprintf(leasefile, PATH_MAX, LEASEFILE, argv[1]);
+    if ((dhcp = get_lease(leasefile)) == NULL) {
+        fprintf(stderr, "Couldn't read lease file: %s\n", strerror(errno));
+        exit(1);
+    }
+    lease = malloc(sizeof(*lease));
+    lease->frominfo = 0;
+    lease->addr.s_addr = dhcp->yiaddr;
+
+    if (get_option_addr(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
+        lease->net.s_addr = get_netmask(dhcp->yiaddr);
+    if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) != 0)
+        lease->leasetime = DEFAULT_LEASETIME;
+    get_option_addr(&lease->server.s_addr, dhcp, DHO_SERVERID);
+    /* Dm: limit lease time value to avoid negative numbers when
+       converting to milliseconds */		
+    if ((lease->leasetime != ~0U) && (lease->leasetime > MAX_LEASETIME))
+        lease->leasetime = MAX_LEASETIME;
+    if (get_option_uint32(&lease->renewaltime, dhcp, DHO_RENEWALTIME) != 0)
+        lease->renewaltime = 0;
+    if (get_option_uint32(&lease->rebindtime, dhcp, DHO_REBINDTIME) != 0)
+        lease->rebindtime = 0;
+    showlease(lease);
+    free(lease);
+    return 0;
+}
diff --git a/signals.c b/signals.c
index fb986d7..58679d6 100644
--- a/signals.c
+++ b/signals.c
@@ -29,7 +29,6 @@
 #include <sys/socket.h>
 
 #include <errno.h>
-#include <poll.h>
 #include <signal.h>
 #include <string.h>
 #include <unistd.h>
@@ -62,31 +61,20 @@
 	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)
+signal_read(void)
 {
 	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));
-	}
+	memset(buf, 0, sizeof(buf));
+	bytes = read(signal_pipe[0], buf, sizeof(buf));
+	if (bytes >= sizeof(sig))
+		memcpy(&sig, buf, sizeof(sig));
 	return sig;
 }
 
diff --git a/signals.h b/signals.h
index 5972b31..f784a68 100644
--- a/signals.h
+++ b/signals.h
@@ -28,13 +28,10 @@
 #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);
+int signal_read(void);
 
 #endif