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