changeset 4169:07bfee79bd7a draft

bpf: store flags in state for a better abort There are cases when dhcp may close and open an ARP socket during an ARP read. This means the fd will not be -1, so we need to set BPF_EOF when closing the socket.
author Roy Marples <roy@marples.name>
date Tue, 24 Oct 2017 23:11:15 +0100
parents e8510a89cdb2
children cc4fbeafc390
files src/arp.c src/arp.h src/bpf.c src/bpf.h src/dhcp.c src/dhcp.h src/if-linux.c
diffstat 7 files changed, 47 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/src/arp.c	Tue Oct 24 00:13:09 2017 +0100
+++ b/src/arp.c	Tue Oct 24 23:11:15 2017 +0100
@@ -98,7 +98,7 @@
 	APPEND(&tip, sizeof(tip));
 
 	state = ARP_CSTATE(ifp);
-	return bpf_send(ifp, state->fd, ETHERTYPE_ARP, arp_buffer, len);
+	return bpf_send(ifp, state->bpf_fd, ETHERTYPE_ARP, arp_buffer, len);
 
 eexit:
 	errno = ENOBUFS;
@@ -177,10 +177,11 @@
 {
 	struct iarp_state *state;
 
-	if ((state = ARP_STATE(ifp)) != NULL && state->fd != -1) {
-		eloop_event_delete(ifp->ctx->eloop, state->fd);
-		bpf_close(ifp, state->fd);
-		state->fd = -1;
+	if ((state = ARP_STATE(ifp)) != NULL && state->bpf_fd != -1) {
+		eloop_event_delete(ifp->ctx->eloop, state->bpf_fd);
+		bpf_close(ifp, state->bpf_fd);
+		state->bpf_fd = -1;
+		state->bpf_flags |= BPF_EOF;
 	}
 }
 
@@ -188,18 +189,18 @@
 arp_read(void *arg)
 {
 	struct interface *ifp = arg;
-	const struct iarp_state *state;
+	struct iarp_state *state;
 	uint8_t buf[ARP_LEN];
-	int flags;
 	ssize_t bytes;
 
 	/* Some RAW mechanisms are generic file descriptors, not sockets.
 	 * This means we have no kernel call to just get one packet,
 	 * so we have to process the entire buffer. */
-	state = ARP_CSTATE(ifp);
-	flags = 0;
-	while (!(flags & BPF_EOF)) {
-		bytes = bpf_read(ifp, state->fd, buf, sizeof(buf), &flags);
+	state = ARP_STATE(ifp);
+	state->bpf_flags &= ~BPF_EOF;
+	while (!(state->bpf_flags & BPF_EOF)) {
+		bytes = bpf_read(ifp, state->bpf_fd, buf, sizeof(buf),
+				 &state->bpf_flags);
 		if (bytes == -1) {
 			logerr("%s: %s", __func__, ifp->name);
 			arp_close(ifp);
@@ -207,7 +208,7 @@
 		}
 		arp_packet(ifp, buf, (size_t)bytes);
 		/* Check we still have a state after processing. */
-		if ((state = ARP_CSTATE(ifp)) == NULL || state->fd == -1)
+		if ((state = ARP_STATE(ifp)))
 			break;
 	}
 }
@@ -218,15 +219,15 @@
 	struct iarp_state *state;
 
 	state = ARP_STATE(ifp);
-	if (state->fd == -1) {
-		state->fd = bpf_open(ifp, bpf_arp);
-		if (state->fd == -1) {
+	if (state->bpf_fd == -1) {
+		state->bpf_fd = bpf_open(ifp, bpf_arp);
+		if (state->bpf_fd == -1) {
 			logerr("%s: %s", __func__, ifp->name);
 			return -1;
 		}
-		eloop_event_add(ifp->ctx->eloop, state->fd, arp_read, ifp);
+		eloop_event_add(ifp->ctx->eloop, state->bpf_fd, arp_read, ifp);
 	}
-	return state->fd;
+	return state->bpf_fd;
 }
 
 static void
@@ -273,7 +274,7 @@
 	} else {
 		const struct iarp_state *state = ARP_CSTATE(astate->iface);
 
-		if (bpf_arp(astate->iface, state->fd) == -1)
+		if (bpf_arp(astate->iface, state->bpf_fd) == -1)
 			logerr(__func__);
 	}
 	astate->probes = 0;
@@ -451,7 +452,8 @@
 			logerr(__func__);
 			return NULL;
 		}
-		state->fd = -1;
+		state->bpf_fd = -1;
+		state->bpf_flags = 0;
 		TAILQ_INIT(&state->arp_states);
 	} else {
 		if (addr && (astate = arp_find(ifp, addr)))
@@ -468,7 +470,7 @@
 	state = ARP_STATE(ifp);
 	TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
 
-	if (bpf_arp(ifp, state->fd) == -1)
+	if (bpf_arp(ifp, state->bpf_fd) == -1)
 		logerr(__func__); /* try and continue */
 
 	return astate;
@@ -504,7 +506,7 @@
 		free(state);
 		ifp->if_data[IF_DATA_ARP] = NULL;
 	} else
-		if (bpf_arp(ifp, state->fd) == -1)
+		if (bpf_arp(ifp, state->bpf_fd) == -1)
 			logerr(__func__);
 }
 
--- a/src/arp.h	Tue Oct 24 00:13:09 2017 +0100
+++ b/src/arp.h	Tue Oct 24 23:11:15 2017 +0100
@@ -76,7 +76,8 @@
 TAILQ_HEAD(arp_statehead, arp_state);
 
 struct iarp_state {
-	int fd;
+	int bpf_fd;
+	unsigned int bpf_flags;
 	struct arp_statehead arp_states;
 };
 
--- a/src/bpf.c	Tue Oct 24 00:13:09 2017 +0100
+++ b/src/bpf.c	Tue Oct 24 23:11:15 2017 +0100
@@ -194,7 +194,8 @@
 /* BPF requires that we read the entire buffer.
  * So we pass the buffer in the API so we can loop on >1 packet. */
 ssize_t
-bpf_read(struct interface *ifp, int fd, void *data, size_t len, int *flags)
+bpf_read(struct interface *ifp, int fd, void *data, size_t len,
+    unsigned int *flags)
 {
 	ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);
 	ssize_t bytes;
@@ -203,7 +204,7 @@
 	struct bpf_hdr packet;
 	const char *payload;
 
-	*flags = 0;
+	*flags &= ~BPF_EOF;
 	for (;;) {
 		if (state->buffer_len == 0) {
 			bytes = read(fd, state->buffer, state->buffer_size);
--- a/src/bpf.h	Tue Oct 24 00:13:09 2017 +0100
+++ b/src/bpf.h	Tue Oct 24 23:11:15 2017 +0100
@@ -28,8 +28,8 @@
 #ifndef BPF_HEADER
 #define BPF_HEADER
 
-#define BPF_EOF			1 << 0
-#define BPF_PARTIALCSUM		2 << 0
+#define BPF_EOF			(1U << 0)
+#define BPF_PARTIALCSUM		(2U << 0)
 
 #include "dhcpcd.h"
 
@@ -39,7 +39,7 @@
 int bpf_close(struct interface *, int);
 int bpf_attach(int, void *, unsigned int);
 ssize_t bpf_send(const struct interface *, int, uint16_t, const void *, size_t);
-ssize_t bpf_read(struct interface *, int, void *, size_t, int *);
+ssize_t bpf_read(struct interface *, int, void *, size_t, unsigned int *);
 int bpf_arp(struct interface *, int);
 int bpf_bootp(struct interface *, int);
 #endif
--- a/src/dhcp.c	Tue Oct 24 00:13:09 2017 +0100
+++ b/src/dhcp.c	Tue Oct 24 23:11:15 2017 +0100
@@ -1551,6 +1551,7 @@
 		eloop_event_delete(ifp->ctx->eloop, state->bpf_fd);
 		bpf_close(ifp, state->bpf_fd);
 		state->bpf_fd = -1;
+		state->bpf_flags |= BPF_EOF;
 	}
 
 	state->interval = 0;
@@ -3236,14 +3237,15 @@
 }
 
 static void
-dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len, int flags)
+dhcp_handlepacket(struct interface *ifp, uint8_t *data, size_t len)
 {
 	struct bootp *bootp;
 	struct in_addr from;
 	size_t udp_len;
 	const struct dhcp_state *state = D_CSTATE(ifp);
 
-	if (valid_udp_packet(data, len, &from, flags & RAW_PARTIALCSUM) == -1)
+	if (valid_udp_packet(data, len, &from,
+			     state->bpf_flags & RAW_PARTIALCSUM) == -1)
 	{
 		if (errno == EINVAL)
 			logerrx("%s: UDP checksum failure from %s",
@@ -3292,15 +3294,15 @@
 	struct interface *ifp = arg;
 	uint8_t buf[MTU_MAX];
 	ssize_t bytes;
-	int flags;
-	const struct dhcp_state *state = D_CSTATE(ifp);
+	struct dhcp_state *state = D_STATE(ifp);
 
 	/* Some RAW mechanisms are generic file descriptors, not sockets.
 	 * This means we have no kernel call to just get one packet,
 	 * so we have to process the entire buffer. */
-	flags = 0;
-	while (!(flags & BPF_EOF)) {
-		bytes = bpf_read(ifp, state->bpf_fd, buf, sizeof(buf), &flags);
+	state->bpf_flags &= ~BPF_EOF;
+	while (!(state->bpf_flags & BPF_EOF)) {
+		bytes = bpf_read(ifp, state->bpf_fd, buf, sizeof(buf),
+				 &state->bpf_flags);
 		if (bytes == -1) {
 			if (state->state != DHS_NONE) {
 				logerr("%s: %s", __func__, ifp->name);
@@ -3308,9 +3310,9 @@
 			}
 			return;
 		}
-		dhcp_handlepacket(ifp, buf, (size_t)bytes, flags);
+		dhcp_handlepacket(ifp, buf, (size_t)bytes);
 		/* Check we still have a state after processing. */
-		if ((state = D_CSTATE(ifp)) == NULL || state->bpf_fd == -1)
+		if ((state = D_STATE(ifp)) == NULL)
 			break;
 	}
 }
--- a/src/dhcp.h	Tue Oct 24 00:13:09 2017 +0100
+++ b/src/dhcp.h	Tue Oct 24 23:11:15 2017 +0100
@@ -215,6 +215,7 @@
 	int socket;
 
 	int bpf_fd;
+	unsigned int bpf_flags;
 	struct ipv4_addr *addr;
 	uint8_t added;
 
--- a/src/if-linux.c	Tue Oct 24 00:13:09 2017 +0100
+++ b/src/if-linux.c	Tue Oct 24 23:11:15 2017 +0100
@@ -1385,7 +1385,8 @@
 /* BPF requires that we read the entire buffer.
  * So we pass the buffer in the API so we can loop on >1 packet. */
 ssize_t
-bpf_read(struct interface *ifp, int s, void *data, size_t len, int *flags)
+bpf_read(struct interface *ifp, int s, void *data, size_t len,
+    unsigned int *flags)
 {
 	ssize_t bytes;
 	struct ipv4_state *state = IPV4_STATE(ifp);
@@ -1409,7 +1410,8 @@
 	bytes = recvmsg(s, &msg, 0);
 	if (bytes == -1)
 		return -1;
-	*flags = BPF_EOF; /* We only ever read one packet. */
+	*flags |= BPF_EOF; /* We only ever read one packet. */
+	*flags &= ~BPF_PARTIALCSUM;
 	if (bytes) {
 		ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);