1 /* $OpenBSD: dhcrelay6.c,v 1.3 2019/08/06 11:07:37 krw Exp $ */ 2 3 /* 4 * Copyright (c) 2017 Rafael Zalamena <rzalamena@openbsd.org> 5 * Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org> 6 * Copyright (c) 1997, 1998, 1999 The Internet Software Consortium. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of The Internet Software Consortium nor the names 19 * of its contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * This software has been written for the Internet Software Consortium 37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 38 * Enterprises. To learn more about the Internet Software Consortium, 39 * see ``http://www.vix.com/isc''. To learn more about Vixie 40 * Enterprises, see ``http://www.vix.com''. 41 */ 42 43 #include <sys/socket.h> 44 45 #include <arpa/inet.h> 46 47 #include <net/if.h> 48 #include <netinet/in.h> 49 #include <netinet/ip.h> 50 #include <netinet/ip6.h> 51 #include <netinet/udp.h> 52 #include <netinet/if_ether.h> 53 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <netdb.h> 57 #include <paths.h> 58 #include <pwd.h> 59 #include <stdarg.h> 60 #include <stdlib.h> 61 #include <stdio.h> 62 #include <stdint.h> 63 #include <string.h> 64 #include <syslog.h> 65 #include <time.h> 66 #include <unistd.h> 67 68 #include "dhcp.h" 69 #include "dhcpd.h" 70 #include "log.h" 71 72 /* 73 * RFC 3315 Section 5.1 Multicast Addresses: 74 * All_DHCP_Relay_Agents_and_Servers: FF02::1:2 75 * All_DHCP_Servers: FF05::1:3 76 */ 77 struct in6_addr in6alldhcprelay = { 78 {{ 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0, 0x02 }} 79 }; 80 struct in6_addr in6alldhcp = { 81 {{ 0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0, 0x03 }} 82 }; 83 84 __dead void usage(void); 85 struct server_list *parse_destination(const char *); 86 int rdaemon(int); 87 void relay6_setup(void); 88 int s6fromaddr(struct sockaddr_in6 *, const char *, const char *, int); 89 char *print_hw_addr(int, int, unsigned char *); 90 const char *dhcp6type2str(uint8_t); 91 int relay6_pushrelaymsg(struct packet_ctx *, struct interface_info *, 92 uint8_t *, size_t *, size_t); 93 int relay6_poprelaymsg(struct packet_ctx *, struct interface_info **, 94 uint8_t *, size_t *); 95 void rai_configure(struct packet_ctx *, struct interface_info *); 96 void relay6_logsrcaddr(struct packet_ctx *, struct interface_info *, 97 uint8_t); 98 void relay6(struct interface_info *, void *, size_t, 99 struct packet_ctx *); 100 void mcast6_recv(struct protocol *); 101 102 /* Shared variables */ 103 int clientsd; 104 int serversd; 105 int oflag; 106 time_t cur_time; 107 108 struct intfq intflist; 109 struct serverq svlist; 110 struct interface_info *interfaces; 111 char *rai_data; 112 size_t rai_datalen; 113 uint32_t enterpriseno = OPENBSD_ENTERPRISENO; 114 char *remote_data; 115 size_t remote_datalen; 116 enum dhcp_relay_mode drm = DRM_LAYER3; 117 118 __dead void 119 usage(void) 120 { 121 extern char *__progname; 122 123 fprintf(stderr, "usage: %s [-dlov] [-E enterprise-number] " 124 "[-I interface-id] [-R remote-id]\n" 125 "\t-i interface destination ...\n", 126 __progname); 127 exit(1); 128 } 129 130 struct server_list * 131 parse_destination(const char *dest) 132 { 133 struct server_list *sp; 134 const char *ifname; 135 char buf[128]; 136 137 if ((sp = calloc(1, sizeof(*sp))) == NULL) 138 fatal("calloc"); 139 TAILQ_INSERT_HEAD(&svlist, sp, entry); 140 141 /* Detect interface only destinations. */ 142 if ((sp->intf = iflist_getbyname(dest)) != NULL) 143 return sp; 144 145 /* Split address from interface and save it. */ 146 ifname = strchr(dest, '%'); 147 if (ifname == NULL) 148 fatalx("%s doesn't specify an output interface", dest); 149 150 if (strlcpy(buf, dest, sizeof(buf)) >= sizeof(buf)) 151 fatalx("%s is an invalid IPv6 address", dest); 152 153 /* Remove '%' from the address string. */ 154 buf[ifname - dest] = 0; 155 if ((sp->intf = iflist_getbyname(ifname + 1)) == NULL) 156 fatalx("interface '%s' not found", ifname); 157 if (s6fromaddr(ss2sin6(&sp->to), buf, 158 DHCP6_SERVER_PORT_STR, 1) == -1) 159 fatalx("%s: unknown host", buf); 160 161 /* 162 * User configured a non-local address, we must require a 163 * proper address to route this. 164 */ 165 if (!IN6_IS_ADDR_LINKLOCAL(&ss2sin6(&sp->to)->sin6_addr)) 166 sp->siteglobaladdr = 1; 167 168 return sp; 169 } 170 171 int 172 main(int argc, char *argv[]) 173 { 174 int devnull = -1, daemonize = 1, debug = 0; 175 const char *errp; 176 struct passwd *pw; 177 int ch; 178 179 log_init(1, LOG_DAEMON); /* log to stderr until daemonized */ 180 log_setverbose(1); 181 182 setup_iflist(); 183 184 while ((ch = getopt(argc, argv, "dE:I:i:loR:v")) != -1) { 185 switch (ch) { 186 case 'd': 187 daemonize = 0; 188 break; 189 case 'E': 190 enterpriseno = strtonum(optarg, 1, UINT32_MAX, &errp); 191 if (errp != NULL) 192 fatalx("invalid enterprise number: %s", errp); 193 break; 194 case 'I': 195 rai_data = optarg; 196 rai_datalen = strlen(optarg); 197 if (rai_datalen == 0) 198 fatalx("can't use empty Interface-ID"); 199 break; 200 case 'i': 201 if (interfaces != NULL) 202 usage(); 203 204 interfaces = iflist_getbyname(optarg); 205 if (interfaces == NULL) 206 fatalx("interface '%s' not found", optarg); 207 break; 208 case 'l': 209 drm = DRM_LAYER2; 210 break; 211 case 'o': 212 oflag = 1; 213 break; 214 case 'R': 215 remote_data = optarg; 216 remote_datalen = strlen(remote_data); 217 if (remote_datalen == 0) 218 fatalx("can't use empty Remote-ID"); 219 break; 220 case 'v': 221 daemonize = 0; 222 debug = 1; 223 break; 224 225 default: 226 usage(); 227 } 228 } 229 230 argc -= optind; 231 argv += optind; 232 while (argc > 0) { 233 parse_destination(argv[0]); 234 argc--; 235 argv++; 236 } 237 238 if (daemonize) { 239 devnull = open(_PATH_DEVNULL, O_RDWR, 0); 240 if (devnull == -1) 241 fatal("open(%s)", _PATH_DEVNULL); 242 } 243 if (interfaces == NULL) 244 fatalx("no interface given"); 245 if (TAILQ_EMPTY(&svlist)) 246 fatalx("no destination selected"); 247 248 relay6_setup(); 249 bootp_packet_handler = relay6; 250 251 tzset(); 252 time(&cur_time); 253 254 if ((pw = getpwnam(DHCRELAY6_USER)) == NULL) 255 fatalx("user \"%s\" not found", DHCRELAY6_USER); 256 if (chroot(pw->pw_dir) == -1) 257 fatal("chroot"); 258 if (chdir("/") == -1) 259 fatal("chdir(\"/\")"); 260 if (setgroups(1, &pw->pw_gid) || 261 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 262 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 263 fatal("can't drop privileges"); 264 265 if (daemonize) { 266 if (rdaemon(devnull) == -1) 267 fatal("rdaemon"); 268 } 269 log_init(daemonize == 0, LOG_DAEMON); /* stop loggoing to stderr */ 270 log_setverbose(debug); 271 272 if (pledge("inet stdio route", NULL) == -1) 273 fatalx("pledge"); 274 275 dispatch(); 276 /* not reached */ 277 278 exit(0); 279 } 280 281 int 282 rdaemon(int devnull) 283 { 284 if (devnull == -1) { 285 errno = EBADF; 286 return (-1); 287 } 288 if (fcntl(devnull, F_GETFL) == -1) 289 return (-1); 290 291 switch (fork()) { 292 case -1: 293 return (-1); 294 case 0: 295 break; 296 default: 297 _exit(0); 298 } 299 300 if (setsid() == -1) 301 return (-1); 302 303 (void)dup2(devnull, STDIN_FILENO); 304 (void)dup2(devnull, STDOUT_FILENO); 305 (void)dup2(devnull, STDERR_FILENO); 306 if (devnull > 2) 307 (void)close(devnull); 308 309 return (0); 310 } 311 312 int 313 s6fromaddr(struct sockaddr_in6 *sin6, const char *addr, const char *serv, 314 int passive) 315 { 316 struct sockaddr_in6 *sin6p; 317 struct addrinfo *aip; 318 struct addrinfo ai; 319 int rv; 320 321 memset(&ai, 0, sizeof(ai)); 322 ai.ai_family = PF_INET6; 323 ai.ai_socktype = SOCK_DGRAM; 324 ai.ai_protocol = IPPROTO_UDP; 325 ai.ai_flags = (passive) ? AI_PASSIVE : 0; 326 if ((rv = getaddrinfo(addr, serv, &ai, &aip)) != 0) { 327 log_debug("getaddrinfo: %s", gai_strerror(rv)); 328 return -1; 329 } 330 331 sin6p = (struct sockaddr_in6 *)aip->ai_addr; 332 *sin6 = *sin6p; 333 334 freeaddrinfo(aip); 335 336 return 0; 337 } 338 339 void 340 relay6_setup(void) 341 { 342 struct interface_info *intf; 343 struct server_list *sp; 344 int flag = 1; 345 struct sockaddr_in6 sin6; 346 struct ipv6_mreq mreq6; 347 348 /* Don't allow disabled interfaces. */ 349 TAILQ_FOREACH(sp, &svlist, entry) { 350 if (sp->intf == NULL) 351 continue; 352 353 if (sp->intf->dead) 354 fatalx("interface '%s' is down", sp->intf->name); 355 } 356 357 /* Check for layer 2 dependencies. */ 358 if (drm == DRM_LAYER2) { 359 TAILQ_FOREACH(sp, &svlist, entry) { 360 sp->intf = register_interface(sp->intf->name, 361 got_one); 362 if (sp->intf == NULL) 363 fatalx("destination interface " 364 "registration failed"); 365 } 366 interfaces = register_interface(interfaces->name, got_one); 367 if (interfaces == NULL) 368 fatalx("input interface not configured"); 369 370 return; 371 } 372 373 /* 374 * Layer 3 requires at least one IPv6 address on all configured 375 * interfaces. 376 */ 377 TAILQ_FOREACH(sp, &svlist, entry) { 378 if (!sp->intf->ipv6) 379 fatalx("%s: no IPv6 address configured", 380 sp->intf->name); 381 382 if (sp->siteglobaladdr && !sp->intf->gipv6) 383 fatalx("%s: no IPv6 site/global address configured", 384 sp->intf->name); 385 } 386 if (!interfaces->ipv6) 387 fatalx("%s: no IPv6 address configured", interfaces->name); 388 389 /* Setup the client side socket. */ 390 clientsd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 391 if (clientsd == -1) 392 fatal("socket"); 393 394 if (setsockopt(clientsd, SOL_SOCKET, SO_REUSEPORT, &flag, 395 sizeof(flag)) == -1) 396 fatal("setsockopt(SO_REUSEPORT)"); 397 398 if (s6fromaddr(&sin6, NULL, DHCP6_SERVER_PORT_STR, 1) == -1) 399 fatalx("s6fromaddr"); 400 if (bind(clientsd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) 401 fatal("bind"); 402 403 if (setsockopt(clientsd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &flag, 404 sizeof(flag)) == -1) 405 fatal("setsockopt(IPV6_RECVPKTINFO)"); 406 407 memset(&mreq6, 0, sizeof(mreq6)); 408 if (s6fromaddr(&sin6, DHCP6_ADDR_RELAYSERVER, NULL, 0) == -1) 409 fatalx("s6fromaddr"); 410 memcpy(&mreq6.ipv6mr_multiaddr, &sin6.sin6_addr, 411 sizeof(mreq6.ipv6mr_multiaddr)); 412 TAILQ_FOREACH(intf, &intflist, entry) { 413 /* Skip interfaces without IPv6. */ 414 if (!intf->ipv6) 415 continue; 416 417 mreq6.ipv6mr_interface = intf->index; 418 if (setsockopt(clientsd, IPPROTO_IPV6, IPV6_JOIN_GROUP, 419 &mreq6, sizeof(mreq6)) == -1) 420 fatal("setsockopt(IPV6_JOIN_GROUP)"); 421 } 422 423 add_protocol("clientsd", clientsd, mcast6_recv, &clientsd); 424 425 /* Setup the server side socket. */ 426 serversd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 427 if (serversd == -1) 428 fatal("socket"); 429 430 if (setsockopt(serversd, SOL_SOCKET, SO_REUSEPORT, &flag, 431 sizeof(flag)) == -1) 432 fatal("setsockopt(SO_REUSEPORT)"); 433 434 if (s6fromaddr(&sin6, NULL, DHCP6_SERVER_PORT_STR, 1) == -1) 435 fatalx("s6fromaddr"); 436 if (bind(serversd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) 437 fatal("bind"); 438 439 if (setsockopt(serversd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &flag, 440 sizeof(flag)) == -1) 441 fatal("setsockopt(IPV6_RECVPKTINFO)"); 442 443 add_protocol("serversd", serversd, mcast6_recv, &serversd); 444 } 445 446 char * 447 print_hw_addr(int htype, int hlen, unsigned char *data) 448 { 449 static char habuf[49]; 450 char *s = habuf; 451 int i, j, slen = sizeof(habuf); 452 453 if (htype == 0 || hlen == 0) { 454 bad: 455 strlcpy(habuf, "<null>", sizeof habuf); 456 return habuf; 457 } 458 459 for (i = 0; i < hlen; i++) { 460 j = snprintf(s, slen, "%02x", data[i]); 461 if (j <= 0 || j >= slen) 462 goto bad; 463 j = strlen (s); 464 s += j; 465 slen -= (j + 1); 466 *s++ = ':'; 467 } 468 *--s = '\0'; 469 return habuf; 470 } 471 472 const char * 473 v6addr2str(struct in6_addr *addr) 474 { 475 static int bufpos = 0; 476 static char buf[3][256]; 477 478 bufpos = (bufpos + 1) % 3; 479 buf[bufpos][0] = '['; 480 if (inet_ntop(AF_INET6, addr, &buf[bufpos][1], 481 sizeof(buf[bufpos])) == NULL) 482 return "[unknown]"; 483 484 strlcat(buf[bufpos], "]", sizeof(buf[bufpos])); 485 486 return buf[bufpos]; 487 } 488 489 const char * 490 dhcp6type2str(uint8_t msgtype) 491 { 492 switch (msgtype) { 493 case DHCP6_MT_REQUEST: 494 return "REQUEST"; 495 case DHCP6_MT_RENEW: 496 return "RENEW"; 497 case DHCP6_MT_REBIND: 498 return "REBIND"; 499 case DHCP6_MT_RELEASE: 500 return "RELEASE"; 501 case DHCP6_MT_DECLINE: 502 return "DECLINE"; 503 case DHCP6_MT_INFORMATIONREQUEST: 504 return "INFORMATION-REQUEST"; 505 case DHCP6_MT_SOLICIT: 506 return "SOLICIT"; 507 case DHCP6_MT_ADVERTISE: 508 return "ADVERTISE"; 509 case DHCP6_MT_CONFIRM: 510 return "CONFIRM"; 511 case DHCP6_MT_REPLY: 512 return "REPLY"; 513 case DHCP6_MT_RECONFIGURE: 514 return "RECONFIGURE"; 515 case DHCP6_MT_RELAYREPL: 516 return "RELAY-REPLY"; 517 case DHCP6_MT_RELAYFORW: 518 return "RELAY-FORWARD"; 519 default: 520 return "UNKNOWN"; 521 } 522 } 523 524 int 525 relay6_pushrelaymsg(struct packet_ctx *pc, struct interface_info *intf, 526 uint8_t *p, size_t *plen, size_t ptotal) 527 { 528 struct dhcp6_relay_packet *dsr; 529 struct dhcp6_option *dso; 530 size_t rmlen, dhcplen, optoff; 531 size_t railen, remotelen; 532 533 if (pc->pc_raidata != NULL) 534 railen = sizeof(*dso) + pc->pc_raidatalen; 535 else 536 railen = 0; 537 538 if (pc->pc_remote) 539 remotelen = sizeof(*dso) + ENTERPRISENO_LEN + 540 pc->pc_remotelen; 541 else 542 remotelen = 0; 543 544 /* 545 * Check if message bigger than MTU and log (RFC 6221 546 * Section 5.3.1). 547 */ 548 dhcplen = sizeof(*dsr) + railen + remotelen + sizeof(*dso) + *plen; 549 rmlen = sizeof(struct ether_header) + sizeof(struct ip6_hdr) + 550 sizeof(struct udphdr) + dhcplen; 551 if (rmlen > ptotal) { 552 log_info("Relay message too big"); 553 return -1; 554 } 555 556 /* Move the DHCP payload to option. */ 557 optoff = sizeof(*dsr) + railen + remotelen + sizeof(*dso); 558 memmove(p + optoff, p, *plen); 559 560 /* Write the new DHCP packet header for relay-message. */ 561 dsr = (struct dhcp6_relay_packet *)p; 562 dsr->dsr_msgtype = DHCP6_MT_RELAYFORW; 563 564 /* 565 * When the destination is All_DHCP_Relay_Agents_and_Servers we 566 * start the hop count from zero, otherwise set it to 567 * DHCP6_HOP_LIMIT to limit the packet to a single network. 568 */ 569 if (memcmp(&ss2sin6(&pc->pc_dst)->sin6_addr, 570 &in6alldhcprelay, sizeof(in6alldhcprelay)) == 0) 571 dsr->dsr_hopcount = 0; 572 else 573 dsr->dsr_hopcount = DHCP6_HOP_LIMIT; 574 575 /* 576 * XXX RFC 6221 Section 6.1: layer 2 mode does not set 577 * linkaddr, but we'll use our link-local always to identify the 578 * interface where the packet came in so we don't need to keep 579 * the interface addresses updated. 580 */ 581 dsr->dsr_linkaddr = intf->linklocal; 582 583 memcpy(&dsr->dsr_peer, &ss2sin6(&pc->pc_src)->sin6_addr, 584 sizeof(dsr->dsr_peer)); 585 586 /* Append Interface-ID DHCP option to identify this segment. */ 587 if (railen > 0) { 588 dso = dsr->dsr_options; 589 dso->dso_code = htons(DHCP6_OPT_INTERFACEID); 590 dso->dso_length = htons(pc->pc_raidatalen); 591 memcpy(dso->dso_data, pc->pc_raidata, pc->pc_raidatalen); 592 } 593 594 /* Append the Remote-ID DHCP option to identify this segment. */ 595 if (remotelen > 0) { 596 dso = (struct dhcp6_option *) 597 ((uint8_t *)dsr->dsr_options + railen); 598 dso->dso_code = htons(DHCP6_OPT_REMOTEID); 599 dso->dso_length = 600 htons(sizeof(pc->pc_enterpriseno) + pc->pc_remotelen); 601 memcpy(dso->dso_data, &pc->pc_enterpriseno, 602 sizeof(pc->pc_enterpriseno)); 603 memcpy(dso->dso_data + sizeof(pc->pc_enterpriseno), 604 pc->pc_remote, pc->pc_remotelen); 605 } 606 607 /* Write the Relay-Message option header. */ 608 dso = (struct dhcp6_option *) 609 ((uint8_t *)dsr->dsr_options + railen + remotelen); 610 dso->dso_code = htons(DHCP6_OPT_RELAY_MSG); 611 dso->dso_length = htons(*plen); 612 613 /* Update the packet length. */ 614 *plen = dhcplen; 615 616 return 0; 617 } 618 619 int 620 relay6_poprelaymsg(struct packet_ctx *pc, struct interface_info **intf, 621 uint8_t *p, size_t *plen) 622 { 623 struct dhcp6_relay_packet *dsr = (struct dhcp6_relay_packet *)p; 624 struct dhcp6_packet *ds = NULL; 625 struct dhcp6_option *dso; 626 struct in6_addr linkaddr; 627 size_t pleft = *plen, ifnamelen = 0; 628 size_t dsolen, dhcplen = 0; 629 uint16_t optcode; 630 char ifname[64]; 631 632 *intf = NULL; 633 634 /* Sanity check: this is a relay message of the right type. */ 635 if (dsr->dsr_msgtype != DHCP6_MT_RELAYREPL) { 636 log_debug("Invalid relay-message (%s) to pop", 637 dhcp6type2str(dsr->dsr_msgtype)); 638 return -1; 639 } 640 641 /* Set the client address based on relay message. */ 642 ss2sin6(&pc->pc_dst)->sin6_addr = dsr->dsr_peer; 643 linkaddr = dsr->dsr_linkaddr; 644 645 dso = dsr->dsr_options; 646 pleft -= sizeof(*dsr); 647 while (pleft > sizeof(*dso)) { 648 optcode = ntohs(dso->dso_code); 649 dsolen = sizeof(*dso) + ntohs(dso->dso_length); 650 651 /* Sanity check: do we have the payload? */ 652 if (dsolen > pleft) { 653 log_debug("invalid packet: payload greater than " 654 "packet content (%ld, bytes left %ld)", 655 dsolen, pleft); 656 return -1; 657 } 658 659 /* Use the interface suggested by the packet. */ 660 if (optcode == DHCP6_OPT_INTERFACEID) { 661 ifnamelen = dsolen - sizeof(*dso); 662 if (ifnamelen >= sizeof(ifname)) { 663 log_info("received interface id with " 664 "truncated interface name"); 665 ifnamelen = sizeof(ifname) - 1; 666 } 667 668 memcpy(ifname, dso->dso_data, ifnamelen); 669 ifname[ifnamelen] = 0; 670 671 dso = (struct dhcp6_option *) 672 ((uint8_t *)dso + dsolen); 673 pleft -= dsolen; 674 continue; 675 } 676 677 /* Ignore unsupported options. */ 678 if (optcode != DHCP6_OPT_RELAY_MSG) { 679 log_debug("ignoring option type %d", optcode); 680 dso = (struct dhcp6_option *) 681 ((uint8_t *)dso + dsolen); 682 pleft -= dsolen; 683 continue; 684 } 685 686 /* Save the pointer for the DHCP payload. */ 687 ds = (struct dhcp6_packet *)dso->dso_data; 688 dhcplen = ntohs(dso->dso_length); 689 690 dso = (struct dhcp6_option *)((uint8_t *)dso + dsolen); 691 pleft -= dsolen; 692 } 693 if (ds == NULL || dhcplen == 0) { 694 log_debug("Could not find relay-message option"); 695 return -1; 696 } 697 698 /* Move the encapsulated DHCP payload. */ 699 memmove(p, ds, dhcplen); 700 *plen = dhcplen; 701 702 /* 703 * If the new message is for the client, we must change the 704 * destination port to the client's, otherwise keep the port 705 * for the next relay. 706 */ 707 ds = (struct dhcp6_packet *)p; 708 if (ds->ds_msgtype != DHCP6_MT_RELAYREPL) 709 ss2sin6(&pc->pc_dst)->sin6_port = 710 htons(DHCP6_CLIENT_PORT); 711 712 /* No Interface-ID specified. */ 713 if (ifnamelen == 0) 714 goto use_linkaddr; 715 716 /* Look out for the specified interface, */ 717 if ((*intf = iflist_getbyname(ifname)) == NULL) { 718 log_debug(" Interface-ID found, but no interface matches."); 719 720 /* 721 * Use client interface as fallback, but try 722 * link-address (if any) before giving up. 723 */ 724 *intf = interfaces; 725 } 726 727 use_linkaddr: 728 /* Use link-addr to determine output interface if present. */ 729 if (memcmp(&linkaddr, &in6addr_any, sizeof(linkaddr)) != 0) { 730 if ((*intf = iflist_getbyaddr6(&linkaddr)) != NULL) 731 return 0; 732 733 log_debug("Could not find interface using " 734 "address %s", v6addr2str(&linkaddr)); 735 } 736 737 return 0; 738 } 739 740 void 741 rai_configure(struct packet_ctx *pc, struct interface_info *intf) 742 { 743 if (remote_data != NULL) { 744 pc->pc_remote = remote_data; 745 pc->pc_remotelen = remote_datalen; 746 pc->pc_enterpriseno = htonl(enterpriseno); 747 } 748 749 /* Layer-2 must include Interface-ID (Option 18). */ 750 if (drm == DRM_LAYER2) 751 goto select_rai; 752 753 /* User did not configure Interface-ID. */ 754 if (oflag == 0) 755 return; 756 757 select_rai: 758 if (rai_data == NULL) { 759 pc->pc_raidata = intf->name; 760 pc->pc_raidatalen = strlen(intf->name); 761 } else { 762 pc->pc_raidata = rai_data; 763 pc->pc_raidatalen = rai_datalen; 764 } 765 } 766 767 void 768 relay6_logsrcaddr(struct packet_ctx *pc, struct interface_info *intf, 769 uint8_t msgtype) 770 { 771 const char *type; 772 773 type = (msgtype == DHCP6_MT_RELAYREPL) ? "reply" : "forward"; 774 if (drm == DRM_LAYER2) 775 log_info("forwarded relay-%s for %s to %s", 776 type, print_hw_addr(pc->pc_htype, pc->pc_hlen, 777 pc->pc_smac), intf->name); 778 else 779 log_info("forwarded relay-%s for %s to %s%%%s", 780 type, 781 v6addr2str(&ss2sin6(&pc->pc_srcorig)->sin6_addr), 782 v6addr2str(&ss2sin6(&pc->pc_dst)->sin6_addr), 783 intf->name); 784 } 785 786 void 787 relay6(struct interface_info *intf, void *p, size_t plen, 788 struct packet_ctx *pc) 789 { 790 struct dhcp6_packet *ds = (struct dhcp6_packet *)p; 791 struct dhcp6_relay_packet *dsr = (struct dhcp6_relay_packet *)p; 792 struct interface_info *dstif = NULL; 793 struct server_list *sp; 794 size_t buflen = plen; 795 int clientdir = (intf != interfaces); 796 uint8_t msgtype, hopcount = 0; 797 798 /* Sanity check: we have at least the DHCP header. */ 799 if (plen < (int)sizeof(*ds)) { 800 log_debug("invalid packet size"); 801 return; 802 } 803 804 /* Set Relay Agent Information fields. */ 805 rai_configure(pc, intf); 806 807 /* 808 * RFC 3315 section 20 relay messages: 809 * For client messages prepend a new DHCP payload with the 810 * relay-forward, otherwise update the DHCP relay header. 811 */ 812 msgtype = ds->ds_msgtype; 813 814 log_debug("%s: received %s from %s", 815 intf->name, dhcp6type2str(msgtype), 816 v6addr2str(&ss2sin6(&pc->pc_src)->sin6_addr)); 817 818 switch (msgtype) { 819 case DHCP6_MT_ADVERTISE: 820 case DHCP6_MT_REPLY: 821 case DHCP6_MT_RECONFIGURE: 822 /* 823 * Don't forward reply packets coming from the client 824 * interface. 825 * 826 * RFC 6221 Section 6.1.1. 827 */ 828 if (clientdir == 0) { 829 log_debug(" dropped reply in opposite direction"); 830 return; 831 } 832 /* FALLTHROUGH */ 833 834 case DHCP6_MT_REQUEST: 835 case DHCP6_MT_RENEW: 836 case DHCP6_MT_REBIND: 837 case DHCP6_MT_RELEASE: 838 case DHCP6_MT_DECLINE: 839 case DHCP6_MT_INFORMATIONREQUEST: 840 case DHCP6_MT_SOLICIT: 841 case DHCP6_MT_CONFIRM: 842 /* 843 * Encapsulate the client/server message with the 844 * relay-message header. 845 */ 846 if (relay6_pushrelaymsg(pc, intf, (uint8_t *)p, 847 &buflen, DHCP_MTU_MAX) == -1) { 848 log_debug(" message encapsulation failed"); 849 return; 850 } 851 break; 852 853 case DHCP6_MT_RELAYREPL: 854 /* 855 * Don't forward reply packets coming from the client 856 * interface. 857 * 858 * RFC 6221 Section 6.1.1. 859 */ 860 if (clientdir == 0) { 861 log_debug(" dropped reply in opposite direction"); 862 return; 863 } 864 865 if (relay6_poprelaymsg(pc, &dstif, (uint8_t *)p, 866 &buflen) == -1) { 867 log_debug(" failed to pop relay-message"); 868 return; 869 } 870 871 pc->pc_sd = clientsd; 872 break; 873 874 case DHCP6_MT_RELAYFORW: 875 /* 876 * We can only have multiple hops when the destination 877 * address is All_DHCP_Relay_Agents_and_Servers, otherwise 878 * drop it. 879 */ 880 if (memcmp(&ss2sin6(&pc->pc_dst)->sin6_addr, 881 &in6alldhcprelay, sizeof(in6alldhcprelay)) != 0) { 882 log_debug(" wrong destination"); 883 return; 884 } 885 886 hopcount = dsr->dsr_hopcount + 1; 887 if (hopcount >= DHCP6_HOP_LIMIT) { 888 log_debug(" hop limit reached"); 889 return; 890 } 891 892 /* Stack into another relay-message. */ 893 if (relay6_pushrelaymsg(pc, intf, (uint8_t *)p, 894 &buflen, DHCP_MTU_MAX) == -1) { 895 log_debug(" failed to push relay message"); 896 return; 897 } 898 899 dsr = (struct dhcp6_relay_packet *)p; 900 dsr->dsr_msgtype = msgtype; 901 dsr->dsr_peer = ss2sin6(&pc->pc_src)->sin6_addr; 902 dsr->dsr_hopcount = hopcount; 903 break; 904 905 default: 906 log_debug(" unknown message type %d", ds->ds_msgtype); 907 return; 908 } 909 910 /* We received an packet with Interface-ID, use it. */ 911 if (dstif != NULL) { 912 relay6_logsrcaddr(pc, dstif, msgtype); 913 send_packet(dstif, p, buflen, pc); 914 return; 915 } 916 917 /* Or send packet to the client. */ 918 if (clientdir) { 919 relay6_logsrcaddr(pc, interfaces, msgtype); 920 send_packet(interfaces, p, buflen, pc); 921 return; 922 } 923 924 /* Otherwise broadcast it to other relays/servers. */ 925 TAILQ_FOREACH(sp, &svlist, entry) { 926 /* 927 * Don't send in the same interface it came in if we are 928 * using multicast. 929 */ 930 if (sp->intf == intf && 931 sp->to.ss_family == 0) 932 continue; 933 934 /* 935 * When forwarding a packet use the configured address 936 * (if any) instead of multicasting. 937 */ 938 if (msgtype != DHCP6_MT_REPLY && 939 sp->to.ss_family == AF_INET6) 940 pc->pc_dst = sp->to; 941 942 relay6_logsrcaddr(pc, sp->intf, msgtype); 943 send_packet(sp->intf, p, buflen, pc); 944 } 945 } 946 947 void 948 mcast6_recv(struct protocol *l) 949 { 950 struct in6_pktinfo *ipi6 = NULL; 951 struct cmsghdr *cmsg; 952 struct interface_info *intf; 953 int sd = *(int *)l->local; 954 ssize_t recvlen; 955 struct packet_ctx pc; 956 struct msghdr msg; 957 struct sockaddr_storage ss; 958 struct iovec iov[2]; 959 uint8_t iovbuf[4096]; 960 uint8_t cmsgbuf[ 961 CMSG_SPACE(sizeof(struct in6_pktinfo)) 962 ]; 963 964 memset(&pc, 0, sizeof(pc)); 965 966 iov[0].iov_base = iovbuf; 967 iov[0].iov_len = sizeof(iovbuf); 968 969 memset(&msg, 0, sizeof(msg)); 970 msg.msg_iov = iov; 971 msg.msg_iovlen = 1; 972 msg.msg_control = cmsgbuf; 973 msg.msg_controllen = sizeof(cmsgbuf); 974 msg.msg_name = &ss; 975 msg.msg_namelen = sizeof(ss); 976 if ((recvlen = recvmsg(sd, &msg, 0)) == -1) { 977 log_warn("%s: recvmsg failed", __func__); 978 return; 979 } 980 981 /* Sanity check: this is an IPv6 packet. */ 982 if (ss.ss_family != AF_INET6) { 983 log_debug("received non IPv6 packet"); 984 return; 985 } 986 987 /* Drop packets that we sent. */ 988 if (iflist_getbyaddr6(&ss2sin6(&ss)->sin6_addr) != NULL) 989 return; 990 991 /* Save the sender address. */ 992 pc.pc_srcorig = pc.pc_src = ss; 993 994 /* Pre-configure destination to the default multicast address. */ 995 ss2sin6(&pc.pc_dst)->sin6_family = AF_INET6; 996 ss2sin6(&pc.pc_dst)->sin6_len = sizeof(struct sockaddr_in6); 997 ss2sin6(&pc.pc_dst)->sin6_addr = in6alldhcprelay; 998 ss2sin6(&pc.pc_dst)->sin6_port = htons(DHCP6_SERVER_PORT); 999 pc.pc_sd = serversd; 1000 1001 /* Find out input interface. */ 1002 for (cmsg = (struct cmsghdr *)CMSG_FIRSTHDR(&msg); cmsg; 1003 cmsg = (struct cmsghdr *)CMSG_NXTHDR(&msg, cmsg)) { 1004 if (cmsg->cmsg_level != IPPROTO_IPV6) 1005 continue; 1006 1007 switch (cmsg->cmsg_type) { 1008 case IPV6_PKTINFO: 1009 ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); 1010 break; 1011 } 1012 } 1013 if (ipi6 == NULL) { 1014 log_debug("failed to get packet interface"); 1015 return; 1016 } 1017 1018 intf = iflist_getbyindex(ipi6->ipi6_ifindex); 1019 if (intf == NULL) { 1020 log_debug("failed to find packet interface: %u", 1021 ipi6->ipi6_ifindex); 1022 return; 1023 } 1024 1025 /* Pass it to the relay routine. */ 1026 if (bootp_packet_handler) 1027 (*bootp_packet_handler)(intf, iovbuf, recvlen, &pc); 1028 } 1029