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