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