dhcpcd: Update to Version 5.2.10

Change-Id: I949331c7aad91b125decd51da4041983d3a352bc
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/dhcp.c b/dhcp.c
index 1854cf2..bd6c719 100644
--- a/dhcp.c
+++ b/dhcp.c
@@ -1,6 +1,6 @@
 /* 
  * dhcpcd - DHCP client daemon
- * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
  * All rights reserved
 
  * Redistribution and use in source and binary forms, with or without
@@ -30,6 +30,7 @@
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
+#include <syslog.h>
 #include <unistd.h>
 
 #include "config.h"
@@ -57,7 +58,7 @@
 /* Our aggregate option buffer.
  * We ONLY use this when options are split, which for most purposes is
  * practically never. See RFC3396 for details. */
-static uint8_t *dhcp_opt_buffer = NULL;
+static uint8_t *opt_buffer;
 
 struct dhcp_opt {
 	uint8_t option;
@@ -67,9 +68,10 @@
 
 static const struct dhcp_opt const dhcp_opts[] = {
 	{ 1,	IPV4 | REQUEST,	"subnet_mask" },
-	/* 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" },
+		/* RFC 3442 states that the CSR has to come before all other
+		 * routes. For completeness, we also specify static routes,
+	 	 * then routers. */
+	{ 121,  RFC3442,	"classless_static_routes" },
 	{ 249,  RFC3442,	"ms_classless_static_routes" },
 	{ 33,	IPV4 | ARRAY | REQUEST,	"static_routes" },
 	{ 3,	IPV4 | ARRAY | REQUEST,	"routers" },
@@ -160,22 +162,52 @@
 	{ 0, 0, NULL }
 };
 
+static const char *if_params[] = {
+	"interface",
+	"reason",
+	"pid",
+	"ifmetric",
+	"ifwireless",
+	"ifflags",
+	"profile",
+	"interface_order",
+	NULL
+};
+
+static const char *dhcp_params[] = {
+	"ip_address",
+	"subnet_cidr",
+	"network_number",
+	"ssid",
+	"filename",
+	"server_name",
+	NULL
+};
+
 void
 print_options(void)
 {
 	const struct dhcp_opt *opt;
+	const char **p;
+
+	for (p = if_params; *p; p++)
+		printf(" -  %s\n", *p);
+
+	for (p = dhcp_params; *p; p++)
+		printf("    %s\n", *p);
 
 	for (opt = dhcp_opts; opt->option; opt++)
 		if (opt->var)
 			printf("%03d %s\n", opt->option, opt->var);
 }
 
-int make_option_mask(uint8_t *mask, char **opts, int add)
+int make_option_mask(uint8_t *mask, const char *opts, int add)
 {
-	char *token, *p = *opts, *t;
+	char *token, *o, *p, *t;
 	const struct dhcp_opt *opt;
 	int match, n;
 
+	o = p = xstrdup(opts);
 	while ((token = strsep(&p, ", "))) {
 		if (*token == '\0')
 			continue;
@@ -192,22 +224,28 @@
 					if (opt->option == n)
 						match = 1;
 			}
-			if (match) {	
-				if (add == 1)
+			if (match) {
+				if (add == 2 && !(opt->type & IPV4)) {
+					free(o);
+					errno = EINVAL;
+					return -1;
+				}
+				if (add == 1 || add == 2)
 					add_option_mask(mask,
-							opt->option);
+					    opt->option);
 				else
 					del_option_mask(mask,
-							opt->option);
+					    opt->option);
 				break;
 			}
 		}
 		if (!opt->option) {
-			*opts = token;
+			free(o);
 			errno = ENOENT;
 			return -1;
 		}
 	}
+	free(o);
 	return 0;
 }
 
@@ -227,7 +265,9 @@
 		if (type)
 			*type = opt->type;
 
-		if (opt->type == 0 || opt->type & STRING || opt->type & RFC3442)
+		if (opt->type == 0 ||
+		    opt->type & STRING ||
+		    opt->type & RFC3442)
 			return 0;
 
 		sz = 0;
@@ -246,11 +286,13 @@
 	return 0;
 }
 
+#ifdef DEBUG_MEMORY
 static void
 free_option_buffer(void)
 {
-	free(dhcp_opt_buffer);
+	free(opt_buffer);
 }
+#endif
 
 #define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL, NULL)
 static const uint8_t *
@@ -269,12 +311,14 @@
 		o = *p++;
 		if (o == opt) {
 			if (op) {
-				if (!dhcp_opt_buffer) {
-					dhcp_opt_buffer = xmalloc(sizeof(struct dhcp_message));
+				if (!opt_buffer) {
+					opt_buffer = xmalloc(sizeof(*dhcp));
+#ifdef DEBUG_MEMORY
 					atexit(free_option_buffer);
+#endif
 				}
 				if (!bp) 
-					bp = dhcp_opt_buffer;
+					bp = opt_buffer;
 				memcpy(bp, op, ol);
 				bp += ol;
 			}
@@ -318,7 +362,7 @@
 		*len = bl;
 	if (bp) {
 		memcpy(bp, op, ol);
-		return (const uint8_t *)&dhcp_opt_buffer;
+		return (const uint8_t *)opt_buffer;
 	}
 	if (op)
 		return op;
@@ -371,12 +415,13 @@
 
 	if (!p)
 		return -1;
-	*i = *(p);
+	if (i)
+		*i = *(p);
 	return 0;
 }
 
 /* Decode an RFC3397 DNS search order option into a space
- * seperated string. Returns length of string (including 
+ * separated string. Returns length of string (including
  * terminating zero) or zero on error. out may be NULL
  * to just determine output length. */
 static ssize_t
@@ -442,10 +487,8 @@
 decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p)
 {
 	const uint8_t *e;
-	ssize_t bytes = 0;
-	ssize_t b;
+	ssize_t b, bytes = 0, ocets;
 	uint8_t cidr;
-	uint8_t ocets;
 	struct in_addr addr;
 	char *o = out;
 
@@ -479,7 +522,7 @@
 		/* If we have ocets then we have a destination and netmask */
 		if (ocets > 0) {
 			addr.s_addr = 0;
-			memcpy(&addr.s_addr, p, (size_t)ocets);
+			memcpy(&addr.s_addr, p, ocets);
 			b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr);
 			p += ocets;
 		} else
@@ -506,7 +549,7 @@
 	const uint8_t *p = data;
 	const uint8_t *e;
 	uint8_t cidr;
-	uint8_t ocets;
+	size_t ocets;
 	struct rt *routes = NULL;
 	struct rt *rt = NULL;
 
@@ -534,15 +577,9 @@
 		ocets = (cidr + 7) / 8;
 		/* If we have ocets then we have a destination and netmask */
 		if (ocets > 0) {
-			memcpy(&rt->dest.s_addr, p, (size_t)ocets);
-			memset(&rt->net.s_addr, 255, (size_t)ocets - 1);
-			memset((uint8_t *)&rt->net.s_addr +
-			       (ocets - 1),
-			       (256 - (1 << (32 - cidr) % 8)), 1);
+			memcpy(&rt->dest.s_addr, p, ocets);
 			p += ocets;
-		} else {
-			rt->dest.s_addr = 0;
-			rt->net.s_addr = 0;
+			rt->net.s_addr = htonl(~0U << (32 - cidr));
 		}
 
 		/* Finally, snag the router */
@@ -664,7 +701,8 @@
  * If we have a CSR then we only use that.
  * Otherwise we add static routes and then routers. */
 struct rt *
-get_option_routes(const struct dhcp_message *dhcp)
+get_option_routes(const struct dhcp_message *dhcp,
+    const char *ifname, int *opts)
 {
 	const uint8_t *p;
 	const uint8_t *e;
@@ -679,8 +717,13 @@
 		p = get_option(dhcp, DHO_MSCSR, &len, NULL);
 	if (p) {
 		routes = decode_rfc3442_rt(len, p);
-		if (routes)
+		if (routes && !(*opts & DHCPCD_CSR_WARNED)) {
+			syslog(LOG_DEBUG,
+			    "%s: using Classless Static Routes (RFC3442)",
+			    ifname);
+			*opts |= DHCPCD_CSR_WARNED;
 			return routes;
+		}
 	}
 
 	/* OK, get our static routes first. */
@@ -747,17 +790,42 @@
 	return p - dst;
 }
 
-#define PUTADDR(_type, _val) \
-{ \
-	*p++ = _type; \
-	*p++ = 4; \
-	memcpy(p, &_val.s_addr, 4); \
-	p += 4; \
+#define PUTADDR(_type, _val)						      \
+	{								      \
+		*p++ = _type;						      \
+		*p++ = 4;						      \
+		memcpy(p, &_val.s_addr, 4);				      \
+		p += 4;							      \
+	}
+
+int
+dhcp_message_add_addr(struct dhcp_message *dhcp,
+    uint8_t type, struct in_addr addr)
+{
+	uint8_t *p;
+	size_t len;
+
+	p = dhcp->options;
+	while (*p != DHO_END) {
+		p++;
+		p += *p + 1;
+	}
+
+	len = p - (uint8_t *)dhcp;
+	if (len + 6 > sizeof(*dhcp)) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	PUTADDR(type, addr);
+	*p = DHO_END;
+	return 0;
 }
+
 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)
+    const struct interface *iface,
+    uint8_t type)
 {
 	struct dhcp_message *dhcp;
 	uint8_t *m, *lp, *p;
@@ -765,27 +833,27 @@
 	time_t up = uptime() - iface->start_uptime;
 	uint32_t ul;
 	uint16_t sz;
-	const struct dhcp_opt *opt;
 	size_t len;
 	const char *hp;
+	const struct dhcp_opt *opt;
+	const struct if_options *ifo = iface->state->options;
+	const struct dhcp_lease *lease = &iface->state->lease;
 
+	printf("%s: Start\n", __func__);
 	dhcp = xzalloc(sizeof (*dhcp));
 	m = (uint8_t *)dhcp;
 	p = dhcp->options;
 
-	if ((type == DHCP_INFORM ||
-	     type == DHCP_RELEASE ||
-	     type == DHCP_REQUEST) &&
-	    !IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
+	if ((type == DHCP_INFORM || type == DHCP_RELEASE ||
+		(type == DHCP_REQUEST &&
+		    iface->net.s_addr == lease->net.s_addr &&
+		    (iface->state->new == NULL ||
+			iface->state->new->cookie == htonl(MAGIC_COOKIE)))))
 	{
 		dhcp->ciaddr = iface->addr.s_addr;
-		/* Just incase we haven't actually configured the address yet */
+		/* In-case we haven't actually configured the address yet */
 		if (type == DHCP_INFORM && iface->addr.s_addr == 0)
 			dhcp->ciaddr = lease->addr.s_addr;
-		/* Zero the address if we're currently on a different subnet */
-		if (type == DHCP_REQUEST &&
-		    iface->net.s_addr != lease->net.s_addr)
-			dhcp->ciaddr = 0;
 	}
 
 	dhcp->op = DHCP_BOOTREQUEST;
@@ -793,25 +861,24 @@
 	switch (iface->family) {
 	case ARPHRD_ETHER:
 	case ARPHRD_IEEE802:
-		dhcp->hwlen = ETHER_ADDR_LEN;
-		memcpy(&dhcp->chaddr, &iface->hwaddr, ETHER_ADDR_LEN);
-		break;
-	case ARPHRD_IEEE1394:
-	case ARPHRD_INFINIBAND:
-		dhcp->hwlen = 0;
-		if (dhcp->ciaddr == 0 &&
-		    type != DHCP_DECLINE && type != DHCP_RELEASE)
-			dhcp->flags = htons(BROADCAST_FLAG);
+		dhcp->hwlen = iface->hwlen;
+		memcpy(&dhcp->chaddr, &iface->hwaddr, iface->hwlen);
 		break;
 	}
 
+	if (ifo->options & DHCPCD_BROADCAST &&
+	    dhcp->ciaddr == 0 &&
+	    type != DHCP_DECLINE &&
+	    type != DHCP_RELEASE)
+		dhcp->flags = htons(BROADCAST_FLAG);
+
 	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->xid = iface->state->xid;
 	dhcp->cookie = htonl(MAGIC_COOKIE);
 
 	*p++ = DHO_MESSAGETYPE; 
@@ -824,16 +891,20 @@
 		p += iface->clientid[0] + 1;
 	}
 
-	if (lease->addr.s_addr && !IN_LINKLOCAL(htonl(lease->addr.s_addr))) {
+	if (lease->addr.s_addr && lease->cookie == htonl(MAGIC_COOKIE)) {
 		if (type == DHCP_DECLINE ||
-		    type == DHCP_DISCOVER ||
 		    (type == DHCP_REQUEST &&
-		     lease->addr.s_addr != iface->addr.s_addr))
+			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_RELEASE) {
+			if (lease->server.s_addr)
+				PUTADDR(DHO_SERVERID, lease->server);
+		}
 	}
 
 	if (type == DHCP_DECLINE) {
@@ -844,10 +915,8 @@
 		p += len;
 	}
 
-	if (type == DHCP_RELEASE) {
-		if (lease->server.s_addr)
-			PUTADDR(DHO_SERVERID, lease->server);
-	}
+	if (type == DHCP_DISCOVER && ifo->options & DHCPCD_REQUEST)
+		PUTADDR(DHO_IPADDRESS, ifo->req_addr);
 
 	if (type == DHCP_DISCOVER ||
 	    type == DHCP_INFORM ||
@@ -859,29 +928,35 @@
 		if (sz < MTU_MIN) {
 			if (set_mtu(iface->name, MTU_MIN) == 0)
 				sz = MTU_MIN;
+		} else if (sz > MTU_MAX) {
+			/* Even though our MTU could be greater than
+			 * MTU_MAX (1500) dhcpcd does not presently
+			 * handle DHCP packets any bigger. */
+			sz = MTU_MAX;
 		}
 		sz = htons(sz);
 		memcpy(p, &sz, 2);
 		p += 2;
 
-		if (options->userclass[0]) {
+		if (ifo->userclass[0]) {
 			*p++ = DHO_USERCLASS;
-			memcpy(p, options->userclass, options->userclass[0] + 1);
-			p += options->userclass[0] + 1;
+			memcpy(p, ifo->userclass, ifo->userclass[0] + 1);
+			p += ifo->userclass[0] + 1;
 		}
 
-		if (options->vendorclassid[0]) {
+		if (ifo->vendorclassid[0]) {
 			*p++ = DHO_VENDORCLASSID;
-			memcpy(p, options->vendorclassid,
-			       options->vendorclassid[0] + 1);
-			p += options->vendorclassid[0] + 1;
+			memcpy(p, ifo->vendorclassid,
+			    ifo->vendorclassid[0] + 1);
+			p += ifo->vendorclassid[0] + 1;
 		}
 
+
 		if (type != DHCP_INFORM) {
-			if (options->leasetime != 0) {
+			if (ifo->leasetime != 0) {
 				*p++ = DHO_LEASETIME;
 				*p++ = 4;
-				ul = htonl(options->leasetime);
+				ul = htonl(ifo->leasetime);
 				memcpy(p, &ul, 4);
 				p += 4;
 			}
@@ -891,18 +966,18 @@
 		 * 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]) {
+		if (ifo->options & DHCPCD_HOSTNAME && ifo->hostname[0]) {
 			*p++ = DHO_HOSTNAME;
-			hp = strchr(options->hostname, '.');
+			hp = strchr(ifo->hostname, '.');
 			if (hp)
-				len = hp - options->hostname;
+				len = hp - ifo->hostname;
 			else
-				len = strlen(options->hostname);
+				len = strlen(ifo->hostname);
 			*p++ = len;
-			memcpy(p, options->hostname, len);
+			memcpy(p, ifo->hostname, len);
 			p += len;
 		}
-		if (options->fqdn != FQDN_DISABLE) {
+		if (ifo->fqdn != FQDN_DISABLE && ifo->hostname[0]) {
 			/* IETF DHC-FQDN option (81), RFC4702 */
 			*p++ = DHO_FQDN;
 			lp = p;
@@ -917,19 +992,19 @@
 			 * N: 1 => Client requests Server to not
 			 *         update DNS
 			 */
-			*p++ = (options->fqdn & 0x09) | 0x04;
+			*p++ = (ifo->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, p);
+			ul = encode_rfc1035(ifo->hostname, p);
 			*lp += ul;
 			p += ul;
 		}
 
 		/* vendor is already encoded correctly, so just add it */
-		if (options->vendor[0]) {
+		if (ifo->vendor[0]) {
 			*p++ = DHO_VENDOR;
-			memcpy(p, options->vendor, options->vendor[0] + 1);
-			p += options->vendor[0] + 1;
+			memcpy(p, ifo->vendor, ifo->vendor[0] + 1);
+			p += ifo->vendor[0] + 1;
 		}
 
 		*p++ = DHO_PARAMETERREQUESTLIST;
@@ -937,15 +1012,12 @@
 		*p++ = 0;
 		for (opt = dhcp_opts; opt->option; opt++) {
 			if (!(opt->type & REQUEST || 
-			      has_option_mask(options->requestmask, opt->option)))
+				has_option_mask(ifo->requestmask, opt->option)))
 				continue;
-			switch (opt->option) {
-			case DHO_RENEWALTIME:	/* FALLTHROUGH */
-			case DHO_REBINDTIME:
-				if (type == DHCP_INFORM)
-					continue;
-				break;
-			}
+			if (type == DHCP_INFORM &&
+			    (opt->option == DHO_RENEWALTIME ||
+				opt->option == DHO_REBINDTIME))
+				continue;
 			*p++ = opt->option;
 		}
 		*n_params = p - n_params - 1;
@@ -974,16 +1046,20 @@
 	uint8_t l;
 	uint8_t o = 0;
 
-	fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
-#ifdef ANDROID
-	if (fd == -1 && errno == EACCES) {
-		/* the lease file might have been created when dhcpcd was running as root */
+	/* We don't write BOOTP leases */
+	if (is_bootp(dhcp)) {
 		unlink(iface->leasefile);
-		fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+		return 0;
 	}
-#endif
-	if (fd == -1)
+
+	syslog(LOG_DEBUG, "%s: writing lease `%s'",
+	    iface->name, iface->leasefile);
+
+	fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0444);
+	if (fd == -1) {
+		syslog(LOG_ERR, "%s: open: %m", iface->name);
 		return -1;
+	}
 
 	/* Only write as much as we need */
 	while (p < e) {
@@ -1011,8 +1087,14 @@
 	ssize_t bytes;
 
 	fd = open(iface->leasefile, O_RDONLY);
-	if (fd == -1)
+	if (fd == -1) {
+		if (errno != ENOENT)
+			syslog(LOG_ERR, "%s: open `%s': %m",
+			    iface->name, iface->leasefile);
 		return NULL;
+	}
+	syslog(LOG_DEBUG, "%s: reading lease `%s'",
+	    iface->name, iface->leasefile);
 	dhcp = xmalloc(sizeof(*dhcp));
 	memset(dhcp, 0, sizeof(*dhcp));
 	bytes = read(fd, dhcp, sizeof(*dhcp));
@@ -1058,21 +1140,21 @@
 			continue;
 		}
 		switch (c) {
-			case '"':  /* FALLTHROUGH */
-			case '\'': /* FALLTHROUGH */
-			case '$':  /* FALLTHROUGH */
-			case '`':  /* FALLTHROUGH */
-			case '\\': /* FALLTHROUGH */
-				if (s) {
-					if (len < 3) {
-						errno = ENOBUFS;
-						return -1;
-					}
-					*s++ = '\\';
-					len--;
+		case '"':  /* FALLTHROUGH */
+		case '\'': /* FALLTHROUGH */
+		case '$':  /* FALLTHROUGH */
+		case '`':  /* FALLTHROUGH */
+ 		case '\\':
+			if (s) {
+				if (len < 3) {
+					errno = ENOBUFS;
+					return -1;
 				}
-				bytes++;
-				break;
+				*s++ = '\\';
+				len--;
+			}
+			bytes++;
+			break;
 		}
 		if (s) {
 			*s++ = c;
@@ -1125,17 +1207,22 @@
 	if (!s) {
 		if (type & UINT8)
 			l = 3;
-		else if (type & UINT16)
+		else if (type & UINT16) {
 			l = 5;
-		else if (type & SINT16)
+			dl /= 2;
+		} else if (type & SINT16) {
 			l = 6;
-		else if (type & UINT32)
+			dl /= 2;
+		} else if (type & UINT32) {
 			l = 10;
-		else if (type & SINT32)
+			dl /= 4;
+		} else if (type & SINT32) {
 			l = 11;
-		else if (type & IPV4)
+			dl /= 4;
+		} else if (type & IPV4) {
 			l = 16;
-		else {
+			dl /= 4;
+		} else {
 			errno = EINVAL;
 			return -1;
 		}
@@ -1199,7 +1286,7 @@
 
 ssize_t
 configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
-	      const struct options *options)
+    const struct if_options *ifo)
 {
 	unsigned int i;
 	const uint8_t *p;
@@ -1220,12 +1307,12 @@
 		for (opt = dhcp_opts; opt->option; opt++) {
 			if (!opt->var)
 				continue;
-			if (has_option_mask(options->nomask, opt->option))
+			if (has_option_mask(ifo->nomask, opt->option))
 				continue;
 			if (get_option_raw(dhcp, opt->option))
 				e++;
 		}
-		if (dhcp->yiaddr)
+		if (dhcp->yiaddr || dhcp->ciaddr)
 			e += 5;
 		if (*dhcp->bootfile && !(overl & 1))
 			e++;
@@ -1235,10 +1322,10 @@
 	}
 
 	ep = env;
-	if (dhcp->yiaddr) {
+	if (dhcp->yiaddr || dhcp->ciaddr) {
 		/* Set some useful variables that we derive from the DHCP
 		 * message but are not necessarily in the options */
-		addr.s_addr = dhcp->yiaddr;
+		addr.s_addr = dhcp->yiaddr ? dhcp->yiaddr : dhcp->ciaddr;
 		setvar(&ep, prefix, "ip_address", inet_ntoa(addr));
 		if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) == -1) {
 			net.s_addr = get_netmask(addr.s_addr);
@@ -1263,7 +1350,7 @@
 	for (opt = dhcp_opts; opt->option; opt++) {
 		if (!opt->var)
 			continue;
-		if (has_option_mask(options->nomask, opt->option))
+		if (has_option_mask(ifo->nomask, opt->option))
 			continue;
 		val = NULL;
 		p = get_option(dhcp, opt->option, &pl, NULL);
@@ -1286,3 +1373,33 @@
 
 	return ep - env;
 }
+
+void
+get_lease(struct dhcp_lease *lease, const struct dhcp_message *dhcp)
+{
+	struct timeval now;
+
+	lease->cookie = dhcp->cookie;
+	/* BOOTP does not set yiaddr for replies when ciaddr is set. */
+	if (dhcp->yiaddr)
+		lease->addr.s_addr = dhcp->yiaddr;
+	else
+		lease->addr.s_addr = dhcp->ciaddr;
+	if (get_option_addr(&lease->net, dhcp, DHO_SUBNETMASK) == -1)
+		lease->net.s_addr = get_netmask(lease->addr.s_addr);
+	if (get_option_addr(&lease->brd, dhcp, DHO_BROADCAST) == -1)
+		lease->brd.s_addr = lease->addr.s_addr | ~lease->net.s_addr;
+	if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) == 0) {
+		/* Ensure that we can use the lease */
+		get_monotonic(&now);
+		if (now.tv_sec + (time_t)lease->leasetime < now.tv_sec)
+			lease->leasetime = ~0U; /* Infinite lease */
+	} else
+		lease->leasetime = ~0U; /* Default to infinite lease */
+	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;
+	if (get_option_addr(&lease->server, dhcp, DHO_SERVERID) != 0)
+		lease->server.s_addr = INADDR_ANY;
+}