xref: /dragonfly/contrib/dhcpcd/src/privsep-inet.c (revision f984587a)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * Privilege Separation for dhcpcd, network proxy
4  * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
5  * All rights reserved
6 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/socket.h>
30 #include <sys/types.h>
31 
32 #include <netinet/in.h>
33 #include <netinet/icmp6.h>
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #include "arp.h"
42 #include "bpf.h"
43 #include "dhcp.h"
44 #include "dhcp6.h"
45 #include "eloop.h"
46 #include "ipv6nd.h"
47 #include "logerr.h"
48 #include "privsep.h"
49 
50 #ifdef INET
51 static void
52 ps_inet_recvbootp(void *arg, unsigned short events)
53 {
54 	struct dhcpcd_ctx *ctx = arg;
55 
56 	if (ps_recvmsg(ctx, ctx->udp_rfd, events,
57 	    PS_BOOTP, ctx->ps_inet->psp_fd) == -1)
58 		logerr(__func__);
59 }
60 #endif
61 
62 #ifdef INET6
63 static void
64 ps_inet_recvra(void *arg, unsigned short events)
65 {
66 #ifdef __sun
67 	struct interface *ifp = arg;
68 	struct rs_state *state = RS_STATE(ifp);
69 	struct dhcpcd_ctx *ctx = ifp->ctx;
70 
71 	if (ps_recvmsg(ctx, state->nd_fd, events,
72 	    PS_ND, ctx->ps_inet->psp_fd) == -1)
73 		logerr(__func__);
74 #else
75 	struct dhcpcd_ctx *ctx = arg;
76 
77 	if (ps_recvmsg(ctx, ctx->nd_fd, events,
78 	    PS_ND, ctx->ps_inet->psp_fd) == -1)
79 		logerr(__func__);
80 #endif
81 }
82 #endif
83 
84 #ifdef DHCP6
85 static void
86 ps_inet_recvdhcp6(void *arg, unsigned short events)
87 {
88 	struct dhcpcd_ctx *ctx = arg;
89 
90 	if (ps_recvmsg(ctx, ctx->dhcp6_rfd, events,
91 	    PS_DHCP6, ctx->ps_inet->psp_fd) == -1)
92 		logerr(__func__);
93 }
94 #endif
95 
96 bool
97 ps_inet_canstart(const struct dhcpcd_ctx *ctx)
98 {
99 
100 #ifdef INET
101 	if ((ctx->options & (DHCPCD_IPV4 | DHCPCD_MANAGER)) ==
102 	    (DHCPCD_IPV4 | DHCPCD_MANAGER))
103 		return true;
104 #endif
105 #if defined(INET6) && !defined(__sun)
106 	if (ctx->options & DHCPCD_IPV6)
107 		return true;
108 #endif
109 #ifdef DHCP6
110 	if ((ctx->options & (DHCPCD_IPV6 | DHCPCD_MANAGER)) ==
111 	    (DHCPCD_IPV6 | DHCPCD_MANAGER))
112 		return true;
113 #endif
114 
115 	return false;
116 }
117 
118 static int
119 ps_inet_startcb(struct ps_process *psp)
120 {
121 	struct dhcpcd_ctx *ctx = psp->psp_ctx;
122 	int ret = 0;
123 
124 	if (ctx->options & DHCPCD_MANAGER)
125 		setproctitle("[network proxy]");
126 	else
127 		setproctitle("[network proxy] %s%s%s",
128 		    ctx->ifc != 0 ? ctx->ifv[0] : "",
129 		    ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
130 		    ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
131 
132 	/* This end is the main engine, so it's useless for us. */
133 	close(ctx->ps_data_fd);
134 	ctx->ps_data_fd = -1;
135 
136 	errno = 0;
137 
138 #ifdef INET
139 	if ((ctx->options & (DHCPCD_IPV4 | DHCPCD_MANAGER)) ==
140 	    (DHCPCD_IPV4 | DHCPCD_MANAGER))
141 	{
142 		ctx->udp_rfd = dhcp_openudp(NULL);
143 		if (ctx->udp_rfd == -1)
144 			logerr("%s: dhcp_open", __func__);
145 #ifdef PRIVSEP_RIGHTS
146 		else if (ps_rights_limit_fd_rdonly(ctx->udp_rfd) == -1) {
147 			logerr("%s: ps_rights_limit_fd_rdonly", __func__);
148 			close(ctx->udp_rfd);
149 			ctx->udp_rfd = -1;
150 		}
151 #endif
152 		else if (eloop_event_add(ctx->eloop, ctx->udp_rfd, ELE_READ,
153 		    ps_inet_recvbootp, ctx) == -1)
154 		{
155 			logerr("%s: eloop_event_add DHCP", __func__);
156 			close(ctx->udp_rfd);
157 			ctx->udp_rfd = -1;
158 		} else
159 			ret++;
160 	}
161 #endif
162 #if defined(INET6) && !defined(__sun)
163 	if (ctx->options & DHCPCD_IPV6) {
164 		ctx->nd_fd = ipv6nd_open(true);
165 		if (ctx->nd_fd == -1)
166 			logerr("%s: ipv6nd_open", __func__);
167 #ifdef PRIVSEP_RIGHTS
168 		else if (ps_rights_limit_fd_rdonly(ctx->nd_fd) == -1) {
169 			logerr("%s: ps_rights_limit_fd_rdonly", __func__);
170 			close(ctx->nd_fd);
171 			ctx->nd_fd = -1;
172 		}
173 #endif
174 		else if (eloop_event_add(ctx->eloop, ctx->nd_fd, ELE_READ,
175 		    ps_inet_recvra, ctx) == -1)
176 		{
177 			logerr("%s: eloop_event_add RA", __func__);
178 			close(ctx->nd_fd);
179 			ctx->nd_fd = -1;
180 		} else
181 			ret++;
182 	}
183 #endif
184 #ifdef DHCP6
185 	if ((ctx->options & (DHCPCD_IPV6 | DHCPCD_MANAGER)) ==
186 	    (DHCPCD_IPV6 | DHCPCD_MANAGER))
187 	{
188 		ctx->dhcp6_rfd = dhcp6_openudp(0, NULL);
189 		if (ctx->dhcp6_rfd == -1)
190 			logerr("%s: dhcp6_open", __func__);
191 #ifdef PRIVSEP_RIGHTS
192 		else if (ps_rights_limit_fd_rdonly(ctx->dhcp6_rfd) == -1) {
193 			logerr("%s: ps_rights_limit_fd_rdonly", __func__);
194 			close(ctx->dhcp6_rfd);
195 			ctx->dhcp6_rfd = -1;
196 		}
197 #endif
198 		else if (eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, ELE_READ,
199 		    ps_inet_recvdhcp6, ctx) == -1)
200 		{
201 			logerr("%s: eloop_event_add DHCP6", __func__);
202 			close(ctx->dhcp6_rfd);
203 			ctx->dhcp6_rfd = -1;
204 		} else
205 			ret++;
206 	}
207 #endif
208 
209 	if (ret == 0 && errno == 0) {
210 		errno = ENXIO;
211 		return -1;
212 	}
213 	return ret;
214 }
215 
216 static bool
217 ps_inet_validudp(struct msghdr *msg, uint16_t sport, uint16_t dport)
218 {
219 	struct udphdr udp;
220 	struct iovec *iov = msg->msg_iov;
221 
222 	if (msg->msg_iovlen == 0 || iov->iov_len < sizeof(udp)) {
223 		errno = EINVAL;
224 		return false;
225 	}
226 
227 	memcpy(&udp, iov->iov_base, sizeof(udp));
228 	if (udp.uh_sport != htons(sport) || udp.uh_dport != htons(dport)) {
229 		errno = EPERM;
230 		return false;
231 	}
232 	return true;
233 }
234 
235 #ifdef INET6
236 static bool
237 ps_inet_validnd(struct msghdr *msg)
238 {
239 	struct icmp6_hdr icmp6;
240 	struct iovec *iov = msg->msg_iov;
241 
242 	if (msg->msg_iovlen == 0 || iov->iov_len < sizeof(icmp6)) {
243 		errno = EINVAL;
244 		return false;
245 	}
246 
247 	memcpy(&icmp6, iov->iov_base, sizeof(icmp6));
248 	switch(icmp6.icmp6_type) {
249 	case ND_ROUTER_SOLICIT:
250 	case ND_NEIGHBOR_ADVERT:
251 		break;
252 	default:
253 		errno = EPERM;
254 		return false;
255 	}
256 
257 	return true;
258 }
259 #endif
260 
261 static ssize_t
262 ps_inet_sendmsg(struct dhcpcd_ctx *ctx,
263     struct ps_msghdr *psm, struct msghdr *msg)
264 {
265 	struct ps_process *psp;
266 	int s;
267 
268 	psp = ps_findprocess(ctx, &psm->ps_id);
269 	if (psp != NULL) {
270 		s = psp->psp_work_fd;
271 		goto dosend;
272 	}
273 
274 	switch (psm->ps_cmd) {
275 #ifdef INET
276 	case PS_BOOTP:
277 		if (!ps_inet_validudp(msg, BOOTPC, BOOTPS))
278 			return -1;
279 		s = ctx->udp_wfd;
280 		break;
281 #endif
282 #if defined(INET6) && !defined(__sun)
283 	case PS_ND:
284 		if (!ps_inet_validnd(msg))
285 			return -1;
286 		s = ctx->nd_fd;
287 		break;
288 #endif
289 #ifdef DHCP6
290 	case PS_DHCP6:
291 		if (!ps_inet_validudp(msg, DHCP6_CLIENT_PORT,DHCP6_SERVER_PORT))
292 			return -1;
293 		s = ctx->dhcp6_wfd;
294 		break;
295 #endif
296 	default:
297 		errno = EINVAL;
298 		return -1;
299 	}
300 
301 dosend:
302 	return sendmsg(s, msg, 0);
303 }
304 
305 static void
306 ps_inet_recvmsg(void *arg, unsigned short events)
307 {
308 	struct ps_process *psp = arg;
309 
310 	/* Receive shutdown */
311 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
312 		logerr(__func__);
313 }
314 
315 ssize_t
316 ps_inet_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
317 {
318 	struct dhcpcd_ctx *ctx = arg;
319 
320 	switch (psm->ps_cmd) {
321 #ifdef INET
322 	case PS_BOOTP:
323 		dhcp_recvmsg(ctx, msg);
324 		break;
325 #endif
326 #ifdef INET6
327 	case PS_ND:
328 		ipv6nd_recvmsg(ctx, msg);
329 		break;
330 #endif
331 #ifdef DHCP6
332 	case PS_DHCP6:
333 		dhcp6_recvmsg(ctx, msg, NULL);
334 		break;
335 #endif
336 	default:
337 		errno = ENOTSUP;
338 		return -1;
339 	}
340 	return 1;
341 }
342 
343 static void
344 ps_inet_dodispatch(void *arg, unsigned short events)
345 {
346 	struct ps_process *psp = arg;
347 
348 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
349 	    ps_inet_dispatch, psp->psp_ctx) == -1)
350 		logerr(__func__);
351 }
352 
353 pid_t
354 ps_inet_start(struct dhcpcd_ctx *ctx)
355 {
356 	struct ps_id id = {
357 		.psi_ifindex = 0,
358 		.psi_cmd = PS_INET,
359 	};
360 	struct ps_process *psp;
361 	pid_t pid;
362 
363 	psp = ctx->ps_inet = ps_newprocess(ctx, &id);
364 	if (psp == NULL)
365 		return -1;
366 
367 	strlcpy(psp->psp_name, "network proxy", sizeof(psp->psp_name));
368 	pid = ps_startprocess(psp, ps_inet_recvmsg, ps_inet_dodispatch,
369 	    ps_inet_startcb, NULL, PSF_DROPPRIVS);
370 
371 	if (pid == 0)
372 		ps_entersandbox("stdio", NULL);
373 
374 	return pid;
375 }
376 
377 int
378 ps_inet_stop(struct dhcpcd_ctx *ctx)
379 {
380 
381 	return ps_stopprocess(ctx->ps_inet);
382 }
383 
384 #ifdef INET
385 static void
386 ps_inet_recvinbootp(void *arg, unsigned short events)
387 {
388 	struct ps_process *psp = arg;
389 
390 	if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd, events,
391 	    PS_BOOTP, psp->psp_ctx->ps_data_fd) == -1)
392 		logerr(__func__);
393 }
394 
395 static int
396 ps_inet_listenin(struct ps_process *psp)
397 {
398 	struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
399 	char buf[INET_ADDRSTRLEN];
400 
401 	inet_ntop(AF_INET, ia, buf, sizeof(buf));
402 	setproctitle("[%s proxy] %s", psp->psp_protostr, buf);
403 
404 	psp->psp_work_fd = dhcp_openudp(ia);
405 	if (psp->psp_work_fd == -1) {
406 		logerr(__func__);
407 		return -1;
408 	}
409 
410 #ifdef PRIVSEP_RIGHTS
411 	if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) {
412 		logerr("%s: ps_rights_limit_fd_rdonly", __func__);
413 		return -1;
414 	}
415 #endif
416 
417 	if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ,
418 	    ps_inet_recvinbootp, psp) == -1)
419 	{
420 		logerr("%s: eloop_event_add DHCP", __func__);
421 		return -1;
422 	}
423 	return 0;
424 }
425 #endif
426 
427 #if defined(INET6) && defined(__sun)
428 static void
429 ps_inet_recvin6nd(void *arg)
430 {
431 	struct ps_process *psp = arg;
432 
433 	if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd,
434 	    PS_ND, psp->psp_ctx->ps_data_fd) == -1)
435 		logerr(__func__);
436 }
437 
438 static int
439 ps_inet_listennd(struct ps_process *psp)
440 {
441 
442 	setproctitle("[ND network proxy]");
443 
444 	psp->psp_work_fd = ipv6nd_open(&psp->psp_ifp);
445 	if (psp->psp_work_fd == -1) {
446 		logerr(__func__);
447 		return -1;
448 	}
449 
450 #ifdef PRIVSEP_RIGHTS
451 	if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) {
452 		logerr("%s: ps_rights_limit_fd_rdonly", __func__);
453 		return -1;
454 	}
455 #endif
456 
457 	if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
458 	    ps_inet_recvin6nd, psp) == -1)
459 	{
460 		logerr(__func__);
461 		return -1;
462 	}
463 	return 0;
464 }
465 #endif
466 
467 #ifdef DHCP6
468 static void
469 ps_inet_recvin6dhcp6(void *arg, unsigned short events)
470 {
471 	struct ps_process *psp = arg;
472 
473 	if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd, events,
474 	    PS_DHCP6, psp->psp_ctx->ps_data_fd) == -1)
475 		logerr(__func__);
476 }
477 
478 static int
479 ps_inet_listenin6(struct ps_process *psp)
480 {
481 	struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr;
482 	char buf[INET6_ADDRSTRLEN];
483 
484 	inet_ntop(AF_INET6, ia, buf, sizeof(buf));
485 	setproctitle("[%s proxy] %s", psp->psp_protostr, buf);
486 
487 	psp->psp_work_fd = dhcp6_openudp(psp->psp_id.psi_ifindex, ia);
488 	if (psp->psp_work_fd == -1) {
489 		logerr(__func__);
490 		return -1;
491 	}
492 
493 #ifdef PRIVSEP_RIGHTS
494 	if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) {
495 		logerr("%s: ps_rights_limit_fd_rdonly", __func__);
496 		return -1;
497 	}
498 #endif
499 
500 	if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ,
501 	    ps_inet_recvin6dhcp6, psp) == -1)
502 	{
503 		logerr("%s: eloop_event_add DHCP", __func__);
504 		return -1;
505 	}
506 	return 0;
507 }
508 #endif
509 
510 static void
511 ps_inet_recvmsgpsp(void *arg, unsigned short events)
512 {
513 	struct ps_process *psp = arg;
514 
515 	/* Receive shutdown. */
516 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
517 		logerr(__func__);
518 }
519 
520 ssize_t
521 ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
522 {
523 	uint16_t cmd;
524 	struct ps_process *psp;
525 	int (*start_func)(struct ps_process *);
526 	pid_t start;
527 	struct ps_addr *psa = &psm->ps_id.psi_addr;
528 	void *ia;
529 	char buf[INET_MAX_ADDRSTRLEN];
530 
531 	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
532 	if (cmd == psm->ps_cmd)
533 		return ps_inet_sendmsg(ctx, psm, msg);
534 
535 	psp = ps_findprocess(ctx, &psm->ps_id);
536 
537 #ifdef PRIVSEP_DEBUG
538 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
539 #endif
540 
541 	if (psm->ps_cmd & PS_STOP) {
542 		assert(psp == NULL);
543 		return 0;
544 	}
545 
546 	if (!(psm->ps_cmd & PS_START)) {
547 		errno = EINVAL;
548 		return -1;
549 	}
550 
551 	if (psp != NULL)
552 		return 1;
553 
554 	psp = ps_newprocess(ctx, &psm->ps_id);
555 	if (psp == NULL)
556 		return -1;
557 
558 
559 	switch (cmd) {
560 #ifdef INET
561 	case PS_BOOTP:
562 		start_func = ps_inet_listenin;
563 		psp->psp_protostr = "BOOTP";
564 		ia = &psa->psa_in_addr;
565 		break;
566 #endif
567 #ifdef INET6
568 #ifdef __sun
569 	case PS_ND:
570 		start_func = ps_inet_listennd;
571 		psp->psp_protostr = "ND";
572 		ia = &psa->psa_in6_addr;
573 		break;
574 #endif
575 #ifdef DHCP6
576 	case PS_DHCP6:
577 		start_func = ps_inet_listenin6;
578 		psp->psp_protostr = "DHCP6";
579 		ia = &psa->psa_in6_addr;
580 		break;
581 #endif
582 #endif
583 	default:
584 		logerrx("%s: unknown command %x", __func__, psm->ps_cmd);
585 		errno = ENOTSUP;
586 		return -1;
587 	}
588 
589 	snprintf(psp->psp_name, sizeof(psp->psp_name),
590 	    "%s proxy %s", psp->psp_protostr,
591 	    inet_ntop(psa->psa_family, ia, buf, sizeof(buf)));
592 	start = ps_startprocess(psp, ps_inet_recvmsgpsp, NULL,
593 	    start_func, NULL, PSF_DROPPRIVS);
594 	switch (start) {
595 	case -1:
596 		ps_freeprocess(psp);
597 		return -1;
598 	case 0:
599 		ps_entersandbox("stdio", NULL);
600 		break;
601 	default:
602 		logdebugx("%s: spawned %s on PID %d",
603 		    psp->psp_ifname, psp->psp_name, psp->psp_pid);
604 		break;
605 	}
606 	return start;
607 }
608 
609 #ifdef INET
610 static ssize_t
611 ps_inet_in_docmd(struct ipv4_addr *ia, uint16_t cmd, const struct msghdr *msg)
612 {
613 	assert(ia != NULL);
614 	struct dhcpcd_ctx *ctx = ia->iface->ctx;
615 	struct ps_msghdr psm = {
616 		.ps_cmd = cmd,
617 		.ps_id = {
618 			.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
619 			.psi_ifindex = ia->iface->index,
620 			.psi_addr.psa_family = AF_INET,
621 			.psi_addr.psa_in_addr = ia->addr,
622 		},
623 	};
624 
625 	return ps_sendpsmmsg(ctx, ctx->ps_root->psp_fd, &psm, msg);
626 }
627 
628 ssize_t
629 ps_inet_openbootp(struct ipv4_addr *ia)
630 {
631 
632 	return ps_inet_in_docmd(ia, PS_START | PS_BOOTP, NULL);
633 }
634 
635 ssize_t
636 ps_inet_closebootp(struct ipv4_addr *ia)
637 {
638 
639 	return ps_inet_in_docmd(ia, PS_STOP | PS_BOOTP, NULL);
640 }
641 
642 ssize_t
643 ps_inet_sendbootp(struct interface *ifp, const struct msghdr *msg)
644 {
645 	struct dhcpcd_ctx *ctx = ifp->ctx;
646 
647 	return ps_sendmsg(ctx, ctx->ps_root->psp_fd, PS_BOOTP, 0, msg);
648 }
649 #endif /* INET */
650 
651 #ifdef INET6
652 #ifdef __sun
653 static ssize_t
654 ps_inet_ifp_docmd(struct interface *ifp, uint16_t cmd, const struct msghdr *msg)
655 {
656 	struct dhcpcd_ctx *ctx = ifp->ctx;
657 	struct ps_msghdr psm = {
658 		.ps_cmd = cmd,
659 		.ps_id = {
660 			.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
661 			.psi_ifindex = ifp->index,
662 			.psi_addr.psa_family = AF_INET6,
663 		},
664 	};
665 
666 	return ps_sendpsmmsg(ctx, ctx->ps_root->psp_fd, &psm, msg);
667 }
668 
669 ssize_t
670 ps_inet_opennd(struct interface *ifp)
671 {
672 
673 	return ps_inet_ifp_docmd(ifp, PS_ND | PS_START, NULL);
674 }
675 
676 ssize_t
677 ps_inet_closend(struct interface *ifp)
678 {
679 
680 	return ps_inet_ifp_docmd(ifp, PS_ND | PS_STOP, NULL);
681 }
682 
683 ssize_t
684 ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)
685 {
686 
687 	return ps_inet_ifp_docmd(ifp, PS_ND, msg);
688 }
689 #else
690 ssize_t
691 ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)
692 {
693 	struct dhcpcd_ctx *ctx = ifp->ctx;
694 
695 	return ps_sendmsg(ctx, ctx->ps_root->psp_fd, PS_ND, 0, msg);
696 }
697 #endif
698 
699 #ifdef DHCP6
700 static ssize_t
701 ps_inet_in6_docmd(struct ipv6_addr *ia, uint16_t cmd, const struct msghdr *msg)
702 {
703 	struct dhcpcd_ctx *ctx = ia->iface->ctx;
704 	struct ps_msghdr psm = {
705 		.ps_cmd = cmd,
706 		.ps_id = {
707 			.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
708 			.psi_ifindex = ia->iface->index,
709 			.psi_addr.psa_family = AF_INET6,
710 			.psi_addr.psa_in6_addr = ia->addr,
711 		},
712 	};
713 
714 	return ps_sendpsmmsg(ctx, ctx->ps_root->psp_fd, &psm, msg);
715 }
716 
717 ssize_t
718 ps_inet_opendhcp6(struct ipv6_addr *ia)
719 {
720 
721 	return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_START, NULL);
722 }
723 
724 ssize_t
725 ps_inet_closedhcp6(struct ipv6_addr *ia)
726 {
727 
728 	return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_STOP, NULL);
729 }
730 
731 ssize_t
732 ps_inet_senddhcp6(struct interface *ifp, const struct msghdr *msg)
733 {
734 	struct dhcpcd_ctx *ctx = ifp->ctx;
735 
736 	return ps_sendmsg(ctx, ctx->ps_root->psp_fd, PS_DHCP6, 0, msg);
737 }
738 #endif /* DHCP6 */
739 #endif /* INET6 */
740