dhcpcd: Upgrade from 4.0.1 to 4.0.15

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/README b/README
index 50294fe..1dff417 100644
--- a/README
+++ b/README
@@ -56,6 +56,13 @@
 the dhcpcd.duid file exits. This keeps the clients working as they were,
 which is good.
 
+dhcpcd no longer sends a default ClientID for ethernet interfaces. 
+This is so we can re-use the address the kernel DHCP client found. 
+To retain the old behaviour of sending a default ClientID based on the 
+hardware address for interface, simply add the keyword clientid to dhcpcd.conf.
+If CMDLINE_COMPAT is defined, we renable the sending of ClientID by default
+AND adding clientid to dhcpcd.conf causes it NOT to be sent.
+
 dhcpcd-4 is NOT fully commandline compatible with dhcpcd-2 and older and
 changes the meaning of some options.
 
@@ -63,5 +70,5 @@
 ChangeLog
 ---------
 We no longer supply a ChangeLog.
-However, you're more than welcome to read the git commit comments at
-http://git.marples.name/?p=dhcpcd.git;a=summary
+However, you're more than welcome to read the commit log at
+http://roy.marples.name/projects/dhcpcd/log/
diff --git a/client.c b/client.c
index c34d318..90657b3 100644
--- a/client.c
+++ b/client.c
@@ -217,7 +217,8 @@
 			setsid();
 			/* Notify parent it's safe to exit as we've detached. */
 			close(sidpipe[0]);
-			write(sidpipe[1], &buf, 1);
+			if (write(sidpipe[1], &buf, 1) != 1)
+				logger(LOG_ERR, "write: %s", strerror(errno));
 			close(sidpipe[1]);
 			close_fds();
 			break;
@@ -226,7 +227,8 @@
 			signal_reset();
 			/* Wait for child to detach */
 			close(sidpipe[1]);
-			read(sidpipe[0], &buf, 1);
+			if (read(sidpipe[0], &buf, 1) != 1)
+				logger(LOG_ERR, "read: %s", strerror(errno));
 			close(sidpipe[0]);
 			break;
 	}
@@ -362,10 +364,11 @@
 {
 	time_t t;
 
-	lease->frominfo = 0;
+	if (lease->frominfo)
+		return;
 	lease->addr.s_addr = dhcp->yiaddr;
 
-	if (get_option_addr(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
+	if (get_option_addr(&lease->net, dhcp, DHO_SUBNETMASK) == -1)
 		lease->net.s_addr = get_netmask(dhcp->yiaddr);
 	if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) {
 		/* Ensure that we can use the lease */
@@ -405,6 +408,7 @@
 		logger(LOG_INFO, "read_lease: %s", strerror(errno));
 		goto eexit;
 	}
+	lease->frominfo = 0;
 	get_lease(&state->lease, dhcp);
 	lease->frominfo = 1;
 	lease->leasedfrom = sb.st_mtime;
@@ -437,12 +441,11 @@
 			else
 				goto eexit;
 		}
+		lease->leasetime -= offset;
+		lease->rebindtime -= offset;
+		lease->renewaltime -= offset;
 	}
 
-	if (lease->leasedfrom == 0)
-		offset = 0;
-	iface->start_uptime = uptime();
-	state->timeout.tv_sec = lease->renewaltime - offset;
 	free(state->old);
 	state->old = state->new;
 	state->new = NULL;
@@ -492,16 +495,6 @@
 		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)
@@ -519,12 +512,18 @@
 		iface->net.s_addr = lease->net.s_addr;
 	}
 
+       /* If we haven't specified a ClientID and our hardware address 
+	* length is greater than DHCP_CHADDR_LEN then we enforce a ClientID 
+	* of the hardware address family and the hardware address. */ 
+	if (!(state->options & DHCPCD_CLIENTID) && iface->hwlen > DHCP_CHADDR_LEN) 
+		state->options |= DHCPCD_CLIENTID; 
+ 
 	if (*options->clientid) {
 		iface->clientid = xmalloc(options->clientid[0] + 1);
 		memcpy(iface->clientid,
 		       options->clientid, options->clientid[0] + 1);
-	} else if (options->options & DHCPCD_CLIENTID) {
-		if (options->options & DHCPCD_DUID) {
+	} else if (state->options & DHCPCD_CLIENTID) {
+		if (state->options & DHCPCD_DUID) {
 			duid = xmalloc(DUID_LEN);
 			if ((len = get_duid(duid, iface)) == 0)
 				logger(LOG_ERR, "get_duid: %s",
@@ -686,7 +685,9 @@
 	if (r == -1) {
 		state->state = STATE_INIT;
 		timerclear(&state->timeout);
-		timerclear(&state->stop);
+		/* We need to set a timeout so we fall through gracefully */
+		state->stop.tv_sec = 1;
+		state->stop.tv_usec = 0;
 		do_socket(state, SOCKET_CLOSED);
 	}
 	return r;
@@ -836,12 +837,24 @@
 	}
 
 	/* We configured our array in the order we should deal with them */
-	for (i = 0; i < nfds; i++)
-		if (fds[i].revents & POLLIN) {
+	for (i = 0; i < nfds; i++) {
+		if (fds[i].revents & POLLERR) {
+			syslog(LOG_ERR, "poll: POLLERR on fd %d", fds[i].fd);
+			errno = EBADF;
+			return -1;
+		}
+		if (fds[i].revents & POLLNVAL) {
+			syslog(LOG_ERR, "poll: POLLNVAL on fd %d", fds[i].fd);
+			errno = EINVAL;
+			return -1;
+		}
+		if (fds[i].revents & (POLLIN | POLLHUP)) {
 			*fd = fds[i].fd;
 			return r;
 		}
-	return r;
+	}
+	/* We should never get here. */
+	return 0;
 }
 
 static int
@@ -940,24 +953,24 @@
 			state->state = STATE_BOUND;
 			timerclear(&state->stop);
 		} else {
-			if (lease->rebindtime >= lease->leasetime) {
+			if (lease->rebindtime == 0)
+				lease->rebindtime = lease->leasetime * T2;
+			else if (lease->rebindtime >= lease->leasetime) {
 				lease->rebindtime = lease->leasetime * T2;
 				logger(LOG_ERR,
 				       "rebind time greater than lease "
 				       "time, forcing to %u seconds",
 				       lease->rebindtime);
 			}
-			if (lease->renewaltime > lease->rebindtime) {
+			if (lease->renewaltime == 0)
+				lease->renewaltime = lease->leasetime * T1;
+			else if (lease->renewaltime > lease->rebindtime) {
 				lease->renewaltime = lease->leasetime * T1;
 				logger(LOG_ERR,
 				       "renewal time greater than rebind time, "
 				       "forcing to %u seconds",
 				       lease->renewaltime);
 			}
-			if (!lease->renewaltime)
-				lease->renewaltime = lease->leasetime * T1;
-			if (!lease->rebindtime)
-				lease->rebindtime = lease->leasetime * T2;
 			logger(LOG_INFO,
 			       "leased %s for %u seconds",
 			       inet_ntoa(lease->addr), lease->leasetime);
@@ -1001,7 +1014,7 @@
 {
 	struct dhcp_lease *lease = &state->lease;
 	struct interface *iface = state->interface;
-	int gotlease = -1;
+	int gotlease = -1, r;
 	const char *reason = NULL;
 
 	timerclear(&state->stop);
@@ -1040,12 +1053,14 @@
 		{
 			logger(LOG_INFO, "probing for an IPV4LL address");
 			free(state->offer);
+			lease->frominfo = 0;
 			state->offer = ipv4ll_get_dhcp(0);
 			gotlease = 0;
 		}
 
 		if (gotlease == 0 &&
-		    state->offer->yiaddr != iface->addr.s_addr)
+		    state->offer->yiaddr != iface->addr.s_addr &&
+		    state->options & DHCPCD_ARP)
 		{
 			state->state = STATE_PROBING;
 			state->claims = 0;
@@ -1055,9 +1070,12 @@
 			return 1;
 		}
 
-		if (gotlease == 0)
-			return bind_dhcp(state, options);
-
+		if (gotlease == 0) {
+			r = bind_dhcp(state, options);
+			logger(LOG_DEBUG, "renew in %ld seconds",
+				(long int)state->stop.tv_sec);
+			return r;
+		}
 		if (iface->addr.s_addr)
 			reason = "EXPIRE";
 		else
@@ -1170,14 +1188,11 @@
 		} else {
 			/* We've waited for ANNOUNCE_WAIT after the final probe
 			 * so the address is now ours */
-			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;
+			i = bind_dhcp(state, options);
+			state->state = STATE_ANNOUNCING;
+			state->timeout.tv_sec = ANNOUNCE_INTERVAL;
+			state->timeout.tv_usec = 0;
+			return i;
 		}
 		break;
 	case STATE_ANNOUNCING:
@@ -1246,6 +1261,8 @@
 		timerclear(&state->stop);
 		/* FALLTHROUGH */
 	case STATE_INIT:
+		if (state->carrier == LINK_DOWN)
+			return 0;
 		do_socket(state, SOCKET_OPEN);
 		state->xid = arc4random();
 		iface->start_uptime = uptime();
@@ -1269,8 +1286,6 @@
 		}
 		/* FALLTHROUGH */
 	case STATE_INIT:
-		if (state->carrier == LINK_DOWN)
-			return 0;
 		if (lease->addr.s_addr == 0 ||
 		    IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
 		{
@@ -1346,7 +1361,7 @@
 		addr.s_addr = dhcp->yiaddr;
 		a = xstrdup(inet_ntoa(addr));
 	}
-	r = get_option_addr(&addr.s_addr, dhcp, DHO_SERVERID);
+	r = get_option_addr(&addr, dhcp, DHO_SERVERID);
 	if (dhcp->servername[0] && r == 0)
 		logger(lvl, "%s %s from %s `%s'", msg, a,
 		       inet_ntoa(addr), dhcp->servername);
@@ -1374,7 +1389,12 @@
 
 	/* 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);
+		logger(LOG_ERR, "ignoring message; no DHCP type");
+		return 0;
+	}
+	/* Every DHCP message should include ServerID */
+	if (get_option_addr(&addr, dhcp, DHO_SERVERID) == -1) {
+		logger(LOG_ERR, "ignoring message; no Server ID");
 		return 0;
 	}
 
@@ -1382,7 +1402,7 @@
 	 * 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)
+	    get_option_addr(&addr, dhcp, DHO_SERVERID) == 0)
 	{
 		for (i = 0; i < options->blacklist_len; i++) {
 			if (options->blacklist[i] != addr.s_addr)
@@ -1434,7 +1454,7 @@
 
 	if (type == DHCP_OFFER && state->state == STATE_DISCOVERING) {
 		lease->addr.s_addr = dhcp->yiaddr;
-		get_option_addr(&lease->server.s_addr, dhcp, DHO_SERVERID);
+		get_option_addr(&lease->server, dhcp, DHO_SERVERID);
 		log_dhcp(LOG_INFO, "offered", dhcp);
 		if (state->options & DHCPCD_TEST) {
 			run_script(options, iface->name, "TEST", dhcp, NULL);
@@ -1446,21 +1466,6 @@
 		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;
 		return 1;
 	}
@@ -1482,7 +1487,7 @@
 	case STATE_RENEWING:
 	case STATE_REBINDING:
 		if (!(state->options & DHCPCD_INFORM)) {
-			get_option_addr(&lease->server.s_addr,
+			get_option_addr(&lease->server,
 					dhcp, DHO_SERVERID);
 			log_dhcp(LOG_INFO, "acknowledged", dhcp);
 		}
@@ -1494,7 +1499,24 @@
 		logger(LOG_ERR, "wrong state %d", state->state);
 	}
 
+	lease->frominfo = 0;
 	do_socket(state, SOCKET_CLOSED);
+	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;
+		}
+	}
+
 	r = bind_dhcp(state, options);
 	if (!(state->options & DHCPCD_ARP)) {
 		if (!(state->options & DHCPCD_INFORM))
@@ -1515,7 +1537,6 @@
 	struct interface *iface = state->interface;
 	struct dhcp_message *dhcp = NULL;
 	const uint8_t *pp;
-	uint8_t *p;
 	ssize_t bytes;
 	int retval = -1;
 
@@ -1532,7 +1553,7 @@
 		}
 		if (bytes == -1)
 			break;
-		if (valid_udp_packet(packet) == -1)
+		if (valid_udp_packet(packet, bytes) == -1)
 			continue;
 		bytes = get_udp_data(&pp, packet);
 		if ((size_t)bytes > sizeof(*dhcp)) {
@@ -1540,7 +1561,7 @@
 			continue;
 		}
 		if (!dhcp)
-			dhcp = xmalloc(sizeof(*dhcp));
+			dhcp = xzalloc(sizeof(*dhcp));
 		memcpy(dhcp, pp, bytes);
 		if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
 			logger(LOG_DEBUG, "bogus cookie, ignoring");
@@ -1563,15 +1584,6 @@
 			       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 == DHO_PAD)
-				p--;
-			if (*p != DHO_END)
-				*++p = DHO_END;
-		}
 		retval = handle_dhcp(state, &dhcp, options);
 		if (retval == 0 && state->options & DHCPCD_TEST)
 			state->options |= DHCPCD_FORKED;
@@ -1658,17 +1670,19 @@
 	int cookie = state->offer->cookie;
 
 	if (!IN_LINKLOCAL(htonl(state->fail.s_addr))) {
+		if (cookie) {
+			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);
+		}
 		state->state = STATE_INIT;
 		free(state->offer);
 		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;
 	}
 
@@ -1720,7 +1734,6 @@
 	if (retval == 0)
 		return 0;
 
-	timerclear(&state->timeout);
 	switch (carrier_status(state->interface->name)) {
 	case -1:
 		logger(LOG_ERR, "carrier_status: %s", strerror(errno));
@@ -1730,6 +1743,7 @@
 			logger(LOG_INFO, "carrier lost");
 			state->carrier = LINK_DOWN;
 			do_socket(state, SOCKET_CLOSED);
+			timerclear(&state->timeout);
 			if (state->state != STATE_BOUND)
 				timerclear(&state->stop);
 		}
@@ -1739,6 +1753,7 @@
 			logger(LOG_INFO, "carrier acquired");
 			state->state = STATE_RENEW_REQUESTED;
 			state->carrier = LINK_UP;
+			timerclear(&state->timeout);
 			timerclear(&state->stop);
 			return 1;
 		}
diff --git a/common.c b/common.c
index d90c7d2..da22a5c 100644
--- a/common.c
+++ b/common.c
@@ -69,7 +69,8 @@
 		}
 		p = *line + last;
 		memset(p, 0, BUFSIZ);
-		fgets(p, BUFSIZ, fp);
+		if (fgets(p, BUFSIZ, fp) == NULL)
+			break;
 		last += strlen(p);
 		if (last && (*line)[last - 1] == '\n') {
 			(*line)[last - 1] = '\0';
@@ -201,9 +202,8 @@
 	if (posix_clock_set == 0) {
 		if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
 			posix_clock = CLOCK_MONOTONIC;
-			clock_monotonic = 1;
+			clock_monotonic = posix_clock_set = 1;
 		}
-		posix_clock_set = 1;
 	}
 
 	if (clock_monotonic) {
@@ -225,9 +225,8 @@
 	if (posix_clock_set == 0) {
 		if (mach_timebase_info(&info) == KERN_SUCCESS) {
 			factor = (double)info.numer / (double)info.denom;
-			clock_monotonic = 1;	
+			clock_monotonic = posix_clock_set = 1;
 		}
-		posix_clock_set = 1;
 	}
 	if (clock_monotonic) {
 		nano = mach_absolute_time();
diff --git a/config.h b/config.h
index cff7079..595fb94 100644
--- a/config.h
+++ b/config.h
@@ -28,7 +28,7 @@
 #define CONFIG_H
 
 #define PACKAGE			"dhcpcd"
-#define VERSION			"4.0.1"
+#define VERSION			"4.0.15"
 
 /*
  * By default we don't add a local link route if we got a routeable address.
diff --git a/configure.c b/configure.c
index dad3bce..1e6daeb 100644
--- a/configure.c
+++ b/configure.c
@@ -181,7 +181,7 @@
 }
 
 static int
-delete_route(const char *iface, struct rt *rt, int metric)
+delete_route(const struct interface *iface, struct rt *rt, int metric)
 {
 	char *addr;
 	int retval;
@@ -206,7 +206,7 @@
 	rt = reverse_routes(iface->routes);
 	while (rt) {
 		rtn = rt->next;
-		retval += delete_route(iface->name, rt, metric);
+		retval += delete_route(iface, rt, metric);
 		free(rt);
 		rt = rtn;
 	}
@@ -272,7 +272,7 @@
 	iface->routes = reverse_routes(iface->routes);
 	for (rt = iface->routes; rt; rt = rt->next)
 		if (in_routes(ort, rt) != 0)
-			delete_route(iface->name, rt, options->metric);
+			delete_route(iface, rt, options->metric);
 
 	for (rt = ort; rt; rt = rt->next) {
 		/* Don't set default routes if not asked to */
@@ -285,7 +285,7 @@
 		logger(LOG_DEBUG, "adding route to %s/%d via %s",
 			addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
 		free(addr);
-		remember = add_route(iface->name, &rt->dest,
+		remember = add_route(iface, &rt->dest,
 				     &rt->net, &rt->gate,
 				     options->metric);
 		retval += remember;
@@ -357,9 +357,9 @@
 		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, DHO_SUBNETMASK) == -1)
+		if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1)
 			net.s_addr = get_netmask(addr.s_addr);
-		if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1)
+		if (get_option_addr(&brd, dhcp, DHO_BROADCAST) == -1)
 			brd.s_addr = addr.s_addr | ~net.s_addr;
 	}
 
@@ -400,21 +400,17 @@
 	{
 		dest.s_addr = addr.s_addr & net.s_addr;
 		gate.s_addr = 0;
-		add_route(iface->name, &dest, &net, &gate, options->metric);
-		del_route(iface->name, &dest, &net, &gate, 0);
+		add_route(iface, &dest, &net, &gate, options->metric);
+		del_route(iface, &dest, &net, &gate, 0);
 	}
 #endif
 
-	configure_routes(iface, dhcp, options);
-	up = (iface->addr.s_addr != addr.s_addr ||
-	      iface->net.s_addr != net.s_addr);
 	iface->addr.s_addr = addr.s_addr;
 	iface->net.s_addr = net.s_addr;
-
+	configure_routes(iface, dhcp, options);
 	if (!lease->frominfo)
 		if (write_lease(iface, dhcp) == -1)
 			logger(LOG_ERR, "write_lease: %s", strerror(errno));
-
 	run_script(options, iface->name, reason, dhcp, old);
 	return 0;
 }
diff --git a/dhcp.c b/dhcp.c
index 2469429..1854cf2 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -52,6 +52,8 @@
 
 #define IPV4R	IPV4 | REQUEST
 
+#define DAD	"Duplicate address detected"
+
 /* Our aggregate option buffer.
  * We ONLY use this when options are split, which for most purposes is
  * practically never. See RFC3396 for details. */
@@ -65,8 +67,13 @@
 
 static const struct dhcp_opt const dhcp_opts[] = {
 	{ 1,	IPV4 | REQUEST,	"subnet_mask" },
-	{ 2,	UINT32,		"time_offset" },
+	/* RFC 3442 states that the CSR has to come before all other routes.
+	 * For completeness, we also specify static routes, then routers. */
+	{ 121,  RFC3442 | REQUEST,	"classless_static_routes" },
+	{ 249,  RFC3442,	"ms_classless_static_routes" },
+	{ 33,	IPV4 | ARRAY | REQUEST,	"static_routes" },
 	{ 3,	IPV4 | ARRAY | REQUEST,	"routers" },
+	{ 2,	UINT32,		"time_offset" },
 	{ 4,	IPV4 | ARRAY,	"time_servers" },
 	{ 5,	IPV4 | ARRAY,	"ien116_name_servers" },
 	{ 6,	IPV4 | ARRAY,	"domain_name_servers" },
@@ -96,7 +103,6 @@
 	{ 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" },
@@ -151,8 +157,6 @@
 	{ 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 }
 };
 
@@ -323,25 +327,27 @@
 }
 
 int
-get_option_addr(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option)
+get_option_addr(struct in_addr *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));
+	memcpy(&a->s_addr, p, sizeof(a->s_addr));
 	return 0;
 }
 
 int
 get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option)
 {
-	uint32_t a;
+	const uint8_t *p = get_option_raw(dhcp, option);
+	uint32_t d;
 
-	if (get_option_addr(&a, dhcp, option) == -1)
+	if (!p)
 		return -1;
-
-	*i = ntohl(a);
+	memcpy(&d, p, sizeof(d));
+	*i = ntohl(d);
 	return 0;
 }
 
@@ -715,34 +721,39 @@
 }
 
 static size_t
-encode_rfc1035(const char *src, uint8_t *dst, size_t len)
+encode_rfc1035(const char *src, uint8_t *dst)
 {
-	const char *c = src;
 	uint8_t *p = dst;
 	uint8_t *lp = p++;
 
-	if (len == 0)
+	if (*src == '\0')
 		return 0;
-	while (c < src + len) {
-		if (*c == '\0')
+	for (; *src; src++) {
+		if (*src == '\0')
 			break;
-		if (*c == '.') {
+		if (*src == '.') {
 			/* Skip the trailing . */
-			if (c == src + len - 1)
+			if (src[1] == '\0')
 				break;
 			*lp = p - lp - 1;
 			if (*lp == '\0')
 				return p - dst;
 			lp = p++;
 		} else
-			*p++ = (uint8_t) *c;
-		c++;
+			*p++ = (uint8_t)*src;
 	}
 	*lp = p - lp - 1;
 	*p++ = '\0';
 	return p - dst;
 }
 
+#define PUTADDR(_type, _val) \
+{ \
+	*p++ = _type; \
+	*p++ = 4; \
+	memcpy(p, &_val.s_addr, 4); \
+	p += 4; \
+}
 ssize_t
 make_message(struct dhcp_message **message,
 	     const struct interface *iface, const struct dhcp_lease *lease,
@@ -755,6 +766,8 @@
 	uint32_t ul;
 	uint16_t sz;
 	const struct dhcp_opt *opt;
+	size_t len;
+	const char *hp;
 
 	dhcp = xzalloc(sizeof (*dhcp));
 	m = (uint8_t *)dhcp;
@@ -786,15 +799,18 @@
 	case ARPHRD_IEEE1394:
 	case ARPHRD_INFINIBAND:
 		dhcp->hwlen = 0;
-		if (dhcp->ciaddr == 0)
+		if (dhcp->ciaddr == 0 &&
+		    type != DHCP_DECLINE && type != DHCP_RELEASE)
 			dhcp->flags = htons(BROADCAST_FLAG);
 		break;
 	}
 
-	if (up < 0 || up > (time_t)UINT16_MAX)
-		dhcp->secs = htons((uint16_t)UINT16_MAX);
-	else
-		dhcp->secs = htons(up);
+	if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
+		if (up < 0 || up > (time_t)UINT16_MAX)
+			dhcp->secs = htons((uint16_t)UINT16_MAX);
+		else
+			dhcp->secs = htons(up);
+	}
 	dhcp->xid = xid;
 	dhcp->cookie = htonl(MAGIC_COOKIE);
 
@@ -802,7 +818,41 @@
 	*p++ = 1;
 	*p++ = type;
 
-	if (type == DHCP_REQUEST) {
+	if (iface->clientid) {
+		*p++ = DHO_CLIENTID;
+		memcpy(p, iface->clientid, iface->clientid[0] + 1);
+		p += iface->clientid[0] + 1;
+	}
+
+	if (lease->addr.s_addr && !IN_LINKLOCAL(htonl(lease->addr.s_addr))) {
+		if (type == DHCP_DECLINE ||
+		    type == DHCP_DISCOVER ||
+		    (type == DHCP_REQUEST &&
+		     lease->addr.s_addr != iface->addr.s_addr))
+		{
+			PUTADDR(DHO_IPADDRESS, lease->addr);
+			if (lease->server.s_addr)
+				PUTADDR(DHO_SERVERID, lease->server);
+		}
+	}
+
+	if (type == DHCP_DECLINE) {
+		*p++ = DHO_MESSAGE;
+		len = strlen(DAD);
+		*p++ = len;
+		memcpy(p, DAD, len);
+		p += len;
+	}
+
+	if (type == DHCP_RELEASE) {
+		if (lease->server.s_addr)
+			PUTADDR(DHO_SERVERID, lease->server);
+	}
+
+	if (type == DHCP_DISCOVER ||
+	    type == DHCP_INFORM ||
+	    type == DHCP_REQUEST)
+	{
 		*p++ = DHO_MAXMESSAGESIZE;
 		*p++ = 2;
 		sz = get_mtu(iface->name);
@@ -813,15 +863,7 @@
 		sz = htons(sz);
 		memcpy(p, &sz, 2);
 		p += 2;
-	}
 
-	if (iface->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++ = DHO_USERCLASS;
 			memcpy(p, options->userclass, options->userclass[0] + 1);
@@ -834,43 +876,31 @@
 			       options->vendorclassid[0] + 1);
 			p += options->vendorclassid[0] + 1;
 		}
-	}
 
-	if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
-#define PUTADDR(_type, _val) \
-		{ \
-			*p++ = _type; \
-			*p++ = 4; \
-			memcpy(p, &_val.s_addr, 4); \
-			p += 4; \
+		if (type != DHCP_INFORM) {
+			if (options->leasetime != 0) {
+				*p++ = DHO_LEASETIME;
+				*p++ = 4;
+				ul = htonl(options->leasetime);
+				memcpy(p, &ul, 4);
+				p += 4;
+			}
 		}
-		if (lease->addr.s_addr &&
-		    lease->addr.s_addr != iface->addr.s_addr &&
-		    !IN_LINKLOCAL(ntohl(lease->addr.s_addr)))
-		{
-			PUTADDR(DHO_IPADDRESS, lease->addr);
-			if (lease->server.s_addr)
-				PUTADDR(DHO_SERVERID, lease->server);
-		}
-#undef PUTADDR
 
-		if (options->leasetime != 0) {
-			*p++ = DHO_LEASETIME;
-			*p++ = 4;
-			ul = htonl(options->leasetime);
-			memcpy(p, &ul, 4);
-			p += 4;
-		}
-	}
-
-	if (type == DHCP_DISCOVER ||
-	    type == DHCP_INFORM ||
-	    type == DHCP_REQUEST)
-	{
+		/* Regardless of RFC2132, we should always send a hostname
+		 * upto the first dot (the short hostname) as otherwise
+		 * confuses some DHCP servers when updating DNS.
+		 * The FQDN option should be used if a FQDN is required. */
 		if (options->hostname[0]) {
 			*p++ = DHO_HOSTNAME;
-			memcpy(p, options->hostname, options->hostname[0] + 1);
-			p += options->hostname[0] + 1;
+			hp = strchr(options->hostname, '.');
+			if (hp)
+				len = hp - options->hostname;
+			else
+				len = strlen(options->hostname);
+			*p++ = len;
+			memcpy(p, options->hostname, len);
+			p += len;
 		}
 		if (options->fqdn != FQDN_DISABLE) {
 			/* IETF DHC-FQDN option (81), RFC4702 */
@@ -890,8 +920,7 @@
 			*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]);
+			ul = encode_rfc1035(options->hostname, p);
 			*lp += ul;
 			p += ul;
 		}
@@ -999,13 +1028,21 @@
 print_string(char *s, ssize_t len, int dl, const uint8_t *data)
 {
 	uint8_t c;
-	const uint8_t *e;
+	const uint8_t *e, *p;
 	ssize_t bytes = 0;
 	ssize_t r;
 
 	e = data + dl;
 	while (data < e) {
 		c = *data++;
+		if (c == '\0') {
+			/* If rest is all NULL, skip it. */
+			for (p = data; p < e; p++)
+				if (*p != '\0')
+					break;
+			if (p == e)
+				break;
+		}
 		if (!isascii(c) || !isprint(c)) {
 			if (s) {
 				if (len < 5) {
@@ -1203,16 +1240,16 @@
 		 * 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, DHO_SUBNETMASK) == -1) {
+		if (get_option_addr(&net, 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, DHO_BROADCAST) == -1) {
+		if (get_option_addr(&brd, dhcp, DHO_BROADCAST) == -1) {
 			brd.s_addr = addr.s_addr | ~net.s_addr;
-			setvar(&ep, prefix, "broadcast_address", inet_ntoa(net));
+			setvar(&ep, prefix, "broadcast_address", inet_ntoa(brd));
 		}
 		addr.s_addr = dhcp->yiaddr & net.s_addr;
 		setvar(&ep, prefix, "network_number", inet_ntoa(addr));
diff --git a/dhcp.h b/dhcp.h
index e584452..09e8ecb 100644
--- a/dhcp.h
+++ b/dhcp.h
@@ -160,7 +160,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);
+int get_option_addr(struct in_addr *, const struct dhcp_message *, uint8_t);
 int get_option_uint32(uint32_t *, const struct dhcp_message *, uint8_t);
 int get_option_uint16(uint16_t *, const struct dhcp_message *, uint8_t);
 int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
diff --git a/dhcpcd-hooks/01-test b/dhcpcd-hooks/01-test
index 609b3a1..ef7896d 100644
--- a/dhcpcd-hooks/01-test
+++ b/dhcpcd-hooks/01-test
@@ -1,7 +1,6 @@
 # Just echo our DHCP options we have
 
-case ${reason} in
-TEST)   set | grep "^\(interface\|metric\|pid\|reason\|skip_hooks\)=" | sort
+if [ "${reason}" = "TEST" ]; then
+	set | grep "^\(interface\|metric\|pid\|reason\|skip_hooks\)=" | sort
 	set | grep "^\(new_\|old_\)" | sort
-	;;
-esac
+fi
diff --git a/dhcpcd-hooks/50-dhcpcd-compat b/dhcpcd-hooks/50-dhcpcd-compat
index cba40a4..bb31fd3 100644
--- a/dhcpcd-hooks/50-dhcpcd-compat
+++ b/dhcpcd-hooks/50-dhcpcd-compat
@@ -28,4 +28,14 @@
 RENEW) x="up";;
 BOUND|INFORM|REBIND|REBOOT|TEST|TIMEOUT|IPV4LL) x="new";;
 esac
-set -- "" "${x}"
+
+if [ "${reason}" != "down" ]; then
+	rm -f /var/lib/dhcpcd-"${INTERFACE}".info
+	for x in IPADDR INTERFACE NETMASK BROADCAST NETWORK DHCPSID GATEWAYS \
+		DNSSERVERS DNSDOMAIN DNSSEARCH NISDOMAIN NISSERVERS \
+		NTPSERVERS GATEWAY DNS; do
+		eval echo "${x}=\'\$${x}\'" >> /var/lib/dhcpcd-"${INTERFACE}".info
+	done
+fi
+
+set -- /var/lib/dhcpcd-"${INTERFACE}".info "${x}"
diff --git a/dhcpcd-hooks/50-ntp.conf b/dhcpcd-hooks/50-ntp.conf
index 536f14e..8c92f27 100644
--- a/dhcpcd-hooks/50-ntp.conf
+++ b/dhcpcd-hooks/50-ntp.conf
@@ -2,21 +2,28 @@
 # Like our resolv.conf hook script, we store a database of ntp.conf files
 # and merge into /etc/ntp.conf
 
+# You can set the env var NTP_CONF to another file like this
+#   dhcpcd -e NTP_CONF=/usr/pkg/etc/ntpd.conf
+# or by adding this to /etc/dhcpcd.enter-hook
+#   NTP_CONF=/usr/pkg/etc/ntpd.conf
+# to use openntpd from pkgsrc instead of the system provided ntp.
+
 # Detect OpenRC or BSD rc
 # Distributions may want to just have their command here instead of this
 if type rc-service >/dev/null 2>&1 && rc-service --exists ntpd; then
 	ntpd_restart_cmd="rc-service ntpd -- --ifstarted --quiet restart"
 elif [ -x /etc/rc.d/ntpd ]; then
-	ntpd_restart_cmd="/etc/rc.d/ntpd restart"
+	ntpd_restart_cmd="/etc/rc.d/ntpd status && /etc/rc.d/ntpd restart"
 elif [ -x /usr/local/etc/rc.d/ntpd ]; then
-	ntpd_restart_cmd="/usr/local/etc/rc.d/ntpd restart"
+	ntpd_restart_cmd="/usr/local/etc/rc.d/ntpd status && /usr/local/etc/rc.d/ntpd restart"
 fi
 
 ntp_conf_dir="${state_dir}/ntp.conf"
+ntp_conf=${NTP_CONF:-/etc/ntp.conf}
 
 build_ntp_conf()
 {
-	local cf="/etc/ntp.conf.${interface}"
+	local cf="${ntp_conf}.${interface}"
 	local interfaces= header= srvs= servers= x=
 
 	# Build a list of interfaces
@@ -49,8 +56,8 @@
 	fi
 
 	# If we changed anything, restart ntpd
-	if change_file /etc/ntp.conf "${cf}"; then
-		[ -n "${ntpd_restart_cmd}" ] && ${ntpd_restart_cmd}
+	if change_file "${ntp_conf}" "${cf}"; then
+		[ -n "${ntpd_restart_cmd}" ] && eval ${ntpd_restart_cmd}
 	fi
 }
 
diff --git a/dhcpcd-run-hooks b/dhcpcd-run-hooks
old mode 100755
new mode 100644
index db9c4f8..8b68c69
--- a/dhcpcd-run-hooks
+++ b/dhcpcd-run-hooks
@@ -20,7 +20,7 @@
 do
 	for skip in ${skip_hooks}; do
 		case "${hook}" in
-			"${skip}")			continue 2;;
+			*/"${skip}")			continue 2;;
 			*/[0-9][0-9]"-${skip}")		continue 2;;
 			*/[0-9][0-9]"-${skip}.sh")	continue 2;;
 		esac
diff --git a/dhcpcd-run-hooks.8 b/dhcpcd-run-hooks.8
index dca5378..a6a1849 100644
--- a/dhcpcd-run-hooks.8
+++ b/dhcpcd-run-hooks.8
@@ -110,4 +110,4 @@
 .Sh AUTHORS
 .An Roy Marples <roy@marples.name>
 .Sh BUGS
-Please report them to http://bugs.marples.name
+Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd-run-hooks.8.in b/dhcpcd-run-hooks.8.in
index 72669f5..6776cf8 100644
--- a/dhcpcd-run-hooks.8.in
+++ b/dhcpcd-run-hooks.8.in
@@ -24,6 +24,7 @@
 .\"
 .Dd August 14, 2008
 .Dt DHCPCD.SH 8 SMM
+.Os
 .Sh NAME
 .Nm dhcpcd-run-hooks
 .Nd DHCP client configuration script 
@@ -110,4 +111,4 @@
 .Sh AUTHORS
 .An Roy Marples <roy@marples.name>
 .Sh BUGS
-Please report them to http://bugs.marples.name
+Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd-run-hooks.in b/dhcpcd-run-hooks.in
index 1e5d5b3..a848260 100644
--- a/dhcpcd-run-hooks.in
+++ b/dhcpcd-run-hooks.in
@@ -79,7 +79,7 @@
 }
 
 # Compare two files
-# It different, replace first with second otherwise remove second
+# If different, replace first with second otherwise remove second
 change_file()
 {
 	if type cmp >/dev/null 2>&1; then
@@ -127,7 +127,7 @@
 do
 	for skip in ${skip_hooks}; do
 		case "${hook}" in
-			"${skip}")			continue 2;;
+			*/"${skip}")			continue 2;;
 			*/[0-9][0-9]"-${skip}")		continue 2;;
 			*/[0-9][0-9]"-${skip}.sh")	continue 2;;
 		esac
diff --git a/dhcpcd.8.in b/dhcpcd.8.in
index 6c82d3f..d3fbebf 100644
--- a/dhcpcd.8.in
+++ b/dhcpcd.8.in
@@ -22,14 +22,15 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd August 20, 2008
+.Dd November 18, 2008
 .Dt DHCPCD 8 SMM
+.Os
 .Sh NAME
 .Nm dhcpcd
 .Nd an RFC 2131 compliant DHCP client
 .Sh SYNOPSIS
 .Nm
-.Op Fl bdknpqABDEGKLSTV
+.Op Fl bdknpqABDEGKLTV
 .Op Fl c , -script Ar script
 .Op Fl f , -config Ar file
 .Op Fl h , -hostname Ar hostname
@@ -324,11 +325,18 @@
 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.
+Send the
+.Ar clientid .
 If the string is of the format 01:02:03 then it is encoded as hex.
-If not set then none is sent.
+For interfaces whose hardware address is longer than 8 bytes, or if the
+.Ar clientid
+is an empty string then
+.Nm
+sends a default
+.Ar clientid
+of the hardware family and the hardware address.
 .El
-.Ss Restriciting behaviour
+.Ss Restricting behaviour
 .Nm
 will try to do as much as it can by default.
 However, there are sometimes situations where you don't want the things to be
@@ -427,4 +435,4 @@
 .Sh AUTHORS
 .An Roy Marples <roy@marples.name>
 .Sh BUGS
-Please report them to http://bugs.marples.name
+Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd.c b/dhcpcd.c
index e674bd2..ff95a83 100644
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -144,14 +144,15 @@
 read_pid(const char *pidfile)
 {
 	FILE *fp;
-	pid_t pid = 0;
+	pid_t pid;
 
 	if ((fp = fopen(pidfile, "r")) == NULL) {
 		errno = ENOENT;
 		return 0;
 	}
 
-	fscanf(fp, "%d", &pid);
+	if (fscanf(fp, "%d", &pid) != 1)
+		pid = 0;
 	fclose(fp);
 
 	return pid;
@@ -240,7 +241,7 @@
 	l = 0;
 	/* If processing a string on the clientid, first byte should be
 	 * 0 to indicate a non hardware type */
-	if (clid) {
+	if (clid && *str) {
 		*sbuf++ = 0;
 		l++;
 	}
@@ -323,7 +324,7 @@
 		break;
 	case 'h':
 		if (oarg)
-			s = parse_string(options->hostname + 1,
+			s = parse_string(options->hostname,
 					 HOSTNAME_MAX_LEN, oarg);
 		else
 			s = 0;
@@ -331,11 +332,11 @@
 			logger(LOG_ERR, "hostname: %s", strerror(errno));
 			return -1;
 		}
-		if (s != 0 && options->hostname[1] == '.') {
+		if (s != 0 && options->hostname[0] == '.') {
 			logger(LOG_ERR, "hostname cannot begin with a .");
 			return -1;
 		}
-		options->hostname[0] = (uint8_t)s;
+		options->hostname[s] = '\0';
 		break;
 	case 'i':
 		if (oarg)
@@ -528,10 +529,14 @@
 			return -1;
 		}
 		options->clientid[0] = (uint8_t)s;
+#ifdef CMDLINE_COMPAT
 		if (s == 0) {
 			options->options &= ~DHCPCD_DUID;
 			options->options &= ~DHCPCD_CLIENTID;
 		}
+#else
+		options->options |= DHCPCD_CLIENTID;
+#endif
 		break;
 	case 'K':
 		options->options &= ~DHCPCD_LINK;
@@ -646,11 +651,11 @@
 	closefrom(3);
 	/* Saves calling fflush(stream) in the logger */
 	setlinebuf(stdout);
-	openlog(PACKAGE, LOG_PID, LOG_LOCAL0);
+	openlog(PACKAGE, LOG_PID, LOG_DAEMON);
 	setlogprefix(PACKAGE ": ");
 
 	options = xzalloc(sizeof(*options));
-	options->options |= DHCPCD_CLIENTID | DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
+	options->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
 	options->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
 	options->timeout = DEFAULT_TIMEOUT;
 	strlcpy(options->script, SCRIPT, sizeof(options->script));
@@ -660,6 +665,7 @@
 					     "%s %s", PACKAGE, VERSION);
 
 #ifdef CMDLINE_COMPAT
+	options->options |= DHCPCD_CLIENTID;
 	add_option_mask(options->requestmask, DHO_DNSSERVER);
 	add_option_mask(options->requestmask, DHO_DNSDOMAIN);
 	add_option_mask(options->requestmask, DHO_DNSSEARCH);
@@ -675,11 +681,12 @@
 	}
 #endif
 
-	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);
+	gethostname(options->hostname, HOSTNAME_MAX_LEN);
+	/* Ensure that the hostname is NULL terminated */ 
+	options->hostname[HOSTNAME_MAX_LEN] = '\0';
+	if (strcmp(options->hostname, "(none)") == 0 ||
+	    strcmp(options->hostname, "localhost") == 0)
+		options->hostname[0] = '\0';
 
 	while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
 				  longopts, &option_index)) != -1)
@@ -815,14 +822,17 @@
 		case 'H': /* FALLTHROUGH */
 		case 'M':
 			del_option_mask(options->requestmask, DHO_MTU);
+			add_environ(options, "skip_hooks=mtu", 0);
 			break;
 		case 'N':
 			del_option_mask(options->requestmask, DHO_NTPSERVER);
+			add_environ(options, "skip_hooks=ntp.conf", 0);
 			break;
 		case 'R':
 			del_option_mask(options->requestmask, DHO_DNSSERVER);
 			del_option_mask(options->requestmask, DHO_DNSDOMAIN);
 			del_option_mask(options->requestmask, DHO_DNSSEARCH);
+			add_environ(options, "skip_hooks=resolv.conf", 0);
 			break;
 		case 'S':
 			add_option_mask(options->requestmask, DHO_MSCSR);
@@ -830,6 +840,7 @@
 		case 'Y':
 			del_option_mask(options->requestmask, DHO_NISSERVER);
 			del_option_mask(options->requestmask, DHO_NISDOMAIN);
+			add_environ(options, "skip_hooks=yp.conf", 0);
 			break;
 #endif
 		default:
@@ -903,7 +914,8 @@
 		goto abort;
 	}
 
-	chdir("/");
+	if (chdir("/") == -1)
+		logger(LOG_ERR, "chdir `/': %s", strerror(errno));
 	umask(022);
 
 	if (sig != 0 && !(options->options & DHCPCD_DAEMONISED)) {
diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in
index 899aea3..9e0a023 100644
--- a/dhcpcd.conf.5.in
+++ b/dhcpcd.conf.5.in
@@ -22,8 +22,9 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd August 18, 2008
+.Dd November 18, 2008
 .Dt DHCPCD.CONF 5 SMM
+.Os
 .Sh NAME
 .Nm dhcpcd.conf
 .Nd dhcpcd configuration file 
@@ -47,10 +48,24 @@
 Background immediately.
 This is useful for startup scripts which don't disable link messages for
 carrier status.
+.It Ic 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 .
 .It Ic clientid Ar string
-Change the default clientid sent from the interface hardware address.
+Send the
+.Ar clientid .
 If the string is of the format 01:02:03 then it is encoded as hex.
-If not set then none is sent.
+For interfaces whose hardware address is longer than 8 bytes, or if the
+.Ar clientid
+is an empty string then
+.Nm dhcpcd
+sends a default
+.Ar clientid
+of the hardware family and the hardware address.
 .It Ic duid
 Generate an
 .Rs
@@ -111,6 +126,8 @@
 .Xr dhcpcd-run-hooks 8
 or the numerical value.
 You can specify more options seperated by commas, spaces or more option lines.
+.Ic quiet
+Supress any dhcpcd output to the console, except for errors.
 .It Ic require Ar option
 Requires the
 .Ar option
@@ -152,4 +169,4 @@
 .Sh AUTHORS
 .An Roy Marples <roy@marples.name>
 .Sh BUGS
-Please report them to http://bugs.marples.name
+Please report them to http://roy.marples.name/projects/dhcpcd
diff --git a/dhcpcd.h b/dhcpcd.h
index 1cd2b5d..7d93315 100644
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -82,12 +82,12 @@
 	char script[PATH_MAX];
 	char pidfile[PATH_MAX];
 
-	char hostname[HOSTNAME_MAX_LEN + 1];
+	char hostname[HOSTNAME_MAX_LEN + 1]; /* We don't store the lenth */
 	int fqdn;
-	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];
+	uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 2];
+	char clientid[CLIENTID_MAX_LEN + 2];
+	uint8_t userclass[USERCLASS_MAX_LEN + 2];
+	uint8_t vendor[VENDOR_MAX_LEN + 2];
 
 	size_t blacklist_len;
 	in_addr_t *blacklist;
diff --git a/if-bsd.c b/if-bsd.c
index bbf1a95..d0ff246 100644
--- a/if-bsd.c
+++ b/if-bsd.c
@@ -98,12 +98,11 @@
 }
 
 int
-if_route(const char *ifname, const struct in_addr *destination,
-	 const struct in_addr *netmask, const struct in_addr *gateway,
+if_route(const struct interface *iface, const struct in_addr *dest,
+	 const struct in_addr *net, const struct in_addr *gate,
 	 _unused int metric, int action)
 {
 	int s;
-	static int seq;
 	union sockunion {
 		struct sockaddr sa;
 		struct sockaddr_in sin;
@@ -116,68 +115,83 @@
 	struct rtm 
 	{
 		struct rt_msghdr hdr;
-		char buffer[sizeof(su) * 3];
+		char buffer[sizeof(su) * 4];
 	} rtm;
-	char *bp = rtm.buffer;
+	char *bp = rtm.buffer, *p;
 	size_t l;
-	unsigned char *hwaddr;
-	size_t hwlen = 0;
 	int retval = 0;
 
+#define ADDSU(_su) { \
+	l = SA_SIZE(&(_su.sa)); \
+	memcpy(bp, &(_su), l); \
+	bp += l; \
+}
+#define ADDADDR(_addr) { \
+	memset (&su, 0, sizeof(su)); \
+	su.sin.sin_family = AF_INET; \
+	su.sin.sin_len = sizeof(su.sin); \
+	memcpy (&su.sin.sin_addr, _addr, sizeof(su.sin.sin_addr)); \
+	ADDSU(su); \
+}
+
 	if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) == -1)
 		return -1;
 
 	memset(&rtm, 0, sizeof(rtm));
 	rtm.hdr.rtm_version = RTM_VERSION;
-	rtm.hdr.rtm_seq = ++seq;
+	rtm.hdr.rtm_seq = 1;
 	if (action == 0)
 		rtm.hdr.rtm_type = RTM_CHANGE;
 	else if (action > 0)
 		rtm.hdr.rtm_type = RTM_ADD;
 	else
 		rtm.hdr.rtm_type = RTM_DELETE;
-	rtm.hdr.rtm_flags = RTF_UP | RTF_STATIC;
-	if (netmask->s_addr == INADDR_BROADCAST)
+	rtm.hdr.rtm_flags = RTF_UP;
+	/* None interface subnet routes are static. */
+	if (gate->s_addr != INADDR_ANY ||
+	    net->s_addr != iface->net.s_addr ||
+	    dest->s_addr != (iface->addr.s_addr & iface->net.s_addr))
+		rtm.hdr.rtm_flags |= RTF_STATIC;
+	rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
+	if (dest->s_addr == gate->s_addr && net->s_addr == INADDR_BROADCAST)
 		rtm.hdr.rtm_flags |= RTF_HOST;
-
-	/* This order is important */
-	rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
-
-#define ADDADDR(_addr) \
-	memset (&su, 0, sizeof(su)); \
-	su.sin.sin_family = AF_INET; \
-	su.sin.sin_len = sizeof(su.sin); \
-	memcpy (&su.sin.sin_addr, _addr, sizeof(su.sin.sin_addr)); \
-	l = SA_SIZE (&(su.sa)); \
-	memcpy (bp, &(su), l); \
-	bp += l;
-
-	ADDADDR(destination);
-
-	if (gateway->s_addr == INADDR_ANY) {
-		/* Make us a link layer socket */
-		hwaddr = xmalloc(sizeof(unsigned char) * HWADDR_LEN);
-		do_interface(ifname, hwaddr, &hwlen, NULL, 0, 0);
-		memset(&su, 0, sizeof(su));
-		su.sdl.sdl_len = sizeof(su.sdl);
-		su.sdl.sdl_family = AF_LINK;
-		su.sdl.sdl_nlen = strlen(ifname);
-		memcpy(&su.sdl.sdl_data, ifname, (size_t)su.sdl.sdl_nlen);
-		su.sdl.sdl_alen = hwlen;
-		memcpy(((unsigned char *)&su.sdl.sdl_data) + su.sdl.sdl_nlen,
-		       hwaddr, (size_t)su.sdl.sdl_alen);
-
-		l = SA_SIZE(&(su.sa));
-		memcpy(bp, &su, l);
-		bp += l;
-		free(hwaddr);
-	} else {
-		rtm.hdr.rtm_flags |= RTF_GATEWAY;
-		ADDADDR(gateway);
+	else {
+		rtm.hdr.rtm_addrs |= RTA_NETMASK;
+		if (rtm.hdr.rtm_flags & RTF_STATIC)
+			rtm.hdr.rtm_flags |= RTF_GATEWAY;
+		if (action >= 0)
+			rtm.hdr.rtm_addrs |= RTA_IFA;
 	}
 
-	ADDADDR(netmask);
-#undef ADDADDR
+	ADDADDR(dest);
+	if (rtm.hdr.rtm_flags & RTF_HOST ||
+	    !(rtm.hdr.rtm_flags & RTF_STATIC))
+	{
+		/* Make us a link layer socket for the host gateway */
+		memset(&su, 0, sizeof(su));
+		su.sdl.sdl_len = sizeof(struct sockaddr_dl);
+		link_addr(iface->name, &su.sdl);
+		ADDSU(su);
+	} else
+		ADDADDR(gate);
+
+	if (rtm.hdr.rtm_addrs & RTA_NETMASK) {
+		/* Ensure that netmask is set correctly */
+		memset(&su, 0, sizeof(su));
+		su.sin.sin_family = AF_INET;
+		su.sin.sin_len = sizeof(su.sin);
+		memcpy(&su.sin.sin_addr, &net->s_addr, sizeof(su.sin.sin_addr));
+		p = su.sa.sa_len + (char *)&su;
+		for (su.sa.sa_len = 0; p > (char *)&su;)
+			if (*--p != 0) {
+				su.sa.sa_len = 1 + p - (char *)&su;
+				break;
+			}
+		ADDSU(su);
+	}
+
+	if (rtm.hdr.rtm_addrs & RTA_IFA)
+		ADDADDR(&iface->addr);
 
 	rtm.hdr.rtm_msglen = l = bp - (char *)&rtm;
 	if (write(s, &rtm, l) == -1)
diff --git a/if-linux.c b/if-linux.c
index 1009270..114a084 100644
--- a/if-linux.c
+++ b/if-linux.c
@@ -317,7 +317,7 @@
 }
 
 int
-if_route(const char *ifname,
+if_route(const struct interface *iface,
 	 const struct in_addr *destination, const struct in_addr *netmask,
 	 const struct in_addr *gateway, int metric, int action)
 {
@@ -325,8 +325,7 @@
 	unsigned int ifindex;
 	int retval = 0;
 
-
-	if (!(ifindex = if_nametoindex(ifname))) {
+	if (!(ifindex = if_nametoindex(iface->name))) {
 		errno = ENODEV;
 		return -1;
 	}
@@ -335,8 +334,8 @@
 	nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
 	nlm->hdr.nlmsg_type = RTM_NEWROUTE;
 	if (action == 0)
-            nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
-	else if (action > 0)
+		nlm->hdr.nlmsg_flags = NLM_F_REPLACE;
+	else if (action == 1)
 		/*
 		 * ers@google:
 		 * commented out NLM_F_EXCL here and below. We
@@ -351,12 +350,20 @@
 	nlm->rt.rtm_family = AF_INET;
 	nlm->rt.rtm_table = RT_TABLE_MAIN;
 
-	if (action < 0)
+	if (action == -1 || action == -2)
 		nlm->rt.rtm_scope = RT_SCOPE_NOWHERE;
 	else {
 		nlm->hdr.nlmsg_flags |= NLM_F_CREATE /*| NLM_F_EXCL*/;
-		nlm->rt.rtm_protocol = RTPROT_BOOT;
-		if (gateway->s_addr == INADDR_ANY)
+		/* We only change route metrics for kernel routes */
+		if (destination->s_addr ==
+		    (iface->addr.s_addr & iface->net.s_addr) &&
+		    netmask->s_addr == iface->net.s_addr)
+			nlm->rt.rtm_protocol = RTPROT_KERNEL;
+		else
+			nlm->rt.rtm_protocol = RTPROT_BOOT;
+		if (gateway->s_addr == INADDR_ANY ||
+		    (gateway->s_addr == destination->s_addr &&
+		     netmask->s_addr == INADDR_BROADCAST))
 			nlm->rt.rtm_scope = RT_SCOPE_LINK;
 		else
 			nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE;
@@ -366,8 +373,15 @@
 	nlm->rt.rtm_dst_len = inet_ntocidr(*netmask);
 	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST,
 		   &destination->s_addr, sizeof(destination->s_addr));
-	add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
-		   &gateway->s_addr, sizeof(gateway->s_addr));
+	if (nlm->rt.rtm_protocol == RTPROT_KERNEL) {
+		add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_PREFSRC,
+			   &iface->addr.s_addr, sizeof(iface->addr.s_addr));
+	}
+	/* If destination == gateway then don't add the gateway */
+	if (destination->s_addr != gateway->s_addr ||
+	    netmask->s_addr != INADDR_BROADCAST)
+		add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY,
+			   &gateway->s_addr, sizeof(gateway->s_addr));
 
 	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, ifindex);
 	add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, metric);
diff --git a/mk/dist.mk b/mk/dist.mk
index 3d9385b..c4c1947 100644
--- a/mk/dist.mk
+++ b/mk/dist.mk
@@ -1,5 +1,5 @@
-# rules to make a distribution tarball from a git repo
-# Copyright 2008 Roy Marples <roy@marples.name>
+# rules to make a distribution tarball from a svn repo
+# Copyright 2008-2009 Roy Marples <roy@marples.name>
 
 GITREF?=	HEAD
 DISTPREFIX?=	${PROG}-${VERSION}
diff --git a/net.c b/net.c
index b905573..29344f8 100644
--- a/net.c
+++ b/net.c
@@ -112,9 +112,9 @@
 	dst = htonl(addr);
 	if (IN_CLASSA(dst))
 		return ntohl(IN_CLASSA_NET);
-	if (IN_CLASSB (dst))
+	if (IN_CLASSB(dst))
 		return ntohl(IN_CLASSB_NET);
-	if (IN_CLASSC (dst))
+	if (IN_CLASSC(dst))
 		return ntohl(IN_CLASSC_NET);
 
 	return 0;
@@ -634,44 +634,42 @@
 }
 
 int
-valid_udp_packet(const uint8_t *data)
+valid_udp_packet(const uint8_t *data, size_t data_len)
 {
 	struct udp_dhcp_packet packet;
-	uint16_t bytes;
-	uint16_t ipsum;
-	uint16_t iplen;
-	uint16_t udpsum;
-	struct in_addr source;
-	struct in_addr dest;
-	int retval = 0;
+	uint16_t bytes, udpsum;
 
-	memcpy(&packet, data, sizeof(packet));
-	bytes = ntohs(packet.ip.ip_len);
-	ipsum = packet.ip.ip_sum;
-	iplen = packet.ip.ip_len;
-	udpsum = packet.udp.uh_sum;
-
-	if (0 != checksum(&packet.ip, sizeof(packet.ip))) {
+	if (data_len > sizeof(packet)) {
+		errno = EINVAL;
+		return -1;
+	}
+	memcpy(&packet, data, data_len);
+	if (checksum(&packet.ip, sizeof(packet.ip)) != 0) {
 		errno = EINVAL;
 		return -1;
 	}
 
-	packet.ip.ip_sum = 0;
-	memcpy(&source, &packet.ip.ip_src, sizeof(packet.ip.ip_src));
-	memcpy(&dest, &packet.ip.ip_dst, sizeof(packet.ip.ip_dst));
-	memset(&packet.ip, 0, sizeof(packet.ip));
-	packet.udp.uh_sum = 0;
-
-	packet.ip.ip_p = IPPROTO_UDP;
-	memcpy(&packet.ip.ip_src, &source, sizeof(packet.ip.ip_src));
-	memcpy(&packet.ip.ip_dst, &dest, sizeof(packet.ip.ip_dst));
-	packet.ip.ip_len = packet.udp.uh_ulen;
-	if (udpsum && udpsum != checksum(&packet, bytes)) {
+	bytes = ntohs(packet.ip.ip_len);
+	if (data_len < bytes) {
 		errno = EINVAL;
-		retval = -1;
+		return -1;
+	}
+	udpsum = packet.udp.uh_sum;
+	packet.udp.uh_sum = 0;
+	packet.ip.ip_hl = 0;
+	packet.ip.ip_v = 0;
+	packet.ip.ip_tos = 0;
+	packet.ip.ip_len = packet.udp.uh_ulen;
+	packet.ip.ip_id = 0;
+	packet.ip.ip_off = 0;
+	packet.ip.ip_ttl = 0;
+	packet.ip.ip_sum = 0;
+	if (udpsum && checksum(&packet, bytes) != udpsum) {
+		errno = EINVAL;
+		return -1;
 	}
 
-	return retval;
+	return 0;
 }
 
 int
diff --git a/net.h b/net.h
index 3ade025..1447aba 100644
--- a/net.h
+++ b/net.h
@@ -144,14 +144,15 @@
 #define get_address(ifname, addr, net) \
 	do_interface(ifname, NULL, NULL, addr, net, 1)
 
-int if_route(const char *, const struct in_addr *, const struct in_addr *,
+int if_route(const struct interface *,
+	     const struct in_addr *,const struct in_addr *,
 	     const struct in_addr *, int, int);
-#define add_route(ifname, dest, mask, gate, metric) \
-	if_route(ifname, dest, mask, gate, metric, 1)
-#define 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)
+#define add_route(iface, dest, mask, gate, metric) \
+	if_route(iface, dest, mask, gate, metric, 1)
+#define change_route(iface, dest, mask, gate, metric) \
+	if_route(iface, dest, mask, gate, metric, 0)
+#define del_route(iface, dest, mask, gate, metric) \
+	if_route(iface, dest, mask, gate, metric, -1)
 void free_routes(struct rt *);
 
 int open_udp_socket(struct interface *);
@@ -159,7 +160,7 @@
 ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t,
 			struct in_addr, struct in_addr);
 ssize_t get_udp_data(const uint8_t **, const uint8_t *);
-int valid_udp_packet(const uint8_t *);
+int valid_udp_packet(const uint8_t *, size_t);
 
 int open_socket(struct interface *, int);
 ssize_t send_packet(const struct interface *, struct in_addr, 
diff --git a/showlease.c b/showlease.c
index 9bee4ab..27cc543 100644
--- a/showlease.c
+++ b/showlease.c
@@ -258,7 +258,7 @@
 }
 
 int
-get_option_addr(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option)
+get_option_addr32(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option)
 {
 	const uint8_t *p = get_option_raw(dhcp, option);
 
@@ -273,7 +273,7 @@
 {
 	uint32_t a;
 
-	if (get_option_addr(&a, dhcp, option) == -1)
+	if (get_option_addr32(&a, dhcp, option) == -1)
 		return -1;
 
 	*i = ntohl(a);
@@ -330,11 +330,11 @@
     lease->frominfo = 0;
     lease->addr.s_addr = dhcp->yiaddr;
 
-    if (get_option_addr(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
+    if (get_option_addr32(&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);
+    get_option_addr32(&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))
diff --git a/signals.c b/signals.c
index 58679d6..6576afd 100644
--- a/signals.c
+++ b/signals.c
@@ -31,6 +31,7 @@
 #include <errno.h>
 #include <signal.h>
 #include <string.h>
+#include <syslog.h>
 #include <unistd.h>
 
 #include "common.h"
@@ -50,7 +51,8 @@
 {
 	int serrno = errno;
 
-	write(signal_pipe[1], &sig, sizeof(sig));
+	if (write(signal_pipe[1], &sig, sizeof(sig)) != sizeof(sig))
+		syslog(LOG_ERR, "write signal %d: %s", sig, strerror(errno));
 	/* Restore errno */
 	errno = serrno;
 }
@@ -69,11 +71,11 @@
 {
 	int sig = -1;
 	char buf[16];
-	size_t bytes;
+	ssize_t bytes;
 
 	memset(buf, 0, sizeof(buf));
 	bytes = read(signal_pipe[0], buf, sizeof(buf));
-	if (bytes >= sizeof(sig))
+	if (bytes >= 0 && (size_t)bytes >= sizeof(sig))
 		memcpy(&sig, buf, sizeof(sig));
 	return sig;
 }