changeset 982:b8b5d9059bb0 draft

Reboot off the last lease and use the last lease if not expired and user has asked for it. Also, add a reboot timeout toggle (default 10 seconds).
author Roy Marples <roy@marples.name>
date Mon, 15 Sep 2008 15:23:46 +0000
parents 8a655d895e9a
children 1f2ad84ae760
files dhcp.c dhcpcd.8.in dhcpcd.c dhcpcd.conf.5.in dhcpcd.h if-options.c if-options.h ipv4ll.c
diffstat 8 files changed, 149 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/dhcp.c	Mon Sep 15 15:20:37 2008 +0000
+++ b/dhcp.c	Mon Sep 15 15:23:46 2008 +0000
@@ -1254,7 +1254,6 @@
 {
 	time_t t;
 
-	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);
--- a/dhcpcd.8.in	Mon Sep 15 15:20:37 2008 +0000
+++ b/dhcpcd.8.in	Mon Sep 15 15:23:46 2008 +0000
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd September 11, 2008
+.Dd September 15, 2008
 .Dt DHCPCD 8 SMM
 .Sh NAME
 .Nm dhcpcd
@@ -42,6 +42,7 @@
 .Op Fl t , -timeout Ar seconds
 .Op Fl u , -userclass Ar class
 .Op Fl v , -vendor Ar code , Ar value
+.Op Fl y , -reboot Ar seconds
 .Op Fl z , -allowinterfaces Ar pattern
 .Op Fl C , -nohook Ar hook
 .Op Fl F , -fqdn Ar FQDN
@@ -304,6 +305,14 @@
 and exit.
 .Nm
 then waits until this process has exited.
+.It Fl y , -reboot Ar seconds
+Allow
+.Ar reboot
+seconds before moving to the discover phase if we have an old lease to use.
+The default is 10 seconds.
+A setting if 0 seconds causes
+.Nm
+to skip the reboot phase and go straight into discover.
 .It Fl D , -duid 
 Generate an
 .Li RFC 4361
--- a/dhcpcd.c	Mon Sep 15 15:20:37 2008 +0000
+++ b/dhcpcd.c	Mon Sep 15 15:23:46 2008 +0000
@@ -127,7 +127,7 @@
 static void
 usage(void)
 {
-	printf("usage: "PACKAGE" [-dknpqxADEGHKLOTV] [-c script] [-f file ] [-h hostname]\n"
+	printf("usage: "PACKAGE" [-dknpqxyADEGHKLOTV] [-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] [-Q option] [-X ipaddr] <interface>\n");
@@ -326,7 +326,7 @@
 
 	syslog(LOG_ERR, "%s: failed to renew, attmepting to rebind",
 	       iface->name);
-	iface->state->state = DHS_REBINDING;
+	iface->state->state = DHS_REBIND;
 	delete_timeout(send_renew, iface);
 	iface->state->lease.server.s_addr = 0;
 	send_rebind(iface);
@@ -336,14 +336,20 @@
 start_expire(void *arg)
 {
 	struct interface *iface = arg;
-	int ll = IN_LINKLOCAL(htonl(iface->state->lease.addr.s_addr));
+
+	if (iface->addr.s_addr == 0) {
+		/* We failed to reboot, so enter discovery. */
+		iface->state->interval = 0;
+		start_discover(iface);
+		return;
+	}
 
 	syslog(LOG_ERR, "%s: lease expired", iface->name);
 	delete_timeout(NULL, iface);
 	drop_config(iface, "EXPIRE");
 	iface->state->interval = 0;
 	if (iface->carrier != LINK_DOWN) {
-		if (ll)
+		if (IN_LINKLOCAL(htonl(iface->state->lease.addr.s_addr)))
 			start_interface(iface);
 		else
 			start_ipv4ll(iface);
@@ -454,7 +460,8 @@
 		}
 	}
 
-	if (type == DHCP_OFFER && state->state == DHS_DISCOVERING) {
+	if (type == DHCP_OFFER && state->state == DHS_DISCOVER) {
+		lease->frominfo = 0;
 		lease->addr.s_addr = dhcp->yiaddr;
 		get_option_addr(&lease->server.s_addr, dhcp, DHO_SERVERID);
 		log_dhcp(LOG_INFO, "offered", iface, dhcp);
@@ -477,7 +484,6 @@
 			 * then we can't ARP for duplicate detection. */
 			addr.s_addr = state->offer->yiaddr;
 			if (!has_address(iface->name, &addr, NULL)) {
-				state->state = DHS_PROBING;
 				state->claims = 0;
 				state->probes = 0;
 				state->conflicts = 0;
@@ -485,7 +491,7 @@
 				return;
 			}
 		}
-		state->state = DHS_REQUESTING;
+		state->state = DHS_REQUEST;
 		send_request(iface);
 		return;
 	}
@@ -509,6 +515,7 @@
 	*dhcpp = NULL;
 	/* Delete all timeouts for this interface. */
 	delete_timeout(NULL, iface);
+	lease->frominfo = 0;
 	bind_interface(iface);
 }
 
@@ -636,7 +643,7 @@
 	struct interface *iface = arg;
 	struct if_options *ifo = iface->state->options;
 
-	iface->state->state = DHS_DISCOVERING;
+	iface->state->state = DHS_DISCOVER;
 	iface->state->xid = arc4random();
 	open_sockets(iface);
 	delete_timeout(NULL, iface);
@@ -660,12 +667,22 @@
 
 	syslog(LOG_INFO, "%s: renewing lease of %s",
 	       iface->name, inet_ntoa(iface->state->lease.addr));
-	iface->state->state = DHS_RENEWING;
+	iface->state->state = DHS_RENEW;
 	iface->state->xid = arc4random();
 	open_sockets(iface);
 	send_renew(iface);
 }
 
+static void
+start_timeout(void *arg)
+{
+	struct interface *iface = arg;
+
+	bind_interface(iface);
+	iface->state->interval = 0;
+	start_discover(iface);
+}
+
 void
 start_reboot(struct interface *iface)
 {
@@ -675,15 +692,33 @@
 		syslog(LOG_INFO, "%s: waiting for carrier", iface->name);
 		return;
 	}
+	if (ifo->reboot == 0) {
+		start_discover(iface);
+		return;
+	}
+	if (IN_LINKLOCAL(htonl(iface->state->lease.addr.s_addr))) {
+		if (ifo->options & DHCPCD_IPV4LL) {
+			iface->state->claims = 0;
+			send_arp_announce(iface);
+		} else
+			start_discover(iface);
+		return;
+	}
 	syslog(LOG_INFO, "%s: rebinding lease of %s",
 	       iface->name, inet_ntoa(iface->state->lease.addr));
-	iface->state->state = DHS_REBINDING;
+	iface->state->state = DHS_REBOOT;
 	iface->state->xid = arc4random();
 	iface->state->lease.server.s_addr = 0;
 	delete_timeout(NULL, iface);
-	add_timeout_sec(ifo->timeout, start_expire, iface);
+	if (ifo->options & DHCPCD_LASTLEASE && iface->state->lease.frominfo)
+		add_timeout_sec(ifo->reboot, start_timeout, iface);
+	else
+		add_timeout_sec(ifo->reboot, start_expire, iface);
 	open_sockets(iface);
-	send_rebind(iface);
+	if (ifo->options & DHCPCD_ARP)
+		send_arp_probe(iface);
+	else
+		send_request(iface);
 }
 
 static void
@@ -703,6 +738,9 @@
 start_interface(void *arg)
 {
 	struct interface *iface = arg;
+	struct stat st;
+	struct timeval now;
+	uint32_t l;
 
 	if (iface->carrier == LINK_DOWN) {
 		syslog(LOG_INFO, "%s: waiting for carrier", iface->name);
@@ -710,9 +748,36 @@
 	}
 
 	iface->start_uptime = uptime();
-	if (!iface->state->lease.addr.s_addr)
+	iface->state->offer = read_lease(iface);
+/*	if (iface->state->offer) {
+		if (IN_LINKLOCAL(htonl(iface->state->offer->yiaddr))) {
+			free(iface->state->offer);
+			iface->state->offer = NULL;
+		}
+	} */
+	if (iface->state->offer) {
+		get_lease(&iface->state->lease, iface->state->offer);
+		iface->state->lease.frominfo = 1;
+		/* Offset lease times and check expiry */
+		if (stat(iface->leasefile, &st) == 0 &&
+		    get_option_uint32(&l, iface->state->offer, DHO_LEASETIME) == 0)
+		{
+			gettimeofday(&now, NULL);
+			if ((time_t)l < now.tv_sec - st.st_mtime) {
+				free(iface->state->offer);
+				iface->state->offer = NULL;
+			} else {
+				l = now.tv_sec - st.st_mtime;
+				iface->state->lease.leasetime -= l;
+				iface->state->lease.renewaltime -= l;
+				iface->state->lease.rebindtime -= l;
+			}
+		}
+	}
+	if (!iface->state->offer)
 		start_discover(iface);
-	else if (IN_LINKLOCAL(htonl(iface->state->lease.addr.s_addr)))
+	else if (IN_LINKLOCAL(htonl(iface->state->lease.addr.s_addr)) &&
+		 iface->state->options->options & DHCPCD_IPV4LL)
 		start_ipv4ll(iface);
 	else
 		start_reboot(iface);
@@ -861,7 +926,7 @@
 {
 	struct interface *iface, *ifl;
 	int sig = signal_read();
-	int do_reboot = 0, do_release = 0;
+	int do_release = 0;
 
 	switch (sig) {
 	case SIGINT:
@@ -872,15 +937,17 @@
 		break;
 	case SIGALRM:
 		syslog(LOG_INFO, "received SIGALRM, rebinding lease");
-		do_reboot = 1;
+		for (iface = ifaces; iface; iface = iface->next)
+			start_reboot(iface);
+		return;
 	case SIGHUP:
 		syslog(LOG_INFO, "received SIGHUP, releasing lease");
 		do_release = 1;
 		break;
 	default:
-		syslog (LOG_ERR,
-			"received signal %d, but don't know what to do with it",
-			sig);
+		syslog(LOG_ERR,
+		       "received signal %d, but don't know what to do with it",
+		       sig);
 		return;
 	}
 
@@ -893,14 +960,10 @@
 				ifl = iface;
 		if (!ifl)
 			break;
-		if (do_reboot)
-			start_reboot(ifl);
-		else {
-			if (do_release)
-				send_release(ifl);
-			if (!(ifl->state->options->options & DHCPCD_PERSISTENT))
-				drop_config(ifl, do_release ? "RELEASE" : "STOP");
-		}
+		if (do_release)
+			send_release(ifl);
+		if (!(ifl->state->options->options & DHCPCD_PERSISTENT))
+			drop_config(ifl, do_release ? "RELEASE" : "STOP");
 	}
 	exit(EXIT_FAILURE);
 }
@@ -1102,6 +1165,8 @@
 			unlink(pidfile);
 			exit(EXIT_FAILURE);
 		}
+		if (sig == SIGALRM)
+			exit(EXIT_SUCCESS);
 		/* Spin until it exits */
 		syslog(LOG_INFO, "waiting for pid %d to exit", pid);
 		ts.tv_sec = 0;
--- a/dhcpcd.conf.5.in	Mon Sep 15 15:20:37 2008 +0000
+++ b/dhcpcd.conf.5.in	Mon Sep 15 15:23:46 2008 +0000
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd September 11, 2008
+.Dd September 15, 2008
 .Dt DHCPCD.CONF 5 SMM
 .Sh NAME
 .Nm dhcpcd.conf
@@ -124,6 +124,14 @@
 .Xr dhcpcd-run-hooks 8
 or the numerical value.
 You can specify more options seperated by commas, spaces or more option lines.
+.Ic reboot Ar seconds
+Allow
+.Ar reboot
+seconds before moving to the discover phase if we have an old lease to use.
+The default is 10 seconds.
+A setting if 0 seconds causes
+.Nm dhcpcd
+to skip the reboot phase and go straight into discover.
 .It Ic require Ar option
 Requires the
 .Ar option
--- a/dhcpcd.h	Mon Sep 15 15:20:37 2008 +0000
+++ b/dhcpcd.h	Mon Sep 15 15:23:46 2008 +0000
@@ -39,16 +39,14 @@
 
 enum DHS {
 	DHS_INIT,
-	DHS_DISCOVERING,
-	DHS_REQUESTING,
+	DHS_DISCOVER,
+	DHS_REQUEST,
 	DHS_BOUND,
-	DHS_RENEWING,
-	DHS_REBINDING,
+	DHS_RENEW,
+	DHS_REBIND,
 	DHS_REBOOT,
 	DHS_RENEW_REQUESTED,
-	DHS_INIT_IPV4LL,
-	DHS_PROBING,
-	DHS_ANNOUNCING
+	DHS_INIT_IPV4LL
 };
 
 #define LINK_UP 	1
--- a/if-options.c	Mon Sep 15 15:20:37 2008 +0000
+++ b/if-options.c	Mon Sep 15 15:23:46 2008 +0000
@@ -48,7 +48,7 @@
 
 /* Don't set any optional arguments here so we retain POSIX
  * compatibility with getopt */
-#define OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xABC:DEF:GI:KLO:Q:TVX:"
+#define OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xy:z:ABC:DEF:GI:KLO:Q:TVX:Z:"
 
 const struct option cf_options[] = {
 	{"background",      no_argument,       NULL, 'b'},
@@ -70,6 +70,7 @@
 	{"userclass",       required_argument, NULL, 'u'},
 	{"vendor",          required_argument, NULL, 'v'},
 	{"exit",            no_argument,       NULL, 'x'},
+	{"reboot",          required_argument, NULL, 'y'},
 	{"allowinterfaces", required_argument, NULL, 'z'},
 	{"noarp",           no_argument,       NULL, 'A'},
 	{"nobackground",    no_argument,       NULL, 'B'},
@@ -382,7 +383,7 @@
 	case 't':
 		ifo->timeout = atoint(arg);
 		if (ifo->timeout < 0) {
-			syslog (LOG_ERR, "timeout must be a positive value");
+			syslog(LOG_ERR, "timeout must be a positive value");
 			return -1;
 		}
 		break;
@@ -435,6 +436,13 @@
 			ifo->vendor[0] += s + 2;
 		}
 		break;
+	case 'y':
+		ifo->reboot = atoint(arg);
+		if (ifo->reboot < 0) {
+			syslog(LOG_ERR, "reboot must be a positive value");
+			return -1;
+		}
+		break;
 	case 'z':
 		/* We only set this if we haven't got any interfaces */
 		if (!ifaces)
@@ -587,6 +595,7 @@
 	ifo->options |= DHCPCD_CLIENTID | DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
 	ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
 	ifo->timeout = DEFAULT_TIMEOUT;
+	ifo->reboot = DEFAULT_REBOOT;
 	ifo->metric = -1;
 	gethostname(ifo->hostname + 1, sizeof(ifo->hostname));
 	if (strcmp(ifo->hostname + 1, "(none)") == 0 ||
--- a/if-options.h	Mon Sep 15 15:20:37 2008 +0000
+++ b/if-options.h	Mon Sep 15 15:23:46 2008 +0000
@@ -40,6 +40,7 @@
 #define IF_OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xz:ABC:DEF:GI:KLO:Q:TVX:Z:"
 
 #define DEFAULT_TIMEOUT		30
+#define DEFAULT_REBOOT		10
 #define DEFAULT_LEASETIME	3600	/* 1 hour */
 
 #define HOSTNAME_MAX_LEN	250	/* 255 - 3 (FQDN) - 2 (DNS enc) */
@@ -76,6 +77,7 @@
 	uint8_t nomask[256 / 8];
 	uint32_t leasetime;
 	time_t timeout;
+	time_t reboot;
 	int options;
 
 	struct in_addr request_address;
--- a/ipv4ll.c	Mon Sep 15 15:20:37 2008 +0000
+++ b/ipv4ll.c	Mon Sep 15 15:23:46 2008 +0000
@@ -87,11 +87,16 @@
 		}
 	}
 
-	syslog(LOG_INFO, "%s: probing for an IPv4LL address", iface->name);
-	delete_timeout(NULL, iface);
-	iface->state->state = DHS_PROBING;
-	free(iface->state->offer);
-	iface->state->offer = make_ipv4ll_lease(0);
+	/* We maybe rebooting of an IPv4LL address. */
+	if (!iface->state->offer ||
+	    !IN_LINKLOCAL(htonl(iface->state->offer->yiaddr)))
+	{
+		syslog(LOG_INFO, "%s: probing for an IPv4LL address", iface->name);
+		delete_timeout(NULL, iface);
+		free(iface->state->offer);
+		iface->state->offer = make_ipv4ll_lease(0);
+		iface->state->lease.frominfo = 0;
+	}
 	send_arp_probe(iface);
 }
 
@@ -102,26 +107,25 @@
 	time_t up;
 
 	if (iface->state->fail.s_addr == iface->state->lease.addr.s_addr) {
-		if (iface->state->state == DHS_PROBING)
+		up = uptime();
+		if (iface->state->defend + DEFEND_INTERVAL > up) {
 			drop_config(iface, "EXPIRE");
-		else {
-			up = uptime();
-			if (iface->state->defend + DEFEND_INTERVAL > up) {
-				drop_config(iface, "EXPIRE");
-				iface->state->conflicts = -1;
-			} else {
-				iface->state->defend = up;
-				return;
-			}
+			iface->state->conflicts = -1;
+		} else {
+			iface->state->defend = up;
+			return;
 		}
 	}
 
 	close_sockets(iface);
+	free(iface->state->offer);
+	iface->state->offer = NULL;
 	if (++iface->state->conflicts > MAX_CONFLICTS) {
 		syslog(LOG_ERR, "%s: failed to acquire an IPv4LL address",
 				iface->name);
 		iface->state->interval = RATE_LIMIT_INTERVAL / 2;
 		start_discover(iface);
-	} else
+	} else {
 		add_timeout_sec(PROBE_WAIT, start_ipv4ll, iface);
+	}
 }