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