]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[master] Support using v6 relay options in server decicions.
authorShawn Routhier <sar@isc.org>
Tue, 26 Nov 2013 05:00:41 +0000 (21:00 -0800)
committerShawn Routhier <sar@isc.org>
Tue, 26 Nov 2013 05:00:41 +0000 (21:00 -0800)
15 files changed:
RELNOTES
common/conflex.c
common/dhcp-options.5
common/parse.c
common/tree.c
includes/dhcp6.h
includes/dhcpd.h
includes/dhctoken.h
includes/tree.h
relay/dhcrelay.8
relay/dhcrelay.c
server/confpars.c
server/dhcpd.conf.5
server/dhcpv6.c
server/mdb.c

index 1eff1dcfec9bd5edac5b8fd7362e0117df670985..d0652ab6096f9a282fe468b48912ea9499ad6a27 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -105,6 +105,14 @@ work on other platforms. Please report any problems and suggested fixes to
   This is "failover peer <name>: Both servers normal."
   [ISC-Bugs 33208]
 
+- Add support for accessing options from v6 relays.  The v6relay
+  statement allows the administrator to choose which relay to
+  use when searching for an option, see the dhcp-options man page
+  for a description.  The host-identifer option has also been
+  updated to support the use of relay options see the dhcpd.conf
+  man page for a description.
+  [ISC-Bugs #19598]
+
                        Changes since 4.2.5
 
 - Address static analysis warnings.
index 595ac4d56fe2ce6cd5fce5d42649e8b70feca1ed..9cdb88e579f1c8a1a3000bcece3fe6deb941adcc 100644 (file)
@@ -1501,6 +1501,10 @@ intern(char *atom, enum dhcp_token dfv) {
                        return UPDATE;
                break;
              case 'v':
+               if (!strcasecmp (atom + 1, "6relay"))
+                       return V6RELAY;
+               if (!strcasecmp (atom + 1, "6relopt"))
+                       return V6RELOPT;
                if (!strcasecmp (atom + 1, "endor-class"))
                        return VENDOR_CLASS;
                if (!strcasecmp (atom + 1, "endor"))
index f57fd731e04a5d210c99a734f9011bc848a86b39..7dee4117f26097c2eb97f562128b70847bb33396 100644 (file)
@@ -1712,6 +1712,22 @@ The \fBlq-relay-data\fR option is used internally by for lease query.
 .PP
 The \fBlq-client-link\fR option is used internally by for lease query.
 .RE
+.SH ACCESSING DHCPV6 RELAY OPTIONS
+.PP
+.B v6relay (\fBrelay-number\f, \fBoption\f)
+This option allows access to an option that has been added to a packet
+by a relay agent.  Relay-number value selects the relay to examine
+and option is the option to find.  In DHCPv6 each relay encapsulates
+the entire previous message into an option, adds its own options (if
+any) and sends the result onwards.  The RFC specifies a limit of 32
+hops.  A relay-number of 0 is a no-op and means don't look at the relays.
+1 is the relay that is closest to the client, 2 would be the next in
+from the client and so on.  Any value greater than the max number of hops
+is which is closest to the server independent of number.  To use this
+option in a class statement you would have something like this:
+.PP
+match if v6relay(1, option dhcp6.subscriber-id) = "client_1";
+.RE
 .PP
 .RE
 .SH DEFINING NEW OPTIONS
index d931b97ca157a16f00a190af7fceb5f9bf3b7d1c..dd40bdbfc9fc3f0564863c209f1ba703fed7313d 100644 (file)
@@ -3463,6 +3463,8 @@ int parse_boolean (cfile)
  *                    HARDWARE |
  *                    PACKET LPAREN numeric-expression COMMA
  *                                  numeric-expression RPAREN |
+ *                    V6RELAY LPAREN numeric-expression COMMA
+ *                                   data-expression RPAREN |
  *                    STRING |
  *                    colon_separated_hex_list
  */
@@ -4333,6 +4335,34 @@ int parse_non_binary (expr, cfile, lose, context)
                        goto norparen;
                break;
 
+             case V6RELAY:
+               skip_token(&val, NULL, cfile);
+               if (!expression_allocate (expr, MDL))
+                       log_fatal ("can't allocate expression");
+               (*expr)->op = expr_v6relay;
+
+               token = next_token (&val, NULL, cfile);
+               if (token != LPAREN)
+                       goto nolparen;
+
+               if (!parse_numeric_expression (&(*expr)->data.v6relay.relay,
+                                               cfile, lose))
+                       goto nodata;
+
+               token = next_token (&val, NULL, cfile);
+               if (token != COMMA)
+                       goto nocomma;
+
+               if (!parse_data_expression (&(*expr)->data.v6relay.roption,
+                                           cfile, lose))
+                       goto nodata;
+
+               token = next_token (&val, NULL, cfile);
+
+               if (token != RPAREN)
+                       goto norparen;
+               break;
+
                /* Not a valid start to an expression... */
              default:
                if (token != NAME && token != NUMBER_OR_NAME)
index ba41bbe750910a3b789ece52a55bfd12cfa15e7c..7bb5ee0b3947ae59339afc918ac8f9d8ee82a939 100644 (file)
@@ -1072,6 +1072,7 @@ int evaluate_boolean_expression (result, packet, lease, client_state,
              case expr_filename:
              case expr_sname:
              case expr_gethostname:
+             case expr_v6relay:
                log_error ("Data opcode in evaluate_boolean_expression: %d",
                      expr -> op);
                return 0;
@@ -1136,6 +1137,8 @@ int evaluate_data_expression (result, packet, lease, client_state,
        struct binding *binding;
        unsigned char *s;
        struct binding_value *bv;
+       struct packet *relay_packet;
+       struct option_state *relay_options;
 
        switch (expr -> op) {
                /* Extract N bytes starting at byte M of a data string. */
@@ -1213,6 +1216,7 @@ int evaluate_data_expression (result, packet, lease, client_state,
                                result -> data += data.len - len;
                                result -> len = len;
                        }
+
                        data_string_forget (&data, MDL);
                }
 
@@ -1224,6 +1228,7 @@ int evaluate_data_expression (result, packet, lease, client_state,
                       ? print_hex_2 (result -> len, result -> data, 30)
                       : "NULL"));
 #endif
+
                return s0 && s1;
 
                /* Convert string to lowercase. */
@@ -1294,8 +1299,9 @@ int evaluate_data_expression (result, packet, lease, client_state,
                          s1 ? print_hex_2(result->len, result->data, 30)
                             : "NULL");
 #endif
-                if (s0)
+               if (s0)
                        data_string_forget(&data, MDL);
+
                 return s1;
 
                /* Extract an option. */
@@ -2028,6 +2034,79 @@ int evaluate_data_expression (result, packet, lease, client_state,
                data_string_forget(result, MDL);
                return 0;
 
+               /* Find an option within a v6relay context
+                *
+                * The numeric expression in relay indicates which relay
+                * to try and use as the context.  The relays are numbered
+                * 1 to 32 with 1 being the one closest to the client and
+                * 32 closest to the server.  A value of greater than 33
+                * indicates using the one closest to the server whatever
+                * the count.  A value of 0 indicates not using the relay
+                * options, this is included for completeness and consistency
+                * with the host-identier code.
+                *
+                * The data expression in roption is evaluated in that
+                * context and the result returned.
+                */
+             case expr_v6relay:
+               len = 0;
+               s1 = 0;
+               memset (&data, 0, sizeof data);
+
+               /* Evaluate the relay count */
+               s0 = evaluate_numeric_expression(&len, packet, lease,
+                                                client_state,
+                                                in_options, cfg_options,
+                                                scope,
+                                                expr->data.v6relay.relay);
+
+               /* no number or an obviously invalid number */
+               if ((s0 == 0) ||
+                   ((len > 0) && 
+                    ((packet == NULL) ||
+                     (packet->dhcpv6_container_packet == NULL)))) {
+#if defined (DEBUG_EXPRESSIONS)
+                       log_debug("data: v6relay(%d) = NULL", len);
+#endif
+                       return (0);
+               }
+
+               /* Find the correct packet for the requested relay */
+               i = len;
+               relay_packet = packet;
+               relay_options = in_options;
+               while ((i != 0) && 
+                      (relay_packet->dhcpv6_container_packet != NULL)) {
+                       relay_packet = relay_packet->dhcpv6_container_packet;
+                       relay_options = relay_packet->options;
+                       i--;
+               }
+               /* We wanted a specific relay but were unable to find it */
+               if ((len <= MAX_V6RELAY_HOPS) && (i != 0)) {
+#if defined (DEBUG_EXPRESSIONS)
+                       log_debug("data: v6relay(%d) = NULL", len);
+#endif
+                       return (0);
+               }
+
+               s1 = evaluate_data_expression(&data, relay_packet, lease,
+                                             client_state, relay_options,
+                                             cfg_options, scope,
+                                             expr->data.v6relay.roption,
+                                             MDL);
+
+               if (s1) {
+                       data_string_copy(result, &data, file, line);
+                       data_string_forget(&data, MDL);
+               }
+
+#if defined (DEBUG_EXPRESSIONS)
+               log_debug("data: v6relay(%d) = %s", len, 
+                         s1 ? print_hex_3(result->len, result->data, 30)
+                         : "NULL");
+#endif
+               return (s1);
+
              case expr_check:
              case expr_equal:
              case expr_not_equal:
@@ -2147,6 +2226,7 @@ int evaluate_numeric_expression (result, packet, lease, client_state,
              case expr_leased_address:
              case expr_null:
              case expr_gethostname:
+             case expr_v6relay:
                log_error ("Data opcode in evaluate_numeric_expression: %d",
                      expr -> op);
                return 0;
@@ -2849,6 +2929,16 @@ void expression_dereference (eptr, file, line)
                fundef_dereference (&expr -> data.func, file, line);
                break;
 
+             case expr_v6relay:
+               if (expr->data.v6relay.relay)
+                       expression_dereference(&expr->data.v6relay.relay,
+                                              file, line);
+
+               if (expr->data.v6relay.roption)
+                       expression_dereference(&expr->data.v6relay.roption,
+                                              file, line);
+               break;
+
                /* No subexpressions. */
              case expr_leased_address:
              case expr_lease_time:
@@ -2913,7 +3003,8 @@ int is_data_expression (expr)
                expr->op == expr_leased_address ||
                expr->op == expr_config_option ||
                expr->op == expr_null ||
-               expr->op == expr_gethostname);
+               expr->op == expr_gethostname ||
+               expr->op == expr_v6relay);
 }
 
 int is_numeric_expression (expr)
@@ -2951,7 +3042,8 @@ int is_compound_expression (expr)
                expr -> op == expr_config_option ||
                expr -> op == expr_extract_int8 ||
                expr -> op == expr_extract_int16 ||
-               expr -> op == expr_extract_int32);
+               expr -> op == expr_extract_int32 ||
+               expr -> op == expr_v6relay);
 }
 
 static int op_val (enum expr_op);
@@ -3011,6 +3103,7 @@ static int op_val (op)
              case expr_binary_xor:
              case expr_client_state:
              case expr_gethostname:
+             case expr_v6relay:
                return 100;
 
              case expr_equal:
@@ -3103,6 +3196,7 @@ enum expression_context op_context (op)
              case expr_funcall:
              case expr_function:
              case expr_gethostname:
+             case expr_v6relay:
                return context_any;
 
              case expr_equal:
@@ -3568,6 +3662,19 @@ int write_expression (file, expr, col, indent, firstp)
                col = token_print_indent(file, col, indent, "", "", ")");
                break;
 
+             case expr_v6relay:
+               col = token_print_indent(file, col, indent, "", "",
+                                        "v6relay");
+               col = token_print_indent(file, col, indent, " ", "", "(");
+               scol = col;
+               col = write_expression(file, expr->data.v6relay.relay,
+                                      col, scol, 1);
+               col = token_print_indent (file, col, scol, "", " ", ",");
+               col = write_expression(file, expr->data.v6relay.roption,
+                                      col, scol, 0);
+               col = token_print_indent(file, col, indent, "", "", ")");
+               break;
+
              default:
                log_fatal ("invalid expression type in print_expression: %d",
                           expr -> op);
@@ -3765,6 +3872,17 @@ int data_subexpression_length (int *rv,
                        *rv = lrhs;
                return 1;
                        
+             case expr_v6relay:
+               clhs = data_subexpression_length (&llhs,
+                                                 expr -> data.v6relay.relay);
+               crhs = data_subexpression_length (&lrhs,
+                                                 expr -> data.v6relay.roption);
+               if (crhs == 0 || clhs == 0)
+                       return 0;
+               *rv = llhs + lrhs;
+               return 1;
+               break;
+
              case expr_binary_to_ascii:
              case expr_config_option:
              case expr_host_decl_name:
index 03db160e93fac440d84be235168753cbed3dd7a1..94ddc5927ed34c1be0f28fec224a922b451beb02 100644 (file)
@@ -3,6 +3,7 @@
    DHCPv6 Protocol structures... */
 
 /*
+ * Copyright (c) 2013 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 2006-2009 by Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -193,6 +194,7 @@ struct dhcpv6_relay_packet {
        unsigned char peer_address[16];
        unsigned char options[FLEXIBLE_ARRAY_MEMBER];
 };
+#define MAX_V6RELAY_HOPS 32
 
 /* Leasequery query-types (RFC 5007) */
 
index 6a538555fc624ecc6773daca54c02e7dd96bc54a..c74fab3581b63158d3198a9e6579de24c5820d25 100644 (file)
@@ -882,6 +882,10 @@ struct host_decl {
 #define HOST_DECL_DELETED      1
 #define HOST_DECL_DYNAMIC      2
 #define HOST_DECL_STATIC       4
+       /* For v6 the host-identifer option can specify which relay
+          to use when trying to look up an option.  We store the
+          value here. */
+       int relays;
 };
 
 struct permit {
index 4822279cf09947a559a6cdc789621b4101b28669..7b5770d67c55c4c95eb07b85130326f85b1e487b 100644 (file)
@@ -371,7 +371,9 @@ enum dhcp_token {
        PRIMARY6 = 666,
        SECONDARY6 = 667,
        TOKEN_INFINIBAND = 668,
-       POOL6 = 669
+       POOL6 = 669,
+       V6RELAY = 670,
+       V6RELOPT = 671
 };
 
 #define is_identifier(x)       ((x) >= FIRST_TOKEN &&  \
index e4a78cd163d51409c1347293cca0809f61f8b40f..fd5f7fc51303e7fe382db0059013bf27a384894e 100644 (file)
@@ -196,7 +196,8 @@ enum expr_op {
        expr_lcase,
        expr_regex_match,
        expr_iregex_match,
-       expr_gethostname
+       expr_gethostname,
+       expr_v6relay
 };
 
 struct expression {
@@ -279,6 +280,10 @@ struct expression {
                        struct expression *arglist;
                } funcall;
                struct fundef *func;
+               struct {
+                       struct expression *relay;
+                       struct expression *roption;
+               } v6relay;
        } data;
        int flags;
 #      define EXPR_EPHEMERAL   1
index 21c53bc06e669d4d57347a3ea23efe9818dd698c..d3f4593b9e6f7b2593bd3d3c5990c55959f24956 100644 (file)
@@ -1,6 +1,6 @@
 .\"    dhcrelay.8
 .\"
-.\" Copyright (c) 2009-2012 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2009-2013 by Internet Systems Consortium, Inc. ("ISC")
 .\" Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
 .\" Copyright (c) 1997-2003 by Internet Software Consortium
 .\"
@@ -103,6 +103,10 @@ dhcrelay - Dynamic Host Configuration Protocol Relay Agent
 [
 .B --no-pid
 ]
+[
+.B -s
+.I subscriber-id
+]
 .B -l
 .I lower0
 [
@@ -223,6 +227,11 @@ in use, to disambiguate between them.  The \fB-I\fR option causes
 dhcrelay to send the option even if there is only one downstream
 interface.
 .TP
+-s subscriber-id
+Add an option with the specified subscriber-id into the packet.  This
+feature is for testing rather than production as it will put the same
+subscriber-id into the packet for all clients.
+.TP
 -l [\fIaddress%\fR]\fIifname\fR[\fI#index\fR]
 Specifies the ``lower'' network interface for DHCPv6 relay mode: the
 interface on which queries will be received from clients or from other
index b2d7bd90ce42162cc9ecb493d4398cc80cc01d1a..b65731ced97715df3ce60ce9afd7c54ce1350190 100644 (file)
@@ -116,6 +116,14 @@ struct stream_list {
 static struct stream_list *parse_downstream(char *);
 static struct stream_list *parse_upstream(char *);
 static void setup_streams(void);
+
+/*
+ * A pointer to a subscriber id to add to the message we forward.
+ * This is primarily for testing purposes as we only have one id
+ * for the entire relay and don't determine one per client which
+ * would be more useful.
+ */
+char *dhcrelay_sub_id = NULL;
 #endif
 
 static void do_relay4(struct interface_info *, struct dhcp_packet *,
@@ -147,7 +155,8 @@ static const char url[] =
 "                     [-i interface0 [ ... -i interfaceN]\n" \
 "                     server0 [ ... serverN]\n\n" \
 "       dhcrelay -6   [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
-"                     [-pf <pid-file>] [--no-pid]\n"\
+"                     [-pf <pid-file>] [--no-pid]\n" \
+"                     [-s <subscriber-id>]\n" \
 "                     -l lower0 [ ... -l lowerN]\n" \
 "                     -u upper0 [ ... -u upperN]\n" \
 "       lower (client link): [address%%]interface[#index]\n" \
@@ -155,7 +164,7 @@ static const char url[] =
 #else
 #define DHCRELAY_USAGE \
 "Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
-"                [-pf <pid-file>] [--no-pid]\n"\
+"                [-pf <pid-file>] [--no-pid]\n" \
 "                [-m append|replace|forward|discard]\n" \
 "                [-i interface0 [ ... -i interfaceN]\n" \
 "                server0 [ ... serverN]\n\n"
@@ -362,6 +371,15 @@ main(int argc, char **argv) {
                        sl = parse_upstream(argv[i]);
                        sl->next = upstreams;
                        upstreams = sl;
+               } else if (!strcmp(argv[i], "-s")) {
+                       if (local_family_set && (local_family == AF_INET)) {
+                               usage();
+                       }
+                       local_family_set = 1;
+                       local_family = AF_INET6;
+                       if (++i == argc)
+                               usage();
+                       dhcrelay_sub_id = argv[i];
 #endif
                } else if (!strcmp(argv[i], "-pf")) {
                        if (++i == argc)
@@ -1341,6 +1359,7 @@ setup_streams(void) {
  */
 static const int required_forw_opts[] = {
        D6O_INTERFACE_ID,
+       D6O_SUBSCRIBER_ID,
        D6O_RELAY_MSG,
        0
 };
@@ -1451,6 +1470,20 @@ process_up6(struct packet *packet, struct stream_list *dp) {
                }
        }
 
+       /* Add a subscriber-id if desired. */
+       /* This is for testing rather than general use */
+       if (dhcrelay_sub_id != NULL) {
+               if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
+                                       (unsigned char *) dhcrelay_sub_id,
+                                       strlen(dhcrelay_sub_id),
+                                       D6O_SUBSCRIBER_ID, 0)) {
+                       log_error("Can't save subsriber-id.");
+                       option_state_dereference(&opts, MDL);
+                       return;
+               }
+       }
+               
+
        /* Add the relay-msg carrying the packet. */
        if (!save_option_buffer(&dhcpv6_universe, opts,
                                NULL, (unsigned char *) packet->raw,
index d27ff448e9a5e77dca49c78ecbdccdc4cb3b143e..fa33a0caf92c28335cdc2b98a03804c3a810b32f 100644 (file)
@@ -1974,9 +1974,27 @@ void parse_host_declaration (cfile, group)
                        }
                        skip_token(&val, NULL, cfile);
                        token = next_token(&val, NULL, cfile);
-                       if (token != OPTION) {
+                       if (token == V6RELOPT) {
+                               token = next_token(&val, NULL, cfile);                          
+                               if (token != NUMBER) {
+                                       parse_warn(cfile,
+                                                  "host-identifier v6relopt "
+                                                  "must have a number");
+                                       skip_to_rbrace(cfile, 1);
+                                       break;
+                               }
+                               host->relays = atoi(val);
+                               if (host->relays < 0) {
+                                       parse_warn(cfile,
+                                                  "host-identifer v6relopt "
+                                                  "must have a number >= 0");
+                                       skip_to_rbrace(cfile, 1);
+                                       break;
+                               }
+                       } else if (token != OPTION) {
                                parse_warn(cfile, 
-                                          "host-identifier must be an option");
+                                          "host-identifier must be an option"
+                                          " or v6relopt");
                                skip_to_rbrace(cfile, 1);
                                break;
                        }
index 8a132b8d3580905abf515853f605be3abacaea67..84603c0fd37d8f81f61a23a0d68f680228d0e900 100644 (file)
@@ -2271,6 +2271,10 @@ statement
 .PP
 .B host-identifier option \fIoption-name option-data\fB;\fR
 .PP
+or
+.PP
+.B host-identifier v6relopt \fInumber option-name option-data\fB;\fR
+.PP
 This identifies a DHCPv6 client in a
 .I host
 statement.
@@ -2279,7 +2283,12 @@ is any option, and
 .I option-data
 is the value for the option that the client will send. The 
 .I option-data
-must be a constant value.
+must be a constant value.  In the v6relopts case the additional number
+is the relay to examine for the specified option name and value.  The
+values are the same as for the v6relay option.  0 is a no-op, 1 is the
+relay closest to the client, 2 the next one in and so on.  Values that
+are larger than the maximum number of relays (currently 32) indicate the
+relay closest to the server independent of number.
 .RE
 .PP
 The
index 6daba5e4b77365ff2f81f832c4dd882f334831b7..45ea753cc1a679e91f28179b3c8343f494c63826 100644 (file)
@@ -6299,41 +6299,54 @@ static void
 build_dhcpv6_reply(struct data_string *reply, struct packet *packet) {
        memset(reply, 0, sizeof(*reply));
 
-       /* Classify the client */
-       classify_client(packet);
+       /* I would like to classify the client once here, but
+        * as I don't want to classify all of the incoming packets
+        * I need to do it before handling specific types.
+        * We don't need to classify if we are tossing the packet
+        * or if it is a relay - the classification step will get
+        * done when we process the inner client packet.
+        */
 
        switch (packet->dhcpv6_msg_type) {
                case DHCPV6_SOLICIT:
+                       classify_client(packet);
                        dhcpv6_solicit(reply, packet);
                        break;
                case DHCPV6_ADVERTISE:
                        dhcpv6_discard(packet);
                        break;
                case DHCPV6_REQUEST:
+                       classify_client(packet);
                        dhcpv6_request(reply, packet);
                        break;
                case DHCPV6_CONFIRM:
+                       classify_client(packet);
                        dhcpv6_confirm(reply, packet);
                        break;
                case DHCPV6_RENEW:
+                       classify_client(packet);
                        dhcpv6_renew(reply, packet);
                        break;
                case DHCPV6_REBIND:
+                       classify_client(packet);
                        dhcpv6_rebind(reply, packet);
                        break;
                case DHCPV6_REPLY:
                        dhcpv6_discard(packet);
                        break;
                case DHCPV6_RELEASE:
+                       classify_client(packet);
                        dhcpv6_release(reply, packet);
                        break;
                case DHCPV6_DECLINE:
+                       classify_client(packet);
                        dhcpv6_decline(reply, packet);
                        break;
                case DHCPV6_RECONFIGURE:
                        dhcpv6_discard(packet);
                        break;
                case DHCPV6_INFORMATION_REQUEST:
+                       classify_client(packet);
                        dhcpv6_information_request(reply, packet);
                        break;
                case DHCPV6_RELAY_FORW:
@@ -6343,6 +6356,7 @@ build_dhcpv6_reply(struct data_string *reply, struct packet *packet) {
                        dhcpv6_discard(packet);
                        break;
                case DHCPV6_LEASEQUERY:
+                       classify_client(packet);
                        dhcpv6_leasequery(reply, packet);
                        break;
                case DHCPV6_LEASEQUERY_REPLY:
index ba2c3d326bb6a4b7190fc7ef29d2c3c8f9d63ba7..51c9566e7c4e81fbee8c8441ba82bb8ad70e851b 100644 (file)
@@ -55,10 +55,18 @@ lease_id_hash_t *lease_hw_addr_hash;
  * identifier. Because of this, we store a list with an entry for
  * each option type. Each of these has a hash table, which contains 
  * hash of the option data.
+ *
+ * For v6 we also include a relay count - this specifies which
+ * relay to check for the requested option.  As each different
+ * value of relays creates a new instance admins should use the
+ * same value across each option for all host-identifers.
+ * A value of 0 indicates that we aren't doing relay options
+ * and should simply look in the current option list.
  */
 typedef struct host_id_info {
        struct option *option;
        host_hash_t *values_hash;
+       int relays;
        struct host_id_info *next;
 } host_id_info_t;
 
@@ -146,11 +154,12 @@ static int find_uid_statement (struct executable_statement *esp,
 
 
 static host_id_info_t *
-find_host_id_info(unsigned int option_code) {
+find_host_id_info(unsigned int option_code, int relays) {
        host_id_info_t *p;
 
-       for (p=host_id_info; p != NULL; p = p->next) {
-               if (p->option->code == option_code) {
+       for (p = host_id_info; p != NULL; p = p->next) {
+               if ((p->option->code == option_code) &&
+                   (p->relays == relays)) {
                        break;
                }
        }
@@ -369,7 +378,8 @@ isc_result_t enter_host (hd, dynamicp, commit)
                 * Look for the host identifier information for this option,
                 * and create a new entry if there is none.
                 */
-               h_id_info = find_host_id_info(hd->host_id_option->code);
+               h_id_info = find_host_id_info(hd->host_id_option->code,
+                                             hd->relays);
                if (h_id_info == NULL) {
                        h_id_info = dmalloc(sizeof(*h_id_info), MDL);
                        if (h_id_info == NULL) {
@@ -383,6 +393,7 @@ isc_result_t enter_host (hd, dynamicp, commit)
                                log_fatal("No memory for host-identifier "
                                          "option hash.");
                        }
+                       h_id_info->relays = hd->relays;
                        h_id_info->next = host_id_info;
                        host_id_info = h_id_info;
                }
@@ -638,14 +649,41 @@ find_hosts_by_option(struct host_decl **hp,
        struct option_cache *oc;
        struct data_string data;
        int found;
+       struct packet *relay_packet;
+       struct option_state *relay_state;
        
        for (p = host_id_info; p != NULL; p = p->next) {
+               relay_packet = packet;  
+               relay_state = opt_state;
+
+               /* If this option block is for a relay (relays != 0)
+                * and we are processing the main options and not
+                * options from the IA (packet->options == opt_state)
+                * try to find the proper relay
+                */
+               if ((p->relays != 0) && (packet->options == opt_state)) {
+                       int i = p->relays;
+                       while ((i != 0) &&
+                              (relay_packet->dhcpv6_container_packet != NULL)) {
+                               relay_packet =
+                                       relay_packet->dhcpv6_container_packet;
+                               i--;
+                       }
+                       /* We wanted a specific relay but were
+                        * unable to find it */
+                       if ((p->relays <= MAX_V6RELAY_HOPS) && (i != 0))
+                               continue;
+
+                       relay_state = relay_packet->options;
+               }
+
                oc = lookup_option(p->option->universe, 
-                                  opt_state, p->option->code);
+                                  relay_state, p->option->code);
                if (oc != NULL) {
                        memset(&data, 0, sizeof(data));
-                       if (!evaluate_option_cache(&data, packet, NULL, NULL,
-                                                  opt_state, NULL,
+
+                       if (!evaluate_option_cache(&data, relay_packet, NULL,
+                                                  NULL, relay_state, NULL,
                                                   &global_scope, oc, 
                                                   MDL)) {
                                log_error("Error evaluating option cache");