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