changeset 2258:83f7b828306e draft

Implement support for RFC 3203, FORCERENEW message.
author Roy Marples <roy@marples.name>
date Fri, 31 Jan 2014 11:47:27 +0000
parents 5d6ccc5a3b11
children 7502a71beb0a
files arp.c arp.h dhcp.c dhcp.h dhcpcd.8.in dhcpcd.c ipv4ll.c
diffstat 7 files changed, 93 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/arp.c	Thu Jan 30 14:20:24 2014 +0000
+++ b/arp.c	Fri Jan 31 11:47:27 2014 +0000
@@ -106,7 +106,6 @@
 	unlink(state->leasefile);
 	if (!state->lease.frominfo)
 		dhcp_decline(ifp);
-	dhcp_close(ifp);
 	eloop_timeout_delete(NULL, ifp);
 	if (state->lease.frominfo)
 		start_interface(ifp);
@@ -336,3 +335,19 @@
 	state->arping_index = 0;
 	arp_probe(ifp);
 }
+
+void
+arp_close(struct interface *ifp)
+{
+	struct dhcp_state *state = D_STATE(ifp);
+
+	if (state == NULL)
+		return;
+
+	if (state->arp_fd != -1) {
+		eloop_event_delete(state->arp_fd);
+		close(state->arp_fd);
+		state->arp_fd = -1;
+	}
+}
+
--- a/arp.h	Thu Jan 30 14:20:24 2014 +0000
+++ b/arp.h	Fri Jan 31 11:47:27 2014 +0000
@@ -45,4 +45,5 @@
 void arp_announce(void *);
 void arp_probe(void *);
 void arp_start(struct interface *);
+void arp_close(struct interface *);
 #endif
--- a/dhcp.c	Thu Jan 30 14:20:24 2014 +0000
+++ b/dhcp.c	Fri Jan 31 11:47:27 2014 +0000
@@ -1718,9 +1718,8 @@
 	struct if_options *ifo = iface->options;
 	struct dhcp_lease *lease = &state->lease;
 	struct timeval tv;
+	uint8_t ipv4ll = 0;
 
-	/* We're binding an address now - ensure that sockets are closed */
-	dhcp_close(iface);
 	state->reason = NULL;
 	if (clock_monotonic)
 		get_monotonic(&lease->boundtime);
@@ -1741,6 +1740,7 @@
 		    iface->name, inet_ntoa(lease->addr));
 		lease->leasetime = ~0U;
 		state->reason = "IPV4LL";
+		ipv4ll = 1;
 	} else if (ifo->options & DHCPCD_INFORM) {
 		if (ifo->req_addr.s_addr != 0)
 			lease->addr.s_addr = ifo->req_addr.s_addr;
@@ -1822,6 +1822,8 @@
 	}
 	ipv4_applyaddr(iface);
 	daemonise();
+	if (!ipv4ll)
+		arp_close(iface);
 	state->state = DHS_BOUND;
 	if (ifo->options & DHCPCD_ARP) {
 		state->claims = 0;
@@ -2011,6 +2013,8 @@
 	state = D_STATE(ifp);
 	if (state == NULL)
 		return;
+	dhcp_close(ifp);
+	arp_close(ifp);
 	eloop_timeouts_delete(ifp, dhcp_expire, NULL);
 	if (ifp->options->options & DHCPCD_RELEASE) {
 		unlink(state->leasefile);
@@ -2164,6 +2168,49 @@
 		log_dhcp1(LOG_WARNING, "no authentication",
 		    iface, dhcp, from, 0);
 
+	/* RFC 3203 */
+	if (type == DHCP_FORCERENEW) {
+		if (from->s_addr == INADDR_ANY ||
+		    from->s_addr == INADDR_BROADCAST)
+		{
+			log_dhcp(LOG_ERR, "discarding Force Renew",
+			    iface, dhcp, from);
+			return;
+		}
+		if (auth == NULL) {
+			log_dhcp(LOG_ERR, "unauthenticated Force Renew",
+			    iface, dhcp, from);
+			return;
+		}
+		if (state->state != DHS_BOUND) {
+			log_dhcp(LOG_DEBUG, "not bound, ignoring Force Renew",
+			    iface, dhcp, from);
+			return;
+		}
+		log_dhcp(LOG_ERR, "Force Renew from", iface, dhcp, from);
+		/* The rebind and expire timings are still the same, we just
+		 * enter the renew state early */
+		eloop_timeout_delete(dhcp_renew, iface);
+		dhcp_renew(iface);
+		return;
+	}
+
+	if (state->state == DHS_BOUND) {
+		/* Before we supported FORCERENEW we closed off the raw
+		 * port so we effectively ignored all messages.
+		 * As such we'll not log by default here. */
+		//log_dhcp(LOG_DEBUG, "bound, ignoring", iface, dhcp, from);
+		return;
+	}
+
+	/* Ensure it's the right transaction */
+	if (state->xid != ntohl(dhcp->xid)) {
+		syslog(LOG_DEBUG,
+		    "%s: wrong xid 0x%x (expecting 0x%x) from %s",
+		    iface->name, ntohl(dhcp->xid), state->xid,
+		    inet_ntoa(*from));
+		return;
+	}
 	/* reset the message counter */
 	state->interval = 0;
 
@@ -2175,13 +2222,14 @@
 			log_dhcp(LOG_WARNING, "reject NAK", iface, dhcp, from);
 			return;
 		}
+
 		/* We should restart on a NAK */
 		log_dhcp(LOG_WARNING, "NAK:", iface, dhcp, from);
 		if (!(options & DHCPCD_TEST)) {
 			dhcp_drop(iface, "NAK");
 			unlink(state->leasefile);
 		}
-		dhcp_close(iface);
+
 		/* If we constantly get NAKS then we should slowly back off */
 		eloop_timeout_add_sec(state->nakoff, dhcp_discover, iface);
 		if (state->nakoff == 0)
@@ -2291,11 +2339,6 @@
 	lease->frominfo = 0;
 	eloop_timeout_delete(NULL, iface);
 
-	/* We now have an offer, so close the DHCP sockets.
-	 * This allows us to safely ARP when broken DHCP servers send an ACK
-	 * follows by an invalid NAK. */
-	dhcp_close(iface);
-
 	if (ifo->options & DHCPCD_ARP &&
 	    state->addr.s_addr != state->offer->yiaddr)
 	{
@@ -2441,14 +2484,6 @@
 			    iface->name, inet_ntoa(from));
 			continue;
 		}
-		/* Ensure it's the right transaction */
-		if (state->xid != ntohl(dhcp->xid)) {
-			syslog(LOG_DEBUG,
-			    "%s: wrong xid 0x%x (expecting 0x%x) from %s",
-			    iface->name, ntohl(dhcp->xid), state->xid,
-			    inet_ntoa(from));
-			continue;
-		}
 		/* Ensure packet is for us */
 		if (iface->hwlen <= sizeof(dhcp->chaddr) &&
 		    memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
@@ -2465,6 +2500,21 @@
 	free(dhcp);
 }
 
+static void
+dhcp_handleudp(void *arg)
+{
+	const struct interface *ifp;
+	const struct dhcp_state *state;
+	ssize_t bytes;
+	uint8_t buffer[sizeof(struct dhcp_message)];
+
+	ifp = arg;
+	state = D_CSTATE(ifp);
+	bytes = read(state->udp_fd, buffer, sizeof(buffer));
+	/* Just read what's in the UDP fd and discard it as we always read
+	 * from the raw fd */
+}
+
 static int
 dhcp_open(struct interface *ifp)
 {
@@ -2491,15 +2541,13 @@
 		eloop_event_add(state->raw_fd, dhcp_handlepacket, ifp);
 	}
 	if (state->udp_fd == -1 &&
-	    state->addr.s_addr != 0 &&
-	    state->new != NULL &&
-	    (state->new->cookie == htonl(MAGIC_COOKIE) ||
-	    ifp->options->options & DHCPCD_INFORM))
+	    ifp->options->options & DHCPCD_DHCP)
 	{
 		if (dhcp_openudp(ifp) == -1 && errno != EADDRINUSE) {
 			syslog(LOG_ERR, "%s: dhcp_openudp: %m", ifp->name);
 			return -1;
 		}
+		eloop_event_add(state->udp_fd, dhcp_handleudp, ifp);
 	}
 	return 0;
 }
@@ -2686,7 +2734,6 @@
 		syslog(LOG_WARNING, "%s: needs a clientid to configure",
 		    ifp->name);
 		dhcp_drop(ifp, "FAIL");
-		dhcp_close(ifp);
 		eloop_timeout_delete(NULL, ifp);
 		return;
 	}
--- a/dhcp.h	Thu Jan 30 14:20:24 2014 +0000
+++ b/dhcp.h	Fri Jan 31 11:47:27 2014 +0000
@@ -57,6 +57,7 @@
 #define DHCP_NAK            6
 #define DHCP_RELEASE        7
 #define DHCP_INFORM         8
+#define DHCP_FORCERENEW     9
 
 /* Constants taken from RFC 2131. */
 #define T1			0.5
--- a/dhcpcd.8.in	Thu Jan 30 14:20:24 2014 +0000
+++ b/dhcpcd.8.in	Fri Jan 31 11:47:27 2014 +0000
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd January 30, 2014
+.Dd January 31, 2014
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -98,7 +98,8 @@
 .Nm
 then daemonises and waits for the lease renewal time to lapse.
 It will then attempt to renew its lease and reconfigure if the new lease
-changes.
+changes when the lease beings to expire or the DHCP server sends message
+to renew early.
 .Pp
 .Nm
 is also an implementation of the BOOTP client specified in
@@ -652,10 +653,10 @@
 .Xr resolvconf 8
 .Sh STANDARDS
 RFC\ 951, RFC\ 1534, RFC\ 2104, RFC\ 2131, RFC\ 2132, RFC\ 2855, RFC\ 3004,
-RFC\ 3118, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396, RFC\ 3397, RFC\ 3442,
-RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075, RFC\ 4242, RFC\ 4361,
-RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833, RFC\ 5227, RFC\ 5942,
-RFC\ 5969, RFC\ 6106.
+RFC\ 3118, RFC\ 3203, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396, RFC\ 3397,
+RFC\ 3442, RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075, RFC\ 4242,
+RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833, RFC\ 5227,
+RFC\ 5942, RFC\ 5969, RFC\ 6106.
 .Sh AUTHORS
 .An Roy Marples Aq Mt roy@marples.name
 .Sh BUGS
--- a/dhcpcd.c	Thu Jan 30 14:20:24 2014 +0000
+++ b/dhcpcd.c	Fri Jan 31 11:47:27 2014 +0000
@@ -328,7 +328,6 @@
 	dhcp6_drop(ifp, NULL);
 	ipv6nd_drop(ifp);
 	dhcp_drop(ifp, "STOP");
-	dhcp_close(ifp);
 	eloop_timeout_delete(NULL, ifp);
 	if (ifp->options->options & DHCPCD_DEPARTED)
 		script_runreason(ifp, "DEPARTED");
@@ -526,7 +525,6 @@
 			if (ifp->carrier == LINK_UP)
 				syslog(LOG_INFO, "%s: carrier lost", ifp->name);
 			ifp->carrier = LINK_DOWN;
-			dhcp_close(ifp);
 			dhcp6_drop(ifp, "EXPIRE6");
 			ipv6nd_drop(ifp);
 			/* Don't blindly delete our knowledge of LL addresses.
--- a/ipv4ll.c	Thu Jan 30 14:20:24 2014 +0000
+++ b/ipv4ll.c	Fri Jan 31 11:47:27 2014 +0000
@@ -151,7 +151,6 @@
 		}
 	}
 
-	dhcp_close(ifp);
 	free(state->offer);
 	state->offer = NULL;
 	eloop_timeout_delete(NULL, ifp);