diff options
-rw-r--r-- | RELNOTES | 8 | ||||
-rw-r--r-- | common/conflex.c | 4 | ||||
-rw-r--r-- | common/dhcp-options.5 | 16 | ||||
-rw-r--r-- | common/parse.c | 30 | ||||
-rw-r--r-- | common/tree.c | 124 | ||||
-rw-r--r-- | includes/dhcp6.h | 2 | ||||
-rw-r--r-- | includes/dhcpd.h | 4 | ||||
-rw-r--r-- | includes/dhctoken.h | 4 | ||||
-rw-r--r-- | includes/tree.h | 7 | ||||
-rw-r--r-- | relay/dhcrelay.8 | 11 | ||||
-rw-r--r-- | relay/dhcrelay.c | 37 | ||||
-rw-r--r-- | server/confpars.c | 22 | ||||
-rw-r--r-- | server/dhcpd.conf.5 | 11 | ||||
-rw-r--r-- | server/dhcpv6.c | 18 | ||||
-rw-r--r-- | server/mdb.c | 52 |
15 files changed, 330 insertions, 20 deletions
@@ -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. diff --git a/common/conflex.c b/common/conflex.c index 595ac4d5..9cdb88e5 100644 --- a/common/conflex.c +++ b/common/conflex.c @@ -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")) diff --git a/common/dhcp-options.5 b/common/dhcp-options.5 index f57fd731..7dee4117 100644 --- a/common/dhcp-options.5 +++ b/common/dhcp-options.5 @@ -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 diff --git a/common/parse.c b/common/parse.c index d931b97c..dd40bdbf 100644 --- a/common/parse.c +++ b/common/parse.c @@ -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) diff --git a/common/tree.c b/common/tree.c index ba41bbe7..7bb5ee0b 100644 --- a/common/tree.c +++ b/common/tree.c @@ -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: diff --git a/includes/dhcp6.h b/includes/dhcp6.h index 03db160e..94ddc592 100644 --- a/includes/dhcp6.h +++ b/includes/dhcp6.h @@ -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) */ diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 6a538555..c74fab35 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -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 { diff --git a/includes/dhctoken.h b/includes/dhctoken.h index 4822279c..7b5770d6 100644 --- a/includes/dhctoken.h +++ b/includes/dhctoken.h @@ -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 && \ diff --git a/includes/tree.h b/includes/tree.h index e4a78cd1..fd5f7fc5 100644 --- a/includes/tree.h +++ b/includes/tree.h @@ -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 diff --git a/relay/dhcrelay.8 b/relay/dhcrelay.8 index 21c53bc0..d3f4593b 100644 --- a/relay/dhcrelay.8 +++ b/relay/dhcrelay.8 @@ -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 diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c index b2d7bd90..b65731ce 100644 --- a/relay/dhcrelay.c +++ b/relay/dhcrelay.c @@ -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, diff --git a/server/confpars.c b/server/confpars.c index d27ff448..fa33a0ca 100644 --- a/server/confpars.c +++ b/server/confpars.c @@ -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; } diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5 index 8a132b8d..84603c0f 100644 --- a/server/dhcpd.conf.5 +++ b/server/dhcpd.conf.5 @@ -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 diff --git a/server/dhcpv6.c b/server/dhcpv6.c index 6daba5e4..45ea753c 100644 --- a/server/dhcpv6.c +++ b/server/dhcpv6.c @@ -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: diff --git a/server/mdb.c b/server/mdb.c index ba2c3d32..51c9566e 100644 --- a/server/mdb.c +++ b/server/mdb.c @@ -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"); |