1 /* $NetBSD: dhcrelay.c,v 1.5 2014/07/12 12:09:37 spz Exp $ */ 2 /* dhcrelay.c 3 4 DHCP/BOOTP Relay Agent. */ 5 6 /* 7 * Copyright(c) 2004-2014 by Internet Systems Consortium, Inc.("ISC") 8 * Copyright(c) 1997-2003 by Internet Software Consortium 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 * 22 * Internet Systems Consortium, Inc. 23 * 950 Charter Street 24 * Redwood City, CA 94063 25 * <info@isc.org> 26 * https://www.isc.org/ 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __RCSID("$NetBSD: dhcrelay.c,v 1.5 2014/07/12 12:09:37 spz Exp $"); 32 33 #include "dhcpd.h" 34 #include <syslog.h> 35 #include <signal.h> 36 #include <sys/time.h> 37 38 TIME default_lease_time = 43200; /* 12 hours... */ 39 TIME max_lease_time = 86400; /* 24 hours... */ 40 struct tree_cache *global_options[256]; 41 42 struct option *requested_opts[2]; 43 44 /* Needed to prevent linking against conflex.c. */ 45 int lexline; 46 int lexchar; 47 char *token_line; 48 char *tlname; 49 50 const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID; 51 isc_boolean_t no_dhcrelay_pid = ISC_FALSE; 52 /* False (default) => we write and use a pid file */ 53 isc_boolean_t no_pid_file = ISC_FALSE; 54 55 int bogus_agent_drops = 0; /* Packets dropped because agent option 56 field was specified and we're not relaying 57 packets that already have an agent option 58 specified. */ 59 int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a 60 client, but with a bogus giaddr. */ 61 int client_packets_relayed = 0; /* Packets relayed from client to server. */ 62 int server_packet_errors = 0; /* Errors sending packets to servers. */ 63 int server_packets_relayed = 0; /* Packets relayed from server to client. */ 64 int client_packet_errors = 0; /* Errors sending packets to clients. */ 65 66 int add_agent_options = 0; /* If nonzero, add relay agent options. */ 67 68 int agent_option_errors = 0; /* Number of packets forwarded without 69 agent options because there was no room. */ 70 int drop_agent_mismatches = 0; /* If nonzero, drop server replies that 71 don't have matching circuit-id's. */ 72 int corrupt_agent_options = 0; /* Number of packets dropped because 73 relay agent information option was bad. */ 74 int missing_agent_option = 0; /* Number of packets dropped because no 75 RAI option matching our ID was found. */ 76 int bad_circuit_id = 0; /* Circuit ID option in matching RAI option 77 did not match any known circuit ID. */ 78 int missing_circuit_id = 0; /* Circuit ID option in matching RAI option 79 was missing. */ 80 int max_hop_count = 10; /* Maximum hop count */ 81 82 #ifdef DHCPv6 83 /* Force use of DHCPv6 interface-id option. */ 84 isc_boolean_t use_if_id = ISC_FALSE; 85 #endif 86 87 /* Maximum size of a packet with agent options added. */ 88 int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN; 89 90 /* What to do about packets we're asked to relay that 91 already have a relay option: */ 92 enum { forward_and_append, /* Forward and append our own relay option. */ 93 forward_and_replace, /* Forward, but replace theirs with ours. */ 94 forward_untouched, /* Forward without changes. */ 95 discard } agent_relay_mode = forward_and_replace; 96 97 u_int16_t local_port; 98 u_int16_t remote_port; 99 100 /* Relay agent server list. */ 101 struct server_list { 102 struct server_list *next; 103 struct sockaddr_in to; 104 } *servers; 105 106 #ifdef DHCPv6 107 struct stream_list { 108 struct stream_list *next; 109 struct interface_info *ifp; 110 struct sockaddr_in6 link; 111 int id; 112 } *downstreams, *upstreams; 113 114 static struct stream_list *parse_downstream(char *); 115 static struct stream_list *parse_upstream(char *); 116 static void setup_streams(void); 117 118 /* 119 * A pointer to a subscriber id to add to the message we forward. 120 * This is primarily for testing purposes as we only have one id 121 * for the entire relay and don't determine one per client which 122 * would be more useful. 123 */ 124 char *dhcrelay_sub_id = NULL; 125 #endif 126 127 static void do_relay4(struct interface_info *, struct dhcp_packet *, 128 unsigned int, unsigned int, struct iaddr, 129 struct hardware *); 130 static int add_relay_agent_options(struct interface_info *, 131 struct dhcp_packet *, unsigned, 132 struct in_addr); 133 static int find_interface_by_agent_option(struct dhcp_packet *, 134 struct interface_info **, u_int8_t *, int); 135 static int strip_relay_agent_options(struct interface_info *, 136 struct interface_info **, 137 struct dhcp_packet *, unsigned); 138 139 static const char copyright[] = 140 "Copyright 2004-2014 Internet Systems Consortium."; 141 static const char arr[] = "All rights reserved."; 142 static const char message[] = 143 "Internet Systems Consortium DHCP Relay Agent"; 144 static const char url[] = 145 "For info, please visit https://www.isc.org/software/dhcp/"; 146 147 #ifdef DHCPv6 148 #define DHCRELAY_USAGE \ 149 "Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\ 150 " [-A <length>] [-c <hops>] [-p <port>]\n" \ 151 " [-pf <pid-file>] [--no-pid]\n"\ 152 " [-m append|replace|forward|discard]\n" \ 153 " [-i interface0 [ ... -i interfaceN]\n" \ 154 " server0 [ ... serverN]\n\n" \ 155 " dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \ 156 " [-pf <pid-file>] [--no-pid]\n" \ 157 " [-s <subscriber-id>]\n" \ 158 " -l lower0 [ ... -l lowerN]\n" \ 159 " -u upper0 [ ... -u upperN]\n" \ 160 " lower (client link): [address%%]interface[#index]\n" \ 161 " upper (server link): [address%%]interface" 162 #else 163 #define DHCRELAY_USAGE \ 164 "Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \ 165 " [-pf <pid-file>] [--no-pid]\n" \ 166 " [-m append|replace|forward|discard]\n" \ 167 " [-i interface0 [ ... -i interfaceN]\n" \ 168 " server0 [ ... serverN]\n\n" 169 #endif 170 171 static void usage(void) { 172 log_fatal(DHCRELAY_USAGE); 173 } 174 175 int 176 main(int argc, char **argv) { 177 isc_result_t status; 178 struct servent *ent; 179 struct server_list *sp = NULL; 180 struct interface_info *tmp = NULL; 181 char *service_local = NULL, *service_remote = NULL; 182 u_int16_t port_local = 0, port_remote = 0; 183 int no_daemon = 0, quiet = 0; 184 int fd; 185 int i; 186 #ifdef DHCPv6 187 struct stream_list *sl = NULL; 188 int local_family_set = 0; 189 #endif 190 191 /* Make sure that file descriptors 0(stdin), 1,(stdout), and 192 2(stderr) are open. To do this, we assume that when we 193 open a file the lowest available file descriptor is used. */ 194 fd = open("/dev/null", O_RDWR); 195 if (fd == 0) 196 fd = open("/dev/null", O_RDWR); 197 if (fd == 1) 198 fd = open("/dev/null", O_RDWR); 199 if (fd == 2) 200 log_perror = 0; /* No sense logging to /dev/null. */ 201 else if (fd != -1) 202 close(fd); 203 204 openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON); 205 206 #if !defined(DEBUG) 207 setlogmask(LOG_UPTO(LOG_INFO)); 208 #endif 209 210 /* Set up the isc and dns library managers */ 211 status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB, 212 NULL, NULL); 213 if (status != ISC_R_SUCCESS) 214 log_fatal("Can't initialize context: %s", 215 isc_result_totext(status)); 216 217 /* Set up the OMAPI. */ 218 status = omapi_init(); 219 if (status != ISC_R_SUCCESS) 220 log_fatal("Can't initialize OMAPI: %s", 221 isc_result_totext(status)); 222 223 /* Set up the OMAPI wrappers for the interface object. */ 224 interface_setup(); 225 226 for (i = 1; i < argc; i++) { 227 if (!strcmp(argv[i], "-4")) { 228 #ifdef DHCPv6 229 if (local_family_set && (local_family == AF_INET6)) { 230 usage(); 231 } 232 local_family_set = 1; 233 local_family = AF_INET; 234 } else if (!strcmp(argv[i], "-6")) { 235 if (local_family_set && (local_family == AF_INET)) { 236 usage(); 237 } 238 local_family_set = 1; 239 local_family = AF_INET6; 240 #endif 241 } else if (!strcmp(argv[i], "-d")) { 242 no_daemon = 1; 243 } else if (!strcmp(argv[i], "-q")) { 244 quiet = 1; 245 quiet_interface_discovery = 1; 246 } else if (!strcmp(argv[i], "-p")) { 247 if (++i == argc) 248 usage(); 249 local_port = validate_port(argv[i]); 250 log_debug("binding to user-specified port %d", 251 ntohs(local_port)); 252 } else if (!strcmp(argv[i], "-c")) { 253 int hcount; 254 if (++i == argc) 255 usage(); 256 hcount = atoi(argv[i]); 257 if (hcount <= 255) 258 max_hop_count= hcount; 259 else 260 usage(); 261 } else if (!strcmp(argv[i], "-i")) { 262 #ifdef DHCPv6 263 if (local_family_set && (local_family == AF_INET6)) { 264 usage(); 265 } 266 local_family_set = 1; 267 local_family = AF_INET; 268 #endif 269 if (++i == argc) { 270 usage(); 271 } 272 if (strlen(argv[i]) >= sizeof(tmp->name)) { 273 log_fatal("%s: interface name too long " 274 "(is %ld)", 275 argv[i], (long)strlen(argv[i])); 276 } 277 status = interface_allocate(&tmp, MDL); 278 if (status != ISC_R_SUCCESS) { 279 log_fatal("%s: interface_allocate: %s", 280 argv[i], 281 isc_result_totext(status)); 282 } 283 strcpy(tmp->name, argv[i]); 284 interface_snorf(tmp, INTERFACE_REQUESTED); 285 interface_dereference(&tmp, MDL); 286 } else if (!strcmp(argv[i], "-a")) { 287 #ifdef DHCPv6 288 if (local_family_set && (local_family == AF_INET6)) { 289 usage(); 290 } 291 local_family_set = 1; 292 local_family = AF_INET; 293 #endif 294 add_agent_options = 1; 295 } else if (!strcmp(argv[i], "-A")) { 296 #ifdef DHCPv6 297 if (local_family_set && (local_family == AF_INET6)) { 298 usage(); 299 } 300 local_family_set = 1; 301 local_family = AF_INET; 302 #endif 303 if (++i == argc) 304 usage(); 305 306 dhcp_max_agent_option_packet_length = atoi(argv[i]); 307 308 if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX) 309 log_fatal("%s: packet length exceeds " 310 "longest possible MTU\n", 311 argv[i]); 312 } else if (!strcmp(argv[i], "-m")) { 313 #ifdef DHCPv6 314 if (local_family_set && (local_family == AF_INET6)) { 315 usage(); 316 } 317 local_family_set = 1; 318 local_family = AF_INET; 319 #endif 320 if (++i == argc) 321 usage(); 322 if (!strcasecmp(argv[i], "append")) { 323 agent_relay_mode = forward_and_append; 324 } else if (!strcasecmp(argv[i], "replace")) { 325 agent_relay_mode = forward_and_replace; 326 } else if (!strcasecmp(argv[i], "forward")) { 327 agent_relay_mode = forward_untouched; 328 } else if (!strcasecmp(argv[i], "discard")) { 329 agent_relay_mode = discard; 330 } else 331 usage(); 332 } else if (!strcmp(argv[i], "-D")) { 333 #ifdef DHCPv6 334 if (local_family_set && (local_family == AF_INET6)) { 335 usage(); 336 } 337 local_family_set = 1; 338 local_family = AF_INET; 339 #endif 340 drop_agent_mismatches = 1; 341 #ifdef DHCPv6 342 } else if (!strcmp(argv[i], "-I")) { 343 if (local_family_set && (local_family == AF_INET)) { 344 usage(); 345 } 346 local_family_set = 1; 347 local_family = AF_INET6; 348 use_if_id = ISC_TRUE; 349 } else if (!strcmp(argv[i], "-l")) { 350 if (local_family_set && (local_family == AF_INET)) { 351 usage(); 352 } 353 local_family_set = 1; 354 local_family = AF_INET6; 355 if (downstreams != NULL) 356 use_if_id = ISC_TRUE; 357 if (++i == argc) 358 usage(); 359 sl = parse_downstream(argv[i]); 360 sl->next = downstreams; 361 downstreams = sl; 362 } else if (!strcmp(argv[i], "-u")) { 363 if (local_family_set && (local_family == AF_INET)) { 364 usage(); 365 } 366 local_family_set = 1; 367 local_family = AF_INET6; 368 if (++i == argc) 369 usage(); 370 sl = parse_upstream(argv[i]); 371 sl->next = upstreams; 372 upstreams = sl; 373 } else if (!strcmp(argv[i], "-s")) { 374 if (local_family_set && (local_family == AF_INET)) { 375 usage(); 376 } 377 local_family_set = 1; 378 local_family = AF_INET6; 379 if (++i == argc) 380 usage(); 381 dhcrelay_sub_id = argv[i]; 382 #endif 383 } else if (!strcmp(argv[i], "-pf")) { 384 if (++i == argc) 385 usage(); 386 path_dhcrelay_pid = argv[i]; 387 no_dhcrelay_pid = ISC_TRUE; 388 } else if (!strcmp(argv[i], "--no-pid")) { 389 no_pid_file = ISC_TRUE; 390 } else if (!strcmp(argv[i], "--version")) { 391 log_info("isc-dhcrelay-%s", PACKAGE_VERSION); 392 exit(0); 393 } else if (!strcmp(argv[i], "--help") || 394 !strcmp(argv[i], "-h")) { 395 log_info(DHCRELAY_USAGE); 396 exit(0); 397 } else if (argv[i][0] == '-') { 398 usage(); 399 } else { 400 struct hostent *he; 401 struct in_addr ia, *iap = NULL; 402 403 #ifdef DHCPv6 404 if (local_family_set && (local_family == AF_INET6)) { 405 usage(); 406 } 407 local_family_set = 1; 408 local_family = AF_INET; 409 #endif 410 if (inet_aton(argv[i], &ia)) { 411 iap = &ia; 412 } else { 413 he = gethostbyname(argv[i]); 414 if (!he) { 415 log_error("%s: host unknown", argv[i]); 416 } else { 417 iap = ((struct in_addr *) 418 he->h_addr_list[0]); 419 } 420 } 421 422 if (iap) { 423 sp = ((struct server_list *) 424 dmalloc(sizeof *sp, MDL)); 425 if (!sp) 426 log_fatal("no memory for server.\n"); 427 sp->next = servers; 428 servers = sp; 429 memcpy(&sp->to.sin_addr, iap, sizeof *iap); 430 } 431 } 432 } 433 434 /* 435 * If the user didn't specify a pid file directly 436 * find one from environment variables or defaults 437 */ 438 if (no_dhcrelay_pid == ISC_FALSE) { 439 if (local_family == AF_INET) { 440 path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID"); 441 if (path_dhcrelay_pid == NULL) 442 path_dhcrelay_pid = _PATH_DHCRELAY_PID; 443 } 444 #ifdef DHCPv6 445 else { 446 path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID"); 447 if (path_dhcrelay_pid == NULL) 448 path_dhcrelay_pid = _PATH_DHCRELAY6_PID; 449 } 450 #endif 451 } 452 453 if (!quiet) { 454 log_info("%s %s", message, PACKAGE_VERSION); 455 log_info(copyright); 456 log_info(arr); 457 log_info(url); 458 } else 459 log_perror = 0; 460 461 /* Set default port */ 462 if (local_family == AF_INET) { 463 service_local = "bootps"; 464 service_remote = "bootpc"; 465 port_local = htons(67); 466 port_remote = htons(68); 467 } 468 #ifdef DHCPv6 469 else { 470 service_local = "dhcpv6-server"; 471 service_remote = "dhcpv6-client"; 472 port_local = htons(547); 473 port_remote = htons(546); 474 } 475 #endif 476 477 if (!local_port) { 478 ent = getservbyname(service_local, "udp"); 479 if (ent) 480 local_port = ent->s_port; 481 else 482 local_port = port_local; 483 484 ent = getservbyname(service_remote, "udp"); 485 if (ent) 486 remote_port = ent->s_port; 487 else 488 remote_port = port_remote; 489 490 endservent(); 491 } 492 493 if (local_family == AF_INET) { 494 /* We need at least one server */ 495 if (servers == NULL) { 496 log_fatal("No servers specified."); 497 } 498 499 500 /* Set up the server sockaddrs. */ 501 for (sp = servers; sp; sp = sp->next) { 502 sp->to.sin_port = local_port; 503 sp->to.sin_family = AF_INET; 504 #ifdef HAVE_SA_LEN 505 sp->to.sin_len = sizeof sp->to; 506 #endif 507 } 508 } 509 #ifdef DHCPv6 510 else { 511 unsigned code; 512 513 /* We need at least one upstream and one downstream interface */ 514 if (upstreams == NULL || downstreams == NULL) { 515 log_info("Must specify at least one lower " 516 "and one upper interface.\n"); 517 usage(); 518 } 519 520 /* Set up the initial dhcp option universe. */ 521 initialize_common_option_spaces(); 522 523 /* Check requested options. */ 524 code = D6O_RELAY_MSG; 525 if (!option_code_hash_lookup(&requested_opts[0], 526 dhcpv6_universe.code_hash, 527 &code, 0, MDL)) 528 log_fatal("Unable to find the RELAY_MSG " 529 "option definition."); 530 code = D6O_INTERFACE_ID; 531 if (!option_code_hash_lookup(&requested_opts[1], 532 dhcpv6_universe.code_hash, 533 &code, 0, MDL)) 534 log_fatal("Unable to find the INTERFACE_ID " 535 "option definition."); 536 } 537 #endif 538 539 /* Get the current time... */ 540 gettimeofday(&cur_tv, NULL); 541 542 /* Discover all the network interfaces. */ 543 discover_interfaces(DISCOVER_RELAY); 544 545 #ifdef DHCPv6 546 if (local_family == AF_INET6) 547 setup_streams(); 548 #endif 549 550 /* Become a daemon... */ 551 if (!no_daemon) { 552 int pid; 553 FILE *pf; 554 int pfdesc; 555 556 log_perror = 0; 557 558 if ((pid = fork()) < 0) 559 log_fatal("Can't fork daemon: %m"); 560 else if (pid) 561 exit(0); 562 563 if (no_pid_file == ISC_FALSE) { 564 pfdesc = open(path_dhcrelay_pid, 565 O_CREAT | O_TRUNC | O_WRONLY, 0644); 566 567 if (pfdesc < 0) { 568 log_error("Can't create %s: %m", 569 path_dhcrelay_pid); 570 } else { 571 pf = fdopen(pfdesc, "w"); 572 if (!pf) 573 log_error("Can't fdopen %s: %m", 574 path_dhcrelay_pid); 575 else { 576 fprintf(pf, "%ld\n",(long)getpid()); 577 fclose(pf); 578 } 579 } 580 } 581 582 (void) close(0); 583 (void) close(1); 584 (void) close(2); 585 (void) setsid(); 586 587 IGNORE_RET (chdir("/")); 588 } 589 590 /* Set up the packet handler... */ 591 if (local_family == AF_INET) 592 bootp_packet_handler = do_relay4; 593 #ifdef DHCPv6 594 else 595 dhcpv6_packet_handler = do_packet6; 596 #endif 597 598 /* install signal handlers */ 599 signal(SIGINT, dhcp_signal_handler); /* control-c */ 600 signal(SIGTERM, dhcp_signal_handler); /* kill */ 601 602 /* Start dispatching packets and timeouts... */ 603 dispatch(); 604 605 /* In fact dispatch() never returns. */ 606 return (0); 607 } 608 609 static void 610 do_relay4(struct interface_info *ip, struct dhcp_packet *packet, 611 unsigned int length, unsigned int from_port, struct iaddr from, 612 struct hardware *hfrom) { 613 struct server_list *sp; 614 struct sockaddr_in to; 615 struct interface_info *out; 616 struct hardware hto, *htop; 617 618 if (packet->hlen > sizeof packet->chaddr) { 619 log_info("Discarding packet with invalid hlen, received on " 620 "%s interface.", ip->name); 621 return; 622 } 623 if (ip->address_count < 1 || ip->addresses == NULL) { 624 log_info("Discarding packet received on %s interface that " 625 "has no IPv4 address assigned.", ip->name); 626 return; 627 } 628 629 /* Find the interface that corresponds to the giaddr 630 in the packet. */ 631 if (packet->giaddr.s_addr) { 632 for (out = interfaces; out; out = out->next) { 633 int i; 634 635 for (i = 0 ; i < out->address_count ; i++ ) { 636 if (out->addresses[i].s_addr == 637 packet->giaddr.s_addr) { 638 i = -1; 639 break; 640 } 641 } 642 643 if (i == -1) 644 break; 645 } 646 } else { 647 out = NULL; 648 } 649 650 /* If it's a bootreply, forward it to the client. */ 651 if (packet->op == BOOTREPLY) { 652 if (!(packet->flags & htons(BOOTP_BROADCAST)) && 653 can_unicast_without_arp(out)) { 654 to.sin_addr = packet->yiaddr; 655 to.sin_port = remote_port; 656 657 /* and hardware address is not broadcast */ 658 htop = &hto; 659 } else { 660 to.sin_addr.s_addr = htonl(INADDR_BROADCAST); 661 to.sin_port = remote_port; 662 663 /* hardware address is broadcast */ 664 htop = NULL; 665 } 666 to.sin_family = AF_INET; 667 #ifdef HAVE_SA_LEN 668 to.sin_len = sizeof to; 669 #endif 670 671 memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen); 672 hto.hbuf[0] = packet->htype; 673 hto.hlen = packet->hlen + 1; 674 675 /* Wipe out the agent relay options and, if possible, figure 676 out which interface to use based on the contents of the 677 option that we put on the request to which the server is 678 replying. */ 679 if (!(length = 680 strip_relay_agent_options(ip, &out, packet, length))) 681 return; 682 683 if (!out) { 684 log_error("Packet to bogus giaddr %s.\n", 685 inet_ntoa(packet->giaddr)); 686 ++bogus_giaddr_drops; 687 return; 688 } 689 690 if (send_packet(out, NULL, packet, length, out->addresses[0], 691 &to, htop) < 0) { 692 ++server_packet_errors; 693 } else { 694 log_debug("Forwarded BOOTREPLY for %s to %s", 695 print_hw_addr(packet->htype, packet->hlen, 696 packet->chaddr), 697 inet_ntoa(to.sin_addr)); 698 699 ++server_packets_relayed; 700 } 701 return; 702 } 703 704 /* If giaddr matches one of our addresses, ignore the packet - 705 we just sent it. */ 706 if (out) 707 return; 708 709 /* Add relay agent options if indicated. If something goes wrong, 710 drop the packet. */ 711 if (!(length = add_relay_agent_options(ip, packet, length, 712 ip->addresses[0]))) 713 return; 714 715 /* If giaddr is not already set, Set it so the server can 716 figure out what net it's from and so that we can later 717 forward the response to the correct net. If it's already 718 set, the response will be sent directly to the relay agent 719 that set giaddr, so we won't see it. */ 720 if (!packet->giaddr.s_addr) 721 packet->giaddr = ip->addresses[0]; 722 if (packet->hops < max_hop_count) 723 packet->hops = packet->hops + 1; 724 else 725 return; 726 727 /* Otherwise, it's a BOOTREQUEST, so forward it to all the 728 servers. */ 729 for (sp = servers; sp; sp = sp->next) { 730 if (send_packet((fallback_interface 731 ? fallback_interface : interfaces), 732 NULL, packet, length, ip->addresses[0], 733 &sp->to, NULL) < 0) { 734 ++client_packet_errors; 735 } else { 736 log_debug("Forwarded BOOTREQUEST for %s to %s", 737 print_hw_addr(packet->htype, packet->hlen, 738 packet->chaddr), 739 inet_ntoa(sp->to.sin_addr)); 740 ++client_packets_relayed; 741 } 742 } 743 744 } 745 746 /* Strip any Relay Agent Information options from the DHCP packet 747 option buffer. If there is a circuit ID suboption, look up the 748 outgoing interface based upon it. */ 749 750 static int 751 strip_relay_agent_options(struct interface_info *in, 752 struct interface_info **out, 753 struct dhcp_packet *packet, 754 unsigned length) { 755 int is_dhcp = 0; 756 u_int8_t *op, *nextop, *sp, *max; 757 int good_agent_option = 0; 758 int status; 759 760 /* If we're not adding agent options to packets, we're not taking 761 them out either. */ 762 if (!add_agent_options) 763 return (length); 764 765 /* If there's no cookie, it's a bootp packet, so we should just 766 forward it unchanged. */ 767 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4)) 768 return (length); 769 770 max = ((u_int8_t *)packet) + length; 771 sp = op = &packet->options[4]; 772 773 while (op < max) { 774 switch(*op) { 775 /* Skip padding... */ 776 case DHO_PAD: 777 if (sp != op) 778 *sp = *op; 779 ++op; 780 ++sp; 781 continue; 782 783 /* If we see a message type, it's a DHCP packet. */ 784 case DHO_DHCP_MESSAGE_TYPE: 785 is_dhcp = 1; 786 goto skip; 787 break; 788 789 /* Quit immediately if we hit an End option. */ 790 case DHO_END: 791 if (sp != op) 792 *sp++ = *op++; 793 goto out; 794 795 case DHO_DHCP_AGENT_OPTIONS: 796 /* We shouldn't see a relay agent option in a 797 packet before we've seen the DHCP packet type, 798 but if we do, we have to leave it alone. */ 799 if (!is_dhcp) 800 goto skip; 801 802 /* Do not process an agent option if it exceeds the 803 * buffer. Fail this packet. 804 */ 805 nextop = op + op[1] + 2; 806 if (nextop > max) 807 return (0); 808 809 status = find_interface_by_agent_option(packet, 810 out, op + 2, 811 op[1]); 812 if (status == -1 && drop_agent_mismatches) 813 return (0); 814 if (status) 815 good_agent_option = 1; 816 op = nextop; 817 break; 818 819 skip: 820 /* Skip over other options. */ 821 default: 822 /* Fail if processing this option will exceed the 823 * buffer(op[1] is malformed). 824 */ 825 nextop = op + op[1] + 2; 826 if (nextop > max) 827 return (0); 828 829 if (sp != op) { 830 memmove(sp, op, op[1] + 2); 831 sp += op[1] + 2; 832 op = nextop; 833 } else 834 op = sp = nextop; 835 836 break; 837 } 838 } 839 out: 840 841 /* If it's not a DHCP packet, we're not supposed to touch it. */ 842 if (!is_dhcp) 843 return (length); 844 845 /* If none of the agent options we found matched, or if we didn't 846 find any agent options, count this packet as not having any 847 matching agent options, and if we're relying on agent options 848 to determine the outgoing interface, drop the packet. */ 849 850 if (!good_agent_option) { 851 ++missing_agent_option; 852 if (drop_agent_mismatches) 853 return (0); 854 } 855 856 /* Adjust the length... */ 857 if (sp != op) { 858 length = sp -((u_int8_t *)packet); 859 860 /* Make sure the packet isn't short(this is unlikely, 861 but WTH) */ 862 if (length < BOOTP_MIN_LEN) { 863 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length); 864 length = BOOTP_MIN_LEN; 865 } 866 } 867 return (length); 868 } 869 870 871 /* Find an interface that matches the circuit ID specified in the 872 Relay Agent Information option. If one is found, store it through 873 the pointer given; otherwise, leave the existing pointer alone. 874 875 We actually deviate somewhat from the current specification here: 876 if the option buffer is corrupt, we suggest that the caller not 877 respond to this packet. If the circuit ID doesn't match any known 878 interface, we suggest that the caller to drop the packet. Only if 879 we find a circuit ID that matches an existing interface do we tell 880 the caller to go ahead and process the packet. */ 881 882 static int 883 find_interface_by_agent_option(struct dhcp_packet *packet, 884 struct interface_info **out, 885 u_int8_t *buf, int len) { 886 int i = 0; 887 u_int8_t *circuit_id = 0; 888 unsigned circuit_id_len = 0; 889 struct interface_info *ip; 890 891 while (i < len) { 892 /* If the next agent option overflows the end of the 893 packet, the agent option buffer is corrupt. */ 894 if (i + 1 == len || 895 i + buf[i + 1] + 2 > len) { 896 ++corrupt_agent_options; 897 return (-1); 898 } 899 switch(buf[i]) { 900 /* Remember where the circuit ID is... */ 901 case RAI_CIRCUIT_ID: 902 circuit_id = &buf[i + 2]; 903 circuit_id_len = buf[i + 1]; 904 i += circuit_id_len + 2; 905 continue; 906 907 default: 908 i += buf[i + 1] + 2; 909 break; 910 } 911 } 912 913 /* If there's no circuit ID, it's not really ours, tell the caller 914 it's no good. */ 915 if (!circuit_id) { 916 ++missing_circuit_id; 917 return (-1); 918 } 919 920 /* Scan the interface list looking for an interface whose 921 name matches the one specified in circuit_id. */ 922 923 for (ip = interfaces; ip; ip = ip->next) { 924 if (ip->circuit_id && 925 ip->circuit_id_len == circuit_id_len && 926 !memcmp(ip->circuit_id, circuit_id, circuit_id_len)) 927 break; 928 } 929 930 /* If we got a match, use it. */ 931 if (ip) { 932 *out = ip; 933 return (1); 934 } 935 936 /* If we didn't get a match, the circuit ID was bogus. */ 937 ++bad_circuit_id; 938 return (-1); 939 } 940 941 /* 942 * Examine a packet to see if it's a candidate to have a Relay 943 * Agent Information option tacked onto its tail. If it is, tack 944 * the option on. 945 */ 946 947 #include <sys/cdefs.h> 948 __RCSID("$NetBSD: dhcrelay.c,v 1.5 2014/07/12 12:09:37 spz Exp $"); 949 static int 950 add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet, 951 unsigned length, struct in_addr giaddr) { 952 int is_dhcp = 0, mms; 953 unsigned optlen; 954 u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL; 955 956 /* If we're not adding agent options to packets, we can skip 957 this. */ 958 if (!add_agent_options) 959 return (length); 960 961 /* If there's no cookie, it's a bootp packet, so we should just 962 forward it unchanged. */ 963 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4)) 964 return (length); 965 966 max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length; 967 968 /* Commence processing after the cookie. */ 969 sp = op = &packet->options[4]; 970 971 while (op < max) { 972 switch(*op) { 973 /* Skip padding... */ 974 case DHO_PAD: 975 /* Remember the first pad byte so we can commandeer 976 * padded space. 977 * 978 * XXX: Is this really a good idea? Sure, we can 979 * seemingly reduce the packet while we're looking, 980 * but if the packet was signed by the client then 981 * this padding is part of the checksum(RFC3118), 982 * and its nonpresence would break authentication. 983 */ 984 if (end_pad == NULL) 985 end_pad = sp; 986 987 if (sp != op) 988 *sp++ = *op++; 989 else 990 sp = ++op; 991 992 continue; 993 994 /* If we see a message type, it's a DHCP packet. */ 995 case DHO_DHCP_MESSAGE_TYPE: 996 is_dhcp = 1; 997 goto skip; 998 999 /* 1000 * If there's a maximum message size option, we 1001 * should pay attention to it 1002 */ 1003 case DHO_DHCP_MAX_MESSAGE_SIZE: 1004 mms = ntohs(*(op + 2)); 1005 if (mms < dhcp_max_agent_option_packet_length && 1006 mms >= DHCP_MTU_MIN) 1007 max = ((u_int8_t *)packet) + mms; 1008 goto skip; 1009 1010 /* Quit immediately if we hit an End option. */ 1011 case DHO_END: 1012 goto out; 1013 1014 case DHO_DHCP_AGENT_OPTIONS: 1015 /* We shouldn't see a relay agent option in a 1016 packet before we've seen the DHCP packet type, 1017 but if we do, we have to leave it alone. */ 1018 if (!is_dhcp) 1019 goto skip; 1020 1021 end_pad = NULL; 1022 1023 /* There's already a Relay Agent Information option 1024 in this packet. How embarrassing. Decide what 1025 to do based on the mode the user specified. */ 1026 1027 switch(agent_relay_mode) { 1028 case forward_and_append: 1029 goto skip; 1030 case forward_untouched: 1031 return (length); 1032 case discard: 1033 return (0); 1034 case forward_and_replace: 1035 default: 1036 break; 1037 } 1038 1039 /* Skip over the agent option and start copying 1040 if we aren't copying already. */ 1041 op += op[1] + 2; 1042 break; 1043 1044 skip: 1045 /* Skip over other options. */ 1046 default: 1047 /* Fail if processing this option will exceed the 1048 * buffer(op[1] is malformed). 1049 */ 1050 nextop = op + op[1] + 2; 1051 if (nextop > max) 1052 return (0); 1053 1054 end_pad = NULL; 1055 1056 if (sp != op) { 1057 memmove(sp, op, op[1] + 2); 1058 sp += op[1] + 2; 1059 op = nextop; 1060 } else 1061 op = sp = nextop; 1062 1063 break; 1064 } 1065 } 1066 out: 1067 1068 /* If it's not a DHCP packet, we're not supposed to touch it. */ 1069 if (!is_dhcp) 1070 return (length); 1071 1072 /* If the packet was padded out, we can store the agent option 1073 at the beginning of the padding. */ 1074 1075 if (end_pad != NULL) 1076 sp = end_pad; 1077 1078 #if 0 1079 /* Remember where the end of the packet was after parsing 1080 it. */ 1081 op = sp; 1082 #endif 1083 1084 /* Sanity check. Had better not ever happen. */ 1085 if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1)) 1086 log_fatal("Circuit ID length %d out of range [1-255] on " 1087 "%s\n", ip->circuit_id_len, ip->name); 1088 optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */ 1089 1090 if (ip->remote_id) { 1091 if (ip->remote_id_len > 255 || ip->remote_id_len < 1) 1092 log_fatal("Remote ID length %d out of range [1-255] " 1093 "on %s\n", ip->circuit_id_len, ip->name); 1094 optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */ 1095 } 1096 1097 /* We do not support relay option fragmenting(multiple options to 1098 * support an option data exceeding 255 bytes). 1099 */ 1100 if ((optlen < 3) ||(optlen > 255)) 1101 log_fatal("Total agent option length(%u) out of range " 1102 "[3 - 255] on %s\n", optlen, ip->name); 1103 1104 /* 1105 * Is there room for the option, its code+len, and DHO_END? 1106 * If not, forward without adding the option. 1107 */ 1108 if (max - sp >= optlen + 3) { 1109 log_debug("Adding %d-byte relay agent option", optlen + 3); 1110 1111 /* Okay, cons up *our* Relay Agent Information option. */ 1112 *sp++ = DHO_DHCP_AGENT_OPTIONS; 1113 *sp++ = optlen; 1114 1115 /* Copy in the circuit id... */ 1116 *sp++ = RAI_CIRCUIT_ID; 1117 *sp++ = ip->circuit_id_len; 1118 memcpy(sp, ip->circuit_id, ip->circuit_id_len); 1119 sp += ip->circuit_id_len; 1120 1121 /* Copy in remote ID... */ 1122 if (ip->remote_id) { 1123 *sp++ = RAI_REMOTE_ID; 1124 *sp++ = ip->remote_id_len; 1125 memcpy(sp, ip->remote_id, ip->remote_id_len); 1126 sp += ip->remote_id_len; 1127 } 1128 } else { 1129 ++agent_option_errors; 1130 log_error("No room in packet (used %d of %d) " 1131 "for %d-byte relay agent option: omitted", 1132 (int) (sp - ((u_int8_t *) packet)), 1133 (int) (max - ((u_int8_t *) packet)), 1134 optlen + 3); 1135 } 1136 1137 /* 1138 * Deposit an END option unless the packet is full (shouldn't 1139 * be possible). 1140 */ 1141 if (sp < max) 1142 *sp++ = DHO_END; 1143 1144 /* Recalculate total packet length. */ 1145 length = sp -((u_int8_t *)packet); 1146 1147 /* Make sure the packet isn't short(this is unlikely, but WTH) */ 1148 if (length < BOOTP_MIN_LEN) { 1149 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length); 1150 return (BOOTP_MIN_LEN); 1151 } 1152 1153 return (length); 1154 } 1155 1156 #ifdef DHCPv6 1157 /* 1158 * Parse a downstream argument: [address%]interface[#index]. 1159 */ 1160 static struct stream_list * 1161 parse_downstream(char *arg) { 1162 struct stream_list *dp, *up; 1163 struct interface_info *ifp = NULL; 1164 char *ifname, *addr, *iid; 1165 isc_result_t status; 1166 1167 if (!supports_multiple_interfaces(ifp) && 1168 (downstreams != NULL)) 1169 log_fatal("No support for multiple interfaces."); 1170 1171 /* Decode the argument. */ 1172 ifname = strchr(arg, '%'); 1173 if (ifname == NULL) { 1174 ifname = arg; 1175 addr = NULL; 1176 } else { 1177 *ifname++ = '\0'; 1178 addr = arg; 1179 } 1180 iid = strchr(ifname, '#'); 1181 if (iid != NULL) { 1182 *iid++ = '\0'; 1183 } 1184 if (strlen(ifname) >= sizeof(ifp->name)) { 1185 log_error("Interface name '%s' too long", ifname); 1186 usage(); 1187 } 1188 1189 /* Don't declare twice. */ 1190 for (dp = downstreams; dp; dp = dp->next) { 1191 if (strcmp(ifname, dp->ifp->name) == 0) 1192 log_fatal("Down interface '%s' declared twice.", 1193 ifname); 1194 } 1195 1196 /* Share with up side? */ 1197 for (up = upstreams; up; up = up->next) { 1198 if (strcmp(ifname, up->ifp->name) == 0) { 1199 log_info("Interface '%s' is both down and up.", 1200 ifname); 1201 ifp = up->ifp; 1202 break; 1203 } 1204 } 1205 1206 /* New interface. */ 1207 if (ifp == NULL) { 1208 status = interface_allocate(&ifp, MDL); 1209 if (status != ISC_R_SUCCESS) 1210 log_fatal("%s: interface_allocate: %s", 1211 arg, isc_result_totext(status)); 1212 strcpy(ifp->name, ifname); 1213 if (interfaces) { 1214 interface_reference(&ifp->next, interfaces, MDL); 1215 interface_dereference(&interfaces, MDL); 1216 } 1217 interface_reference(&interfaces, ifp, MDL); 1218 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM; 1219 } 1220 1221 /* New downstream. */ 1222 dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL); 1223 if (!dp) 1224 log_fatal("No memory for downstream."); 1225 dp->ifp = ifp; 1226 if (iid != NULL) { 1227 dp->id = atoi(iid); 1228 } else { 1229 dp->id = -1; 1230 } 1231 /* !addr case handled by setup. */ 1232 if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0)) 1233 log_fatal("Bad link address '%s'", addr); 1234 1235 return dp; 1236 } 1237 1238 /* 1239 * Parse an upstream argument: [address]%interface. 1240 */ 1241 static struct stream_list * 1242 parse_upstream(char *arg) { 1243 struct stream_list *up, *dp; 1244 struct interface_info *ifp = NULL; 1245 char *ifname, *addr; 1246 isc_result_t status; 1247 1248 /* Decode the argument. */ 1249 ifname = strchr(arg, '%'); 1250 if (ifname == NULL) { 1251 ifname = arg; 1252 addr = All_DHCP_Servers; 1253 } else { 1254 *ifname++ = '\0'; 1255 addr = arg; 1256 } 1257 if (strlen(ifname) >= sizeof(ifp->name)) { 1258 log_fatal("Interface name '%s' too long", ifname); 1259 } 1260 1261 /* Shared up interface? */ 1262 for (up = upstreams; up; up = up->next) { 1263 if (strcmp(ifname, up->ifp->name) == 0) { 1264 ifp = up->ifp; 1265 break; 1266 } 1267 } 1268 for (dp = downstreams; dp; dp = dp->next) { 1269 if (strcmp(ifname, dp->ifp->name) == 0) { 1270 ifp = dp->ifp; 1271 break; 1272 } 1273 } 1274 1275 /* New interface. */ 1276 if (ifp == NULL) { 1277 status = interface_allocate(&ifp, MDL); 1278 if (status != ISC_R_SUCCESS) 1279 log_fatal("%s: interface_allocate: %s", 1280 arg, isc_result_totext(status)); 1281 strcpy(ifp->name, ifname); 1282 if (interfaces) { 1283 interface_reference(&ifp->next, interfaces, MDL); 1284 interface_dereference(&interfaces, MDL); 1285 } 1286 interface_reference(&interfaces, ifp, MDL); 1287 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM; 1288 } 1289 1290 /* New upstream. */ 1291 up = (struct stream_list *) dmalloc(sizeof(*up), MDL); 1292 if (up == NULL) 1293 log_fatal("No memory for upstream."); 1294 1295 up->ifp = ifp; 1296 1297 if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0) 1298 log_fatal("Bad address %s", addr); 1299 1300 return up; 1301 } 1302 1303 /* 1304 * Setup downstream interfaces. 1305 */ 1306 static void 1307 setup_streams(void) { 1308 struct stream_list *dp, *up; 1309 int i; 1310 isc_boolean_t link_is_set; 1311 1312 for (dp = downstreams; dp; dp = dp->next) { 1313 /* Check interface */ 1314 if (dp->ifp->v6address_count == 0) 1315 log_fatal("Interface '%s' has no IPv6 addresses.", 1316 dp->ifp->name); 1317 1318 /* Check/set link. */ 1319 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr)) 1320 link_is_set = ISC_FALSE; 1321 else 1322 link_is_set = ISC_TRUE; 1323 for (i = 0; i < dp->ifp->v6address_count; i++) { 1324 if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i])) 1325 continue; 1326 if (!link_is_set) 1327 break; 1328 if (!memcmp(&dp->ifp->v6addresses[i], 1329 &dp->link.sin6_addr, 1330 sizeof(dp->link.sin6_addr))) 1331 break; 1332 } 1333 if (i == dp->ifp->v6address_count) 1334 log_fatal("Interface %s does not have global IPv6 " 1335 "address assigned.", dp->ifp->name); 1336 if (!link_is_set) 1337 memcpy(&dp->link.sin6_addr, 1338 &dp->ifp->v6addresses[i], 1339 sizeof(dp->link.sin6_addr)); 1340 1341 /* Set interface-id. */ 1342 if (dp->id == -1) 1343 dp->id = dp->ifp->index; 1344 } 1345 1346 for (up = upstreams; up; up = up->next) { 1347 up->link.sin6_port = local_port; 1348 up->link.sin6_family = AF_INET6; 1349 #ifdef HAVE_SA_LEN 1350 up->link.sin6_len = sizeof(up->link); 1351 #endif 1352 1353 if (up->ifp->v6address_count == 0) 1354 log_fatal("Interface '%s' has no IPv6 addresses.", 1355 up->ifp->name); 1356 } 1357 } 1358 1359 /* 1360 * Add DHCPv6 agent options here. 1361 */ 1362 static const int required_forw_opts[] = { 1363 D6O_INTERFACE_ID, 1364 D6O_SUBSCRIBER_ID, 1365 D6O_RELAY_MSG, 1366 0 1367 }; 1368 1369 /* 1370 * Process a packet upwards, i.e., from client to server. 1371 */ 1372 static void 1373 process_up6(struct packet *packet, struct stream_list *dp) { 1374 char forw_data[65535]; 1375 unsigned cursor; 1376 struct dhcpv6_relay_packet *relay; 1377 struct option_state *opts; 1378 struct stream_list *up; 1379 1380 /* Check if the message should be relayed to the server. */ 1381 switch (packet->dhcpv6_msg_type) { 1382 case DHCPV6_SOLICIT: 1383 case DHCPV6_REQUEST: 1384 case DHCPV6_CONFIRM: 1385 case DHCPV6_RENEW: 1386 case DHCPV6_REBIND: 1387 case DHCPV6_RELEASE: 1388 case DHCPV6_DECLINE: 1389 case DHCPV6_INFORMATION_REQUEST: 1390 case DHCPV6_RELAY_FORW: 1391 case DHCPV6_LEASEQUERY: 1392 log_info("Relaying %s from %s port %d going up.", 1393 dhcpv6_type_names[packet->dhcpv6_msg_type], 1394 piaddr(packet->client_addr), 1395 ntohs(packet->client_port)); 1396 break; 1397 1398 case DHCPV6_ADVERTISE: 1399 case DHCPV6_REPLY: 1400 case DHCPV6_RECONFIGURE: 1401 case DHCPV6_RELAY_REPL: 1402 case DHCPV6_LEASEQUERY_REPLY: 1403 log_info("Discarding %s from %s port %d going up.", 1404 dhcpv6_type_names[packet->dhcpv6_msg_type], 1405 piaddr(packet->client_addr), 1406 ntohs(packet->client_port)); 1407 return; 1408 1409 default: 1410 log_info("Unknown %d type from %s port %d going up.", 1411 packet->dhcpv6_msg_type, 1412 piaddr(packet->client_addr), 1413 ntohs(packet->client_port)); 1414 return; 1415 } 1416 1417 /* Build the relay-forward header. */ 1418 relay = (struct dhcpv6_relay_packet *) forw_data; 1419 cursor = offsetof(struct dhcpv6_relay_packet, options); 1420 relay->msg_type = DHCPV6_RELAY_FORW; 1421 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) { 1422 if (packet->dhcpv6_hop_count >= max_hop_count) { 1423 log_info("Hop count exceeded,"); 1424 return; 1425 } 1426 relay->hop_count = packet->dhcpv6_hop_count + 1; 1427 if (dp) { 1428 memcpy(&relay->link_address, &dp->link.sin6_addr, 16); 1429 } else { 1430 /* On smart relay add: && !global. */ 1431 if (!use_if_id && downstreams->next) { 1432 log_info("Shan't get back the interface."); 1433 return; 1434 } 1435 memset(&relay->link_address, 0, 16); 1436 } 1437 } else { 1438 relay->hop_count = 0; 1439 if (!dp) 1440 return; 1441 memcpy(&relay->link_address, &dp->link.sin6_addr, 16); 1442 } 1443 memcpy(&relay->peer_address, packet->client_addr.iabuf, 16); 1444 1445 /* Get an option state. */ 1446 opts = NULL; 1447 if (!option_state_allocate(&opts, MDL)) { 1448 log_fatal("No memory for upwards options."); 1449 } 1450 1451 /* Add an interface-id (if used). */ 1452 if (use_if_id) { 1453 int if_id; 1454 1455 if (dp) { 1456 if_id = dp->id; 1457 } else if (!downstreams->next) { 1458 if_id = downstreams->id; 1459 } else { 1460 log_info("Don't know the interface."); 1461 option_state_dereference(&opts, MDL); 1462 return; 1463 } 1464 1465 if (!save_option_buffer(&dhcpv6_universe, opts, 1466 NULL, (unsigned char *) &if_id, 1467 sizeof(int), 1468 D6O_INTERFACE_ID, 0)) { 1469 log_error("Can't save interface-id."); 1470 option_state_dereference(&opts, MDL); 1471 return; 1472 } 1473 } 1474 1475 /* Add a subscriber-id if desired. */ 1476 /* This is for testing rather than general use */ 1477 if (dhcrelay_sub_id != NULL) { 1478 if (!save_option_buffer(&dhcpv6_universe, opts, NULL, 1479 (unsigned char *) dhcrelay_sub_id, 1480 strlen(dhcrelay_sub_id), 1481 D6O_SUBSCRIBER_ID, 0)) { 1482 log_error("Can't save subsriber-id."); 1483 option_state_dereference(&opts, MDL); 1484 return; 1485 } 1486 } 1487 1488 1489 /* Add the relay-msg carrying the packet. */ 1490 if (!save_option_buffer(&dhcpv6_universe, opts, 1491 NULL, (unsigned char *) packet->raw, 1492 packet->packet_length, 1493 D6O_RELAY_MSG, 0)) { 1494 log_error("Can't save relay-msg."); 1495 option_state_dereference(&opts, MDL); 1496 return; 1497 } 1498 1499 /* Finish the relay-forward message. */ 1500 cursor += store_options6(forw_data + cursor, 1501 sizeof(forw_data) - cursor, 1502 opts, packet, 1503 required_forw_opts, NULL); 1504 option_state_dereference(&opts, MDL); 1505 1506 /* Send it to all upstreams. */ 1507 for (up = upstreams; up; up = up->next) { 1508 send_packet6(up->ifp, (unsigned char *) forw_data, 1509 (size_t) cursor, &up->link); 1510 } 1511 } 1512 1513 /* 1514 * Process a packet downwards, i.e., from server to client. 1515 */ 1516 static void 1517 process_down6(struct packet *packet) { 1518 struct stream_list *dp; 1519 struct option_cache *oc; 1520 struct data_string relay_msg; 1521 const struct dhcpv6_packet *msg; 1522 struct data_string if_id; 1523 struct sockaddr_in6 to; 1524 struct iaddr peer; 1525 1526 /* The packet must be a relay-reply message. */ 1527 if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) { 1528 if (packet->dhcpv6_msg_type < dhcpv6_type_name_max) 1529 log_info("Discarding %s from %s port %d going down.", 1530 dhcpv6_type_names[packet->dhcpv6_msg_type], 1531 piaddr(packet->client_addr), 1532 ntohs(packet->client_port)); 1533 else 1534 log_info("Unknown %d type from %s port %d going down.", 1535 packet->dhcpv6_msg_type, 1536 piaddr(packet->client_addr), 1537 ntohs(packet->client_port)); 1538 return; 1539 } 1540 1541 /* Inits. */ 1542 memset(&relay_msg, 0, sizeof(relay_msg)); 1543 memset(&if_id, 0, sizeof(if_id)); 1544 memset(&to, 0, sizeof(to)); 1545 to.sin6_family = AF_INET6; 1546 #ifdef HAVE_SA_LEN 1547 to.sin6_len = sizeof(to); 1548 #endif 1549 to.sin6_port = remote_port; 1550 peer.len = 16; 1551 1552 /* Get the relay-msg option (carrying the message to relay). */ 1553 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG); 1554 if (oc == NULL) { 1555 log_info("No relay-msg."); 1556 return; 1557 } 1558 if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL, 1559 packet->options, NULL, 1560 &global_scope, oc, MDL) || 1561 (relay_msg.len < offsetof(struct dhcpv6_packet, options))) { 1562 log_error("Can't evaluate relay-msg."); 1563 return; 1564 } 1565 msg = (const struct dhcpv6_packet *) relay_msg.data; 1566 1567 /* Get the interface-id (if exists) and the downstream. */ 1568 oc = lookup_option(&dhcpv6_universe, packet->options, 1569 D6O_INTERFACE_ID); 1570 if (oc != NULL) { 1571 int if_index; 1572 1573 if (!evaluate_option_cache(&if_id, packet, NULL, NULL, 1574 packet->options, NULL, 1575 &global_scope, oc, MDL) || 1576 (if_id.len != sizeof(int))) { 1577 log_info("Can't evaluate interface-id."); 1578 goto cleanup; 1579 } 1580 memcpy(&if_index, if_id.data, sizeof(int)); 1581 for (dp = downstreams; dp; dp = dp->next) { 1582 if (dp->id == if_index) 1583 break; 1584 } 1585 } else { 1586 if (use_if_id) { 1587 /* Require an interface-id. */ 1588 log_info("No interface-id."); 1589 goto cleanup; 1590 } 1591 for (dp = downstreams; dp; dp = dp->next) { 1592 /* Get the first matching one. */ 1593 if (!memcmp(&dp->link.sin6_addr, 1594 &packet->dhcpv6_link_address, 1595 sizeof(struct in6_addr))) 1596 break; 1597 } 1598 } 1599 /* Why bother when there is no choice. */ 1600 if (!dp && downstreams && !downstreams->next) 1601 dp = downstreams; 1602 if (!dp) { 1603 log_info("Can't find the down interface."); 1604 goto cleanup; 1605 } 1606 memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len); 1607 to.sin6_addr = packet->dhcpv6_peer_address; 1608 1609 /* Check if we should relay the carried message. */ 1610 switch (msg->msg_type) { 1611 /* Relay-Reply of for another relay, not a client. */ 1612 case DHCPV6_RELAY_REPL: 1613 to.sin6_port = local_port; 1614 /* Fall into: */ 1615 1616 case DHCPV6_ADVERTISE: 1617 case DHCPV6_REPLY: 1618 case DHCPV6_RECONFIGURE: 1619 case DHCPV6_RELAY_FORW: 1620 case DHCPV6_LEASEQUERY_REPLY: 1621 log_info("Relaying %s to %s port %d down.", 1622 dhcpv6_type_names[msg->msg_type], 1623 piaddr(peer), 1624 ntohs(to.sin6_port)); 1625 break; 1626 1627 case DHCPV6_SOLICIT: 1628 case DHCPV6_REQUEST: 1629 case DHCPV6_CONFIRM: 1630 case DHCPV6_RENEW: 1631 case DHCPV6_REBIND: 1632 case DHCPV6_RELEASE: 1633 case DHCPV6_DECLINE: 1634 case DHCPV6_INFORMATION_REQUEST: 1635 case DHCPV6_LEASEQUERY: 1636 log_info("Discarding %s to %s port %d down.", 1637 dhcpv6_type_names[msg->msg_type], 1638 piaddr(peer), 1639 ntohs(to.sin6_port)); 1640 goto cleanup; 1641 1642 default: 1643 log_info("Unknown %d type to %s port %d down.", 1644 msg->msg_type, 1645 piaddr(peer), 1646 ntohs(to.sin6_port)); 1647 goto cleanup; 1648 } 1649 1650 /* Send the message to the downstream. */ 1651 send_packet6(dp->ifp, (unsigned char *) relay_msg.data, 1652 (size_t) relay_msg.len, &to); 1653 1654 cleanup: 1655 if (relay_msg.data != NULL) 1656 data_string_forget(&relay_msg, MDL); 1657 if (if_id.data != NULL) 1658 data_string_forget(&if_id, MDL); 1659 } 1660 1661 /* 1662 * Called by the dispatch packet handler with a decoded packet. 1663 */ 1664 void 1665 dhcpv6(struct packet *packet) { 1666 struct stream_list *dp; 1667 1668 /* Try all relay-replies downwards. */ 1669 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) { 1670 process_down6(packet); 1671 return; 1672 } 1673 /* Others are candidates to go up if they come from down. */ 1674 for (dp = downstreams; dp; dp = dp->next) { 1675 if (packet->interface != dp->ifp) 1676 continue; 1677 process_up6(packet, dp); 1678 return; 1679 } 1680 /* Relay-forward could work from an unknown interface. */ 1681 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) { 1682 process_up6(packet, NULL); 1683 return; 1684 } 1685 1686 log_info("Can't process packet from interface '%s'.", 1687 packet->interface->name); 1688 } 1689 #endif 1690 1691 /* Stub routines needed for linking with DHCP libraries. */ 1692 void 1693 bootp(struct packet *packet) { 1694 return; 1695 } 1696 1697 void 1698 dhcp(struct packet *packet) { 1699 return; 1700 } 1701 1702 void 1703 classify(struct packet *p, struct class *c) { 1704 return; 1705 } 1706 1707 int 1708 check_collection(struct packet *p, struct lease *l, struct collection *c) { 1709 return 0; 1710 } 1711 1712 isc_result_t 1713 find_class(struct class **class, const char *c1, const char *c2, int i) { 1714 return ISC_R_NOTFOUND; 1715 } 1716 1717 int 1718 parse_allow_deny(struct option_cache **oc, struct parse *p, int i) { 1719 return 0; 1720 } 1721 1722 isc_result_t 1723 dhcp_set_control_state(control_object_state_t oldstate, 1724 control_object_state_t newstate) { 1725 if (newstate != server_shutdown) 1726 return ISC_R_SUCCESS; 1727 exit(0); 1728 } 1729