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