#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
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)) {
/* 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
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);
/* 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;
}
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;
} 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;
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,
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;
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;
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;
int i;
struct option_cache *op;
struct data_string ds;
- pair pp;
+ pair pp, *hash;
memset (&ds, 0, sizeof ds);
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");
/* 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++] =
/* 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++]
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;
}
/* 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;
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 != ':')
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
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];
return 0;
}
-int server_option_lookup (result, options, code)
- struct data_string *result;
- 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)
+void hashed_option_set (universe, options, option, op)
+ struct universe *universe;
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;
{
struct option_cache *oc, *noc;
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. */
}
}
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) {
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");
+ /* 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 {
- /* 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.");
+ /* 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) +
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 ();
}
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;
+}