1 /* $OpenBSD: dhcrelay.c,v 1.65 2019/08/06 11:07:37 krw Exp $ */ 2 3 /* 4 * Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org> 5 * Copyright (c) 1997, 1998, 1999 The Internet Software Consortium. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of The Internet Software Consortium nor the names 18 * of its contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 22 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 26 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 29 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * This software has been written for the Internet Software Consortium 36 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 37 * Enterprises. To learn more about the Internet Software Consortium, 38 * see ``http://www.vix.com/isc''. To learn more about Vixie 39 * Enterprises, see ``http://www.vix.com''. 40 */ 41 42 #include <sys/types.h> 43 #include <sys/ioctl.h> 44 #include <sys/socket.h> 45 46 #include <arpa/inet.h> 47 48 #include <net/if.h> 49 50 #include <errno.h> 51 #include <fcntl.h> 52 #include <netdb.h> 53 #include <paths.h> 54 #include <pwd.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <syslog.h> 59 #include <time.h> 60 #include <unistd.h> 61 62 #include "dhcp.h" 63 #include "dhcpd.h" 64 #include "log.h" 65 66 void usage(void); 67 int rdaemon(int); 68 void relay(struct interface_info *, struct dhcp_packet *, int, 69 struct packet_ctx *); 70 void l2relay(struct interface_info *, struct dhcp_packet *, int, 71 struct packet_ctx *); 72 char *print_hw_addr(int, int, unsigned char *); 73 void got_response(struct protocol *); 74 int get_rdomain(char *); 75 76 void relay_agentinfo(struct packet_ctx *, struct interface_info *, int); 77 78 int relay_agentinfo_cmp(struct packet_ctx *pc, uint8_t *, int); 79 ssize_t relay_agentinfo_append(struct packet_ctx *, struct dhcp_packet *, 80 size_t); 81 ssize_t relay_agentinfo_remove(struct packet_ctx *, struct dhcp_packet *, 82 size_t); 83 84 time_t cur_time; 85 86 struct interface_info *interfaces = NULL; 87 struct server_list *servers; 88 struct iflist intflist; 89 int server_fd; 90 int oflag; 91 92 enum dhcp_relay_mode drm = DRM_UNKNOWN; 93 const char *rai_circuit = NULL; 94 const char *rai_remote = NULL; 95 int rai_replace = 0; 96 97 int 98 main(int argc, char *argv[]) 99 { 100 int ch, devnull = -1, daemonize, opt, rdomain; 101 struct server_list *sp = NULL; 102 struct passwd *pw; 103 struct sockaddr_in laddr; 104 int optslen; 105 106 daemonize = 1; 107 108 log_init(1, LOG_DAEMON); /* log to stderr until daemonized */ 109 110 setup_iflist(); 111 112 while ((ch = getopt(argc, argv, "aC:di:oR:r")) != -1) { 113 switch (ch) { 114 case 'C': 115 rai_circuit = optarg; 116 break; 117 case 'd': 118 daemonize = 0; 119 break; 120 case 'i': 121 if (interfaces != NULL) 122 usage(); 123 124 interfaces = iflist_getbyname(optarg); 125 if (interfaces == NULL) 126 fatalx("interface '%s' not found", optarg); 127 break; 128 case 'o': 129 /* add the relay agent information option */ 130 oflag++; 131 break; 132 case 'R': 133 rai_remote = optarg; 134 break; 135 case 'r': 136 rai_replace = 1; 137 break; 138 139 default: 140 usage(); 141 /* not reached */ 142 } 143 } 144 145 argc -= optind; 146 argv += optind; 147 148 if (argc < 1) 149 usage(); 150 151 if (rai_remote != NULL && rai_circuit == NULL) 152 fatalx("you must specify a circuit-id with a remote-id"); 153 154 /* Validate that we have space for all suboptions. */ 155 if (rai_circuit != NULL) { 156 optslen = 2 + strlen(rai_circuit); 157 if (rai_remote != NULL) 158 optslen += 2 + strlen(rai_remote); 159 160 if (optslen > DHCP_OPTION_MAXLEN) 161 fatalx("relay agent information is too long"); 162 } 163 164 while (argc > 0) { 165 struct hostent *he; 166 struct in_addr ia, *iap = NULL; 167 168 if ((sp = calloc(1, sizeof(*sp))) == NULL) 169 fatalx("calloc"); 170 171 if ((sp->intf = register_interface(argv[0], got_one, 172 1)) != NULL) { 173 if (drm == DRM_LAYER3) 174 fatalx("don't mix interfaces with hosts"); 175 176 if (sp->intf->hw_address.htype == HTYPE_IPSEC_TUNNEL) 177 fatalx("can't use IPsec with layer 2"); 178 179 sp->next = servers; 180 servers = sp; 181 182 drm = DRM_LAYER2; 183 argc--; 184 argv++; 185 continue; 186 } 187 188 if (inet_aton(argv[0], &ia)) 189 iap = &ia; 190 else { 191 he = gethostbyname(argv[0]); 192 if (!he) 193 log_warnx("%s: host unknown", argv[0]); 194 else 195 iap = ((struct in_addr *)he->h_addr_list[0]); 196 } 197 if (iap) { 198 if (drm == DRM_LAYER2) 199 fatalx("don't mix interfaces with hosts"); 200 201 drm = DRM_LAYER3; 202 sp->next = servers; 203 servers = sp; 204 memcpy(&ss2sin(&sp->to)->sin_addr, iap, sizeof(*iap)); 205 } else 206 free(sp); 207 208 argc--; 209 argv++; 210 } 211 212 if (daemonize) { 213 devnull = open(_PATH_DEVNULL, O_RDWR, 0); 214 if (devnull == -1) 215 fatal("open(%s)", _PATH_DEVNULL); 216 } 217 218 if (interfaces == NULL || 219 register_interface(interfaces->name, got_one, 0) == NULL) 220 fatalx("no interface given"); 221 222 /* We need an address for running layer 3 mode. */ 223 if (drm == DRM_LAYER3 && 224 (interfaces->hw_address.htype != HTYPE_IPSEC_TUNNEL && 225 interfaces->primary_address.s_addr == 0)) 226 fatalx("interface '%s' does not have an address", 227 interfaces->name); 228 229 /* We need at least one server. */ 230 if (!sp) 231 usage(); 232 233 rdomain = get_rdomain(interfaces->name); 234 235 /* Enable the relay agent option by default for enc0 */ 236 if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) 237 oflag++; 238 239 bzero(&laddr, sizeof laddr); 240 laddr.sin_len = sizeof laddr; 241 laddr.sin_family = AF_INET; 242 laddr.sin_port = htons(SERVER_PORT); 243 laddr.sin_addr.s_addr = interfaces->primary_address.s_addr; 244 /* Set up the server sockaddrs. */ 245 for (sp = servers; sp; sp = sp->next) { 246 if (sp->intf != NULL) 247 break; 248 249 ss2sin(&sp->to)->sin_port = htons(SERVER_PORT); 250 ss2sin(&sp->to)->sin_family = AF_INET; 251 ss2sin(&sp->to)->sin_len = sizeof(struct sockaddr_in); 252 sp->fd = socket(AF_INET, SOCK_DGRAM, 0); 253 if (sp->fd == -1) 254 fatal("socket"); 255 opt = 1; 256 if (setsockopt(sp->fd, SOL_SOCKET, SO_REUSEPORT, 257 &opt, sizeof(opt)) == -1) 258 fatal("setsockopt"); 259 if (setsockopt(sp->fd, SOL_SOCKET, SO_RTABLE, &rdomain, 260 sizeof(rdomain)) == -1) 261 fatal("setsockopt"); 262 if (bind(sp->fd, (struct sockaddr *)&laddr, sizeof laddr) == 263 -1) 264 fatal("bind"); 265 if (connect(sp->fd, (struct sockaddr *)&sp->to, 266 sp->to.ss_len) == -1) 267 fatal("connect"); 268 add_protocol("server", sp->fd, got_response, sp); 269 } 270 271 /* Socket used to forward packets to the DHCP client */ 272 if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) { 273 laddr.sin_addr.s_addr = INADDR_ANY; 274 server_fd = socket(AF_INET, SOCK_DGRAM, 0); 275 if (server_fd == -1) 276 fatal("socket"); 277 opt = 1; 278 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, 279 &opt, sizeof(opt)) == -1) 280 fatal("setsockopt"); 281 if (setsockopt(server_fd, SOL_SOCKET, SO_RTABLE, &rdomain, 282 sizeof(rdomain)) == -1) 283 fatal("setsockopt"); 284 if (bind(server_fd, (struct sockaddr *)&laddr, 285 sizeof(laddr)) == -1) 286 fatal("bind"); 287 } 288 289 tzset(); 290 291 time(&cur_time); 292 if (drm == DRM_LAYER3) 293 bootp_packet_handler = relay; 294 else 295 bootp_packet_handler = l2relay; 296 297 if ((pw = getpwnam("_dhcp")) == NULL) 298 fatalx("user \"_dhcp\" not found"); 299 if (chroot(pw->pw_dir) == -1) 300 fatal("chroot"); 301 if (chdir("/") == -1) 302 fatal("chdir(\"/\")"); 303 if (setgroups(1, &pw->pw_gid) || 304 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 305 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 306 fatal("can't drop privileges"); 307 308 if (daemonize) { 309 if (rdaemon(devnull) == -1) 310 fatal("rdaemon"); 311 312 log_init(0, LOG_DAEMON); /* stop logging to stderr */ 313 } 314 315 if (pledge("stdio route", NULL) == -1) 316 fatalx("pledge"); 317 318 dispatch(); 319 /* not reached */ 320 321 exit(0); 322 } 323 324 void 325 relay(struct interface_info *ip, struct dhcp_packet *packet, int length, 326 struct packet_ctx *pc) 327 { 328 struct server_list *sp; 329 struct sockaddr_in to; 330 331 if (packet->hlen > sizeof packet->chaddr) { 332 log_info("Discarding packet with invalid hlen."); 333 return; 334 } 335 336 /* If it's a bootreply, forward it to the client. */ 337 if (packet->op == BOOTREPLY) { 338 /* Filter packet that were not meant for us. */ 339 if (packet->giaddr.s_addr != 340 interfaces->primary_address.s_addr) 341 return; 342 343 bzero(&to, sizeof(to)); 344 if (!(packet->flags & htons(BOOTP_BROADCAST))) { 345 to.sin_addr = packet->yiaddr; 346 to.sin_port = htons(CLIENT_PORT); 347 } else { 348 to.sin_addr.s_addr = htonl(INADDR_BROADCAST); 349 to.sin_port = htons(CLIENT_PORT); 350 } 351 to.sin_family = AF_INET; 352 to.sin_len = sizeof to; 353 *ss2sin(&pc->pc_dst) = to; 354 355 /* 356 * Set up the hardware destination address. If it's a reply 357 * with the BROADCAST flag set, we should send an L2 broad- 358 * cast as well. 359 */ 360 if (!(packet->flags & htons(BOOTP_BROADCAST))) { 361 pc->pc_hlen = packet->hlen; 362 if (pc->pc_hlen > CHADDR_SIZE) 363 pc->pc_hlen = CHADDR_SIZE; 364 memcpy(pc->pc_dmac, packet->chaddr, pc->pc_hlen); 365 pc->pc_htype = packet->htype; 366 } else { 367 memset(pc->pc_dmac, 0xff, sizeof(pc->pc_dmac)); 368 } 369 370 relay_agentinfo(pc, interfaces, packet->op); 371 if ((length = relay_agentinfo_remove(pc, packet, 372 length)) == -1) { 373 log_info("ignoring BOOTREPLY with invalid " 374 "relay agent information"); 375 return; 376 } 377 378 /* 379 * VMware PXE "ROMs" confuse the DHCP gateway address 380 * with the IP gateway address. This is a problem if your 381 * DHCP relay is running on something that's not your 382 * network gateway. 383 * 384 * It is purely informational from the relay to the client 385 * so we can safely clear it. 386 */ 387 packet->giaddr.s_addr = 0x0; 388 389 ss2sin(&pc->pc_src)->sin_addr = interfaces->primary_address; 390 if (send_packet(interfaces, packet, length, pc) != -1) 391 log_debug("forwarded BOOTREPLY for %s to %s", 392 print_hw_addr(packet->htype, packet->hlen, 393 packet->chaddr), inet_ntoa(to.sin_addr)); 394 return; 395 } 396 397 if (ip == NULL) { 398 log_info("ignoring non BOOTREPLY from server"); 399 return; 400 } 401 402 if (packet->hops > 16) { 403 log_info("ignoring BOOTREQUEST with hop count of %d", 404 packet->hops); 405 return; 406 } 407 packet->hops++; 408 409 /* 410 * Set the giaddr so the server can figure out what net it's 411 * from and so that we can later forward the response to the 412 * correct net. The RFC specifies that we have to keep the 413 * initial giaddr (in case we relay over multiple hops). 414 */ 415 if (!packet->giaddr.s_addr) 416 packet->giaddr = ip->primary_address; 417 418 relay_agentinfo(pc, interfaces, packet->op); 419 if ((length = relay_agentinfo_append(pc, packet, length)) == -1) { 420 log_info("ignoring BOOTREQUEST with invalid " 421 "relay agent information"); 422 return; 423 } 424 425 /* Otherwise, it's a BOOTREQUEST, so forward it to all the 426 servers. */ 427 for (sp = servers; sp; sp = sp->next) { 428 if (send(sp->fd, packet, length, 0) != -1) { 429 log_debug("forwarded BOOTREQUEST for %s to %s", 430 print_hw_addr(packet->htype, packet->hlen, 431 packet->chaddr), 432 inet_ntoa(ss2sin(&sp->to)->sin_addr)); 433 } 434 } 435 436 } 437 438 void 439 usage(void) 440 { 441 extern char *__progname; 442 443 fprintf(stderr, "usage: %s [-dor] [-C circuit-id] [-R remote-id] " 444 "-i interface\n\tdestination ...\n", 445 __progname); 446 exit(1); 447 } 448 449 int 450 rdaemon(int devnull) 451 { 452 if (devnull == -1) { 453 errno = EBADF; 454 return (-1); 455 } 456 if (fcntl(devnull, F_GETFL) == -1) 457 return (-1); 458 459 switch (fork()) { 460 case -1: 461 return (-1); 462 case 0: 463 break; 464 default: 465 _exit(0); 466 } 467 468 if (setsid() == -1) 469 return (-1); 470 471 (void)dup2(devnull, STDIN_FILENO); 472 (void)dup2(devnull, STDOUT_FILENO); 473 (void)dup2(devnull, STDERR_FILENO); 474 if (devnull > 2) 475 (void)close(devnull); 476 477 return (0); 478 } 479 480 char * 481 print_hw_addr(int htype, int hlen, unsigned char *data) 482 { 483 static char habuf[49]; 484 char *s = habuf; 485 int i, j, slen = sizeof(habuf); 486 487 if (htype == 0 || hlen == 0) { 488 bad: 489 strlcpy(habuf, "<null>", sizeof habuf); 490 return habuf; 491 } 492 493 for (i = 0; i < hlen; i++) { 494 j = snprintf(s, slen, "%02x", data[i]); 495 if (j <= 0 || j >= slen) 496 goto bad; 497 j = strlen (s); 498 s += j; 499 slen -= (j + 1); 500 *s++ = ':'; 501 } 502 *--s = '\0'; 503 return habuf; 504 } 505 506 void 507 got_response(struct protocol *l) 508 { 509 struct packet_ctx pc; 510 ssize_t result; 511 union { 512 /* 513 * Packet input buffer. Must be as large as largest 514 * possible MTU. 515 */ 516 unsigned char packbuf[4095]; 517 struct dhcp_packet packet; 518 } u; 519 struct server_list *sp = l->local; 520 521 memset(&u, DHO_END, sizeof(u)); 522 if ((result = recv(l->fd, u.packbuf, sizeof(u), 0)) == -1 && 523 errno != ECONNREFUSED) { 524 /* 525 * Ignore ECONNREFUSED as too many dhcp servers send a bogus 526 * icmp unreach for every request. 527 */ 528 log_warn("recv failed for %s", 529 inet_ntoa(ss2sin(&sp->to)->sin_addr)); 530 return; 531 } 532 if (result == -1 && errno == ECONNREFUSED) 533 return; 534 535 if (result == 0) 536 return; 537 538 if (result < BOOTP_MIN_LEN) { 539 log_info("Discarding packet with invalid size."); 540 return; 541 } 542 543 memset(&pc, 0, sizeof(pc)); 544 pc.pc_src.ss_family = AF_INET; 545 pc.pc_src.ss_len = sizeof(struct sockaddr_in); 546 memcpy(&ss2sin(&pc.pc_src)->sin_addr, &ss2sin(&sp->to)->sin_addr, 547 sizeof(ss2sin(&pc.pc_src)->sin_addr)); 548 ss2sin(&pc.pc_src)->sin_port = htons(SERVER_PORT); 549 550 pc.pc_dst.ss_family = AF_INET; 551 pc.pc_dst.ss_len = sizeof(struct sockaddr_in); 552 ss2sin(&pc.pc_dst)->sin_port = htons(CLIENT_PORT); 553 554 if (bootp_packet_handler) 555 (*bootp_packet_handler)(NULL, &u.packet, result, &pc); 556 } 557 558 void 559 relay_agentinfo(struct packet_ctx *pc, struct interface_info *intf, 560 int bootop) 561 { 562 static u_int8_t buf[8]; 563 struct sockaddr_in *sin; 564 565 if (oflag == 0) 566 return; 567 568 if (rai_remote != NULL) { 569 pc->pc_remote = rai_remote; 570 pc->pc_remotelen = strlen(rai_remote); 571 } else 572 pc->pc_remotelen = 0; 573 574 if (rai_circuit == NULL) { 575 buf[0] = (uint8_t)(intf->index << 8); 576 buf[1] = intf->index & 0xff; 577 pc->pc_circuit = buf; 578 pc->pc_circuitlen = 2; 579 580 if (rai_remote == NULL) { 581 if (bootop == BOOTREPLY) 582 sin = ss2sin(&pc->pc_dst); 583 else 584 sin = ss2sin(&pc->pc_src); 585 586 pc->pc_remote = 587 (uint8_t *)&sin->sin_addr; 588 pc->pc_remotelen = 589 sizeof(sin->sin_addr); 590 } 591 } else { 592 pc->pc_circuit = rai_circuit; 593 pc->pc_circuitlen = strlen(rai_circuit); 594 } 595 } 596 597 int 598 relay_agentinfo_cmp(struct packet_ctx *pc, uint8_t *p, int plen) 599 { 600 int len; 601 char buf[256]; 602 603 if (oflag == 0) 604 return (-1); 605 606 len = *(p + 1); 607 if (len > plen) 608 return (-1); 609 610 switch (*p) { 611 case RAI_CIRCUIT_ID: 612 if (pc->pc_circuit == NULL) 613 return (-1); 614 if (pc->pc_circuitlen != len) 615 return (-1); 616 617 memcpy(buf, p + DHCP_OPTION_HDR_LEN, len); 618 return (memcmp(pc->pc_circuit, buf, len)); 619 620 case RAI_REMOTE_ID: 621 if (pc->pc_remote == NULL) 622 return (-1); 623 if (pc->pc_remotelen != len) 624 return (-1); 625 626 memcpy(buf, p + DHCP_OPTION_HDR_LEN, len); 627 return (memcmp(pc->pc_remote, buf, len)); 628 629 default: 630 /* Unmatched type */ 631 log_info("unmatched relay info %d", *p); 632 return (0); 633 } 634 } 635 636 ssize_t 637 relay_agentinfo_append(struct packet_ctx *pc, struct dhcp_packet *dp, 638 size_t dplen) 639 { 640 uint8_t *p, *startp; 641 ssize_t newtotal = dplen; 642 int opttotal, optlen, i, hasinfo = 0; 643 int maxlen, neededlen; 644 645 /* Only append when enabled. */ 646 if (oflag == 0) 647 return (dplen); 648 649 startp = (uint8_t *)dp; 650 p = (uint8_t *)&dp->options; 651 if (memcmp(p, DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN)) { 652 log_info("invalid dhcp options cookie"); 653 return (-1); 654 } 655 656 p += DHCP_OPTIONS_COOKIE_LEN; 657 opttotal = dplen - DHCP_FIXED_NON_UDP - DHCP_OPTIONS_COOKIE_LEN; 658 maxlen = DHCP_MTU_MAX - DHCP_FIXED_LEN - DHCP_OPTIONS_COOKIE_LEN - 1; 659 if (maxlen < 1 || opttotal < 1) 660 return (dplen); 661 662 for (i = 0; i < opttotal && *p != DHO_END;) { 663 if (*p == DHO_PAD) 664 optlen = 1; 665 else 666 optlen = p[1] + DHCP_OPTION_HDR_LEN; 667 668 if ((i + optlen) > opttotal) { 669 log_info("truncated dhcp options"); 670 return (-1); 671 } 672 673 if (*p == DHO_RELAY_AGENT_INFORMATION) { 674 if (rai_replace) { 675 memmove(p, p + optlen, opttotal - i); 676 opttotal -= optlen; 677 optlen = 0; 678 } else 679 hasinfo = 1; 680 } 681 682 p += optlen; 683 i += optlen; 684 685 /* We reached the end, append the relay agent info. */ 686 if (i < opttotal && *p == DHO_END) { 687 /* We already have the Relay Agent Info, skip it. */ 688 if (hasinfo) 689 continue; 690 691 /* Calculate needed length to append new data. */ 692 neededlen = newtotal + DHCP_OPTION_HDR_LEN; 693 if (pc->pc_circuitlen > 0) 694 neededlen += DHCP_OPTION_HDR_LEN + 695 pc->pc_circuitlen; 696 if (pc->pc_remotelen > 0) 697 neededlen += DHCP_OPTION_HDR_LEN + 698 pc->pc_remotelen; 699 700 /* Save one byte for DHO_END. */ 701 neededlen += 1; 702 703 /* Check if we have space for the new options. */ 704 if (neededlen > maxlen) { 705 log_warnx("no space for relay agent info"); 706 return (newtotal); 707 } 708 709 /* New option header: 2 bytes. */ 710 newtotal += DHCP_OPTION_HDR_LEN; 711 712 *p++ = DHO_RELAY_AGENT_INFORMATION; 713 *p = 0; 714 if (pc->pc_circuitlen > 0) { 715 newtotal += DHCP_OPTION_HDR_LEN + 716 pc->pc_circuitlen; 717 *p = (*p) + DHCP_OPTION_HDR_LEN + 718 pc->pc_circuitlen; 719 } 720 721 if (pc->pc_remotelen > 0) { 722 newtotal += DHCP_OPTION_HDR_LEN + 723 pc->pc_remotelen; 724 *p = (*p) + DHCP_OPTION_HDR_LEN + 725 pc->pc_remotelen; 726 } 727 728 p++; 729 730 /* Sub-option circuit-id header plus value. */ 731 if (pc->pc_circuitlen > 0) { 732 *p++ = RAI_CIRCUIT_ID; 733 *p++ = pc->pc_circuitlen; 734 memcpy(p, pc->pc_circuit, pc->pc_circuitlen); 735 736 p += pc->pc_circuitlen; 737 } 738 739 /* Sub-option remote-id header plus value. */ 740 if (pc->pc_remotelen > 0) { 741 *p++ = RAI_REMOTE_ID; 742 *p++ = pc->pc_remotelen; 743 memcpy(p, pc->pc_remote, pc->pc_remotelen); 744 745 p += pc->pc_remotelen; 746 } 747 748 *p = DHO_END; 749 } 750 } 751 752 /* Zero the padding so we don't leak anything. */ 753 p++; 754 if (p < (startp + maxlen)) 755 memset(p, 0, (startp + maxlen) - p); 756 757 return (newtotal); 758 } 759 760 ssize_t 761 relay_agentinfo_remove(struct packet_ctx *pc, struct dhcp_packet *dp, 762 size_t dplen) 763 { 764 uint8_t *p, *np, *startp, *endp; 765 int opttotal, optleft; 766 int suboptlen, optlen, i; 767 int maxlen, remaining, matched = 0; 768 769 startp = (uint8_t *)dp; 770 p = (uint8_t *)&dp->options; 771 if (memcmp(p, DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN)) { 772 log_info("invalid dhcp options cookie"); 773 return (-1); 774 } 775 776 maxlen = DHCP_MTU_MAX - DHCP_FIXED_LEN - DHCP_OPTIONS_COOKIE_LEN - 1; 777 opttotal = dplen - DHCP_FIXED_NON_UDP - DHCP_OPTIONS_COOKIE_LEN; 778 optleft = opttotal; 779 780 p += DHCP_OPTIONS_COOKIE_LEN; 781 endp = p + opttotal; 782 783 for (i = 0; i < opttotal && *p != DHO_END;) { 784 if (*p == DHO_PAD) 785 optlen = 1; 786 else 787 optlen = p[1] + DHCP_OPTION_HDR_LEN; 788 789 if ((i + optlen) > opttotal) { 790 log_info("truncated dhcp options"); 791 return (-1); 792 } 793 794 if (*p == DHO_RELAY_AGENT_INFORMATION) { 795 /* Fast case: there is no next option. */ 796 np = p + optlen; 797 if (*np == DHO_END) { 798 *p = *np; 799 endp = p + 1; 800 /* Zero the padding so we don't leak data. */ 801 if (endp < (startp + maxlen)) 802 memset(endp, 0, 803 (startp + maxlen) - endp); 804 805 return (dplen); 806 } 807 808 remaining = optlen; 809 while (remaining > 0) { 810 suboptlen = *(p + 1); 811 remaining -= DHCP_OPTION_HDR_LEN + suboptlen; 812 813 matched = 1; 814 if (relay_agentinfo_cmp(pc, p, suboptlen) == 0) 815 continue; 816 817 matched = 0; 818 break; 819 } 820 /* It is not ours Relay Agent Info, don't remove it. */ 821 if (matched == 0) 822 break; 823 824 /* Move the other options on top of this one. */ 825 optleft -= optlen; 826 endp -= optlen; 827 828 /* Replace the old agent relay info. */ 829 memmove(p, dp, optleft); 830 831 endp++; 832 /* Zero the padding so we don't leak data. */ 833 if (endp < (startp + maxlen)) 834 memset(endp, 0, 835 (startp + maxlen) - endp); 836 837 return (endp - startp); 838 } 839 840 p += optlen; 841 i += optlen; 842 optleft -= optlen; 843 } 844 845 return (endp - startp); 846 } 847 848 int 849 get_rdomain(char *name) 850 { 851 int rv = 0, s; 852 struct ifreq ifr; 853 854 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 855 fatal("get_rdomain socket"); 856 857 bzero(&ifr, sizeof(ifr)); 858 strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); 859 if (ioctl(s, SIOCGIFRDOMAIN, (caddr_t)&ifr) != -1) 860 rv = ifr.ifr_rdomainid; 861 862 close(s); 863 return rv; 864 } 865 866 void 867 l2relay(struct interface_info *ip, struct dhcp_packet *dp, int length, 868 struct packet_ctx *pc) 869 { 870 struct server_list *sp; 871 ssize_t dplen; 872 873 if (dp->hlen > sizeof(dp->chaddr)) { 874 log_info("Discarding packet with invalid hlen."); 875 return; 876 } 877 878 relay_agentinfo(pc, ip, dp->op); 879 880 switch (dp->op) { 881 case BOOTREQUEST: 882 /* Add the relay agent info asked by the user. */ 883 if ((dplen = relay_agentinfo_append(pc, dp, length)) == -1) 884 return; 885 886 /* 887 * Re-send the packet to every interface except the one 888 * it came in. 889 */ 890 for (sp = servers; sp != NULL; sp = sp->next) { 891 if (sp->intf == ip) 892 continue; 893 894 log_debug("forwarded BOOTREQUEST for %s to %s", 895 print_hw_addr(pc->pc_htype, pc->pc_hlen, 896 pc->pc_smac), sp->intf->name); 897 898 send_packet(sp->intf, dp, dplen, pc); 899 } 900 if (ip != interfaces) { 901 log_debug("forwarded BOOTREQUEST for %s to %s", 902 print_hw_addr(pc->pc_htype, pc->pc_hlen, 903 pc->pc_smac), interfaces->name); 904 905 send_packet(interfaces, dp, dplen, pc); 906 } 907 break; 908 909 case BOOTREPLY: 910 /* Remove relay agent info on offer. */ 911 if ((dplen = relay_agentinfo_remove(pc, dp, length)) == -1) 912 return; 913 914 if (ip != interfaces) { 915 log_debug("forwarded BOOTREPLY for %s to %s", 916 print_hw_addr(pc->pc_htype, pc->pc_hlen, 917 pc->pc_dmac), interfaces->name); 918 send_packet(interfaces, dp, dplen, pc); 919 } 920 break; 921 922 default: 923 log_debug("invalid operation type '%d'", dp->op); 924 return; 925 } 926 } 927