From c9605ddb0b312e2e40b3310dcd4eb4d5919c6cec Mon Sep 17 00:00:00 2001 From: Ted Lemon Date: Mon, 5 Apr 1999 15:40:59 +0000 Subject: [PATCH] Make option state support more general. --- common/options.c | 423 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 320 insertions(+), 103 deletions(-) diff --git a/common/options.c b/common/options.c index 02ab6510..13cfc888 100644 --- a/common/options.c +++ b/common/options.c @@ -22,7 +22,7 @@ #ifndef lint static char copyright[] = -"$Id: options.c,v 1.37 1999/03/16 05:50:35 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: options.c,v 1.38 1999/04/05 15:40:59 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #define DHCP_OPTION_DATA @@ -40,8 +40,11 @@ int parse_options (packet) int i; struct option_cache *op = (struct option_cache *)0; - /* Initially, zero all option pointers. */ - memset (&packet -> options, 0, sizeof (packet -> options)); + /* Allocate a new option state. */ + if (!option_state_allocate (&packet -> options, "parse_options")) { + packet -> options_valid = 0; + return 0; + } /* If we don't see the magic cookie, there's nothing to parse. */ if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) { @@ -59,7 +62,7 @@ int parse_options (packet) /* If we parsed a DHCP Option Overload option, parse more options out of the buffer(s) containing them. */ if (packet -> options_valid && - (op = lookup_option (packet -> options.dhcp_hash, + (op = lookup_option (&dhcp_universe, packet -> options, DHO_DHCP_OPTION_OVERLOAD))) { if (op -> data.data [0] & 1) { if (!parse_option_buffer @@ -95,7 +98,7 @@ int parse_option_buffer (packet, buffer, length) struct buffer *bp = (struct buffer *)0; if (!buffer_allocate (&bp, length, "parse_option_buffer")) { - log_error ("parse_option_buffer: no memory for option buffer."); + log_error ("no memory for option buffer."); return 0; } memcpy (bp -> data, buffer, length); @@ -114,9 +117,8 @@ int parse_option_buffer (packet, buffer, length) /* If the length is outrageous, the options are bad. */ if (offset + len + 2 > length) { - log_error ("Option %s length %d overflows input buffer.", - dhcp_options [code].name, - len); + log_error ("Client option %s (%d) larger than buffer.", + dhcp_options [code].name, len); buffer_dereference (&bp, "parse_option_buffer"); return 0; } @@ -126,7 +128,7 @@ int parse_option_buffer (packet, buffer, length) if (code == DHO_DHCP_AGENT_OPTIONS) { if (!parse_agent_information_option (packet, len, buffer + offset + 2)) { - log_error ("malformed agent information option."); + log_error ("bad agent information option."); buffer_dereference (&bp, "parse_option_buffer"); return 0; @@ -134,8 +136,8 @@ int parse_option_buffer (packet, buffer, length) } else { if (!option_cache_allocate (&op, "parse_option_buffer")) { - log_error ("Can't allocate storage for option %s.", - dhcp_options [code].name); + log_error ("No memory for option %s.", + dhcp_options [code].name); buffer_dereference (&bp, "parse_option_buffer"); return 0; @@ -161,7 +163,7 @@ int parse_option_buffer (packet, buffer, length) op -> option = &dhcp_options [code]; /* Now store the option. */ - save_option (packet -> options.dhcp_hash, op); + save_option (&dhcp_universe, packet -> options, op); /* And let go of our reference. */ option_cache_dereference (&op, @@ -196,7 +198,7 @@ int parse_agent_information_option (packet, len, data) dmalloc (op [1] + 1 + sizeof *t, "parse_agent_information_option"); if (!t) - log_fatal ("can't allocate space for option tag data."); + log_fatal ("no memory for option tag data."); /* Link it in at the tail of the list. */ t -> next = (struct option_tag *)0; @@ -215,7 +217,8 @@ int parse_agent_information_option (packet, len, data) log_fatal ("can't allocate space for agent option structure."); /* Find the tail of the list. */ - for (tail = &packet -> options.agent_options; + for (tail = ((struct agent_options **) + &packet -> options -> universes [agent_universe.index]); *tail; tail = &((*tail) -> next)) ; *tail = a; @@ -230,13 +233,12 @@ int parse_agent_information_option (packet, len, data) three seperate buffers if needed. This allows us to cons up a set of vendor options using the same routine. */ -int cons_options (inpacket, outpacket, mms, options, - agent_options, overload, terminate, bootpp, prl) +int cons_options (inpacket, outpacket, + mms, options, overload, terminate, bootpp, prl) struct packet *inpacket; struct dhcp_packet *outpacket; int mms; struct option_state *options; - struct agent_options *agent_options; int overload; /* Overload flags that may be set. */ int terminate; int bootpp; @@ -253,7 +255,7 @@ int cons_options (inpacket, outpacket, mms, options, int i; struct option_cache *op; struct data_string ds; - pair pp; + pair pp, *hash; memset (&ds, 0, sizeof ds); @@ -262,10 +264,10 @@ int cons_options (inpacket, outpacket, mms, options, one in the packet. */ if (!mms && inpacket && - (op = lookup_option (inpacket -> options.dhcp_hash, + (op = lookup_option (&dhcp_universe, inpacket -> options, DHO_DHCP_MAX_MESSAGE_SIZE))) { evaluate_option_cache (&ds, inpacket, - &inpacket -> options, op); + inpacket -> options, op); if (ds.len >= sizeof (u_int16_t)) mms = getUShort (ds.data); data_string_forget (&ds, "cons_options"); @@ -322,8 +324,8 @@ int cons_options (inpacket, outpacket, mms, options, /* Now just tack on the list of all the options we have, and any duplicates will be eliminated. */ for (i = 0; i < OPTION_HASH_SIZE; i++) { - for (pp = options -> dhcp_hash [i]; - pp; pp = pp -> cdr) { + hash = options -> universes [dhcp_universe.index]; + for (pp = hash [i]; pp; pp = pp -> cdr) { op = (struct option_cache *)(pp -> car); if (priority_len < PRIORITY_COUNT) priority_list [priority_len++] = @@ -409,14 +411,17 @@ int cons_options (inpacket, outpacket, mms, options, /* We tack any agent options onto the end of the packet after we've put it together. */ - if (agent_options) { + if (options -> universe_count > agent_universe.index && + options -> universes [agent_universe.index]) { int len = 0; struct agent_options *a; struct option_tag *o; /* Cycle through the options, appending them to the buffer. */ - for (a = options -> agent_options; a; a = a -> next) { + for (a = ((struct agent_options *) + options -> universes [agent_universe.index]); + a; a = a -> next) { if (agentix + a -> length + 3 + DHCP_FIXED_LEN <= dhcp_max_agent_option_packet_length) { outpacket -> options [agentix++] @@ -491,7 +496,7 @@ int store_options (buffer, buflen, options, priority_list, priority_len, int length; /* If no data is available for this option, skip it. */ - if (!(oc = lookup_option (options -> dhcp_hash, code))) { + if (!(oc = lookup_option (&dhcp_universe, options, code))) { continue; } @@ -595,7 +600,7 @@ char *pretty_print_option (code, data, len, emit_commas, emit_quotes) /* Figure out the size of the data. */ for (i = 0; dhcp_options [code].format [i]; i++) { if (!numhunk) { - log_error ("%s: Excess information in format string: %s\n", + log_error ("%s: Extra codes in format string: %s\n", dhcp_options [code].name, &(dhcp_options [code].format [i])); break; @@ -718,7 +723,8 @@ char *pretty_print_option (code, data, len, emit_commas, emit_quotes) strcpy (op, *dp++ ? "true" : "false"); break; default: - log_error ("Unexpected format code %c", fmtbuf [j]); + log_error ("Unexpected format code %c", + fmtbuf [j]); } op += strlen (op); if (j + 1 < numelem && comma != ':') @@ -753,20 +759,21 @@ void do_packet (interface, packet, len, from_port, from, hfrom) tp.haddr = hfrom; if (packet -> hlen > sizeof packet -> chaddr) { - log_info ("Discarding packet with bogus hardware address length."); + log_info ("Discarding packet with bogus hlen."); return; } if (!parse_options (&tp)) { - option_state_dereference (&tp.options); + if (tp.options) + option_state_dereference (&tp.options, "do_packet"); return; } if (tp.options_valid && - (op = lookup_option (tp.options.dhcp_hash, + (op = lookup_option (&dhcp_universe, tp.options, DHO_DHCP_MESSAGE_TYPE))) { struct data_string dp; memset (&dp, 0, sizeof dp); - evaluate_option_cache (&dp, &tp, &tp.options, op); + evaluate_option_cache (&dp, &tp, tp.options, op); if (dp.len > 0) tp.packet_type = dp.data [0]; else @@ -779,46 +786,57 @@ void do_packet (interface, packet, len, from_port, from, hfrom) else bootp (&tp); - option_state_dereference (&tp.options); + option_state_dereference (&tp.options, "do_packet"); } -int dhcp_option_lookup (result, options, code) +int hashed_option_get (result, universe, options, code) struct data_string *result; + struct universe *universe; struct option_state *options; int code; { struct option_cache *oc; - if (!(oc = lookup_option (options -> dhcp_hash, code))) + if (!universe -> lookup_func) + return 0; + oc = ((*universe -> lookup_func) + (universe, options -> universes [universe -> index], code)); + if (!oc) return 0; if (!evaluate_option_cache (result, (struct packet *)0, options, oc)) return 0; return 1; } -int agent_suboption_lookup (result, options, code) +int agent_option_get (result, universe, options, code) struct data_string *result; + struct universe *universe; struct option_state *options; int code; { struct agent_options *ao; struct option_tag *t; + /* Make sure there's agent option state. */ + if (universe -> index >= options -> universe_count || + !(options -> universes [universe -> index])) + return 0; + ao = (struct agent_options *)options -> universes [universe -> index]; + /* Find the last set of agent options and consider it definitive. */ - for (ao = options -> agent_options; ao -> next; ao = ao -> next) + for (; ao -> next; ao = ao -> next) ; if (ao) { for (t = ao -> first; t; t = t -> next) { if (t -> data [0] == code) { result -> len = t -> data [1]; - if (!buffer_allocate (&result -> buffer, - result -> len + 1, - "agent_suboption_lookup" - )) { + if (!(buffer_allocate + (&result -> buffer, result -> len + 1, + "agent_suboption_get"))) { result -> len = 0; buffer_dereference (&result -> buffer, - "agent_suboption_lookup"); + "agent_suboption_get"); return 0; } result -> data = &result -> buffer -> data [0]; @@ -833,34 +851,9 @@ int agent_suboption_lookup (result, options, code) return 0; } -int server_option_lookup (result, options, code) - struct data_string *result; +void hashed_option_set (universe, options, option, op) + struct universe *universe; struct option_state *options; - int code; -{ - return 0; -} - -void dhcp_option_set (options, option, op) - struct option_state *options; - struct option_cache *option; - enum statement_op op; -{ - struct option_cache *thecache; - - do_option_set (options -> dhcp_hash, option, op); -} - -void server_option_set (options, option, op) - struct option_state *options; - struct option_cache *option; - enum statement_op op; -{ - do_option_set (options -> server_hash, option, op); -} - -static void do_option_set (hash, option, op) - pair *hash; struct option_cache *option; enum statement_op op; { @@ -876,22 +869,24 @@ static void do_option_set (hash, option, op) break; case default_option_statement: - oc = lookup_option (hash, option -> option -> code); + oc = lookup_option (universe, options, + option -> option -> code); if (oc) break; - save_option (hash, option); + save_option (universe, options, option); break; case supersede_option_statement: /* Install the option, replacing any existing version. */ - save_option (hash, option); + save_option (universe, options, option); break; case append_option_statement: case prepend_option_statement: - oc = lookup_option (hash, option -> option -> code); + oc = lookup_option (universe, options, + option -> option -> code); if (!oc) { - save_option (hash, option); + save_option (universe, options, option); break; } /* If it's not an expression, make it into one. */ @@ -928,18 +923,40 @@ static void do_option_set (hash, option, op) } } noc -> option = oc -> option; - save_option (hash, noc); + save_option (universe, options, noc); option_cache_dereference (&noc, "do_option_set"); break; } } -struct option_cache *lookup_option (hash, code) - pair *hash; +struct option_cache *lookup_option (universe, options, code) + struct universe *universe; + struct option_state *options; + int code; +{ + if (universe -> lookup_func) + return (*universe -> lookup_func) (universe, options, code); + else + log_error ("can't look up options in %s space.", + universe -> name); + return (struct option_cache *)0; +} + +struct option_cache *lookup_hashed_option (universe, options, code) + struct universe *universe; + struct option_state *options; int code; { int hashix; pair bptr; + pair *hash; + + /* Make sure there's a hash table. */ + if (universe -> index >= options -> universe_count || + !(options -> universes [universe -> index])) + return (struct option_cache *)0; + + hash = options -> universes [universe -> index]; hashix = ((code & 31) + ((code >> 5) & 31)) % 17; for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { @@ -950,49 +967,101 @@ struct option_cache *lookup_option (hash, code) return (struct option_cache *)0; } -void save_option (hash, oc) - pair *hash; +void save_option (universe, options, oc) + struct universe *universe; + struct option_state *options; + struct option_cache *oc; +{ + if (universe -> save_func) + (*universe -> save_func) (universe, options, oc); + else + log_error ("can't store options in %s space.", + universe -> name); +} + +void save_hashed_option (universe, options, oc) + struct universe *universe; + struct option_state *options; struct option_cache *oc; { int hashix; pair bptr; + pair *hash = options -> universes [universe -> index]; - /* Try to find an existing option matching the new one. */ + /* Compute the hash. */ hashix = ((oc -> option -> code & 31) + ((oc -> option -> code >> 5) & 31)) % 17; - for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { - if (((struct option_cache *)(bptr -> car)) -> option -> code == - oc -> option -> code) - break; - } - /* If we find one, dereference it and put the new one in its place. */ - if (bptr) { - option_cache_dereference ((struct option_cache **)&bptr -> car, - "save_option"); - option_cache_reference ((struct option_cache **)&bptr -> car, - oc, "save_option"); - } else { - /* Otherwise, just put the new one at the head of the list. */ - bptr = new_pair ("save_option"); - if (!bptr) { - log_error ("No memory for option_cache reference."); + /* If there's no hash table, make one. */ + if (!hash) { + hash = (pair *)dmalloc (OPTION_HASH_SIZE * sizeof *hash, + "save_hashed_options"); + if (!hash) { + log_error ("no memory to store %s.%s", + universe -> name, oc -> option -> name); + return; + } + memset (hash, 0, OPTION_HASH_SIZE * sizeof *hash); + options -> universes [universe -> index] = (VOIDPTR)hash; + } else { + /* Try to find an existing option matching the new one. */ + for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) { + if (((struct option_cache *) + (bptr -> car)) -> option -> code == + oc -> option -> code) + break; + } + + /* If we find one, dereference it and put the new one + in its place. */ + if (bptr) { + option_cache_dereference + ((struct option_cache **)&bptr -> car, + "save_option"); + option_cache_reference + ((struct option_cache **)&bptr -> car, + oc, "save_option"); return; } - bptr -> cdr = hash [hashix]; - bptr -> car = 0; - option_cache_reference ((struct option_cache **)&bptr -> car, - oc, "save_option"); - hash [hashix] = bptr; } + + /* Otherwise, just put the new one at the head of the list. */ + bptr = new_pair ("save_option"); + if (!bptr) { + log_error ("No memory for option_cache reference."); + return; + } + bptr -> cdr = hash [hashix]; + bptr -> car = 0; + option_cache_reference ((struct option_cache **)&bptr -> car, + oc, "save_option"); + hash [hashix] = bptr; } -void delete_option (hash, code) - pair *hash; +void delete_option (universe, options, code) + struct universe *universe; + struct option_state *options; + int code; +{ + if (universe -> delete_func) + (*universe -> delete_func) (universe, options, code); + else + log_error ("can't delete options from %s space.", + universe -> name); +} + +void delete_hashed_option (universe, options, code) + struct universe *universe; + struct option_state *options; int code; { int hashix; pair bptr, prev = (pair)0; + pair *hash = options -> universes [universe -> index]; + + /* There may not be any options in this space. */ + if (!hash) + return; /* Try to find an existing option matching the new one. */ hashix = ((code & 31) + @@ -1023,7 +1092,8 @@ int option_cache_dereference (ptr, name) char *name; { if (!ptr || !*ptr) { - log_error ("Null pointer in option_cache_dereference: %s", name); + log_error ("Null pointer in option_cache_dereference: %s", + name); abort (); } @@ -1041,3 +1111,150 @@ int option_cache_dereference (ptr, name) return 1; } + +int hashed_option_state_dereference (universe, state) + struct universe *universe; + struct option_state *state; +{ + pair *heads; + pair cp, next; + int i; + + /* Get the pointer to the array of hash table bucket heads. */ + heads = (pair *)(state -> universes [universe -> index]); + if (!heads) + return 0; + + /* For each non-null head, loop through all the buckets dereferencing + the attached option cache structures and freeing the buckets. */ + for (i = 0; i < OPTION_HASH_SIZE; i++) { + for (cp = heads [i]; cp; cp = next) { + next = cp -> cdr; + option_cache_dereference + ((struct option_cache **)&cp -> car, + "option_state_dereference"); + free_pair (cp, "hashed_option_state_dereference"); + } + } + + dfree (heads, "hashed_option_state_dereference"); + state -> universes [universe -> index] = (void *)0; + return 1; +} + +int agent_option_state_dereference (universe, state) + struct universe *universe; + struct option_state *state; +{ + struct agent_options *a, *na; + struct option_tag *ot, *not; + + if (universe -> index >= state -> universe_count || + !state -> universes [universe -> index]) + return 0; + + /* We can also release the agent options, if any... */ + for (a = (struct agent_options *)(state -> universes + [universe -> index]); a; a = na) { + na = a -> next; + for (ot = a -> first; ot; ot = not) { + not = ot -> next; + free (ot); + } + } + + dfree (state -> universes [universe -> index], + "agent_option_state_dereference"); + state -> universes [universe -> index] = (void *)0; + return 1; +} + +int store_option (result, universe, oc) + struct data_string *result; + struct universe *universe; + struct option_cache *oc; +{ + struct data_string d1, d2; + + memset (&d1, 0, sizeof d1); + memset (&d2, 0, sizeof d2); + + if (evaluate_option_cache (&d2, (struct packet *)0, + (struct option_state *)0, oc)) { + if (!buffer_allocate (&d1.buffer, + (result -> len + + universe -> length_size + + universe -> tag_size + + d2.len), "store_option")) { + data_string_forget (result, "store_option"); + data_string_forget (&d2, "store_option"); + return 0; + } + d1.data = &d1.buffer -> data [0]; + if (result -> len) + memcpy (d1.data, result -> data, result -> len); + d1.len = result -> len; + (*universe -> store_tag) (&d1.data [d1.len], + oc -> option -> code); + d1.len += universe -> tag_size; + (*universe -> store_length) (&d1.data [d1.len], d2.len); + d1.len += universe -> length_size; + memcpy (&d1.data [d1.len], d2.data, d2.len); + d1.len += d2.len; + data_string_forget (&d2, "store_option"); + data_string_forget (result, "store_option"); + data_string_copy (result, &d1, "store_option"); + data_string_forget (&d1, "store_option"); + return 1; + } + return 0; +} + +int option_space_encapsulate (result, options, name) + struct data_string *result; + struct option_state *options; + struct data_string *name; +{ + struct universe *u; + + u = (struct universe *)hash_lookup (&universe_hash, + name -> data, name -> len); + if (!u) { + log_error ("unknown option space %s.", name -> data); + return 0; + } + + if (u -> encapsulate) + return (*u -> encapsulate) (result, options, u); + log_error ("encapsulation requested for %s with no support.", + name -> data); + return 0; +} + +int hashed_option_space_encapsulate (result, options, universe) + struct data_string *result; + struct option_state *options; + struct universe *universe; +{ + pair p, *hash; + int status; + int i; + + if (universe -> index >= options -> universe_count) + return 0; + + hash = options -> universes [universe -> index]; + if (!hash) + return 0; + + status = 0; + for (i = 0; i < OPTION_HASH_SIZE; i++) { + for (p = hash [i]; p; p = p -> cdr) { + if (store_option (result, universe, + (struct option_cache *)p -> car)) + status = 1; + } + } + + return status; +}