2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-29 21:38:10 +00:00

Make option state support more general.

This commit is contained in:
Ted Lemon 1999-04-05 15:40:59 +00:00
parent 0d252fd9bf
commit c9605ddb0b

View File

@ -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;
}