changeset 2462:20a062773c4d draft

Change the readrawsocket API a little so that we know when EOF is reached. This allows us to remove the non blocking option from our sockets due to the way BPF works.
author Roy Marples <roy@marples.name>
date Fri, 02 May 2014 23:54:29 +0000
parents 76bb5e8b8491
children 187ad3d6020b
files arp.c dhcp.c dhcp6.c dhcpcd.c eloop.c if-bsd.c if-linux.c if.h ipv6nd.c
diffstat 9 files changed, 200 insertions(+), 217 deletions(-) [+]
line wrap: on
line diff
--- a/arp.c	Fri May 02 20:57:21 2014 +0000
+++ b/arp.c	Fri May 02 23:54:29 2014 +0000
@@ -134,14 +134,20 @@
 	const char *hwaddr;
 	struct in_addr ina;
 	char hwbuf[HWADDR_LEN * 3];
+	int flags;
 
 	state = D_STATE(ifp);
 	state->fail.s_addr = 0;
-	for(;;) {
+	flags = 0;
+	while (!(flags & RAW_EOF)) {
 		bytes = if_readrawpacket(ifp, ETHERTYPE_ARP,
-		    arp_buffer, sizeof(arp_buffer), NULL);
-		if (bytes == 0 || bytes == -1)
+		    arp_buffer, sizeof(arp_buffer), &flags);
+		if (bytes == 0 || bytes == -1) {
+			syslog(LOG_ERR, "%s: arp if_readrawpacket: %m",
+			    ifp->name);
+			dhcp_close(ifp);
 			return;
+		}
 		/* We must have a full ARP header */
 		if ((size_t)bytes < sizeof(ar))
 			continue;
--- a/dhcp.c	Fri May 02 20:57:21 2014 +0000
+++ b/dhcp.c	Fri May 02 23:54:29 2014 +0000
@@ -2557,55 +2557,58 @@
 static void
 dhcp_handlepacket(void *arg)
 {
-	struct interface *iface = arg;
+	struct interface *ifp = arg;
 	struct dhcp_message *dhcp = NULL;
 	const uint8_t *pp;
 	size_t bytes;
 	struct in_addr from;
-	int i, partialcsum = 0;
-	const struct dhcp_state *state = D_CSTATE(iface);
+	int i, flags;
+	const struct dhcp_state *state = D_CSTATE(ifp);
 
-	/* We loop through until our buffer is empty.
-	 * The benefit is that if we get >1 DHCP packet in our buffer and
-	 * the first one fails for any reason, we can use the next. */
-	for(;;) {
-		bytes = (size_t)if_readrawpacket(iface, ETHERTYPE_IP,
-		    iface->ctx->packet, udp_dhcp_len, &partialcsum);
-		if (bytes == 0 || (ssize_t)bytes == -1)
+	/* Need this API due to BPF */
+	flags = 0;
+	while (!(flags & RAW_EOF)) {
+		bytes = (size_t)if_readrawpacket(ifp, ETHERTYPE_IP,
+		    ifp->ctx->packet, udp_dhcp_len, &flags);
+		if (bytes == 0 || (ssize_t)bytes == -1) {
+			syslog(LOG_ERR, "%s: dhcp if_readrawpacket: %m",
+			    ifp->name);
+			dhcp_close(ifp);
 			break;
-		if (valid_udp_packet(iface->ctx->packet, bytes,
-			&from, partialcsum) == -1)
+		}
+		if (valid_udp_packet(ifp->ctx->packet, bytes,
+			&from, flags & RAW_PARTIALCSUM) == -1)
 		{
 			syslog(LOG_ERR, "%s: invalid UDP packet from %s",
-			    iface->name, inet_ntoa(from));
+			    ifp->name, inet_ntoa(from));
 			continue;
 		}
-		i = whitelisted_ip(iface->options, from.s_addr);
+		i = whitelisted_ip(ifp->options, from.s_addr);
 		if (i == 0) {
 			syslog(LOG_WARNING,
 			    "%s: non whitelisted DHCP packet from %s",
-			    iface->name, inet_ntoa(from));
+			    ifp->name, inet_ntoa(from));
 			continue;
 		} else if (i != 1 &&
-		    blacklisted_ip(iface->options, from.s_addr) == 1)
+		    blacklisted_ip(ifp->options, from.s_addr) == 1)
 		{
 			syslog(LOG_WARNING,
 			    "%s: blacklisted DHCP packet from %s",
-			    iface->name, inet_ntoa(from));
+			    ifp->name, inet_ntoa(from));
 			continue;
 		}
-		if (iface->flags & IFF_POINTOPOINT &&
+		if (ifp->flags & IFF_POINTOPOINT &&
 		    state->dst.s_addr != from.s_addr)
 		{
 			syslog(LOG_WARNING,
 			    "%s: server %s is not destination",
-			    iface->name, inet_ntoa(from));
+			    ifp->name, inet_ntoa(from));
 		}
-		bytes = get_udp_data(&pp, iface->ctx->packet);
+		bytes = get_udp_data(&pp, ifp->ctx->packet);
 		if (bytes > sizeof(*dhcp)) {
 			syslog(LOG_ERR,
 			    "%s: packet greater than DHCP size from %s",
-			    iface->name, inet_ntoa(from));
+			    ifp->name, inet_ntoa(from));
 			continue;
 		}
 		if (dhcp == NULL) {
@@ -2618,22 +2621,22 @@
 		memcpy(dhcp, pp, bytes);
 		if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
 			syslog(LOG_DEBUG, "%s: bogus cookie from %s",
-			    iface->name, inet_ntoa(from));
+			    ifp->name, inet_ntoa(from));
 			continue;
 		}
 		/* Ensure packet is for us */
-		if (iface->hwlen <= sizeof(dhcp->chaddr) &&
-		    memcmp(dhcp->chaddr, iface->hwaddr, iface->hwlen))
+		if (ifp->hwlen <= sizeof(dhcp->chaddr) &&
+		    memcmp(dhcp->chaddr, ifp->hwaddr, ifp->hwlen))
 		{
 			char buf[sizeof(dhcp->chaddr) * 3];
 
 			syslog(LOG_DEBUG, "%s: xid 0x%x is not for hwaddr %s",
-			    iface->name, ntohl(dhcp->xid),
+			    ifp->name, ntohl(dhcp->xid),
 			    hwaddr_ntoa(dhcp->chaddr, sizeof(dhcp->chaddr),
 				buf, sizeof(buf)));
 			continue;
 		}
-		dhcp_handledhcp(iface, &dhcp, &from);
+		dhcp_handledhcp(ifp, &dhcp, &from);
 		if (state->raw_fd == -1)
 			break;
 	}
--- a/dhcp6.c	Fri May 02 20:57:21 2014 +0000
+++ b/dhcp6.c	Fri May 02 23:54:29 2014 +0000
@@ -2036,8 +2036,11 @@
 	ctx = dhcpcd_ctx->ipv6;
 	ctx->rcvhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
 	bytes = recvmsg(ctx->dhcp_fd, &ctx->rcvhdr, 0);
-	if (bytes == -1) {
+	if (bytes == -1 || bytes == 0) {
 		syslog(LOG_ERR, "recvmsg: %m");
+		close(ctx->dhcp_fd);
+		eloop_event_delete(dhcpcd_ctx->eloop, ctx->dhcp_fd);
+		ctx->dhcp_fd = -1;
 		return;
 	}
 	len = (size_t)bytes;
--- a/dhcpcd.c	Fri May 02 20:57:21 2014 +0000
+++ b/dhcpcd.c	Fri May 02 23:54:29 2014 +0000
@@ -641,8 +641,12 @@
 	struct dhcpcd_ctx *ctx;
 
 	ctx = arg;
-	if (if_managelink(ctx) == -1 && errno != ENXIO && errno != ENODEV)
+	if (if_managelink(ctx) == -1) {
 		syslog(LOG_ERR, "if_managelink: %m");
+		eloop_event_delete(ctx->eloop, ctx->link_fd);
+		close(ctx->link_fd);
+		ctx->link_fd = -1;
+	}
 }
 
 static void
--- a/eloop.c	Fri May 02 20:57:21 2014 +0000
+++ b/eloop.c	Fri May 02 23:54:29 2014 +0000
@@ -386,7 +386,7 @@
 		n = poll(ctx->fds, ctx->events_len, timeout);
 #endif
 		if (n == -1) {
-			if (errno == EAGAIN || errno == EINTR)
+			if (errno == EINTR)
 				continue;
 			syslog(LOG_ERR, "poll: %m");
 			break;
@@ -395,7 +395,7 @@
 		/* Process any triggered events. */
 		if (n > 0) {
 			TAILQ_FOREACH(e, &ctx->events, next) {
-				if (e->pollfd->revents & (POLLIN | POLLHUP)) {
+				if (e->pollfd->revents & (POLLIN)) {
 					e->callback(e->arg);
 					/* We need to break here as the
 					 * callback could destroy the next
--- a/if-bsd.c	Fri May 02 20:57:21 2014 +0000
+++ b/if-bsd.c	Fri May 02 23:54:29 2014 +0000
@@ -114,7 +114,7 @@
 {
 
 #ifdef SOCK_CLOEXEC
-	return socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+	return socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC, 0);
 #else
 	int s, flags;
 
@@ -126,12 +126,6 @@
 		close(s);
 	        return -1;
 	}
-	if ((flags = fcntl(s, F_GETFL, 0)) == -1 ||
-	    fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
-	{
-		close(s);
-	        return -1;
-	}
 	return s;
 #endif
 }
@@ -228,14 +222,14 @@
 	int flags;
 #endif
 #ifdef _PATH_BPF
-	fd = open(_PATH_BPF, O_RDWR | O_CLOEXEC | O_NONBLOCK);
+	fd = open(_PATH_BPF, O_RDWR | O_CLOEXEC);
 #else
 	char device[32];
 	int n = 0;
 
 	do {
 		snprintf(device, sizeof(device), "/dev/bpf%d", n++);
-		fd = open(device, O_RDWR | O_CLOEXEC | O_NONBLOCK);
+		fd = open(device, O_RDWR | O_CLOEXEC);
 	} while (fd == -1 && errno == EBUSY);
 #endif
 
@@ -289,13 +283,6 @@
 	if (ioctl(fd, BIOCSETF, &pf) == -1)
 		goto eexit;
 
-#ifdef __OpenBSD__
-	/* For some reason OpenBSD fails to open the fd as non blocking */
-	if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
-	    fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
-		goto eexit;
-#endif
-
 	return fd;
 
 eexit:
@@ -333,9 +320,9 @@
  * So we pass the buffer in the API so we can loop on >1 packet. */
 ssize_t
 if_readrawpacket(struct interface *ifp, int protocol,
-    void *data, size_t len, int *partialcsum)
+    void *data, size_t len, int *flags)
 {
-	int fd = -1;
+	int fd;
 	struct bpf_hdr packet;
 	ssize_t bytes;
 	const unsigned char *payload;
@@ -347,16 +334,14 @@
 	else
 		fd = state->raw_fd;
 
-	if (partialcsum != NULL)
-		*partialcsum = 0; /* Not supported on BSD */
+	if (flags != NULL)
+		*flags = 0; /* Not supported on BSD */
 
 	for (;;) {
 		if (state->buffer_len == 0) {
 			bytes = read(fd, state->buffer, state->buffer_size);
-			if (bytes == -1)
-				return errno == EAGAIN ? 0 : -1;
-			else if ((size_t)bytes < sizeof(packet))
-				return -1;
+			if (bytes == -1 || bytes == 0)
+				return bytes;
 			state->buffer_len = (size_t)bytes;
 			state->buffer_pos = 0;
 		}
@@ -377,8 +362,10 @@
 next:
 		state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
 		    packet.bh_caplen);
-		if (state->buffer_pos >= state->buffer_len)
+		if (state->buffer_pos >= state->buffer_len) {
 			state->buffer_len = state->buffer_pos = 0;
+			*flags |= RAW_EOF;
+		}
 		if (bytes != -1)
 			return bytes;
 	}
@@ -774,143 +761,136 @@
 	int ifa_flags;
 #endif
 
-	for (;;) {
-		bytes = read(ctx->link_fd, msg, sizeof(msg));
-		if (bytes == -1) {
-			if (errno == EAGAIN)
-				return 0;
-			if (errno == EINTR)
-				continue;
-			return -1;
-		}
-		e = msg + bytes;
-		for (p = msg; p < e; p += rtm->rtm_msglen) {
-			rtm = (struct rt_msghdr *)(void *)p;
-			// Ignore messages generated by us
-			if (rtm->rtm_pid == getpid())
+	bytes = read(ctx->link_fd, msg, sizeof(msg));
+	if (bytes == -1 || bytes == 0)
+		return bytes;
+	e = msg + bytes;
+	for (p = msg; p < e; p += rtm->rtm_msglen) {
+		rtm = (struct rt_msghdr *)(void *)p;
+		// Ignore messages generated by us
+		if (rtm->rtm_pid == getpid())
+			break;
+		switch(rtm->rtm_type) {
+#ifdef RTM_IFANNOUNCE
+		case RTM_IFANNOUNCE:
+			ifan = (struct if_announcemsghdr *)(void *)p;
+			switch(ifan->ifan_what) {
+			case IFAN_ARRIVAL:
+				dhcpcd_handleinterface(ctx, 1,
+				    ifan->ifan_name);
+				break;
+			case IFAN_DEPARTURE:
+				dhcpcd_handleinterface(ctx, -1,
+				    ifan->ifan_name);
+				break;
+			}
+			break;
+#endif
+		case RTM_IFINFO:
+			ifm = (struct if_msghdr *)(void *)p;
+			memset(ifname, 0, sizeof(ifname));
+			if (!(if_indextoname(ifm->ifm_index, ifname)))
+				break;
+			switch (ifm->ifm_data.ifi_link_state) {
+			case LINK_STATE_DOWN:
+				len = LINK_DOWN;
+				break;
+			case LINK_STATE_UP:
+				len = LINK_UP;
+				break;
+			default:
+				/* handle_carrier will re-load
+				 * the interface flags and check for
+				 * IFF_RUNNING as some drivers that
+				 * don't handle link state also don't
+				 * set IFF_RUNNING when this routing
+				 * message is generated.
+				 * As such, it is a race ...*/
+				len = LINK_UNKNOWN;
 				break;
-			switch(rtm->rtm_type) {
-#ifdef RTM_IFANNOUNCE
-			case RTM_IFANNOUNCE:
-				ifan = (struct if_announcemsghdr *)(void *)p;
-				switch(ifan->ifan_what) {
-				case IFAN_ARRIVAL:
-					dhcpcd_handleinterface(ctx, 1,
-					    ifan->ifan_name);
+			}
+			dhcpcd_handlecarrier(ctx, len,
+			    (unsigned int)ifm->ifm_flags, ifname);
+			break;
+		case RTM_DELETE:
+			if (~rtm->rtm_addrs &
+			    (RTA_DST | RTA_GATEWAY | RTA_NETMASK))
+				break;
+			cp = (char *)(void *)(rtm + 1);
+			sa = (struct sockaddr *)(void *)cp;
+			if (sa->sa_family != AF_INET)
+				break;
+#ifdef INET
+			get_addrs(rtm->rtm_addrs, cp, rti_info);
+			memset(&rt, 0, sizeof(rt));
+			rt.iface = NULL;
+			COPYOUT(rt.dest, rti_info[RTAX_DST]);
+			COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
+			COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]);
+			ipv4_routedeleted(ctx, &rt);
+#endif
+			break;
+#ifdef RTM_CHGADDR
+		case RTM_CHGADDR:	/* FALLTHROUGH */
+#endif
+		case RTM_DELADDR:	/* FALLTHROUGH */
+		case RTM_NEWADDR:
+			ifam = (struct ifa_msghdr *)(void *)p;
+			if (!if_indextoname(ifam->ifam_index, ifname))
+				break;
+			cp = (char *)(void *)(ifam + 1);
+			get_addrs(ifam->ifam_addrs, cp, rti_info);
+			if (rti_info[RTAX_IFA] == NULL)
+				break;
+			switch (rti_info[RTAX_IFA]->sa_family) {
+			case AF_LINK:
+#ifdef RTM_CHGADDR
+				if (rtm->rtm_type != RTM_CHGADDR)
 					break;
-				case IFAN_DEPARTURE:
-					dhcpcd_handleinterface(ctx, -1,
-					    ifan->ifan_name);
+#else
+				if (rtm->rtm_type != RTM_NEWADDR)
 					break;
-				}
+#endif
+				memcpy(&sdl, rti_info[RTAX_IFA],
+				    rti_info[RTAX_IFA]->sa_len);
+				dhcpcd_handlehwaddr(ctx, ifname,
+				    (const unsigned char*)CLLADDR(&sdl),
+				    sdl.sdl_alen);
+				break;
+#ifdef INET
+			case AF_INET:
+			case 255: /* FIXME: Why 255? */
+				COPYOUT(rt.dest, rti_info[RTAX_IFA]);
+				COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
+				COPYOUT(rt.gate, rti_info[RTAX_BRD]);
+				ipv4_handleifa(ctx, rtm->rtm_type,
+				    NULL, ifname,
+				    &rt.dest, &rt.net, &rt.gate);
 				break;
 #endif
-			case RTM_IFINFO:
-				ifm = (struct if_msghdr *)(void *)p;
-				memset(ifname, 0, sizeof(ifname));
-				if (!(if_indextoname(ifm->ifm_index, ifname)))
-					break;
-				switch (ifm->ifm_data.ifi_link_state) {
-				case LINK_STATE_DOWN:
-					len = LINK_DOWN;
-					break;
-				case LINK_STATE_UP:
-					len = LINK_UP;
-					break;
-				default:
-					/* handle_carrier will re-load
-					 * the interface flags and check for
-					 * IFF_RUNNING as some drivers that
-					 * don't handle link state also don't
-					 * set IFF_RUNNING when this routing
-					 * message is generated.
-					 * As such, it is a race ...*/
-					len = LINK_UNKNOWN;
-					break;
-				}
-				dhcpcd_handlecarrier(ctx, len,
-				    (unsigned int)ifm->ifm_flags, ifname);
+#ifdef INET6
+			case AF_INET6:
+				sin6 = (struct sockaddr_in6*)(void *)
+				    rti_info[RTAX_IFA];
+				memcpy(ia6.s6_addr,
+				    sin6->sin6_addr.s6_addr,
+				    sizeof(ia6.s6_addr));
+				if (rtm->rtm_type == RTM_NEWADDR) {
+					ifa_flags = if_addrflags6(ifname, &ia6);
+					if (ifa_flags == -1)
+						break;
+				} else
+					ifa_flags = 0;
+				ipv6_handleifa(ctx, rtm->rtm_type, NULL,
+				    ifname, &ia6, ifa_flags);
 				break;
-			case RTM_DELETE:
-				if (~rtm->rtm_addrs &
-				    (RTA_DST | RTA_GATEWAY | RTA_NETMASK))
-					break;
-				cp = (char *)(void *)(rtm + 1);
-				sa = (struct sockaddr *)(void *)cp;
-				if (sa->sa_family != AF_INET)
-					break;
-#ifdef INET
-				get_addrs(rtm->rtm_addrs, cp, rti_info);
-				memset(&rt, 0, sizeof(rt));
-				rt.iface = NULL;
-				COPYOUT(rt.dest, rti_info[RTAX_DST]);
-				COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
-				COPYOUT(rt.gate, rti_info[RTAX_GATEWAY]);
-				ipv4_routedeleted(ctx, &rt);
-#endif
-				break;
-#ifdef RTM_CHGADDR
-			case RTM_CHGADDR:	/* FALLTHROUGH */
 #endif
-			case RTM_DELADDR:	/* FALLTHROUGH */
-			case RTM_NEWADDR:
-				ifam = (struct ifa_msghdr *)(void *)p;
-				if (!if_indextoname(ifam->ifam_index, ifname))
-					break;
-				cp = (char *)(void *)(ifam + 1);
-				get_addrs(ifam->ifam_addrs, cp, rti_info);
-				if (rti_info[RTAX_IFA] == NULL)
-					break;
-				switch (rti_info[RTAX_IFA]->sa_family) {
-				case AF_LINK:
-#ifdef RTM_CHGADDR
-					if (rtm->rtm_type != RTM_CHGADDR)
-						break;
-#else
-					if (rtm->rtm_type != RTM_NEWADDR)
-						break;
-#endif
-					memcpy(&sdl, rti_info[RTAX_IFA],
-					    rti_info[RTAX_IFA]->sa_len);
-					dhcpcd_handlehwaddr(ctx, ifname,
-					    (const unsigned char*)CLLADDR(&sdl),
-					    sdl.sdl_alen);
-					break;
-#ifdef INET
-				case AF_INET:
-				case 255: /* FIXME: Why 255? */
-					COPYOUT(rt.dest, rti_info[RTAX_IFA]);
-					COPYOUT(rt.net, rti_info[RTAX_NETMASK]);
-					COPYOUT(rt.gate, rti_info[RTAX_BRD]);
-					ipv4_handleifa(ctx, rtm->rtm_type,
-					    NULL, ifname,
-					    &rt.dest, &rt.net, &rt.gate);
-					break;
-#endif
-#ifdef INET6
-				case AF_INET6:
-					sin6 = (struct sockaddr_in6*)(void *)
-					    rti_info[RTAX_IFA];
-					memcpy(ia6.s6_addr,
-					    sin6->sin6_addr.s6_addr,
-					    sizeof(ia6.s6_addr));
-					if (rtm->rtm_type == RTM_NEWADDR) {
-						ifa_flags = if_addrflags6(
-								ifname,
-								&ia6);
-						if (ifa_flags == -1)
-							break;
-					} else
-						ifa_flags = 0;
-					ipv6_handleifa(ctx, rtm->rtm_type, NULL,
-					    ifname, &ia6, ifa_flags);
-					break;
-#endif
-				}
-				break;
 			}
+			break;
 		}
 	}
+
+	return 0;
 }
 
 #ifndef SYS_NMLN	/* OSX */
--- a/if-linux.c	Fri May 02 20:57:21 2014 +0000
+++ b/if-linux.c	Fri May 02 23:54:29 2014 +0000
@@ -272,15 +272,9 @@
 	for (;;) {
 		bytes = recv(fd, NULL, 0,
 		    flags | MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC);
-		if (bytes == -1) {
-			if (errno == EAGAIN) {
-				r = 0;
-				goto eexit;
-			}
-			if (errno == EINTR)
-				continue;
+		if (bytes == -1 || bytes == 0)
 			goto eexit;
-		} else if ((size_t)bytes == buflen) {
+		if ((size_t)bytes == buflen) {
 			/* Support kernels older than 2.6.22 */
 			if (bytes == 0)
 				bytes = 512;
@@ -297,15 +291,8 @@
 		}
 		bytes = recvfrom(fd, buf, buflen, flags,
 		    (struct sockaddr *)&nladdr, &nladdr_len);
-		if (bytes == -1) {
-			if (errno == EAGAIN) {
-				r = 0;
-				goto eexit;
-			}
-			if (errno == EINTR)
-				continue;
+		if (bytes == -1 || bytes == 0)
 			goto eexit;
-		}
 
 		/* Check sender */
 		if (nladdr_len != sizeof(nladdr)) {
@@ -752,7 +739,7 @@
 #endif
 
 #ifdef SOCK_CLOEXEC
-	if ((s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+	if ((s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC,
 	    htons(protocol))) == -1)
 		return -1;
 #else
@@ -766,12 +753,6 @@
 		close(s);
 	        return -1;
 	}
-	if ((flags = fcntl(s, F_GETFL, 0)) == -1 ||
-	    fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
-	{
-		close(s);
-	        return -1;
-	}
 #endif
 	/* Install the DHCP filter */
 	memset(&pf, 0, sizeof(pf));
@@ -839,7 +820,7 @@
 
 ssize_t
 if_readrawpacket(struct interface *ifp, int protocol,
-    void *data, size_t len, int *partialcsum)
+    void *data, size_t len, int *flags)
 {
 	struct iovec iov = {
 		.iov_base = data,
@@ -871,9 +852,9 @@
 		fd = state->raw_fd;
 	bytes = recvmsg(fd, &msg, 0);
 	if (bytes == -1)
-		return errno == EAGAIN ? 0 : -1;
-	if (partialcsum != NULL) {
-		*partialcsum = 0;
+		return -1;
+	if (bytes) {
+		*flags &= ~RAW_PARTIALCSUM;
 #ifdef PACKET_AUXDATA
 		for (cmsg = CMSG_FIRSTHDR(&msg);
 		     cmsg;
@@ -882,8 +863,8 @@
 			if (cmsg->cmsg_level == SOL_PACKET &&
 			    cmsg->cmsg_type == PACKET_AUXDATA) {
 				aux = (void *)CMSG_DATA(cmsg);
-				*partialcsum = aux->tp_status &
-				    TP_STATUS_CSUMNOTREADY;
+				if (aux->tp_status & TP_STATUS_CSUMNOTREADY)
+					*flags |= RAW_PARTIALCSUM;
 			}
 		}
 #endif
--- a/if.h	Fri May 02 20:57:21 2014 +0000
+++ b/if.h	Fri May 02 23:54:29 2014 +0000
@@ -80,6 +80,9 @@
 # define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
 #endif
 
+#define RAW_EOF			1 << 0
+#define RAW_PARTIALCSUM		2 << 0
+
 struct if_head *if_discover(struct dhcpcd_ctx *, int, char * const *);
 struct interface *if_find(struct dhcpcd_ctx *, const char *);
 void if_free(struct interface *);
--- a/ipv6nd.c	Fri May 02 20:57:21 2014 +0000
+++ b/ipv6nd.c	Fri May 02 23:54:29 2014 +0000
@@ -1166,10 +1166,10 @@
 	pi.ipi6_ifindex = rap->iface->index;
 	memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
 
-#ifdef DEBUG_NS
+//#ifdef DEBUG_NS
 	syslog(LOG_INFO, "%s: sending IPv6 NS for %s",
 	    rap->iface->name, rap->sfrom);
-#endif
+//#endif
 	if (sendmsg(ctx->nd_fd, &ctx->sndhdr, 0) == -1) {
 		syslog(LOG_ERR, "%s: %s: sendmsg: %m",
 		    rap->iface->name, __func__);
@@ -1432,8 +1432,11 @@
 	ctx->rcvhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
 	    CMSG_SPACE(sizeof(int));
 	len = recvmsg(ctx->nd_fd, &ctx->rcvhdr, 0);
-	if (len == -1) {
+	if (len == -1 || len == 0) {
 		syslog(LOG_ERR, "recvmsg: %m");
+		eloop_event_delete(dhcpcd_ctx->eloop, ctx->nd_fd);
+		close(ctx->nd_fd);
+		ctx->nd_fd = -1;
 		return;
 	}
 	ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr,