relay/dhcrelay.c

Go to the documentation of this file.
00001 /* dhcrelay.c
00002 
00003    DHCP/BOOTP Relay Agent. */
00004 
00005 /*
00006  * Copyright(c) 2004-2014 by Internet Systems Consortium, Inc.("ISC")
00007  * Copyright(c) 1997-2003 by Internet Software Consortium
00008  *
00009  * Permission to use, copy, modify, and distribute this software for any
00010  * purpose with or without fee is hereby granted, provided that the above
00011  * copyright notice and this permission notice appear in all copies.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00014  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00015  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00016  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00017  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00018  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00019  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00020  *
00021  *   Internet Systems Consortium, Inc.
00022  *   950 Charter Street
00023  *   Redwood City, CA 94063
00024  *   <info@isc.org>
00025  *   https://www.isc.org/
00026  *
00027  */
00028 
00029 #include "dhcpd.h"
00030 #include <syslog.h>
00031 #include <signal.h>
00032 #include <sys/time.h>
00033 
00034 #ifdef HAVE_LIBCAP_NG
00035 #  include <cap-ng.h>
00036    int keep_capabilities = 0;
00037 #endif
00038 
00039 TIME default_lease_time = 43200; /* 12 hours... */
00040 TIME max_lease_time = 86400; /* 24 hours... */
00041 struct tree_cache *global_options[256];
00042 
00043 struct option *requested_opts[2];
00044 
00045 /* Needed to prevent linking against conflex.c. */
00046 int lexline;
00047 int lexchar;
00048 char *token_line;
00049 char *tlname;
00050 
00051 const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
00052 isc_boolean_t no_dhcrelay_pid = ISC_FALSE;
00053 /* False (default) => we write and use a pid file */
00054 isc_boolean_t no_pid_file = ISC_FALSE;
00055 
00056 int bogus_agent_drops = 0;      /* Packets dropped because agent option
00057                                    field was specified and we're not relaying
00058                                    packets that already have an agent option
00059                                    specified. */
00060 int bogus_giaddr_drops = 0;     /* Packets sent to us to relay back to a
00061                                    client, but with a bogus giaddr. */
00062 int client_packets_relayed = 0; /* Packets relayed from client to server. */
00063 int server_packet_errors = 0;   /* Errors sending packets to servers. */
00064 int server_packets_relayed = 0; /* Packets relayed from server to client. */
00065 int client_packet_errors = 0;   /* Errors sending packets to clients. */
00066 
00067 int add_agent_options = 0;      /* If nonzero, add relay agent options. */
00068 
00069 int agent_option_errors = 0;    /* Number of packets forwarded without
00070                                    agent options because there was no room. */
00071 int drop_agent_mismatches = 0;  /* If nonzero, drop server replies that
00072                                    don't have matching circuit-id's. */
00073 int corrupt_agent_options = 0;  /* Number of packets dropped because
00074                                    relay agent information option was bad. */
00075 int missing_agent_option = 0;   /* Number of packets dropped because no
00076                                    RAI option matching our ID was found. */
00077 int bad_circuit_id = 0;         /* Circuit ID option in matching RAI option
00078                                    did not match any known circuit ID. */
00079 int missing_circuit_id = 0;     /* Circuit ID option in matching RAI option
00080                                    was missing. */
00081 int max_hop_count = 10;         /* Maximum hop count */
00082 
00083 #ifdef DHCPv6
00084         /* Force use of DHCPv6 interface-id option. */
00085 isc_boolean_t use_if_id = ISC_FALSE;
00086 #endif
00087 
00088         /* Maximum size of a packet with agent options added. */
00089 int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
00090 
00091         /* What to do about packets we're asked to relay that
00092            already have a relay option: */
00093 enum { forward_and_append,      /* Forward and append our own relay option. */
00094        forward_and_replace,     /* Forward, but replace theirs with ours. */
00095        forward_untouched,       /* Forward without changes. */
00096        discard } agent_relay_mode = forward_and_replace;
00097 
00098 u_int16_t local_port;
00099 u_int16_t remote_port;
00100 
00101 /* Relay agent server list. */
00102 struct server_list {
00103         struct server_list *next;
00104         struct sockaddr_in to;
00105 } *servers;
00106 
00107 #ifdef DHCPv6
00108 struct stream_list {
00109         struct stream_list *next;
00110         struct interface_info *ifp;
00111         struct sockaddr_in6 link;
00112         int id;
00113 } *downstreams, *upstreams;
00114 
00115 static struct stream_list *parse_downstream(char *);
00116 static struct stream_list *parse_upstream(char *);
00117 static void setup_streams(void);
00118 
00119 /*
00120  * A pointer to a subscriber id to add to the message we forward.
00121  * This is primarily for testing purposes as we only have one id
00122  * for the entire relay and don't determine one per client which
00123  * would be more useful.
00124  */
00125 char *dhcrelay_sub_id = NULL;
00126 #endif
00127 
00128 static void do_relay4(struct interface_info *, struct dhcp_packet *,
00129                       unsigned int, unsigned int, struct iaddr,
00130                       struct hardware *);
00131 static int add_relay_agent_options(struct interface_info *,
00132                                    struct dhcp_packet *, unsigned,
00133                                    struct in_addr);
00134 static int find_interface_by_agent_option(struct dhcp_packet *,
00135                                struct interface_info **, u_int8_t *, int);
00136 static int strip_relay_agent_options(struct interface_info *,
00137                                      struct interface_info **,
00138                                      struct dhcp_packet *, unsigned);
00139 
00140 static const char copyright[] =
00141 "Copyright 2004-2014 Internet Systems Consortium.";
00142 static const char arr[] = "All rights reserved.";
00143 static const char message[] =
00144 "Internet Systems Consortium DHCP Relay Agent";
00145 static const char url[] =
00146 "For info, please visit https://www.isc.org/software/dhcp/";
00147 
00148 #ifdef DHCPv6
00149 #define DHCRELAY_USAGE \
00150 "Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
00151 "                     [-A <length>] [-c <hops>] [-p <port>]\n" \
00152 "                     [-pf <pid-file>] [--no-pid]\n"\
00153 "                     [-m append|replace|forward|discard]\n" \
00154 "                     [-i interface0 [ ... -i interfaceN]\n" \
00155 "                     server0 [ ... serverN]\n\n" \
00156 "       dhcrelay -6   [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
00157 "                     [-pf <pid-file>] [--no-pid]\n" \
00158 "                     [-s <subscriber-id>]\n" \
00159 "                     -l lower0 [ ... -l lowerN]\n" \
00160 "                     -u upper0 [ ... -u upperN]\n" \
00161 "       lower (client link): [address%%]interface[#index]\n" \
00162 "       upper (server link): [address%%]interface"
00163 #else
00164 #define DHCRELAY_USAGE \
00165 "Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
00166 "                [-pf <pid-file>] [--no-pid]\n" \
00167 "                [-m append|replace|forward|discard]\n" \
00168 "                [-i interface0 [ ... -i interfaceN]\n" \
00169 "                server0 [ ... serverN]\n\n"
00170 #endif
00171 
00172 static void usage() {
00173         log_fatal(DHCRELAY_USAGE);
00174 }
00175 
00176 int 
00177 main(int argc, char **argv) {
00178         isc_result_t status;
00179         struct servent *ent;
00180         struct server_list *sp = NULL;
00181         struct interface_info *tmp = NULL;
00182         char *service_local = NULL, *service_remote = NULL;
00183         u_int16_t port_local = 0, port_remote = 0;
00184         int no_daemon = 0, quiet = 0;
00185         int fd;
00186         int i;
00187 #ifdef DHCPv6
00188         struct stream_list *sl = NULL;
00189         int local_family_set = 0;
00190 #endif
00191 
00192         /* Make sure that file descriptors 0(stdin), 1,(stdout), and
00193            2(stderr) are open. To do this, we assume that when we
00194            open a file the lowest available file descriptor is used. */
00195         fd = open("/dev/null", O_RDWR | O_CLOEXEC);
00196         if (fd == 0)
00197                 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
00198         if (fd == 1)
00199                 fd = open("/dev/null", O_RDWR | O_CLOEXEC);
00200         if (fd == 2)
00201                 log_perror = 0; /* No sense logging to /dev/null. */
00202         else if (fd != -1)
00203                 close(fd);
00204 
00205         openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
00206 
00207 #if !defined(DEBUG)
00208         setlogmask(LOG_UPTO(LOG_INFO));
00209 #endif  
00210 
00211         /* Set up the isc and dns library managers */
00212         status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB,
00213                                      NULL, NULL);
00214         if (status != ISC_R_SUCCESS)
00215                 log_fatal("Can't initialize context: %s",
00216                           isc_result_totext(status));
00217 
00218         /* Set up the OMAPI. */
00219         status = omapi_init();
00220         if (status != ISC_R_SUCCESS)
00221                 log_fatal("Can't initialize OMAPI: %s",
00222                            isc_result_totext(status));
00223 
00224         /* Set up the OMAPI wrappers for the interface object. */
00225         interface_setup();
00226 
00227         for (i = 1; i < argc; i++) {
00228                 if (!strcmp(argv[i], "-4")) {
00229 #ifdef DHCPv6
00230                         if (local_family_set && (local_family == AF_INET6)) {
00231                                 usage();
00232                         }
00233                         local_family_set = 1;
00234                         local_family = AF_INET;
00235                 } else if (!strcmp(argv[i], "-6")) {
00236                         if (local_family_set && (local_family == AF_INET)) {
00237                                 usage();
00238                         }
00239                         local_family_set = 1;
00240                         local_family = AF_INET6;
00241 #endif
00242                 } else if (!strcmp(argv[i], "-d")) {
00243                         no_daemon = 1;
00244                 } else if (!strcmp(argv[i], "-q")) {
00245                         quiet = 1;
00246                         quiet_interface_discovery = 1;
00247                 } else if (!strcmp(argv[i], "-p")) {
00248                         if (++i == argc)
00249                                 usage();
00250                         local_port = validate_port(argv[i]);
00251                         log_debug("binding to user-specified port %d",
00252                                   ntohs(local_port));
00253                 } else if (!strcmp(argv[i], "-c")) {
00254                         int hcount;
00255                         if (++i == argc)
00256                                 usage();
00257                         hcount = atoi(argv[i]);
00258                         if (hcount <= 255)
00259                                 max_hop_count= hcount;
00260                         else
00261                                 usage();
00262                 } else if (!strcmp(argv[i], "-i")) {
00263 #ifdef DHCPv6
00264                         if (local_family_set && (local_family == AF_INET6)) {
00265                                 usage();
00266                         }
00267                         local_family_set = 1;
00268                         local_family = AF_INET;
00269 #endif
00270                         if (++i == argc) {
00271                                 usage();
00272                         }
00273                         if (strlen(argv[i]) >= sizeof(tmp->name)) {
00274                                 log_fatal("%s: interface name too long "
00275                                           "(is %ld)",
00276                                           argv[i], (long)strlen(argv[i]));
00277                         }
00278                         status = interface_allocate(&tmp, MDL);
00279                         if (status != ISC_R_SUCCESS) {
00280                                 log_fatal("%s: interface_allocate: %s",
00281                                           argv[i],
00282                                           isc_result_totext(status));
00283                         }
00284                         strcpy(tmp->name, argv[i]);
00285                         interface_snorf(tmp, INTERFACE_REQUESTED);
00286                         interface_dereference(&tmp, MDL);
00287                 } else if (!strcmp(argv[i], "-a")) {
00288 #ifdef DHCPv6
00289                         if (local_family_set && (local_family == AF_INET6)) {
00290                                 usage();
00291                         }
00292                         local_family_set = 1;
00293                         local_family = AF_INET;
00294 #endif
00295                         add_agent_options = 1;
00296                 } else if (!strcmp(argv[i], "-A")) {
00297 #ifdef DHCPv6
00298                         if (local_family_set && (local_family == AF_INET6)) {
00299                                 usage();
00300                         }
00301                         local_family_set = 1;
00302                         local_family = AF_INET;
00303 #endif
00304                         if (++i == argc)
00305                                 usage();
00306 
00307                         dhcp_max_agent_option_packet_length = atoi(argv[i]);
00308 
00309                         if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
00310                                 log_fatal("%s: packet length exceeds "
00311                                           "longest possible MTU\n",
00312                                           argv[i]);
00313                 } else if (!strcmp(argv[i], "-m")) {
00314 #ifdef DHCPv6
00315                         if (local_family_set && (local_family == AF_INET6)) {
00316                                 usage();
00317                         }
00318                         local_family_set = 1;
00319                         local_family = AF_INET;
00320 #endif
00321                         if (++i == argc)
00322                                 usage();
00323                         if (!strcasecmp(argv[i], "append")) {
00324                                 agent_relay_mode = forward_and_append;
00325                         } else if (!strcasecmp(argv[i], "replace")) {
00326                                 agent_relay_mode = forward_and_replace;
00327                         } else if (!strcasecmp(argv[i], "forward")) {
00328                                 agent_relay_mode = forward_untouched;
00329                         } else if (!strcasecmp(argv[i], "discard")) {
00330                                 agent_relay_mode = discard;
00331                         } else
00332                                 usage();
00333                 } else if (!strcmp(argv[i], "-D")) {
00334 #ifdef DHCPv6
00335                         if (local_family_set && (local_family == AF_INET6)) {
00336                                 usage();
00337                         }
00338                         local_family_set = 1;
00339                         local_family = AF_INET;
00340 #endif
00341                         drop_agent_mismatches = 1;
00342 #ifdef DHCPv6
00343                 } else if (!strcmp(argv[i], "-I")) {
00344                         if (local_family_set && (local_family == AF_INET)) {
00345                                 usage();
00346                         }
00347                         local_family_set = 1;
00348                         local_family = AF_INET6;
00349                         use_if_id = ISC_TRUE;
00350                 } else if (!strcmp(argv[i], "-l")) {
00351                         if (local_family_set && (local_family == AF_INET)) {
00352                                 usage();
00353                         }
00354                         local_family_set = 1;
00355                         local_family = AF_INET6;
00356                         if (downstreams != NULL)
00357                                 use_if_id = ISC_TRUE;
00358                         if (++i == argc)
00359                                 usage();
00360                         sl = parse_downstream(argv[i]);
00361                         sl->next = downstreams;
00362                         downstreams = sl;
00363                 } else if (!strcmp(argv[i], "-u")) {
00364                         if (local_family_set && (local_family == AF_INET)) {
00365                                 usage();
00366                         }
00367                         local_family_set = 1;
00368                         local_family = AF_INET6;
00369                         if (++i == argc)
00370                                 usage();
00371                         sl = parse_upstream(argv[i]);
00372                         sl->next = upstreams;
00373                         upstreams = sl;
00374                 } else if (!strcmp(argv[i], "-s")) {
00375                         if (local_family_set && (local_family == AF_INET)) {
00376                                 usage();
00377                         }
00378                         local_family_set = 1;
00379                         local_family = AF_INET6;
00380                         if (++i == argc)
00381                                 usage();
00382                         dhcrelay_sub_id = argv[i];
00383 #endif
00384                 } else if (!strcmp(argv[i], "-nc")) {
00385 #ifdef HAVE_LIBCAP_NG
00386                         keep_capabilities = 1;
00387 #endif
00388                 } else if (!strcmp(argv[i], "-pf")) {
00389                         if (++i == argc)
00390                                 usage();
00391                         path_dhcrelay_pid = argv[i];
00392                         no_dhcrelay_pid = ISC_TRUE;
00393                 } else if (!strcmp(argv[i], "--no-pid")) {
00394                         no_pid_file = ISC_TRUE;
00395                 } else if (!strcmp(argv[i], "--version")) {
00396                         log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
00397                         exit(0);
00398                 } else if (!strcmp(argv[i], "--help") ||
00399                            !strcmp(argv[i], "-h")) {
00400                         log_info(DHCRELAY_USAGE);
00401                         exit(0);
00402                 } else if (argv[i][0] == '-') {
00403                         usage();
00404                 } else {
00405                         struct hostent *he;
00406                         struct in_addr ia, *iap = NULL;
00407 
00408 #ifdef DHCPv6
00409                         if (local_family_set && (local_family == AF_INET6)) {
00410                                 usage();
00411                         }
00412                         local_family_set = 1;
00413                         local_family = AF_INET;
00414 #endif
00415                         if (inet_aton(argv[i], &ia)) {
00416                                 iap = &ia;
00417                         } else {
00418                                 he = gethostbyname(argv[i]);
00419                                 if (!he) {
00420                                         log_error("%s: host unknown", argv[i]);
00421                                 } else {
00422                                         iap = ((struct in_addr *)
00423                                                he->h_addr_list[0]);
00424                                 }
00425                         }
00426 
00427                         if (iap) {
00428                                 sp = ((struct server_list *)
00429                                       dmalloc(sizeof *sp, MDL));
00430                                 if (!sp)
00431                                         log_fatal("no memory for server.\n");
00432                                 sp->next = servers;
00433                                 servers = sp;
00434                                 memcpy(&sp->to.sin_addr, iap, sizeof *iap);
00435                         }
00436                 }
00437         }
00438 
00439         /*
00440          * If the user didn't specify a pid file directly
00441          * find one from environment variables or defaults
00442          */
00443         if (no_dhcrelay_pid == ISC_FALSE) {
00444                 if (local_family == AF_INET) {
00445                         path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
00446                         if (path_dhcrelay_pid == NULL)
00447                                 path_dhcrelay_pid = _PATH_DHCRELAY_PID;
00448                 }
00449 #ifdef DHCPv6
00450                 else {
00451                         path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
00452                         if (path_dhcrelay_pid == NULL)
00453                                 path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
00454                 }
00455 #endif
00456         }
00457 
00458 #ifdef HAVE_LIBCAP_NG
00459         /* Drop capabilities */
00460         if (!keep_capabilities) {
00461                 capng_clear(CAPNG_SELECT_BOTH);
00462                 capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED,
00463                                 CAP_NET_RAW, CAP_NET_BIND_SERVICE, -1);
00464                 capng_apply(CAPNG_SELECT_BOTH);
00465                 log_info ("Dropped all unnecessary capabilities.");
00466         }
00467 #endif
00468 
00469         if (!quiet) {
00470                 log_info("%s %s", message, PACKAGE_VERSION);
00471                 log_info(copyright);
00472                 log_info(arr);
00473                 log_info(url);
00474         } else 
00475                 log_perror = 0;
00476 
00477         /* Set default port */
00478         if (local_family == AF_INET) {
00479                 service_local = "bootps";
00480                 service_remote = "bootpc";
00481                 port_local = htons(67);
00482                 port_remote = htons(68);
00483         }
00484 #ifdef DHCPv6
00485         else {
00486                 service_local = "dhcpv6-server";
00487                 service_remote = "dhcpv6-client";
00488                 port_local = htons(547);
00489                 port_remote = htons(546);
00490         }
00491 #endif
00492 
00493         if (!local_port) {
00494                 ent = getservbyname(service_local, "udp");
00495                 if (ent)
00496                         local_port = ent->s_port;
00497                 else
00498                         local_port = port_local;
00499 
00500                 ent = getservbyname(service_remote, "udp");
00501                 if (ent)
00502                         remote_port = ent->s_port;
00503                 else
00504                         remote_port = port_remote;
00505 
00506                 endservent();
00507         }
00508 
00509         if (local_family == AF_INET) {
00510                 /* We need at least one server */
00511                 if (servers == NULL) {
00512                         log_fatal("No servers specified.");
00513                 }
00514 
00515 
00516                 /* Set up the server sockaddrs. */
00517                 for (sp = servers; sp; sp = sp->next) {
00518                         sp->to.sin_port = local_port;
00519                         sp->to.sin_family = AF_INET;
00520 #ifdef HAVE_SA_LEN
00521                         sp->to.sin_len = sizeof sp->to;
00522 #endif
00523                 }
00524         }
00525 #ifdef DHCPv6
00526         else {
00527                 unsigned code;
00528 
00529                 /* We need at least one upstream and one downstream interface */
00530                 if (upstreams == NULL || downstreams == NULL) {
00531                         log_info("Must specify at least one lower "
00532                                  "and one upper interface.\n");
00533                         usage();
00534                 }
00535 
00536                 /* Set up the initial dhcp option universe. */
00537                 initialize_common_option_spaces();
00538 
00539                 /* Check requested options. */
00540                 code = D6O_RELAY_MSG;
00541                 if (!option_code_hash_lookup(&requested_opts[0],
00542                                              dhcpv6_universe.code_hash,
00543                                              &code, 0, MDL))
00544                         log_fatal("Unable to find the RELAY_MSG "
00545                                   "option definition.");
00546                 code = D6O_INTERFACE_ID;
00547                 if (!option_code_hash_lookup(&requested_opts[1],
00548                                              dhcpv6_universe.code_hash,
00549                                              &code, 0, MDL))
00550                         log_fatal("Unable to find the INTERFACE_ID "
00551                                   "option definition.");
00552         }
00553 #endif
00554 
00555         /* Get the current time... */
00556         gettimeofday(&cur_tv, NULL);
00557 
00558         /* Discover all the network interfaces. */
00559         discover_interfaces(DISCOVER_RELAY);
00560 
00561 #ifdef DHCPv6
00562         if (local_family == AF_INET6)
00563                 setup_streams();
00564 #endif
00565 
00566         /* Become a daemon... */
00567         if (!no_daemon) {
00568                 int pid;
00569                 FILE *pf;
00570                 int pfdesc;
00571 
00572                 log_perror = 0;
00573 
00574                 if ((pid = fork()) < 0)
00575                         log_fatal("Can't fork daemon: %m");
00576                 else if (pid)
00577                         exit(0);
00578 
00579                 if (no_pid_file == ISC_FALSE) {
00580                         pfdesc = open(path_dhcrelay_pid,
00581                                       O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644);
00582 
00583                         if (pfdesc < 0) {
00584                                 log_error("Can't create %s: %m",
00585                                           path_dhcrelay_pid);
00586                         } else {
00587                                 pf = fdopen(pfdesc, "we");
00588                                 if (!pf)
00589                                         log_error("Can't fdopen %s: %m",
00590                                                   path_dhcrelay_pid);
00591                                 else {
00592                                         fprintf(pf, "%ld\n",(long)getpid());
00593                                         fclose(pf);
00594                                 }       
00595                         }
00596                 }
00597 
00598                 (void) close(0);
00599                 (void) close(1);
00600                 (void) close(2);
00601                 (void) setsid();
00602 
00603                 IGNORE_RET (chdir("/"));
00604         }
00605 
00606         /* Set up the packet handler... */
00607         if (local_family == AF_INET)
00608                 bootp_packet_handler = do_relay4;
00609 #ifdef DHCPv6
00610         else
00611                 dhcpv6_packet_handler = do_packet6;
00612 #endif
00613 
00614         /* install signal handlers */
00615         signal(SIGINT, dhcp_signal_handler);   /* control-c */
00616         signal(SIGTERM, dhcp_signal_handler);  /* kill */
00617 
00618 #ifdef HAVE_LIBCAP_NG
00619         /* Drop all capabilities */
00620         if (!keep_capabilities) {
00621                 capng_clear(CAPNG_SELECT_BOTH);
00622                 capng_apply(CAPNG_SELECT_BOTH);
00623                 log_info ("Dropped all capabilities.");
00624         }
00625 #endif
00626 
00627         /* Start dispatching packets and timeouts... */
00628         dispatch();
00629 
00630         /* In fact dispatch() never returns. */
00631         return (0);
00632 }
00633 
00634 static void
00635 do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
00636           unsigned int length, unsigned int from_port, struct iaddr from,
00637           struct hardware *hfrom) {
00638         struct server_list *sp;
00639         struct sockaddr_in to;
00640         struct interface_info *out;
00641         struct hardware hto, *htop;
00642 
00643         if (packet->hlen > sizeof packet->chaddr) {
00644                 log_info("Discarding packet with invalid hlen, received on "
00645                          "%s interface.", ip->name);
00646                 return;
00647         }
00648         if (ip->address_count < 1 || ip->addresses == NULL) {
00649                 log_info("Discarding packet received on %s interface that "
00650                          "has no IPv4 address assigned.", ip->name);
00651                 return;
00652         }
00653 
00654         /* Find the interface that corresponds to the giaddr
00655            in the packet. */
00656         if (packet->giaddr.s_addr) {
00657                 for (out = interfaces; out; out = out->next) {
00658                         int i;
00659 
00660                         for (i = 0 ; i < out->address_count ; i++ ) {
00661                                 if (out->addresses[i].s_addr ==
00662                                     packet->giaddr.s_addr) {
00663                                         i = -1;
00664                                         break;
00665                                 }
00666                         }
00667 
00668                         if (i == -1)
00669                                 break;
00670                 }
00671         } else {
00672                 out = NULL;
00673         }
00674 
00675         /* If it's a bootreply, forward it to the client. */
00676         if (packet->op == BOOTREPLY) {
00677                 if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
00678                         can_unicast_without_arp(out)) {
00679                         to.sin_addr = packet->yiaddr;
00680                         to.sin_port = remote_port;
00681 
00682                         /* and hardware address is not broadcast */
00683                         htop = &hto;
00684                 } else {
00685                         to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
00686                         to.sin_port = remote_port;
00687 
00688                         /* hardware address is broadcast */
00689                         htop = NULL;
00690                 }
00691                 to.sin_family = AF_INET;
00692 #ifdef HAVE_SA_LEN
00693                 to.sin_len = sizeof to;
00694 #endif
00695 
00696                 memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
00697                 hto.hbuf[0] = packet->htype;
00698                 hto.hlen = packet->hlen + 1;
00699 
00700                 /* Wipe out the agent relay options and, if possible, figure
00701                    out which interface to use based on the contents of the
00702                    option that we put on the request to which the server is
00703                    replying. */
00704                 if (!(length =
00705                       strip_relay_agent_options(ip, &out, packet, length)))
00706                         return;
00707 
00708                 if (!out) {
00709                         log_error("Packet to bogus giaddr %s.\n",
00710                               inet_ntoa(packet->giaddr));
00711                         ++bogus_giaddr_drops;
00712                         return;
00713                 }
00714 
00715                 if (send_packet(out, NULL, packet, length, out->addresses[0],
00716                                 &to, htop) < 0) {
00717                         ++server_packet_errors;
00718                 } else {
00719                         log_debug("Forwarded BOOTREPLY for %s to %s",
00720                                print_hw_addr(packet->htype, packet->hlen,
00721                                               packet->chaddr),
00722                                inet_ntoa(to.sin_addr));
00723 
00724                         ++server_packets_relayed;
00725                 }
00726                 return;
00727         }
00728 
00729         /* If giaddr matches one of our addresses, ignore the packet -
00730            we just sent it. */
00731         if (out)
00732                 return;
00733 
00734         /* Add relay agent options if indicated.   If something goes wrong,
00735            drop the packet. */
00736         if (!(length = add_relay_agent_options(ip, packet, length,
00737                                                ip->addresses[0])))
00738                 return;
00739 
00740         /* If giaddr is not already set, Set it so the server can
00741            figure out what net it's from and so that we can later
00742            forward the response to the correct net.    If it's already
00743            set, the response will be sent directly to the relay agent
00744            that set giaddr, so we won't see it. */
00745         if (!packet->giaddr.s_addr)
00746                 packet->giaddr = ip->addresses[0];
00747         if (packet->hops < max_hop_count)
00748                 packet->hops = packet->hops + 1;
00749         else
00750                 return;
00751 
00752         /* Otherwise, it's a BOOTREQUEST, so forward it to all the
00753            servers. */
00754         for (sp = servers; sp; sp = sp->next) {
00755                 if (send_packet((fallback_interface
00756                                  ? fallback_interface : interfaces),
00757                                  NULL, packet, length, ip->addresses[0],
00758                                  &sp->to, NULL) < 0) {
00759                         ++client_packet_errors;
00760                 } else {
00761                         log_debug("Forwarded BOOTREQUEST for %s to %s",
00762                                print_hw_addr(packet->htype, packet->hlen,
00763                                               packet->chaddr),
00764                                inet_ntoa(sp->to.sin_addr));
00765                         ++client_packets_relayed;
00766                 }
00767         }
00768                                  
00769 }
00770 
00771 /* Strip any Relay Agent Information options from the DHCP packet
00772    option buffer.   If there is a circuit ID suboption, look up the
00773    outgoing interface based upon it. */
00774 
00775 static int
00776 strip_relay_agent_options(struct interface_info *in,
00777                           struct interface_info **out,
00778                           struct dhcp_packet *packet,
00779                           unsigned length) {
00780         int is_dhcp = 0;
00781         u_int8_t *op, *nextop, *sp, *max;
00782         int good_agent_option = 0;
00783         int status;
00784 
00785         /* If we're not adding agent options to packets, we're not taking
00786            them out either. */
00787         if (!add_agent_options)
00788                 return (length);
00789 
00790         /* If there's no cookie, it's a bootp packet, so we should just
00791            forward it unchanged. */
00792         if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
00793                 return (length);
00794 
00795         max = ((u_int8_t *)packet) + length;
00796         sp = op = &packet->options[4];
00797 
00798         while (op < max) {
00799                 switch(*op) {
00800                         /* Skip padding... */
00801                       case DHO_PAD:
00802                         if (sp != op)
00803                                 *sp = *op;
00804                         ++op;
00805                         ++sp;
00806                         continue;
00807 
00808                         /* If we see a message type, it's a DHCP packet. */
00809                       case DHO_DHCP_MESSAGE_TYPE:
00810                         is_dhcp = 1;
00811                         goto skip;
00812                         break;
00813 
00814                         /* Quit immediately if we hit an End option. */
00815                       case DHO_END:
00816                         if (sp != op)
00817                                 *sp++ = *op++;
00818                         goto out;
00819 
00820                       case DHO_DHCP_AGENT_OPTIONS:
00821                         /* We shouldn't see a relay agent option in a
00822                            packet before we've seen the DHCP packet type,
00823                            but if we do, we have to leave it alone. */
00824                         if (!is_dhcp)
00825                                 goto skip;
00826 
00827                         /* Do not process an agent option if it exceeds the
00828                          * buffer.  Fail this packet.
00829                          */
00830                         nextop = op + op[1] + 2;
00831                         if (nextop > max)
00832                                 return (0);
00833 
00834                         status = find_interface_by_agent_option(packet,
00835                                                                 out, op + 2,
00836                                                                 op[1]);
00837                         if (status == -1 && drop_agent_mismatches)
00838                                 return (0);
00839                         if (status)
00840                                 good_agent_option = 1;
00841                         op = nextop;
00842                         break;
00843 
00844                       skip:
00845                         /* Skip over other options. */
00846                       default:
00847                         /* Fail if processing this option will exceed the
00848                          * buffer(op[1] is malformed).
00849                          */
00850                         nextop = op + op[1] + 2;
00851                         if (nextop > max)
00852                                 return (0);
00853 
00854                         if (sp != op) {
00855                                 memmove(sp, op, op[1] + 2);
00856                                 sp += op[1] + 2;
00857                                 op = nextop;
00858                         } else
00859                                 op = sp = nextop;
00860 
00861                         break;
00862                 }
00863         }
00864       out:
00865 
00866         /* If it's not a DHCP packet, we're not supposed to touch it. */
00867         if (!is_dhcp)
00868                 return (length);
00869 
00870         /* If none of the agent options we found matched, or if we didn't
00871            find any agent options, count this packet as not having any
00872            matching agent options, and if we're relying on agent options
00873            to determine the outgoing interface, drop the packet. */
00874 
00875         if (!good_agent_option) {
00876                 ++missing_agent_option;
00877                 if (drop_agent_mismatches)
00878                         return (0);
00879         }
00880 
00881         /* Adjust the length... */
00882         if (sp != op) {
00883                 length = sp -((u_int8_t *)packet);
00884 
00885                 /* Make sure the packet isn't short(this is unlikely,
00886                    but WTH) */
00887                 if (length < BOOTP_MIN_LEN) {
00888                         memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
00889                         length = BOOTP_MIN_LEN;
00890                 }
00891         }
00892         return (length);
00893 }
00894 
00895 
00896 /* Find an interface that matches the circuit ID specified in the
00897    Relay Agent Information option.   If one is found, store it through
00898    the pointer given; otherwise, leave the existing pointer alone.
00899 
00900    We actually deviate somewhat from the current specification here:
00901    if the option buffer is corrupt, we suggest that the caller not
00902    respond to this packet.  If the circuit ID doesn't match any known
00903    interface, we suggest that the caller to drop the packet.  Only if
00904    we find a circuit ID that matches an existing interface do we tell
00905    the caller to go ahead and process the packet. */
00906 
00907 static int
00908 find_interface_by_agent_option(struct dhcp_packet *packet,
00909                                struct interface_info **out,
00910                                u_int8_t *buf, int len) {
00911         int i = 0;
00912         u_int8_t *circuit_id = 0;
00913         unsigned circuit_id_len = 0;
00914         struct interface_info *ip;
00915 
00916         while (i < len) {
00917                 /* If the next agent option overflows the end of the
00918                    packet, the agent option buffer is corrupt. */
00919                 if (i + 1 == len ||
00920                     i + buf[i + 1] + 2 > len) {
00921                         ++corrupt_agent_options;
00922                         return (-1);
00923                 }
00924                 switch(buf[i]) {
00925                         /* Remember where the circuit ID is... */
00926                       case RAI_CIRCUIT_ID:
00927                         circuit_id = &buf[i + 2];
00928                         circuit_id_len = buf[i + 1];
00929                         i += circuit_id_len + 2;
00930                         continue;
00931 
00932                       default:
00933                         i += buf[i + 1] + 2;
00934                         break;
00935                 }
00936         }
00937 
00938         /* If there's no circuit ID, it's not really ours, tell the caller
00939            it's no good. */
00940         if (!circuit_id) {
00941                 ++missing_circuit_id;
00942                 return (-1);
00943         }
00944 
00945         /* Scan the interface list looking for an interface whose
00946            name matches the one specified in circuit_id. */
00947 
00948         for (ip = interfaces; ip; ip = ip->next) {
00949                 if (ip->circuit_id &&
00950                     ip->circuit_id_len == circuit_id_len &&
00951                     !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
00952                         break;
00953         }
00954 
00955         /* If we got a match, use it. */
00956         if (ip) {
00957                 *out = ip;
00958                 return (1);
00959         }
00960 
00961         /* If we didn't get a match, the circuit ID was bogus. */
00962         ++bad_circuit_id;
00963         return (-1);
00964 }
00965 
00966 /*
00967  * Examine a packet to see if it's a candidate to have a Relay
00968  * Agent Information option tacked onto its tail.   If it is, tack
00969  * the option on.
00970  */
00971 static int
00972 add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
00973                         unsigned length, struct in_addr giaddr) {
00974         int is_dhcp = 0, mms;
00975         unsigned optlen;
00976         u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
00977 
00978         /* If we're not adding agent options to packets, we can skip
00979            this. */
00980         if (!add_agent_options)
00981                 return (length);
00982 
00983         /* If there's no cookie, it's a bootp packet, so we should just
00984            forward it unchanged. */
00985         if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
00986                 return (length);
00987 
00988         max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
00989 
00990         /* Commence processing after the cookie. */
00991         sp = op = &packet->options[4];
00992 
00993         while (op < max) {
00994                 switch(*op) {
00995                         /* Skip padding... */
00996                       case DHO_PAD:
00997                         /* Remember the first pad byte so we can commandeer
00998                          * padded space.
00999                          *
01000                          * XXX: Is this really a good idea?  Sure, we can
01001                          * seemingly reduce the packet while we're looking,
01002                          * but if the packet was signed by the client then
01003                          * this padding is part of the checksum(RFC3118),
01004                          * and its nonpresence would break authentication.
01005                          */
01006                         if (end_pad == NULL)
01007                                 end_pad = sp;
01008 
01009                         if (sp != op)
01010                                 *sp++ = *op++;
01011                         else
01012                                 sp = ++op;
01013 
01014                         continue;
01015 
01016                         /* If we see a message type, it's a DHCP packet. */
01017                       case DHO_DHCP_MESSAGE_TYPE:
01018                         is_dhcp = 1;
01019                         goto skip;
01020 
01021                         /*
01022                          * If there's a maximum message size option, we
01023                          * should pay attention to it
01024                          */
01025                       case DHO_DHCP_MAX_MESSAGE_SIZE:
01026                         mms = ntohs(*(op + 2));
01027                         if (mms < dhcp_max_agent_option_packet_length &&
01028                             mms >= DHCP_MTU_MIN)
01029                                 max = ((u_int8_t *)packet) + mms;
01030                         goto skip;
01031 
01032                         /* Quit immediately if we hit an End option. */
01033                       case DHO_END:
01034                         goto out;
01035 
01036                       case DHO_DHCP_AGENT_OPTIONS:
01037                         /* We shouldn't see a relay agent option in a
01038                            packet before we've seen the DHCP packet type,
01039                            but if we do, we have to leave it alone. */
01040                         if (!is_dhcp)
01041                                 goto skip;
01042 
01043                         end_pad = NULL;
01044 
01045                         /* There's already a Relay Agent Information option
01046                            in this packet.   How embarrassing.   Decide what
01047                            to do based on the mode the user specified. */
01048 
01049                         switch(agent_relay_mode) {
01050                               case forward_and_append:
01051                                 goto skip;
01052                               case forward_untouched:
01053                                 return (length);
01054                               case discard:
01055                                 return (0);
01056                               case forward_and_replace:
01057                               default:
01058                                 break;
01059                         }
01060 
01061                         /* Skip over the agent option and start copying
01062                            if we aren't copying already. */
01063                         op += op[1] + 2;
01064                         break;
01065 
01066                       skip:
01067                         /* Skip over other options. */
01068                       default:
01069                         /* Fail if processing this option will exceed the
01070                          * buffer(op[1] is malformed).
01071                          */
01072                         nextop = op + op[1] + 2;
01073                         if (nextop > max)
01074                                 return (0);
01075 
01076                         end_pad = NULL;
01077 
01078                         if (sp != op) {
01079                                 memmove(sp, op, op[1] + 2);
01080                                 sp += op[1] + 2;
01081                                 op = nextop;
01082                         } else
01083                                 op = sp = nextop;
01084 
01085                         break;
01086                 }
01087         }
01088       out:
01089 
01090         /* If it's not a DHCP packet, we're not supposed to touch it. */
01091         if (!is_dhcp)
01092                 return (length);
01093 
01094         /* If the packet was padded out, we can store the agent option
01095            at the beginning of the padding. */
01096 
01097         if (end_pad != NULL)
01098                 sp = end_pad;
01099 
01100 #if 0
01101         /* Remember where the end of the packet was after parsing
01102            it. */
01103         op = sp;
01104 #endif
01105 
01106         /* Sanity check.  Had better not ever happen. */
01107         if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
01108                 log_fatal("Circuit ID length %d out of range [1-255] on "
01109                           "%s\n", ip->circuit_id_len, ip->name);
01110         optlen = ip->circuit_id_len + 2;            /* RAI_CIRCUIT_ID + len */
01111 
01112         if (ip->remote_id) {
01113                 if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
01114                         log_fatal("Remote ID length %d out of range [1-255] "
01115                                   "on %s\n", ip->circuit_id_len, ip->name);
01116                 optlen += ip->remote_id_len + 2;    /* RAI_REMOTE_ID + len */
01117         }
01118 
01119         /* We do not support relay option fragmenting(multiple options to
01120          * support an option data exceeding 255 bytes).
01121          */
01122         if ((optlen < 3) ||(optlen > 255))
01123                 log_fatal("Total agent option length(%u) out of range "
01124                            "[3 - 255] on %s\n", optlen, ip->name);
01125 
01126         /*
01127          * Is there room for the option, its code+len, and DHO_END?
01128          * If not, forward without adding the option.
01129          */
01130         if (max - sp >= optlen + 3) {
01131                 log_debug("Adding %d-byte relay agent option", optlen + 3);
01132 
01133                 /* Okay, cons up *our* Relay Agent Information option. */
01134                 *sp++ = DHO_DHCP_AGENT_OPTIONS;
01135                 *sp++ = optlen;
01136 
01137                 /* Copy in the circuit id... */
01138                 *sp++ = RAI_CIRCUIT_ID;
01139                 *sp++ = ip->circuit_id_len;
01140                 memcpy(sp, ip->circuit_id, ip->circuit_id_len);
01141                 sp += ip->circuit_id_len;
01142 
01143                 /* Copy in remote ID... */
01144                 if (ip->remote_id) {
01145                         *sp++ = RAI_REMOTE_ID;
01146                         *sp++ = ip->remote_id_len;
01147                         memcpy(sp, ip->remote_id, ip->remote_id_len);
01148                         sp += ip->remote_id_len;
01149                 }
01150         } else {
01151                 ++agent_option_errors;
01152                 log_error("No room in packet (used %d of %d) "
01153                           "for %d-byte relay agent option: omitted",
01154                            (int) (sp - ((u_int8_t *) packet)),
01155                            (int) (max - ((u_int8_t *) packet)),
01156                            optlen + 3);
01157         }
01158 
01159         /*
01160          * Deposit an END option unless the packet is full (shouldn't
01161          * be possible).
01162          */
01163         if (sp < max)
01164                 *sp++ = DHO_END;
01165 
01166         /* Recalculate total packet length. */
01167         length = sp -((u_int8_t *)packet);
01168 
01169         /* Make sure the packet isn't short(this is unlikely, but WTH) */
01170         if (length < BOOTP_MIN_LEN) {
01171                 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
01172                 return (BOOTP_MIN_LEN);
01173         }
01174 
01175         return (length);
01176 }
01177 
01178 #ifdef DHCPv6
01179 /*
01180  * Parse a downstream argument: [address%]interface[#index].
01181  */
01182 static struct stream_list *
01183 parse_downstream(char *arg) {
01184         struct stream_list *dp, *up;
01185         struct interface_info *ifp = NULL;
01186         char *ifname, *addr, *iid;
01187         isc_result_t status;
01188 
01189         if (!supports_multiple_interfaces(ifp) &&
01190             (downstreams != NULL))
01191                 log_fatal("No support for multiple interfaces.");
01192 
01193         /* Decode the argument. */
01194         ifname = strchr(arg, '%');
01195         if (ifname == NULL) {
01196                 ifname = arg;
01197                 addr = NULL;
01198         } else {
01199                 *ifname++ = '\0';
01200                 addr = arg;
01201         }
01202         iid = strchr(ifname, '#');
01203         if (iid != NULL) {
01204                 *iid++ = '\0';
01205         }
01206         if (strlen(ifname) >= sizeof(ifp->name)) {
01207                 log_error("Interface name '%s' too long", ifname);
01208                 usage();
01209         }
01210 
01211         /* Don't declare twice. */
01212         for (dp = downstreams; dp; dp = dp->next) {
01213                 if (strcmp(ifname, dp->ifp->name) == 0)
01214                         log_fatal("Down interface '%s' declared twice.",
01215                                   ifname);
01216         }
01217 
01218         /* Share with up side? */
01219         for (up = upstreams; up; up = up->next) {
01220                 if (strcmp(ifname, up->ifp->name) == 0) {
01221                         log_info("Interface '%s' is both down and up.",
01222                                  ifname);
01223                         ifp = up->ifp;
01224                         break;
01225                 }
01226         }
01227 
01228         /* New interface. */
01229         if (ifp == NULL) {
01230                 status = interface_allocate(&ifp, MDL);
01231                 if (status != ISC_R_SUCCESS)
01232                         log_fatal("%s: interface_allocate: %s",
01233                                   arg, isc_result_totext(status));
01234                 strcpy(ifp->name, ifname);
01235                 if (interfaces) {
01236                         interface_reference(&ifp->next, interfaces, MDL);
01237                         interface_dereference(&interfaces, MDL);
01238                 }
01239                 interface_reference(&interfaces, ifp, MDL);
01240                 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
01241         }
01242 
01243         /* New downstream. */
01244         dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
01245         if (!dp)
01246                 log_fatal("No memory for downstream.");
01247         dp->ifp = ifp;
01248         if (iid != NULL) {
01249                 dp->id = atoi(iid);
01250         } else {
01251                 dp->id = -1;
01252         }
01253         /* !addr case handled by setup. */
01254         if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
01255                 log_fatal("Bad link address '%s'", addr);
01256 
01257         return dp;
01258 }
01259 
01260 /*
01261  * Parse an upstream argument: [address]%interface.
01262  */
01263 static struct stream_list *
01264 parse_upstream(char *arg) {
01265         struct stream_list *up, *dp;
01266         struct interface_info *ifp = NULL;
01267         char *ifname, *addr;
01268         isc_result_t status;
01269 
01270         /* Decode the argument. */
01271         ifname = strchr(arg, '%');
01272         if (ifname == NULL) {
01273                 ifname = arg;
01274                 addr = All_DHCP_Servers;
01275         } else {
01276                 *ifname++ = '\0';
01277                 addr = arg;
01278         }
01279         if (strlen(ifname) >= sizeof(ifp->name)) {
01280                 log_fatal("Interface name '%s' too long", ifname);
01281         }
01282 
01283         /* Shared up interface? */
01284         for (up = upstreams; up; up = up->next) {
01285                 if (strcmp(ifname, up->ifp->name) == 0) {
01286                         ifp = up->ifp;
01287                         break;
01288                 }
01289         }
01290         for (dp = downstreams; dp; dp = dp->next) {
01291                 if (strcmp(ifname, dp->ifp->name) == 0) {
01292                         ifp = dp->ifp;
01293                         break;
01294                 }
01295         }
01296 
01297         /* New interface. */
01298         if (ifp == NULL) {
01299                 status = interface_allocate(&ifp, MDL);
01300                 if (status != ISC_R_SUCCESS)
01301                         log_fatal("%s: interface_allocate: %s",
01302                                   arg, isc_result_totext(status));
01303                 strcpy(ifp->name, ifname);
01304                 if (interfaces) {
01305                         interface_reference(&ifp->next, interfaces, MDL);
01306                         interface_dereference(&interfaces, MDL);
01307                 }
01308                 interface_reference(&interfaces, ifp, MDL);
01309                 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
01310         }
01311 
01312         /* New upstream. */
01313         up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
01314         if (up == NULL)
01315                 log_fatal("No memory for upstream.");
01316 
01317         up->ifp = ifp;
01318 
01319         if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
01320                 log_fatal("Bad address %s", addr);
01321 
01322         return up;
01323 }
01324 
01325 /*
01326  * Setup downstream interfaces.
01327  */
01328 static void
01329 setup_streams(void) {
01330         struct stream_list *dp, *up;
01331         int i;
01332         isc_boolean_t link_is_set;
01333 
01334         for (dp = downstreams; dp; dp = dp->next) {
01335                 /* Check interface */
01336                 if (dp->ifp->v6address_count == 0)
01337                         log_fatal("Interface '%s' has no IPv6 addresses.",
01338                                   dp->ifp->name);
01339 
01340                 /* Check/set link. */
01341                 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
01342                         link_is_set = ISC_FALSE;
01343                 else
01344                         link_is_set = ISC_TRUE;
01345                 for (i = 0; i < dp->ifp->v6address_count; i++) {
01346                         if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
01347                                 continue;
01348                         if (!link_is_set)
01349                                 break;
01350                         if (!memcmp(&dp->ifp->v6addresses[i],
01351                                     &dp->link.sin6_addr,
01352                                     sizeof(dp->link.sin6_addr)))
01353                                 break;
01354                 }
01355                 if (i == dp->ifp->v6address_count)
01356                         log_fatal("Interface %s does not have global IPv6 "
01357                                   "address assigned.", dp->ifp->name);
01358                 if (!link_is_set)
01359                         memcpy(&dp->link.sin6_addr,
01360                                &dp->ifp->v6addresses[i],
01361                                sizeof(dp->link.sin6_addr));
01362 
01363                 /* Set interface-id. */
01364                 if (dp->id == -1)
01365                         dp->id = dp->ifp->index;
01366         }
01367 
01368         for (up = upstreams; up; up = up->next) {
01369                 up->link.sin6_port = local_port;
01370                 up->link.sin6_family = AF_INET6;
01371 #ifdef HAVE_SA_LEN
01372                 up->link.sin6_len = sizeof(up->link);
01373 #endif
01374 
01375                 if (up->ifp->v6address_count == 0)
01376                         log_fatal("Interface '%s' has no IPv6 addresses.",
01377                                   up->ifp->name);
01378         }
01379 }
01380 
01381 /*
01382  * Add DHCPv6 agent options here.
01383  */
01384 static const int required_forw_opts[] = {
01385         D6O_INTERFACE_ID,
01386         D6O_SUBSCRIBER_ID,
01387         D6O_RELAY_MSG,
01388         0
01389 };
01390 
01391 /*
01392  * Process a packet upwards, i.e., from client to server.
01393  */
01394 static void
01395 process_up6(struct packet *packet, struct stream_list *dp) {
01396         char forw_data[65535];
01397         unsigned cursor;
01398         struct dhcpv6_relay_packet *relay;
01399         struct option_state *opts;
01400         struct stream_list *up;
01401 
01402         /* Check if the message should be relayed to the server. */
01403         switch (packet->dhcpv6_msg_type) {
01404               case DHCPV6_SOLICIT:
01405               case DHCPV6_REQUEST:
01406               case DHCPV6_CONFIRM:
01407               case DHCPV6_RENEW:
01408               case DHCPV6_REBIND:
01409               case DHCPV6_RELEASE:
01410               case DHCPV6_DECLINE:
01411               case DHCPV6_INFORMATION_REQUEST:
01412               case DHCPV6_RELAY_FORW:
01413               case DHCPV6_LEASEQUERY:
01414                 log_info("Relaying %s from %s port %d going up.",
01415                          dhcpv6_type_names[packet->dhcpv6_msg_type],
01416                          piaddr(packet->client_addr),
01417                          ntohs(packet->client_port));
01418                 break;
01419 
01420               case DHCPV6_ADVERTISE:
01421               case DHCPV6_REPLY:
01422               case DHCPV6_RECONFIGURE:
01423               case DHCPV6_RELAY_REPL:
01424               case DHCPV6_LEASEQUERY_REPLY:
01425                 log_info("Discarding %s from %s port %d going up.",
01426                          dhcpv6_type_names[packet->dhcpv6_msg_type],
01427                          piaddr(packet->client_addr),
01428                          ntohs(packet->client_port));
01429                 return;
01430 
01431               default:
01432                 log_info("Unknown %d type from %s port %d going up.",
01433                          packet->dhcpv6_msg_type,
01434                          piaddr(packet->client_addr),
01435                          ntohs(packet->client_port));
01436                 return;
01437         }
01438 
01439         /* Build the relay-forward header. */
01440         relay = (struct dhcpv6_relay_packet *) forw_data;
01441         cursor = offsetof(struct dhcpv6_relay_packet, options);
01442         relay->msg_type = DHCPV6_RELAY_FORW;
01443         if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
01444                 if (packet->dhcpv6_hop_count >= max_hop_count) {
01445                         log_info("Hop count exceeded,");
01446                         return;
01447                 }
01448                 relay->hop_count = packet->dhcpv6_hop_count + 1;
01449                 if (dp) {
01450                         memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
01451                 } else {
01452                         /* On smart relay add: && !global. */
01453                         if (!use_if_id && downstreams->next) {
01454                                 log_info("Shan't get back the interface.");
01455                                 return;
01456                         }
01457                         memset(&relay->link_address, 0, 16);
01458                 }
01459         } else {
01460                 relay->hop_count = 0;
01461                 if (!dp)
01462                         return;
01463                 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
01464         }
01465         memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
01466 
01467         /* Get an option state. */
01468         opts = NULL;
01469         if (!option_state_allocate(&opts, MDL)) {
01470                 log_fatal("No memory for upwards options.");
01471         }
01472         
01473         /* Add an interface-id (if used). */
01474         if (use_if_id) {
01475                 int if_id;
01476 
01477                 if (dp) {
01478                         if_id = dp->id;
01479                 } else if (!downstreams->next) {
01480                         if_id = downstreams->id;
01481                 } else {
01482                         log_info("Don't know the interface.");
01483                         option_state_dereference(&opts, MDL);
01484                         return;
01485                 }
01486 
01487                 if (!save_option_buffer(&dhcpv6_universe, opts,
01488                                         NULL, (unsigned char *) &if_id,
01489                                         sizeof(int),
01490                                         D6O_INTERFACE_ID, 0)) {
01491                         log_error("Can't save interface-id.");
01492                         option_state_dereference(&opts, MDL);
01493                         return;
01494                 }
01495         }
01496 
01497         /* Add a subscriber-id if desired. */
01498         /* This is for testing rather than general use */
01499         if (dhcrelay_sub_id != NULL) {
01500                 if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
01501                                         (unsigned char *) dhcrelay_sub_id,
01502                                         strlen(dhcrelay_sub_id),
01503                                         D6O_SUBSCRIBER_ID, 0)) {
01504                         log_error("Can't save subsriber-id.");
01505                         option_state_dereference(&opts, MDL);
01506                         return;
01507                 }
01508         }
01509                 
01510 
01511         /* Add the relay-msg carrying the packet. */
01512         if (!save_option_buffer(&dhcpv6_universe, opts,
01513                                 NULL, (unsigned char *) packet->raw,
01514                                 packet->packet_length,
01515                                 D6O_RELAY_MSG, 0)) {
01516                 log_error("Can't save relay-msg.");
01517                 option_state_dereference(&opts, MDL);
01518                 return;
01519         }
01520 
01521         /* Finish the relay-forward message. */
01522         cursor += store_options6(forw_data + cursor,
01523                                  sizeof(forw_data) - cursor,
01524                                  opts, packet, 
01525                                  required_forw_opts, NULL);
01526         option_state_dereference(&opts, MDL);
01527 
01528         /* Send it to all upstreams. */
01529         for (up = upstreams; up; up = up->next) {
01530                 send_packet6(up->ifp, (unsigned char *) forw_data,
01531                              (size_t) cursor, &up->link);
01532         }
01533 }
01534                              
01535 /*
01536  * Process a packet downwards, i.e., from server to client.
01537  */
01538 static void
01539 process_down6(struct packet *packet) {
01540         struct stream_list *dp;
01541         struct option_cache *oc;
01542         struct data_string relay_msg;
01543         const struct dhcpv6_packet *msg;
01544         struct data_string if_id;
01545         struct sockaddr_in6 to;
01546         struct iaddr peer;
01547 
01548         /* The packet must be a relay-reply message. */
01549         if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
01550                 if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
01551                         log_info("Discarding %s from %s port %d going down.",
01552                                  dhcpv6_type_names[packet->dhcpv6_msg_type],
01553                                  piaddr(packet->client_addr),
01554                                  ntohs(packet->client_port));
01555                 else
01556                         log_info("Unknown %d type from %s port %d going down.",
01557                                  packet->dhcpv6_msg_type,
01558                                  piaddr(packet->client_addr),
01559                                  ntohs(packet->client_port));
01560                 return;
01561         }
01562 
01563         /* Inits. */
01564         memset(&relay_msg, 0, sizeof(relay_msg));
01565         memset(&if_id, 0, sizeof(if_id));
01566         memset(&to, 0, sizeof(to));
01567         to.sin6_family = AF_INET6;
01568 #ifdef HAVE_SA_LEN
01569         to.sin6_len = sizeof(to);
01570 #endif
01571         to.sin6_port = remote_port;
01572         peer.len = 16;
01573 
01574         /* Get the relay-msg option (carrying the message to relay). */
01575         oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
01576         if (oc == NULL) {
01577                 log_info("No relay-msg.");
01578                 return;
01579         }
01580         if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
01581                                    packet->options, NULL,
01582                                    &global_scope, oc, MDL) ||
01583             (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
01584                 log_error("Can't evaluate relay-msg.");
01585                 return;
01586         }
01587         msg = (const struct dhcpv6_packet *) relay_msg.data;
01588 
01589         /* Get the interface-id (if exists) and the downstream. */
01590         oc = lookup_option(&dhcpv6_universe, packet->options,
01591                            D6O_INTERFACE_ID);
01592         if (oc != NULL) {
01593                 int if_index;
01594 
01595                 if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
01596                                            packet->options, NULL,
01597                                            &global_scope, oc, MDL) ||
01598                     (if_id.len != sizeof(int))) {
01599                         log_info("Can't evaluate interface-id.");
01600                         goto cleanup;
01601                 }
01602                 memcpy(&if_index, if_id.data, sizeof(int));
01603                 for (dp = downstreams; dp; dp = dp->next) {
01604                         if (dp->id == if_index)
01605                                 break;
01606                 }
01607         } else {
01608                 if (use_if_id) {
01609                         /* Require an interface-id. */
01610                         log_info("No interface-id.");
01611                         goto cleanup;
01612                 }
01613                 for (dp = downstreams; dp; dp = dp->next) {
01614                         /* Get the first matching one. */
01615                         if (!memcmp(&dp->link.sin6_addr,
01616                                     &packet->dhcpv6_link_address,
01617                                     sizeof(struct in6_addr)))
01618                                 break;
01619                 }
01620         }
01621         /* Why bother when there is no choice. */
01622         if (!dp && downstreams && !downstreams->next)
01623                 dp = downstreams;
01624         if (!dp) {
01625                 log_info("Can't find the down interface.");
01626                 goto cleanup;
01627         }
01628         memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
01629         to.sin6_addr = packet->dhcpv6_peer_address;
01630 
01631         /* Check if we should relay the carried message. */
01632         switch (msg->msg_type) {
01633                 /* Relay-Reply of for another relay, not a client. */
01634               case DHCPV6_RELAY_REPL:
01635                 to.sin6_port = local_port;
01636                 /* Fall into: */
01637 
01638               case DHCPV6_ADVERTISE:
01639               case DHCPV6_REPLY:
01640               case DHCPV6_RECONFIGURE:
01641               case DHCPV6_RELAY_FORW:
01642               case DHCPV6_LEASEQUERY_REPLY:
01643                 log_info("Relaying %s to %s port %d down.",
01644                          dhcpv6_type_names[msg->msg_type],
01645                          piaddr(peer),
01646                          ntohs(to.sin6_port));
01647                 break;
01648 
01649               case DHCPV6_SOLICIT:
01650               case DHCPV6_REQUEST:
01651               case DHCPV6_CONFIRM:
01652               case DHCPV6_RENEW:
01653               case DHCPV6_REBIND:
01654               case DHCPV6_RELEASE:
01655               case DHCPV6_DECLINE:
01656               case DHCPV6_INFORMATION_REQUEST:
01657               case DHCPV6_LEASEQUERY:
01658                 log_info("Discarding %s to %s port %d down.",
01659                          dhcpv6_type_names[msg->msg_type],
01660                          piaddr(peer),
01661                          ntohs(to.sin6_port));
01662                 goto cleanup;
01663 
01664               default:
01665                 log_info("Unknown %d type to %s port %d down.",
01666                          msg->msg_type,
01667                          piaddr(peer),
01668                          ntohs(to.sin6_port));
01669                 goto cleanup;
01670         }
01671 
01672         /* Send the message to the downstream. */
01673         send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
01674                      (size_t) relay_msg.len, &to);
01675 
01676       cleanup:
01677         if (relay_msg.data != NULL)
01678                 data_string_forget(&relay_msg, MDL);
01679         if (if_id.data != NULL)
01680                 data_string_forget(&if_id, MDL);
01681 }
01682 
01683 /*
01684  * Called by the dispatch packet handler with a decoded packet.
01685  */
01686 void
01687 dhcpv6(struct packet *packet) {
01688         struct stream_list *dp;
01689 
01690         /* Try all relay-replies downwards. */
01691         if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
01692                 process_down6(packet);
01693                 return;
01694         }
01695         /* Others are candidates to go up if they come from down. */
01696         for (dp = downstreams; dp; dp = dp->next) {
01697                 if (packet->interface != dp->ifp)
01698                         continue;
01699                 process_up6(packet, dp);
01700                 return;
01701         }
01702         /* Relay-forward could work from an unknown interface. */
01703         if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
01704                 process_up6(packet, NULL);
01705                 return;
01706         }
01707 
01708         log_info("Can't process packet from interface '%s'.",
01709                  packet->interface->name);
01710 }
01711 #endif
01712 
01713 /* Stub routines needed for linking with DHCP libraries. */
01714 void
01715 bootp(struct packet *packet) {
01716         return;
01717 }
01718 
01719 void
01720 dhcp(struct packet *packet) {
01721         return;
01722 }
01723 
01724 void
01725 classify(struct packet *p, struct class *c) {
01726         return;
01727 }
01728 
01729 int
01730 check_collection(struct packet *p, struct lease *l, struct collection *c) {
01731         return 0;
01732 }
01733 
01734 isc_result_t
01735 find_class(struct class **class, const char *c1, const char *c2, int i) {
01736         return ISC_R_NOTFOUND;
01737 }
01738 
01739 int
01740 parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
01741         return 0;
01742 }
01743 
01744 isc_result_t
01745 dhcp_set_control_state(control_object_state_t oldstate,
01746                        control_object_state_t newstate) {
01747         if (newstate != server_shutdown)
01748                 return ISC_R_SUCCESS;
01749         exit(0);
01750 }

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1