Index | Thread | Search

From:
Stuart Henderson <stu@spacehopper.org>
Subject:
Re: Issues with dhcpcd 10.3.0v0
To:
Courtney Hicks <courtney@courtnix.systems>, Zack Newman <zack@philomathiclife.com>
Cc:
ports@openbsd.org
Date:
Sun, 28 Dec 2025 14:41:16 +0000

Download raw body.

Thread
On 2025/12/22 10:48, Courtney Hicks wrote:
> I recently updated my router to the latest -current as well as ports,
> which upgraded my previous version of dhcpcd 10.2.4v0 to 10.3.0v0. I
> eventually realized I was no longer getting IPv6 addresses on my
> clients. I checked my logs and got a bunch of "no buffer space
> available" messages on my LAN interfaces that I have PDs going to. 
> 
> dhcpcd[55787]: if_getmtu: No buffer space available
> 
> I also noticed that this was messing with arp info too for some reason.
> This is my config:
> 
> ipv6only
> noipv6rs
> duid
> persistent
> option rapid_commit
> require dhcp_server_identifier
> 
> script ""
> 
> allowinterfaces igc0 ixl0 vlan5
> interface igc0
>         ipv6rs
>         ia_na 1
>         ia_pd 2/::/60 ixl0/1/64 vlan5/2/64
> 
> I built dhcpcd 10.2.4v0 and replaced 10.3.0v0 and everything is working
> as expected. I imagine this is a dhcpcd bug, but I wanted to bring this
> up here on the ports list.
> 
> Courtney
> 
> PS: I hope Thunderbird formats my line width to 72 correctly. My
> apologies if it does not and breaks netiquette
> 

On 2025/12/27 14:43, Zack Newman wrote:
> Yeah, this is a known issue[^1]. If you compile the code yourself with
> the proposed patch, it should work[^2]. Note I don't run -current, so
> I'm merely speaking as a reader. I recommend staying in the loop with
> upstream seeing how disruptive issues with dhcpcd can be. I always look
> at the issues on GitHub before updating it.
> 
> [^1]: https://github.com/NetworkConfiguration/dhcpcd/issues/555
> [^2]: https://github.com/NetworkConfiguration/dhcpcd/issues/555#issuecomment-3546211800
> 


Can you try this please? It is the result of the set of patches from
https://github.com/NetworkConfiguration/dhcpcd/pull/556

Would be good to comment on the upstream PR if it helps too.

Index: Makefile
===================================================================
RCS file: /cvs/ports/net/dhcpcd/Makefile,v
diff -u -p -r1.119 Makefile
--- Makefile	17 Nov 2025 18:36:41 -0000	1.119
+++ Makefile	28 Dec 2025 14:39:02 -0000
@@ -1,6 +1,7 @@
 COMMENT=	DHCPv4/IPv4LL/IPv6RS/DHCPv6 quad stack client
 
 V=		10.3.0
+REVISION=	0
 DISTNAME=	dhcpcd-$V
 EXTRACT_SUFX=	.tar.xz
 
Index: patches/patch-src_privsep-root_c
===================================================================
RCS file: patches/patch-src_privsep-root_c
diff -N patches/patch-src_privsep-root_c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-src_privsep-root_c	28 Dec 2025 14:39:02 -0000
@@ -0,0 +1,253 @@
+https://patch-diff.githubusercontent.com/raw/NetworkConfiguration/dhcpcd/pull/556.patch
+
+Index: src/privsep-root.c
+--- src/privsep-root.c.orig
++++ src/privsep-root.c
+@@ -71,21 +71,20 @@ struct psr_ctx {
+ 	struct psr_error psr_error;
+ 	size_t psr_datalen;
+ 	void *psr_data;
+-	size_t psr_mdatalen;
+-	void *psr_mdata;
+-	bool psr_usemdata;
++	bool psr_mallocdata;
+ };
+ 
+ static ssize_t
+-ps_root_readerrorcb(struct psr_ctx *psr_ctx)
++ps_root_readerrorcb(struct psr_ctx *pc)
+ {
+-	struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
++	struct dhcpcd_ctx *ctx = pc->psr_ctx;
+ 	int fd = PS_ROOT_FD(ctx);
+-	struct psr_error *psr_error = &psr_ctx->psr_error;
++	struct psr_error *psr_error = &pc->psr_error;
+ 	struct iovec iov[] = {
+ 		{ .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
+-		{ .iov_base = NULL, .iov_len = 0 },
++		{ .iov_base = pc->psr_data, .iov_len = pc->psr_datalen },
+ 	};
++	struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };
+ 	ssize_t len;
+ 
+ #define PSR_ERROR(e)				\
+@@ -98,81 +97,84 @@ ps_root_readerrorcb(struct psr_ctx *psr_ctx)
+ 	if (eloop_waitfd(fd) == -1)
+ 		PSR_ERROR(errno);
+ 
+-	len = recv(fd, psr_error, sizeof(*psr_error), MSG_PEEK);
++	if (!pc->psr_mallocdata)
++		goto recv;
++
++	/* We peek at the psr_error structure to tell us how much of a buffer
++	 * we need to read the whole packet. */
++	msg.msg_iovlen--;
++	len = recvmsg(fd, &msg, MSG_PEEK | MSG_WAITALL);
+ 	if (len == -1)
+ 		PSR_ERROR(errno);
+-	else if ((size_t)len < sizeof(*psr_error))
+-		PSR_ERROR(EINVAL);
+ 
+-	if (psr_error->psr_datalen > SSIZE_MAX)
+-		PSR_ERROR(ENOBUFS);
+-	if (psr_ctx->psr_usemdata &&
+-	    psr_error->psr_datalen > psr_ctx->psr_mdatalen)
+-	{
+-		void *d = realloc(psr_ctx->psr_mdata, psr_error->psr_datalen);
+-		if (d == NULL)
+-			PSR_ERROR(errno);
+-		psr_ctx->psr_mdata = d;
+-		psr_ctx->psr_mdatalen = psr_error->psr_datalen;
++	/* After this point, we MUST do another recvmsg even on a failure
++	 * to remove the message after peeking. */
++	if ((size_t)len < sizeof(*psr_error)) {
++		/* We can't use the header to work out buffers, so
++		 * remove the message and bail. */
++		(void)recvmsg(fd, &msg, MSG_WAITALL);
++		PSR_ERROR(EINVAL);
+ 	}
+-	if (psr_error->psr_datalen != 0) {
+-		if (psr_ctx->psr_usemdata)
+-			iov[1].iov_base = psr_ctx->psr_mdata;
+-		else {
+-			if (psr_error->psr_datalen > psr_ctx->psr_datalen)
+-				PSR_ERROR(ENOBUFS);
+-			iov[1].iov_base = psr_ctx->psr_data;
+-		}
++
++	/* No data to read? Unlikely but ... */
++	if (psr_error->psr_datalen == 0)
++		goto recv;
++
++	pc->psr_data = malloc(psr_error->psr_datalen);
++	if (pc->psr_data != NULL) {
++		iov[1].iov_base = pc->psr_data;
+ 		iov[1].iov_len = psr_error->psr_datalen;
++		msg.msg_iovlen++;
+ 	}
+ 
+-	len = readv(fd, iov, __arraycount(iov));
++recv:
++	len = recvmsg(fd, &msg, MSG_WAITALL);
+ 	if (len == -1)
+ 		PSR_ERROR(errno);
+-	else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen)
++	else if ((size_t)len < sizeof(*psr_error))
+ 		PSR_ERROR(EINVAL);
++	else if (msg.msg_flags & MSG_TRUNC)
++		PSR_ERROR(ENOBUFS);
++	else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen) {
++		logerrx("%s: recvmsg returned %zd, expecting %zu", __func__,
++		    len, sizeof(*psr_error) + psr_error->psr_datalen);
++		PSR_ERROR(EBADMSG);
++	}
+ 	return len;
+ }
+ 
+ ssize_t
+ ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)
+ {
+-	struct psr_ctx *pc = ctx->ps_root->psp_data;
++	struct psr_ctx pc = {
++		.psr_ctx = ctx,
++		.psr_data = data,
++		.psr_datalen = len,
++		.psr_mallocdata = false
++	};
+ 
+-	pc->psr_data = data;
+-	pc->psr_datalen = len;
+-	pc->psr_usemdata = false;
+-	ps_root_readerrorcb(pc);
++	ps_root_readerrorcb(&pc);
+ 
+-	errno = pc->psr_error.psr_errno;
+-	return pc->psr_error.psr_result;
++	errno = pc.psr_error.psr_errno;
++	return pc.psr_error.psr_result;
+ }
+ 
+ ssize_t
+ ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len)
+ {
+-	struct psr_ctx *pc = ctx->ps_root->psp_data;
+-	void *d;
++	struct psr_ctx pc = {
++		.psr_ctx = ctx,
++		.psr_data = NULL,
++		.psr_datalen = 0,
++		.psr_mallocdata = true
++	};
+ 
+-	pc->psr_usemdata = true;
+-	ps_root_readerrorcb(pc);
++	ps_root_readerrorcb(&pc);
+ 
+-	if (pc->psr_error.psr_datalen != 0) {
+-		if (pc->psr_error.psr_datalen > pc->psr_mdatalen) {
+-			errno = EINVAL;
+-			return -1;
+-		}
+-		d = malloc(pc->psr_error.psr_datalen);
+-		if (d == NULL)
+-			return -1;
+-		memcpy(d, pc->psr_mdata, pc->psr_error.psr_datalen);
+-	} else
+-		d = NULL;
+-
+-	errno = pc->psr_error.psr_errno;
+-	*data = d;
+-	*len = pc->psr_error.psr_datalen;
+-	return pc->psr_error.psr_result;
++	errno = pc.psr_error.psr_errno;
++	*data = pc.psr_data;
++	*len = pc.psr_error.psr_datalen;
++	return pc.psr_error.psr_result;
+ }
+ 
+ static ssize_t
+@@ -196,6 +198,8 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t res
+ 	logdebugx("%s: result %zd errno %d", __func__, result, errno);
+ #endif
+ 
++	if (len == 0)
++		msg.msg_iovlen = 1;
+ 	err = sendmsg(fd, &msg, MSG_EOR);
+ 
+ 	/* Error sending the message? Try sending the error of sending. */
+@@ -204,8 +208,8 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t res
+ 		    __func__, result, data, len);
+ 		psr.psr_result = err;
+ 		psr.psr_errno = errno;
+-		iov[1].iov_base = NULL;
+-		iov[1].iov_len = 0;
++		psr.psr_datalen = 0;
++		msg.msg_iovlen = 1;
+ 		err = sendmsg(fd, &msg, MSG_EOR);
+ 	}
+ 
+@@ -602,7 +606,7 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, st
+ 		break;
+ 	}
+ 
+-	err = ps_root_writeerror(ctx, err, rlen != 0 ? rdata : 0, rlen);
++	err = ps_root_writeerror(ctx, err, rdata, rlen);
+ 	if (free_rdata)
+ 		free(rdata);
+ 	return err;
+@@ -843,17 +847,6 @@ ps_root_log(void *arg, unsigned short events)
+ 		logerr(__func__);
+ }
+ 
+-static void
+-ps_root_freepsdata(void *arg)
+-{
+-	struct psr_ctx *pc = arg;
+-
+-	if (pc == NULL)
+-		return;
+-	free(pc->psr_mdata);
+-	free(pc);
+-}
+-
+ pid_t
+ ps_root_start(struct dhcpcd_ctx *ctx)
+ {
+@@ -864,7 +857,6 @@ ps_root_start(struct dhcpcd_ctx *ctx)
+ 	struct ps_process *psp;
+ 	int logfd[2] = { -1, -1}, datafd[2] = { -1, -1};
+ 	pid_t pid;
+-	struct psr_ctx *pc;
+ 
+ 	if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, logfd) == -1)
+ 		return -1;
+@@ -883,27 +875,15 @@ ps_root_start(struct dhcpcd_ctx *ctx)
+ 		return -1;
+ #endif
+ 
+-	pc = calloc(1, sizeof(*pc));
+-	if (pc == NULL)
+-		return -1;
+-	pc->psr_ctx = ctx;
+-
+ 	psp = ctx->ps_root = ps_newprocess(ctx, &id);
+ 	if (psp == NULL)
+-	{
+-		free(pc);
+ 		return -1;
+-	}
+-	psp->psp_freedata = ps_root_freepsdata;
++
+ 	strlcpy(psp->psp_name, "privileged proxy", sizeof(psp->psp_name));
+ 	pid = ps_startprocess(psp, ps_root_recvmsg, NULL,
+ 	    ps_root_startcb, PSF_ELOOP);
+-	if (pid == -1) {
+-		free(pc);
++	if (pid == -1)
+ 		return -1;
+-	}
+-
+-	psp->psp_data = pc;
+ 
+ 	if (pid == 0) {
+ 		ctx->ps_log_fd = logfd[0]; /* Keep open to pass to processes */
Index: patches/patch-src_privsep_c
===================================================================
RCS file: patches/patch-src_privsep_c
diff -N patches/patch-src_privsep_c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-src_privsep_c	28 Dec 2025 14:39:02 -0000
@@ -0,0 +1,17 @@
+https://patch-diff.githubusercontent.com/raw/NetworkConfiguration/dhcpcd/pull/556.patch
+
+Index: src/privsep.c
+--- src/privsep.c.orig
++++ src/privsep.c
+@@ -761,11 +761,6 @@ ps_freeprocess(struct ps_process *psp)
+ 
+ 	TAILQ_REMOVE(&ctx->ps_processes, psp, next);
+ 
+-	if (psp->psp_freedata != NULL)
+-		psp->psp_freedata(psp->psp_data);
+-	else
+-		free(psp->psp_data);
+-
+ 	if (psp->psp_fd != -1) {
+ 		eloop_event_delete(ctx->eloop, psp->psp_fd);
+ 		close(psp->psp_fd);
Index: patches/patch-src_privsep_h
===================================================================
RCS file: patches/patch-src_privsep_h
diff -N patches/patch-src_privsep_h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-src_privsep_h	28 Dec 2025 14:39:02 -0000
@@ -0,0 +1,14 @@
+https://patch-diff.githubusercontent.com/raw/NetworkConfiguration/dhcpcd/pull/556.patch
+
+Index: src/privsep.h
+--- src/privsep.h.orig
++++ src/privsep.h
+@@ -184,8 +184,6 @@ struct ps_process {
+ 	char psp_name[PSP_NAMESIZE];
+ 	uint16_t psp_proto;
+ 	const char *psp_protostr;
+-	void *psp_data;
+-	void (*psp_freedata)(void *);
+ 	bool psp_started;
+ 
+ #ifdef INET