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