"Fossies" - the Fresh Open Source Software Archive

Member "dhcpcd-9.4.1/src/privsep-inet.c" (22 Oct 2021, 14907 Bytes) of package /linux/misc/dhcpcd-9.4.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "privsep-inet.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 9.4.0_vs_9.4.1.

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