changeset 2278:54eea0036437 draft

Open UDP sockets for *.*.*.*:bootpc and $ip_address:bootpc
author Roy Marples <roy@marples.name>
date Mon, 03 Feb 2014 14:12:14 +0000
parents 0f315f198852
children 83ca8e0b70bd
files dhcp.c
diffstat 1 files changed, 44 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/dhcp.c	Mon Feb 03 13:24:04 2014 +0000
+++ b/dhcp.c	Mon Feb 03 14:12:14 2014 +0000
@@ -124,6 +124,7 @@
 struct dhcp_opt *dhcp_opts = NULL;
 size_t dhcp_opts_len = 0;
 
+static int udp_fd = -1;
 static const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet);
 
 static int dhcp_open(struct interface *);
@@ -1356,7 +1357,7 @@
 }
 
 static int
-dhcp_openudp(struct interface *iface)
+dhcp_openudp(struct interface *ifp)
 {
 	int s;
 	struct sockaddr_in sin;
@@ -1374,26 +1375,34 @@
 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
 		goto eexit;
 #ifdef SO_BINDTODEVICE
-	memset(&ifr, 0, sizeof(ifr));
-	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
-	/* We can only bind to the real device */
-	p = strchr(ifr.ifr_name, ':');
-	if (p)
-	    *p = '\0';
-	if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr,
+	if (ifp) {
+		memset(&ifr, 0, sizeof(ifr));
+		strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
+		/* We can only bind to the real device */
+		p = strchr(ifr.ifr_name, ':');
+		if (p)
+			*p = '\0';
+		if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr,
 		    sizeof(ifr)) == -1)
-		goto eexit;
+		        goto eexit;
+	}
 #endif
-	state = D_STATE(iface);
 	memset(&sin, 0, sizeof(sin));
 	sin.sin_family = AF_INET;
 	sin.sin_port = htons(DHCP_CLIENT_PORT);
-	sin.sin_addr.s_addr = state->addr.s_addr;
+	if (ifp) {
+		state = D_STATE(ifp);
+		sin.sin_addr.s_addr = state->addr.s_addr;
+	} else
+		state = NULL; /* appease gcc */
 	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
 		goto eexit;
 
-	state->udp_fd = s;
 	set_cloexec(s);
+	if (ifp)
+		state->udp_fd = s;
+	else
+		udp_fd = s;
 	return 0;
 
 eexit:
@@ -2060,6 +2069,20 @@
 	state->auth.replay = 0;
 	free(state->auth.reconf);
 	state->auth.reconf = NULL;
+
+	/* If we don't have any more DHCP enabled interfaces,
+	 * close the global socket */
+	if (ifaces) {
+		TAILQ_FOREACH(ifp, ifaces, next) {
+			if (D_STATE(ifp))
+				break;
+		}
+	}
+	if (ifp == NULL && udp_fd != -1) {
+		close(udp_fd);
+		eloop_event_delete(udp_fd);
+		udp_fd = -1;
+	}
 }
 
 static void
@@ -2604,7 +2627,10 @@
 		eloop_event_add(state->raw_fd, dhcp_handlepacket, ifp);
 	}
 	if (state->udp_fd == -1 &&
-	    ifp->options->options & DHCPCD_DHCP)
+	    state->addr.s_addr != 0 &&
+	    state->new != NULL &&
+	    (state->new->cookie == htonl(MAGIC_COOKIE) ||
+	    ifp->options->options & DHCPCD_INFORM))
 	{
 		if (dhcp_openudp(ifp) == -1 && errno != EADDRINUSE) {
 			syslog(LOG_ERR, "%s: dhcp_openudp: %m", ifp->name);
@@ -2682,6 +2708,11 @@
 	const struct if_options *ifo;
 	size_t len;
 
+	/* Listen on *.*.*.*:bootpc so that the kernel never sends an
+	 * ICMP port unreachable message back to the DHCP server */
+	if (udp_fd == -1 && dhcp_openudp(NULL) != -1)
+		eloop_event_add(udp_fd, dhcp_handleudp, NULL);
+
 	state = D_STATE(ifp);
 	if (state == NULL) {
 		ifp->if_data[IF_DATA_DHCP] = calloc(1, sizeof(*state));