]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Make option state support more general.
authorTed Lemon <source@isc.org>
Mon, 5 Apr 1999 15:40:59 +0000 (15:40 +0000)
committerTed Lemon <source@isc.org>
Mon, 5 Apr 1999 15:40:59 +0000 (15:40 +0000)
common/options.c

index 02ab65104ddd4c1b9b1724c3960cb42d7295fa1f..13cfc888af5c07b4f576506545c758cd0211ce66 100644 (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,36 +851,11 @@ int agent_suboption_lookup (result, options, code)
        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;
 
@@ -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");
+       /* 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) +
@@ -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;
+}