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 /* 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 static bool 219 ps_inet_validudp(struct msghdr *msg, uint16_t sport, uint16_t dport) 220 { 221 struct udphdr udp; 222 struct iovec *iov = msg->msg_iov; 223 224 if (msg->msg_iovlen == 0 || iov->iov_len < sizeof(udp)) { 225 errno = EINVAL; 226 return false; 227 } 228 229 memcpy(&udp, iov->iov_base, sizeof(udp)); 230 if (udp.uh_sport != htons(sport) || udp.uh_dport != htons(dport)) { 231 errno = EPERM; 232 return false; 233 } 234 return true; 235 } 236 237 #ifdef INET6 238 static bool 239 ps_inet_validnd(struct msghdr *msg) 240 { 241 struct icmp6_hdr icmp6; 242 struct iovec *iov = msg->msg_iov; 243 244 if (msg->msg_iovlen == 0 || iov->iov_len < sizeof(icmp6)) { 245 errno = EINVAL; 246 return false; 247 } 248 249 memcpy(&icmp6, iov->iov_base, sizeof(icmp6)); 250 switch(icmp6.icmp6_type) { 251 case ND_ROUTER_SOLICIT: 252 case ND_NEIGHBOR_ADVERT: 253 break; 254 default: 255 errno = EPERM; 256 return false; 257 } 258 259 return true; 260 } 261 #endif 262 263 static ssize_t 264 ps_inet_sendmsg(struct dhcpcd_ctx *ctx, 265 struct ps_msghdr *psm, struct msghdr *msg) 266 { 267 struct ps_process *psp; 268 int s; 269 270 psp = ps_findprocess(ctx, &psm->ps_id); 271 if (psp != NULL) { 272 s = psp->psp_work_fd; 273 goto dosend; 274 } 275 276 switch (psm->ps_cmd) { 277 #ifdef INET 278 case PS_BOOTP: 279 if (!ps_inet_validudp(msg, BOOTPC, BOOTPS)) 280 return -1; 281 s = ctx->udp_wfd; 282 break; 283 #endif 284 #if defined(INET6) && !defined(__sun) 285 case PS_ND: 286 if (!ps_inet_validnd(msg)) 287 return -1; 288 s = ctx->nd_fd; 289 break; 290 #endif 291 #ifdef DHCP6 292 case PS_DHCP6: 293 if (!ps_inet_validudp(msg, DHCP6_CLIENT_PORT,DHCP6_SERVER_PORT)) 294 return -1; 295 s = ctx->dhcp6_wfd; 296 break; 297 #endif 298 default: 299 errno = EINVAL; 300 return -1; 301 } 302 303 dosend: 304 return sendmsg(s, msg, 0); 305 } 306 307 static void 308 ps_inet_recvmsg(void *arg, unsigned short events) 309 { 310 struct ps_process *psp = arg; 311 312 /* Receive shutdown */ 313 if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1) 314 logerr(__func__); 315 } 316 317 ssize_t 318 ps_inet_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg) 319 { 320 struct dhcpcd_ctx *ctx = arg; 321 322 switch (psm->ps_cmd) { 323 #ifdef INET 324 case PS_BOOTP: 325 dhcp_recvmsg(ctx, msg); 326 break; 327 #endif 328 #ifdef INET6 329 case PS_ND: 330 ipv6nd_recvmsg(ctx, msg); 331 break; 332 #endif 333 #ifdef DHCP6 334 case PS_DHCP6: 335 dhcp6_recvmsg(ctx, msg, NULL); 336 break; 337 #endif 338 default: 339 errno = ENOTSUP; 340 return -1; 341 } 342 return 1; 343 } 344 345 static void 346 ps_inet_dodispatch(void *arg, unsigned short events) 347 { 348 struct ps_process *psp = arg; 349 350 if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, 351 ps_inet_dispatch, psp->psp_ctx) == -1) 352 logerr(__func__); 353 } 354 355 pid_t 356 ps_inet_start(struct dhcpcd_ctx *ctx) 357 { 358 struct ps_id id = { 359 .psi_ifindex = 0, 360 .psi_cmd = PS_INET, 361 }; 362 struct ps_process *psp; 363 pid_t pid; 364 365 psp = ctx->ps_inet = ps_newprocess(ctx, &id); 366 if (psp == NULL) 367 return -1; 368 369 strlcpy(psp->psp_name, "network proxy", sizeof(psp->psp_name)); 370 pid = ps_startprocess(psp, ps_inet_recvmsg, ps_inet_dodispatch, 371 ps_inet_startcb, NULL, PSF_DROPPRIVS); 372 373 if (pid == 0) 374 ps_entersandbox("stdio", NULL); 375 376 return pid; 377 } 378 379 int 380 ps_inet_stop(struct dhcpcd_ctx *ctx) 381 { 382 383 return ps_stopprocess(ctx->ps_inet); 384 } 385 386 #ifdef INET 387 static void 388 ps_inet_recvinbootp(void *arg, unsigned short events) 389 { 390 struct ps_process *psp = arg; 391 392 if (ps_recvmsg(psp->psp_work_fd, events, 393 PS_BOOTP, psp->psp_ctx->ps_data_fd) == -1) 394 logerr(__func__); 395 } 396 397 static int 398 ps_inet_listenin(struct ps_process *psp) 399 { 400 struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr; 401 char buf[INET_ADDRSTRLEN]; 402 403 inet_ntop(AF_INET, ia, buf, sizeof(buf)); 404 setproctitle("[%s proxy] %s", psp->psp_protostr, buf); 405 406 psp->psp_work_fd = dhcp_openudp(ia); 407 if (psp->psp_work_fd == -1) { 408 logerr(__func__); 409 return -1; 410 } 411 412 #ifdef PRIVSEP_RIGHTS 413 if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) { 414 logerr("%s: ps_rights_limit_fd_rdonly", __func__); 415 return -1; 416 } 417 #endif 418 419 if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ, 420 ps_inet_recvinbootp, psp) == -1) 421 { 422 logerr("%s: eloop_event_add DHCP", __func__); 423 return -1; 424 } 425 return 0; 426 } 427 #endif 428 429 #if defined(INET6) && defined(__sun) 430 static void 431 ps_inet_recvin6nd(void *arg) 432 { 433 struct ps_process *psp = arg; 434 435 if (ps_recvmsg(psp->psp_work_fd, 436 PS_ND, psp->psp_ctx->ps_data_fd) == -1) 437 logerr(__func__); 438 } 439 440 static int 441 ps_inet_listennd(struct ps_process *psp) 442 { 443 444 setproctitle("[ND network proxy]"); 445 446 psp->psp_work_fd = ipv6nd_open(&psp->psp_ifp); 447 if (psp->psp_work_fd == -1) { 448 logerr(__func__); 449 return -1; 450 } 451 452 #ifdef PRIVSEP_RIGHTS 453 if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) { 454 logerr("%s: ps_rights_limit_fd_rdonly", __func__); 455 return -1; 456 } 457 #endif 458 459 if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, 460 ps_inet_recvin6nd, psp) == -1) 461 { 462 logerr(__func__); 463 return -1; 464 } 465 return 0; 466 } 467 #endif 468 469 #ifdef DHCP6 470 static void 471 ps_inet_recvin6dhcp6(void *arg, unsigned short events) 472 { 473 struct ps_process *psp = arg; 474 475 if (ps_recvmsg(psp->psp_work_fd, events, 476 PS_DHCP6, psp->psp_ctx->ps_data_fd) == -1) 477 logerr(__func__); 478 } 479 480 static int 481 ps_inet_listenin6(struct ps_process *psp) 482 { 483 struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr; 484 char buf[INET6_ADDRSTRLEN]; 485 486 inet_ntop(AF_INET6, ia, buf, sizeof(buf)); 487 setproctitle("[%s proxy] %s", psp->psp_protostr, buf); 488 489 psp->psp_work_fd = dhcp6_openudp(psp->psp_id.psi_ifindex, ia); 490 if (psp->psp_work_fd == -1) { 491 logerr(__func__); 492 return -1; 493 } 494 495 #ifdef PRIVSEP_RIGHTS 496 if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) { 497 logerr("%s: ps_rights_limit_fd_rdonly", __func__); 498 return -1; 499 } 500 #endif 501 502 if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ, 503 ps_inet_recvin6dhcp6, psp) == -1) 504 { 505 logerr("%s: eloop_event_add DHCP", __func__); 506 return -1; 507 } 508 return 0; 509 } 510 #endif 511 512 static void 513 ps_inet_recvmsgpsp(void *arg, unsigned short events) 514 { 515 struct ps_process *psp = arg; 516 517 /* Receive shutdown. */ 518 if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1) 519 logerr(__func__); 520 } 521 522 ssize_t 523 ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) 524 { 525 uint16_t cmd; 526 struct ps_process *psp; 527 int (*start_func)(struct ps_process *); 528 pid_t start; 529 struct ps_addr *psa = &psm->ps_id.psi_addr; 530 void *ia; 531 char buf[INET_MAX_ADDRSTRLEN]; 532 533 cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP)); 534 if (cmd == psm->ps_cmd) 535 return ps_inet_sendmsg(ctx, psm, msg); 536 537 psp = ps_findprocess(ctx, &psm->ps_id); 538 539 #ifdef PRIVSEP_DEBUG 540 logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp); 541 #endif 542 543 if (psm->ps_cmd & PS_STOP) { 544 assert(psp == NULL); 545 return 0; 546 } 547 548 if (!(psm->ps_cmd & PS_START)) { 549 errno = EINVAL; 550 return -1; 551 } 552 553 if (psp != NULL) 554 return 1; 555 556 psp = ps_newprocess(ctx, &psm->ps_id); 557 if (psp == NULL) 558 return -1; 559 560 561 switch (cmd) { 562 #ifdef INET 563 case PS_BOOTP: 564 start_func = ps_inet_listenin; 565 psp->psp_protostr = "BOOTP"; 566 ia = &psa->psa_in_addr; 567 break; 568 #endif 569 #ifdef INET6 570 #ifdef __sun 571 case PS_ND: 572 start_func = ps_inet_listennd; 573 psp->psp_protostr = "ND"; 574 ia = &psa->psa_in6_addr; 575 break; 576 #endif 577 #ifdef DHCP6 578 case PS_DHCP6: 579 start_func = ps_inet_listenin6; 580 psp->psp_protostr = "DHCP6"; 581 ia = &psa->psa_in6_addr; 582 break; 583 #endif 584 #endif 585 default: 586 logerrx("%s: unknown command %x", __func__, psm->ps_cmd); 587 errno = ENOTSUP; 588 return -1; 589 } 590 591 snprintf(psp->psp_name, sizeof(psp->psp_name), 592 "%s proxy %s", psp->psp_protostr, 593 inet_ntop(psa->psa_family, ia, buf, sizeof(buf))); 594 start = ps_startprocess(psp, ps_inet_recvmsgpsp, NULL, 595 start_func, NULL, PSF_DROPPRIVS); 596 switch (start) { 597 case -1: 598 ps_freeprocess(psp); 599 return -1; 600 case 0: 601 ps_entersandbox("stdio", NULL); 602 break; 603 default: 604 logdebugx("%s: spawned %s on PID %d", 605 psp->psp_ifname, psp->psp_name, psp->psp_pid); 606 break; 607 } 608 return start; 609 } 610 611 #ifdef INET 612 static ssize_t 613 ps_inet_in_docmd(struct ipv4_addr *ia, uint16_t cmd, const struct msghdr *msg) 614 { 615 assert(ia != NULL); 616 struct dhcpcd_ctx *ctx = ia->iface->ctx; 617 struct ps_msghdr psm = { 618 .ps_cmd = cmd, 619 .ps_id = { 620 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), 621 .psi_ifindex = ia->iface->index, 622 .psi_addr.psa_family = AF_INET, 623 .psi_addr.psa_in_addr = ia->addr, 624 }, 625 }; 626 627 return ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg); 628 } 629 630 ssize_t 631 ps_inet_openbootp(struct ipv4_addr *ia) 632 { 633 634 return ps_inet_in_docmd(ia, PS_START | PS_BOOTP, NULL); 635 } 636 637 ssize_t 638 ps_inet_closebootp(struct ipv4_addr *ia) 639 { 640 641 return ps_inet_in_docmd(ia, PS_STOP | PS_BOOTP, NULL); 642 } 643 644 ssize_t 645 ps_inet_sendbootp(struct interface *ifp, const struct msghdr *msg) 646 { 647 struct dhcpcd_ctx *ctx = ifp->ctx; 648 649 return ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_BOOTP, 0, msg); 650 } 651 #endif /* INET */ 652 653 #ifdef INET6 654 #ifdef __sun 655 static ssize_t 656 ps_inet_ifp_docmd(struct interface *ifp, uint16_t cmd, const struct msghdr *msg) 657 { 658 struct dhcpcd_ctx *ctx = ifp->ctx; 659 struct ps_msghdr psm = { 660 .ps_cmd = cmd, 661 .ps_id = { 662 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), 663 .psi_ifindex = ifp->index, 664 .psi_addr.psa_family = AF_INET6, 665 }, 666 }; 667 668 return ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg); 669 } 670 671 ssize_t 672 ps_inet_opennd(struct interface *ifp) 673 { 674 675 return ps_inet_ifp_docmd(ifp, PS_ND | PS_START, NULL); 676 } 677 678 ssize_t 679 ps_inet_closend(struct interface *ifp) 680 { 681 682 return ps_inet_ifp_docmd(ifp, PS_ND | PS_STOP, NULL); 683 } 684 685 ssize_t 686 ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg) 687 { 688 689 return ps_inet_ifp_docmd(ifp, PS_ND, msg); 690 } 691 #else 692 ssize_t 693 ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg) 694 { 695 struct dhcpcd_ctx *ctx = ifp->ctx; 696 697 return ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_ND, 0, msg); 698 } 699 #endif 700 701 #ifdef DHCP6 702 static ssize_t 703 ps_inet_in6_docmd(struct ipv6_addr *ia, uint16_t cmd, const struct msghdr *msg) 704 { 705 struct dhcpcd_ctx *ctx = ia->iface->ctx; 706 struct ps_msghdr psm = { 707 .ps_cmd = cmd, 708 .ps_id = { 709 .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), 710 .psi_ifindex = ia->iface->index, 711 .psi_addr.psa_family = AF_INET6, 712 .psi_addr.psa_in6_addr = ia->addr, 713 }, 714 }; 715 716 return ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg); 717 } 718 719 ssize_t 720 ps_inet_opendhcp6(struct ipv6_addr *ia) 721 { 722 723 return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_START, NULL); 724 } 725 726 ssize_t 727 ps_inet_closedhcp6(struct ipv6_addr *ia) 728 { 729 730 return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_STOP, NULL); 731 } 732 733 ssize_t 734 ps_inet_senddhcp6(struct interface *ifp, const struct msghdr *msg) 735 { 736 struct dhcpcd_ctx *ctx = ifp->ctx; 737 738 return ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_DHCP6, 0, msg); 739 } 740 #endif /* DHCP6 */ 741 #endif /* INET6 */ 742