]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
-n [master]
authorShawn Routhier <sar@isc.org>
Tue, 27 Aug 2013 21:20:09 +0000 (14:20 -0700)
committerShawn Routhier <sar@isc.org>
Tue, 27 Aug 2013 21:20:09 +0000 (14:20 -0700)
 26510
 Add support for classes in the IPv6 code

RELNOTES
common/conflex.c
includes/dhcpd.h
includes/dhctoken.h
server/confpars.c
server/dhcpd.conf.5
server/dhcpv6.c
server/mdb6.c
tests/DHCPv6/README

index dabff9e62a3d91aca2fcf7ec1028e0a41f3da997..3e73ae7af5a91dd50d9dcdd3f166894e9fda2d2c 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -72,6 +72,9 @@ work on other platforms. Please report any problems and suggested fixes to
 - Add support in v6 for on-commit, on-expire and on-release.
   [ISC-Bugs #27912
 
+- Add support for using classes with v6.
+  [ISC-Bugs #26510]
+
                        Changes since 4.2.5
 
 - Address static analysis warnings.
index ce688d53243320a17fe19ba4d0135a69f59183bf..f5d08b2b2109d17a6209345457f745a586bc3b2e 100644 (file)
@@ -1231,6 +1231,8 @@ intern(char *atom, enum dhcp_token dfv) {
                        return PACKET;
                if (!strcasecmp (atom + 1, "ool"))
                        return POOL;
+               if (!strcasecmp (atom + 1, "ool6"))
+                       return POOL6;
                if (!strcasecmp (atom + 1, "refix6"))
                        return PREFIX6;
                if (!strcasecmp (atom + 1, "seudo"))
index 0e96d5b396e89ccfc04edf4e911b3b8ac2f688a0..7bb74c5d2e3bdc811d5af3e78e291925aa50b0a2 100644 (file)
@@ -32,6 +32,8 @@
  * ``http://www.nominum.com''.
  */
 
+/*! \file includes/dhcpd.h */
+
 #include "config.h"
 
 #ifndef __CYGWIN32__
@@ -933,9 +935,7 @@ struct shared_network {
        struct subnet *subnets;
        struct interface_info *interface;
        struct pool *pools;
-       struct ipv6_pool **ipv6_pools;          /* NULL-terminated array */
-       int last_ipv6_pool;                     /* offset of last IPv6 pool
-                                                  used to issue a lease */
+       struct ipv6_pond *ipv6_pond;
        struct group *group;
 #if defined (FAILOVER_PROTOCOL)
        dhcp_failover_state_t *failover_peer;
@@ -1530,6 +1530,26 @@ extern ia_hash_t *ia_na_active;
 extern ia_hash_t *ia_ta_active;
 extern ia_hash_t *ia_pd_active;
 
+/*!
+ *
+ * \brief ipv6_pool structure
+ *
+ * This structure is part of a range of addresses or prefixes.
+ * A range6 or prefix6 statement will map to one or more of these
+ * with each pool being a simple block of the form xxxx/yyy and
+ * all the pools adding up to comprise the entire range.  When
+ * choosing an address or prefix the code will walk through the
+ * pools until it finds one that is available.
+ *
+ * The naming for this structure is unfortunate as there is also
+ * a v4 pool structure and the two are not equivalent.  The v4
+ * pool matches the ipv6_pond structure.  I considered changing the
+ * name of this structure but concluded that doing so would be worse
+ * than leaving it as is.  Changing it adds some risk and makes for
+ * larger differences between the 4.1 & 4.2 code and the 4.3 code.
+ *
+ */
+
 struct ipv6_pool {
        int refcnt;                             /* reference count */
        u_int16_t pool_type;                    /* IA_xx */
@@ -1545,6 +1565,34 @@ struct ipv6_pool {
        struct shared_network *shared_network;  /* shared_network for
                                                   this pool */
        struct subnet *subnet;                  /* subnet for this pool */
+       struct ipv6_pond *ipv6_pond;            /* pond for this pool */
+};
+
+/*!
+ *
+ * \brief ipv6_pond structure
+ *
+ * This structure is the ipv6 version of the v4 pool structure.
+ * It contains the address and prefix information via the pointers
+ * to the ipv6_pools and the allowability of this pool for a given
+ * client via the permit lists and the valid TIMEs.
+ *
+ */
+
+struct ipv6_pond {
+       int refcnt;
+       struct ipv6_pond *next;
+       struct group *group;
+       struct shared_network *shared_network; /* backpointer to the enclosing
+                                                 shared network */
+       struct permit *permit_list;     /* allow clients from this list */
+       struct permit *prohibit_list;   /* deny clients from this list */
+       TIME valid_from;                /* deny pool use before this date */
+       TIME valid_until;               /* deny pool use after this date */
+
+       struct ipv6_pool **ipv6_pools;  /* NULL-terminated array */
+       int last_ipv6_pool;             /* offset of last IPv6 pool
+                                          used to issue a lease */
 };
 
 /* Flags and state for dhcp_ddns_cb_t */
@@ -1945,14 +1993,17 @@ int parse_ip6_addr_expr(struct expression **, struct parse *);
 int parse_ip6_prefix(struct parse *, struct iaddr *, u_int8_t *);
 void parse_address_range (struct parse *, struct group *, int,
                          struct pool *, struct lease **);
-void parse_address_range6(struct parse *cfile, struct group *group);
-void parse_prefix6(struct parse *cfile, struct group *group);
+void parse_address_range6(struct parse *cfile, struct group *group,
+                         struct ipv6_pond *);
+void parse_prefix6(struct parse *cfile, struct group *group,
+                         struct ipv6_pond *);
 void parse_fixed_prefix6(struct parse *cfile, struct host_decl *host_decl);
 void parse_ia_na_declaration(struct parse *);
 void parse_ia_ta_declaration(struct parse *);
 void parse_ia_pd_declaration(struct parse *);
 void parse_server_duid(struct parse *cfile);
 void parse_server_duid_conf(struct parse *cfile);
+void parse_pool6_statement (struct parse *, struct group *, int);
 
 /* ddns.c */
 int ddns_updates(struct packet *, struct lease *, struct lease *,
@@ -3549,6 +3600,13 @@ isc_result_t find_ipv6_pool(struct ipv6_pool **pool, u_int16_t type,
                            const struct in6_addr *addr);
 isc_boolean_t ipv6_in_pool(const struct in6_addr *addr,
                           const struct ipv6_pool *pool);
+isc_result_t ipv6_pond_allocate(struct ipv6_pond **pond,
+                               const char *file, int line);
+isc_result_t ipv6_pond_reference(struct ipv6_pond **pond,
+                                struct ipv6_pond *src,
+                                const char *file, int line);
+isc_result_t ipv6_pond_dereference(struct ipv6_pond **pond,
+                                  const char *file, int line);
 
 isc_result_t renew_leases(struct ia_xx *ia);
 isc_result_t release_leases(struct ia_xx *ia);
index d88a157f82777268d2f498eb012eda805bb2cf4f..6ca68c39b223a1ef2623d4848f8aa9c5dc229ee6 100644 (file)
@@ -3,7 +3,7 @@
    Tokens for config file lexer and parser. */
 
 /*
- * Copyright (c) 2011-2012 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2011-2013 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 2004,2007-2009 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 1996-2003 by Internet Software Consortium
  *
@@ -365,7 +365,8 @@ enum dhcp_token {
        GETHOSTBYNAME = 665,
        PRIMARY6 = 666,
        SECONDARY6 = 667,
-       TOKEN_INFINIBAND = 668
+       TOKEN_INFINIBAND = 668,
+       POOL6 = 669
 };
 
 #define is_identifier(x)       ((x) >= FIRST_TOKEN &&  \
index 3cd35a03b04928a42a94bd33d136eca16b375576..d27ff448e9a5e77dca49c78ecbdccdc4cb3b143e 100644 (file)
  * ``http://www.nominum.com''.
  */
 
+/*! \file server/confpars.c */
+
 #include "dhcpd.h"
 
 static unsigned char global_host_once = 1;
-static unsigned char dhcpv6_class_once = 1;
 
 static int parse_binding_value(struct parse *cfile,
                                struct binding_value *value);
@@ -634,7 +635,7 @@ int parse_statement (cfile, group, type, host_decl, declaration)
                        skip_to_semi(cfile);
                        return declaration;
                }
-               parse_address_range6(cfile, group);
+               parse_address_range6(cfile, group, NULL);
                return declaration;
 
              case PREFIX6:
@@ -645,7 +646,7 @@ int parse_statement (cfile, group, type, host_decl, declaration)
                        skip_to_semi(cfile);
                        return declaration;
                }
-               parse_prefix6(cfile, group);
+               parse_prefix6(cfile, group, NULL);
                return declaration;
 
              case FIXED_PREFIX6:
@@ -660,6 +661,19 @@ int parse_statement (cfile, group, type, host_decl, declaration)
                parse_fixed_prefix6(cfile, host_decl);
                break;
 
+             case POOL6:
+               skip_token(&val, NULL, cfile);
+               if (type == POOL_DECL) {
+                       parse_warn (cfile, "pool declared within pool.");
+                       skip_to_semi(cfile);
+               } else if (type != SUBNET_DECL) {
+                       parse_warn (cfile, "pool declared outside of network");
+                       skip_to_semi(cfile);
+               } else 
+                       parse_pool6_statement (cfile, group, type);
+
+               return declaration;
+
 #endif /* DHCPv6 */
 
              case TOKEN_NOT:
@@ -1377,6 +1391,163 @@ void parse_failover_state (cfile, state, stos)
 }
 #endif /* defined (FAILOVER_PROTOCOL) */
 
+/*! 
+ * 
+ * \brief Parse allow and deny statements
+ *
+ * This function handles the common processing code for permit and deny
+ * statements in the parse_pool_statement and parse_pool6_statement functions.
+ * It reads in the configuration and constructs a new permit structure that it
+ * attachs to the permit_head passed in from the caller.
+ * 
+ * The allow or deny token should already be consumed, this function expects
+ * one of the following:
+ *   known-clients;
+ *   unknown-clients;
+ *   known clients;
+ *   unknown clients;
+ *   authenticated clients;
+ *   unauthenticated clients;
+ *   all clients;
+ *   dynamic bootp clients;
+ *   members of <class name>;
+ *   after <date>;
+ *
+ * \param[in] cfile       = the configuration file being parsed
+ * \param[in] permit_head = the head of the permit list (permit or prohibit)
+ *                         to which to attach the newly created  permit structure
+ * \param[in] is_allow    = 1 if this is being invoked for an allow statement
+ *                       = 0 if this is being invoked for a deny statement
+ * \param[in] valid_from   = pointers to the time values from the enclosing pool
+ * \param[in] valid_until    or pond structure. One of them will be filled in if
+ *                          the configuration includes an "after" clause
+ */
+
+void get_permit(cfile, permit_head, is_allow, valid_from, valid_until)
+       struct parse *cfile;
+       struct permit **permit_head;
+       int is_allow;
+       TIME *valid_from, *valid_until;
+{
+       enum dhcp_token token;
+       struct permit *permit;
+       const char *val;
+       int need_clients = 1;
+       TIME t;
+
+       /* Create our permit structure */
+       permit = new_permit(MDL);
+       if (!permit)
+               log_fatal ("no memory for permit");
+
+       token = next_token(&val, NULL, cfile);
+       switch (token) {
+             case UNKNOWN:
+               permit->type = permit_unknown_clients;
+               break;
+                               
+             case KNOWN_CLIENTS:
+               need_clients = 0;
+               permit->type = permit_known_clients;
+               break;
+
+             case UNKNOWN_CLIENTS:
+               need_clients = 0;
+               permit->type = permit_unknown_clients;
+               break;
+
+             case KNOWN:
+               permit->type = permit_known_clients;
+               break;
+                               
+             case AUTHENTICATED:
+               permit->type = permit_authenticated_clients;
+               break;
+                               
+             case UNAUTHENTICATED:
+               permit->type = permit_unauthenticated_clients;
+               break;
+
+             case ALL:
+               permit->type = permit_all_clients;
+               break;
+                               
+             case DYNAMIC:
+               permit->type = permit_dynamic_bootp_clients;
+               if (next_token (&val, NULL, cfile) != TOKEN_BOOTP) {
+                       parse_warn (cfile, "expecting \"bootp\"");
+                       skip_to_semi (cfile);
+                       free_permit (permit, MDL);
+                       return;
+               }
+               break;
+
+             case MEMBERS:
+               need_clients = 0;
+               if (next_token (&val, NULL, cfile) != OF) {
+                       parse_warn (cfile, "expecting \"of\"");
+                       skip_to_semi (cfile);
+                       free_permit (permit, MDL);
+                       return;
+               }
+               if (next_token (&val, NULL, cfile) != STRING) {
+                       parse_warn (cfile, "expecting class name.");
+                       skip_to_semi (cfile);
+                       free_permit (permit, MDL);
+                       return;
+               }
+               permit->type = permit_class;
+               permit->class = NULL;
+               find_class(&permit->class, val, MDL);
+               if (!permit->class)
+                       parse_warn(cfile, "no such class: %s", val);
+               break;
+
+             case AFTER:
+               need_clients = 0;
+               if (*valid_from || *valid_until) {
+                       parse_warn(cfile, "duplicate \"after\" clause.");
+                       skip_to_semi(cfile);
+                       free_permit(permit, MDL);
+                       return;
+               }
+               t = parse_date_core(cfile);
+               permit->type = permit_after;
+               permit->after = t;
+               if (is_allow) {
+                       *valid_from = t;
+               } else {
+                       *valid_until = t;
+               }
+               break;
+
+             default:
+               parse_warn (cfile, "expecting permit type.");
+               skip_to_semi (cfile);
+               free_permit (permit, MDL);
+               return;
+       }
+
+       /*
+        * The need_clients flag is set if we are expecting the
+        * CLIENTS token
+        */
+       if ((need_clients != 0)  &&
+           (next_token (&val, NULL, cfile) != CLIENTS)) {
+               parse_warn (cfile, "expecting \"clients\"");
+               skip_to_semi (cfile);
+               free_permit (permit, MDL);
+               return;
+       }
+
+       while (*permit_head)
+               permit_head = &((*permit_head)->next);
+       *permit_head = permit;
+       parse_semi (cfile);
+
+       return;
+}
+
 /* Permit_list_match returns 1 if every element of the permit list in lhs
    also appears in rhs.   Note that this doesn't by itself mean that the
    two lists are equal - to check for equality, permit_list_match has to
@@ -1407,6 +1578,25 @@ int permit_list_match (struct permit *lhs, struct permit *rhs)
        return 1;
 }
 
+/*!
+ *
+ * \brief Parse a pool statement
+ *
+ * Pool statements are used to group declarations and permit & deny information
+ * with a specific address range.  They must be declared within a shared network
+ * or subnet and there may be multiple pools withing a shared network or subnet.
+ * Each pool may have a different set of permit or deny options.
+ *
+ * \param[in] cfile = the configuration file being parsed
+ * \param[in] group = the group structure for this pool
+ * \param[in] type  = the type of the enclosing statement.  This must be
+ *                   SHARED_NET_DECL or SUBNET_DECL for this function.
+ *
+ * \return
+ * void - This function either parses the statement and updates the structures
+ *        or it generates an error message and possible halts the program if
+ *        it encounters a problem.
+ */
 void parse_pool_statement (cfile, group, type)
        struct parse *cfile;
        struct group *group;
@@ -1416,27 +1606,23 @@ void parse_pool_statement (cfile, group, type)
        const char *val;
        int done = 0;
        struct pool *pool, **p, *pp;
-       struct permit *permit;
-       struct permit **permit_head;
        int declaration = 0;
        isc_result_t status;
-       struct lease *lpchain = (struct lease *)0, *lp;
-       TIME t;
-       int is_allow = 0;
+       struct lease *lpchain = NULL, *lp;
 
-       pool = (struct pool *)0;
-       status = pool_allocate (&pool, MDL);
+       pool = NULL;
+       status = pool_allocate(&pool, MDL);
        if (status != ISC_R_SUCCESS)
                log_fatal ("no memory for pool: %s",
                           isc_result_totext (status));
 
        if (type == SUBNET_DECL)
-               shared_network_reference (&pool -> shared_network,
-                                         group -> subnet -> shared_network,
-                                         MDL);
+               shared_network_reference(&pool->shared_network,
+                                        group->subnet->shared_network,
+                                        MDL);
        else if (type == SHARED_NET_DECL)
-               shared_network_reference (&pool -> shared_network,
-                                         group -> shared_network, MDL);
+               shared_network_reference(&pool->shared_network,
+                                        group->shared_network, MDL);
        else {
                parse_warn(cfile, "Dynamic pools are only valid inside "
                                  "subnet or shared-network statements.");
@@ -1450,196 +1636,86 @@ void parse_pool_statement (cfile, group, type)
 
 #if defined (FAILOVER_PROTOCOL)
        /* Inherit the failover peer from the shared network. */
-       if (pool -> shared_network -> failover_peer)
+       if (pool->shared_network->failover_peer)
            dhcp_failover_state_reference
-                   (&pool -> failover_peer, 
-                    pool -> shared_network -> failover_peer, MDL);
+                   (&pool->failover_peer, 
+                    pool->shared_network->failover_peer, MDL);
 #endif
 
-       if (!parse_lbrace (cfile)) {
-               pool_dereference (&pool, MDL);
+       if (!parse_lbrace(cfile)) {
+               pool_dereference(&pool, MDL);
                return;
        }
 
        do {
-               token = peek_token (&val, (unsigned *)0, cfile);
+               token = peek_token(&val, NULL, cfile);
                switch (token) {
                      case TOKEN_NO:
-                       skip_token(&val, (unsigned *)0, cfile);
-                       token = next_token (&val, (unsigned *)0, cfile);
+                       skip_token(&val, NULL, cfile);
+                       token = next_token(&val, NULL, cfile);
                        if (token != FAILOVER ||
-                           (token = next_token (&val, (unsigned *)0,
-                                                cfile)) != PEER) {
-                               parse_warn (cfile,
-                                           "expecting \"failover peer\".");
-                               skip_to_semi (cfile);
+                           (token = next_token(&val, NULL, cfile)) != PEER) {
+                               parse_warn(cfile,
+                                          "expecting \"failover peer\".");
+                               skip_to_semi(cfile);
                                continue;
                        }
 #if defined (FAILOVER_PROTOCOL)
-                       if (pool -> failover_peer)
+                       if (pool->failover_peer)
                                dhcp_failover_state_dereference
-                                       (&pool -> failover_peer, MDL);
+                                       (&pool->failover_peer, MDL);
 #endif
                        break;
                                
 #if defined (FAILOVER_PROTOCOL)
                      case FAILOVER:
-                       skip_token(&val, (unsigned *)0, cfile);
-                       token = next_token (&val, (unsigned *)0, cfile);
+                       skip_token(&val, NULL, cfile);
+                       token = next_token (&val, NULL, cfile);
                        if (token != PEER) {
-                               parse_warn (cfile, "expecting 'peer'.");
-                               skip_to_semi (cfile);
+                               parse_warn(cfile, "expecting 'peer'.");
+                               skip_to_semi(cfile);
                                break;
                        }
-                       token = next_token (&val, (unsigned *)0, cfile);
+                       token = next_token(&val, NULL, cfile);
                        if (token != STRING) {
-                               parse_warn (cfile, "expecting string.");
-                               skip_to_semi (cfile);
+                               parse_warn(cfile, "expecting string.");
+                               skip_to_semi(cfile);
                                break;
                        }
-                       if (pool -> failover_peer)
+                       if (pool->failover_peer)
                                dhcp_failover_state_dereference
-                                       (&pool -> failover_peer, MDL);
-                       status = find_failover_peer (&pool -> failover_peer,
-                                                    val, MDL);
+                                       (&pool->failover_peer, MDL);
+                       status = find_failover_peer(&pool->failover_peer,
+                                                   val, MDL);
                        if (status != ISC_R_SUCCESS)
-                               parse_warn (cfile,
-                                           "failover peer %s: %s", val,
-                                           isc_result_totext (status));
+                               parse_warn(cfile,
+                                          "failover peer %s: %s", val,
+                                          isc_result_totext (status));
                        else
-                               pool -> failover_peer -> pool_count++;
-                       parse_semi (cfile);
+                               pool->failover_peer->pool_count++;
+                       parse_semi(cfile);
                        break;
 #endif
 
                      case RANGE:
-                       skip_token(&val, (unsigned *)0, cfile);
+                       skip_token(&val, NULL, cfile);
                        parse_address_range (cfile, group, type,
                                             pool, &lpchain);
                        break;
                      case ALLOW:
-                       permit_head = &pool -> permit_list;
-                       /* remember the clause which leads to get_permit */
-                       is_allow = 1;
-                     get_permit:
-                       permit = new_permit (MDL);
-                       if (!permit)
-                               log_fatal ("no memory for permit");
-                       skip_token(&val, (unsigned *)0, cfile);
-                       token = next_token (&val, (unsigned *)0, cfile);
-                       switch (token) {
-                             case UNKNOWN:
-                               permit -> type = permit_unknown_clients;
-                             get_clients:
-                               if (next_token (&val, (unsigned *)0,
-                                               cfile) != CLIENTS) {
-                                       parse_warn (cfile,
-                                                   "expecting \"clients\"");
-                                       skip_to_semi (cfile);
-                                       free_permit (permit, MDL);
-                                       continue;
-                               }
-                               break;
-                               
-                             case KNOWN_CLIENTS:
-                               permit -> type = permit_known_clients;
-                               break;
-
-                             case UNKNOWN_CLIENTS:
-                               permit -> type = permit_unknown_clients;
-                               break;
-
-                             case KNOWN:
-                               permit -> type = permit_known_clients;
-                               goto get_clients;
-                               
-                             case AUTHENTICATED:
-                               permit -> type = permit_authenticated_clients;
-                               goto get_clients;
-                               
-                             case UNAUTHENTICATED:
-                               permit -> type =
-                                       permit_unauthenticated_clients;
-                               goto get_clients;
-
-                             case ALL:
-                               permit -> type = permit_all_clients;
-                               goto get_clients;
-                               break;
-                               
-                             case DYNAMIC:
-                               permit -> type = permit_dynamic_bootp_clients;
-                               if (next_token (&val, (unsigned *)0,
-                                               cfile) != TOKEN_BOOTP) {
-                                       parse_warn (cfile,
-                                                   "expecting \"bootp\"");
-                                       skip_to_semi (cfile);
-                                       free_permit (permit, MDL);
-                                       continue;
-                               }
-                               goto get_clients;
-                               
-                             case MEMBERS:
-                               if (next_token (&val, (unsigned *)0,
-                                               cfile) != OF) {
-                                       parse_warn (cfile, "expecting \"of\"");
-                                       skip_to_semi (cfile);
-                                       free_permit (permit, MDL);
-                                       continue;
-                               }
-                               if (next_token (&val, (unsigned *)0,
-                                               cfile) != STRING) {
-                                       parse_warn (cfile,
-                                                   "expecting class name.");
-                                       skip_to_semi (cfile);
-                                       free_permit (permit, MDL);
-                                       continue;
-                               }
-                               permit -> type = permit_class;
-                               permit -> class = (struct class *)0;
-                               find_class (&permit -> class, val, MDL);
-                               if (!permit -> class)
-                                       parse_warn (cfile,
-                                                   "no such class: %s", val);
-                               break;
-
-                             case AFTER:
-                               if (pool->valid_from || pool->valid_until) {
-                                       parse_warn(cfile,
-                                                   "duplicate \"after\" clause.");
-                                       skip_to_semi(cfile);
-                                       free_permit(permit, MDL);
-                                       continue;
-                               }
-                               t = parse_date_core(cfile);
-                               permit->type = permit_after;
-                               permit->after = t;
-                               if (is_allow) {
-                                       pool->valid_from = t;
-                               } else {
-                                       pool->valid_until = t;
-                               }
-                               break;
-
-                             default:
-                               parse_warn (cfile, "expecting permit type.");
-                               skip_to_semi (cfile);
-                               break;
-                       }
-                       while (*permit_head)
-                               permit_head = &((*permit_head) -> next);
-                       *permit_head = permit;
-                       parse_semi (cfile);
+                       skip_token(&val, NULL, cfile);
+                       get_permit(cfile, &pool->permit_list, 1,
+                                  &pool->valid_from, &pool->valid_until);
                        break;
 
                      case DENY:
-                       permit_head = &pool -> prohibit_list;
-                       /* remember the clause which leads to get_permit */
-                       is_allow = 0; 
-                       goto get_permit;
+                       skip_token(&val, NULL, cfile);
+                       get_permit(cfile, &pool->prohibit_list, 0,
+                                  &pool->valid_from, &pool->valid_until);
+                       break;
                        
                      case RBRACE:
-                       skip_token(&val, (unsigned *)0, cfile);
+                       skip_token(&val, NULL, cfile);
                        done = 1;
                        break;
 
@@ -1653,37 +1729,36 @@ void parse_pool_statement (cfile, group, type)
                        goto cleanup;
 
                      default:
-                       declaration = parse_statement (cfile, pool -> group,
-                                                      POOL_DECL,
-                                                      (struct host_decl *)0,
+                       declaration = parse_statement(cfile, pool->group,
+                                                     POOL_DECL, NULL,
                                                       declaration);
                        break;
                }
        } while (!done);
 
        /* See if there's already a pool into which we can merge this one. */
-       for (pp = pool -> shared_network -> pools; pp; pp = pp -> next) {
-               if (pp -> group -> statements != pool -> group -> statements)
+       for (pp = pool->shared_network->pools; pp; pp = pp->next) {
+               if (pp->group->statements != pool->group->statements)
                        continue;
 #if defined (FAILOVER_PROTOCOL)
-               if (pool -> failover_peer != pp -> failover_peer)
+               if (pool->failover_peer != pp->failover_peer)
                        continue;
 #endif
-               if (!permit_list_match (pp -> permit_list,
-                                       pool -> permit_list) ||
-                   !permit_list_match (pool -> permit_list,
-                                       pp -> permit_list) ||
-                   !permit_list_match (pp -> prohibit_list,
-                                       pool -> prohibit_list) ||
-                   !permit_list_match (pool -> prohibit_list,
-                                       pp -> prohibit_list))
+               if (!permit_list_match(pp->permit_list,
+                                      pool->permit_list) ||
+                   !permit_list_match(pool->permit_list,
+                                      pp->permit_list) ||
+                   !permit_list_match(pp->prohibit_list,
+                                      pool->prohibit_list) ||
+                   !permit_list_match(pool->prohibit_list,
+                                      pp->prohibit_list))
                        continue;
 
                /* Okay, we can merge these two pools.    All we have to
                   do is fix up the leases, which all point to their pool. */
-               for (lp = lpchain; lp; lp = lp -> next) {
-                       pool_dereference (&lp -> pool, MDL);
-                       pool_reference (&lp -> pool, pp, MDL);
+               for (lp = lpchain; lp; lp = lp->next) {
+                       pool_dereference(&lp->pool, MDL);
+                       pool_reference(&lp->pool, pp, MDL);
                }
                break;
        }
@@ -1691,33 +1766,33 @@ void parse_pool_statement (cfile, group, type)
        /* If we didn't succeed in merging this pool into another, put
           it on the list. */
        if (!pp) {
-               p = &pool -> shared_network -> pools;
-               for (; *p; p = &((*p) -> next))
+               p = &pool->shared_network->pools;
+               for (; *p; p = &((*p)->next))
                        ;
-               pool_reference (p, pool, MDL);
+               pool_reference(p, pool, MDL);
        }
 
        /* Don't allow a pool declaration with no addresses, since it is
           probably a configuration error. */
        if (!lpchain) {
-               parse_warn (cfile, "Pool declaration with no address range.");
-               log_error ("Pool declarations must always contain at least");
-               log_error ("one range statement.");
+               parse_warn(cfile, "Pool declaration with no address range.");
+               log_error("Pool declarations must always contain at least");
+               log_error("one range statement.");
        }
 
 cleanup:
        /* Dereference the lease chain. */
-       lp = (struct lease *)0;
+       lp = NULL;
        while (lpchain) {
-               lease_reference (&lp, lpchain, MDL);
-               lease_dereference (&lpchain, MDL);
-               if (lp -> next) {
-                       lease_reference (&lpchain, lp -> next, MDL);
-                       lease_dereference (&lp -> next, MDL);
-                       lease_dereference (&lp, MDL);
+               lease_reference(&lp, lpchain, MDL);
+               lease_dereference(&lpchain, MDL);
+               if (lp->next) {
+                       lease_reference(&lpchain, lp->next, MDL);
+                       lease_dereference(&lp->next, MDL);
+                       lease_dereference(&lp, MDL);
                }
        }
-       pool_dereference (&pool, MDL);
+       pool_dereference(&pool, MDL);
 }
 
 /* Expect a left brace; if there isn't one, skip over the rest of the
@@ -2008,12 +2083,6 @@ int parse_class_declaration (cp, cfile, group, type)
        int submatchedonce = 0;
        unsigned code;
 
-       if (dhcpv6_class_once && local_family == AF_INET6) {
-               dhcpv6_class_once = 0;
-               log_error("WARNING: class declarations are not supported "
-                         "for DHCPv6.");
-       }
-
        token = next_token (&val, NULL, cfile);
        if (token != STRING) {
                parse_warn (cfile, "Expecting class name");
@@ -3688,15 +3757,13 @@ void parse_address_range (cfile, group, type, inpool, lpchain)
 #ifdef DHCPv6
 static void
 add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type,
-                       struct iaddr *lo_addr, int bits, int units) {
+                       struct iaddr *lo_addr, int bits, int units,
+                       struct ipv6_pond *pond) {
        struct ipv6_pool *pool;
-       struct shared_network *share;
        struct in6_addr tmp_in6_addr;
        int num_pools;
        struct ipv6_pool **tmp;
 
-       share = subnet->shared_network;
-
        /*
         * Create our pool.
         */
@@ -3724,16 +3791,19 @@ add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type,
        pool->subnet = NULL;
        subnet_reference(&pool->subnet, subnet, MDL);
        pool->shared_network = NULL;
-       shared_network_reference(&pool->shared_network, share, MDL);
+       shared_network_reference(&pool->shared_network,
+                                subnet->shared_network, MDL);
+       pool->ipv6_pond = NULL;
+       ipv6_pond_reference(&pool->ipv6_pond, pond, MDL);
 
        /* 
-        * Increase our array size for ipv6_pools in the shared_network.
+        * Increase our array size for ipv6_pools in the pond
         */
-       if (share->ipv6_pools == NULL) {
+       if (pond->ipv6_pools == NULL) {
                num_pools = 0;
        } else {
                num_pools = 0;
-               while (share->ipv6_pools[num_pools] != NULL) {
+               while (pond->ipv6_pools[num_pools] != NULL) {
                        num_pools++;
                }
        }
@@ -3742,34 +3812,114 @@ add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type,
                log_fatal("Out of memory");
        }
        if (num_pools > 0) {
-               memcpy(tmp, share->ipv6_pools, 
+               memcpy(tmp, pond->ipv6_pools, 
                       sizeof(struct ipv6_pool *) * num_pools);
        }
-       if (share->ipv6_pools != NULL) {
-               dfree(share->ipv6_pools, MDL);
+       if (pond->ipv6_pools != NULL) {
+               dfree(pond->ipv6_pools, MDL);
        }
-       share->ipv6_pools = tmp;
+       pond->ipv6_pools = tmp;
 
        /* 
         * Record this pool in our array of pools for this shared network.
         */
-       ipv6_pool_reference(&share->ipv6_pools[num_pools], pool, MDL);
-       share->ipv6_pools[num_pools+1] = NULL;
+       ipv6_pool_reference(&pond->ipv6_pools[num_pools], pool, MDL);
+       pond->ipv6_pools[num_pools+1] = NULL;
+}
+
+/*!
+ *
+ * \brief Find or create a default pond
+ *
+ * Find or create an ipv6_pond on which to attach the ipv6_pools.  We
+ * check the shared network to see if there is a general purpose
+ * entry - this will have an empty prohibit list and a permit list
+ * with a single entry that permits all clients.  If the shared
+ * network doesn't have one of them create it and attach it to
+ * the shared network and the return argument.
+ * 
+ * This function is used when we have a range6 or prefix6 statement
+ * inside a subnet6 statement but outside of a pool6 statement.
+ * This routine constructs the missing ipv6_pond structure so
+ * we always have 
+ * shared_network -> ipv6_pond -> ipv6_pool
+ *
+ * \param[in] group     = a pointer to the group structure from which
+ *                        we can find the subnet and shared netowrk
+ *                        structures
+ * \param[out] ret_pond = a pointer to space for the pointer to
+ *                        the structure to return
+ *
+ * \return
+ * void
+ */
+static void
+add_ipv6_pond_to_network(struct group *group,
+                        struct ipv6_pond **ret_pond) {
+
+       struct ipv6_pond *pond = NULL, *last = NULL;
+       struct permit *p;
+       isc_result_t status;
+       struct shared_network *shared = group->subnet->shared_network;
+
+       for (pond = shared->ipv6_pond; pond; pond = pond->next) {
+               if ((pond->group->statements == group->statements) &&
+                   (pond->prohibit_list == NULL) &&
+                   (pond->permit_list != NULL) &&
+                   (pond->permit_list->next == NULL) &&
+                   (pond->permit_list->type == permit_all_clients)) {
+                       ipv6_pond_reference(ret_pond, pond, MDL);
+                       return;
+               }
+               last = pond;
+       }
+
+       /* no pond available, make one */
+       status = ipv6_pond_allocate(&pond, MDL);
+       if (status != ISC_R_SUCCESS) 
+               log_fatal ("no memory for ad-hoc ipv6 pond: %s",
+                          isc_result_totext (status));
+       p = new_permit (MDL);
+       if (p == NULL)
+               log_fatal ("no memory for ad-hoc ipv6 permit.");
+
+       /* we permit all clients */
+       p->type = permit_all_clients;
+       pond->permit_list = p;
+
+       /* and attach the pond to the return argument and the shared network */
+       ipv6_pond_reference(ret_pond, pond, MDL);
+
+       if (shared->ipv6_pond)
+               ipv6_pond_reference(&last->next, pond, MDL);
+       else 
+               ipv6_pond_reference(&shared->ipv6_pond, pond, MDL);
+
+       shared_network_reference(&pond->shared_network, shared, MDL);
+       if (!clone_group (&pond->group, group, MDL))
+               log_fatal ("no memory for anon pool group.");
+
+       ipv6_pond_dereference(&pond, MDL);
+       return;
 }
 
+
 /* address-range6-declaration :== ip-address6 ip-address6 SEMI
                               | ip-address6 SLASH number SEMI
                               | ip-address6 [SLASH number] TEMPORARY SEMI */
 
 void 
-parse_address_range6(struct parse *cfile, struct group *group) {
+parse_address_range6(struct parse *cfile,
+                    struct group *group,
+                    struct ipv6_pond *inpond) {
        struct iaddr lo, hi;
        int bits;
        enum dhcp_token token;
        const char *val;
-       struct iaddrcidrnetlist *nets;
+       struct iaddrcidrnetlist *nets, net;
        struct iaddrcidrnetlist *p;
        u_int16_t type = D6O_IA_NA;
+       struct ipv6_pond *pond = NULL;
 
         if (local_family != AF_INET6) {
                 parse_warn(cfile, "range6 statement is only supported "
@@ -3789,6 +3939,12 @@ parse_address_range6(struct parse *cfile, struct group *group) {
                return;
        }
 
+       /*
+        * zero out the net entry in case we use it
+        */
+       memset(&net, 0, sizeof(net));
+       net.cidrnet.lo_addr = lo;
+
        /* 
         * See if we we're using range or CIDR notation or TEMPORARY
         */
@@ -3804,13 +3960,15 @@ parse_address_range6(struct parse *cfile, struct group *group) {
                        skip_to_semi(cfile);
                        return;
                }
-               bits = atoi(val);
+               net.cidrnet.bits = atoi(val);
+               bits = net.cidrnet.bits;
                if ((bits < 0) || (bits > 128)) {
                        parse_warn(cfile, "networks have 0 to 128 bits");
                        skip_to_semi(cfile);
                        return;
                }
-               if (!is_cidr_mask_valid(&lo, bits)) {
+
+               if (!is_cidr_mask_valid(&net.cidrnet.lo_addr, bits)) {
                        parse_warn(cfile, "network mask too short");
                        skip_to_semi(cfile);
                        return;
@@ -3829,8 +3987,7 @@ parse_address_range6(struct parse *cfile, struct group *group) {
                        type = D6O_IA_TA;
                }
 
-               add_ipv6_pool_to_subnet(group->subnet, type, &lo,
-                                       bits, 128);
+               nets = &net;
 
        } else if (token == TEMPORARY) {
                /*
@@ -3838,15 +3995,16 @@ parse_address_range6(struct parse *cfile, struct group *group) {
                 */
                type = D6O_IA_TA;
                skip_token(NULL, NULL, cfile);
-               bits = 64;
-               if (!is_cidr_mask_valid(&lo, bits)) {
+               net.cidrnet.bits = 64;
+               if (!is_cidr_mask_valid(&net.cidrnet.lo_addr,
+                                       net.cidrnet.bits)) {
                        parse_warn(cfile, "network mask too short");
                        skip_to_semi(cfile);
                        return;
                }
 
-               add_ipv6_pool_to_subnet(group->subnet, type, &lo,
-                                       bits, 128);
+               nets = &net;
+
        } else {
                /*
                 * No '/', so we are looking for the end address of 
@@ -3864,15 +4022,33 @@ parse_address_range6(struct parse *cfile, struct group *group) {
                        log_fatal("Error converting range to CIDR networks");
                }
 
-               for (p=nets; p != NULL; p=p->next) {
-                       add_ipv6_pool_to_subnet(group->subnet, type,
-                                               &p->cidrnet.lo_addr, 
-                                               p->cidrnet.bits, 128);
-               }
+       }
 
-               free_iaddrcidrnetlist(&nets);
+       /*
+        * See if we have a pond for this set of pools.
+        * If the caller supplied one we use it, otherwise
+        * check the shared network
+        */
+
+       if (inpond != NULL) {
+               ipv6_pond_reference(&pond, inpond, MDL);
+       } else {
+               add_ipv6_pond_to_network(group, &pond);
        }
 
+       /* Now that we have a pond add the nets we have parsed */
+       for (p=nets; p != NULL; p=p->next) {
+               add_ipv6_pool_to_subnet(group->subnet, type,
+                                       &p->cidrnet.lo_addr, 
+                                       p->cidrnet.bits, 128, pond);
+       }
+
+       /* if we allocated a list free it now */
+       if (nets != &net) 
+               free_iaddrcidrnetlist(&nets);
+
+       ipv6_pond_dereference(&pond, MDL);
+
        token = next_token(NULL, NULL, cfile);
        if (token != SEMI) {
                parse_warn(cfile, "semicolon expected.");
@@ -3884,13 +4060,16 @@ parse_address_range6(struct parse *cfile, struct group *group) {
 /* prefix6-declaration :== ip-address6 ip-address6 SLASH number SEMI */
 
 void 
-parse_prefix6(struct parse *cfile, struct group *group) {
+parse_prefix6(struct parse *cfile,
+             struct group *group,
+             struct ipv6_pond *inpond) {
        struct iaddr lo, hi;
        int bits;
        enum dhcp_token token;
        const char *val;
        struct iaddrcidrnetlist *nets;
        struct iaddrcidrnetlist *p;
+       struct ipv6_pond *pond = NULL;
 
        if (local_family != AF_INET6) {
                parse_warn(cfile, "prefix6 statement is only supported "
@@ -3955,6 +4134,18 @@ parse_prefix6(struct parse *cfile, struct group *group) {
                log_fatal("Error converting prefix to CIDR");
        }
 
+       /*
+        * See if we have a pond for this set of pools.
+        * If the caller supplied one we use it, otherwise
+        * check the shared network
+        */
+
+       if (inpond != NULL) {
+               ipv6_pond_reference(&pond, inpond, MDL);
+       } else {
+               add_ipv6_pond_to_network(group, &pond);
+       }
+               
        for (p = nets; p != NULL; p = p->next) {
                /* Normalize and check. */
                if (p->cidrnet.bits == 128) {
@@ -3966,7 +4157,7 @@ parse_prefix6(struct parse *cfile, struct group *group) {
                }
                add_ipv6_pool_to_subnet(group->subnet, D6O_IA_PD,
                                        &p->cidrnet.lo_addr,
-                                       p->cidrnet.bits, bits);
+                                       p->cidrnet.bits, bits, pond);
        }
 
        free_iaddrcidrnetlist(&nets);
@@ -4052,6 +4243,141 @@ parse_fixed_prefix6(struct parse *cfile, struct host_decl *host_decl) {
        *h = ia;
        return;
 }
+
+/*!
+ *
+ * \brief Parse a pool6 statement
+ *
+ * Pool statements are used to group declarations and permit & deny information
+ * with a specific address range.  They must be declared within a shared network
+ * or subnet and there may be multiple pools withing a shared network or subnet.
+ * Each pool may have a different set of permit or deny options.
+ *
+ * \param[in] cfile = the configuration file being parsed
+ * \param[in] group = the group structure for this pool
+ * \param[in] type  = the type of the enclosing statement.  This must be
+ *                   SUBNET_DECL for this function.
+ *
+ * \return
+ * void - This function either parses the statement and updates the structures
+ *        or it generates an error message and possible halts the program if
+ *        it encounters a problem.
+ */
+void parse_pool6_statement (cfile, group, type)
+       struct parse *cfile;
+       struct group *group;
+       int type;
+{
+       enum dhcp_token token;
+       const char *val;
+       int done = 0;
+       struct ipv6_pond *pond, **p;
+       int declaration = 0;
+       isc_result_t status;
+
+       pond = NULL;
+       status = ipv6_pond_allocate(&pond, MDL);
+       if (status != ISC_R_SUCCESS)
+               log_fatal("no memory for pool6: %s",
+                         isc_result_totext (status));
+
+       if (type == SUBNET_DECL)
+               shared_network_reference(&pond->shared_network,
+                                        group->subnet->shared_network,
+                                        MDL);
+       else {
+               parse_warn(cfile, "Dynamic pool6s are only valid inside "
+                                 "subnet statements.");
+               skip_to_semi(cfile);
+               return;
+       }
+
+       if (clone_group(&pond->group, group, MDL) == 0)
+               log_fatal("can't clone pool6 group.");
+
+       if (parse_lbrace(cfile) == 0) {
+               ipv6_pond_dereference(&pond, MDL);
+               return;
+       }
+
+       do {
+               token = peek_token(&val, NULL, cfile);
+               switch (token) {
+                     case RANGE6:
+                       skip_token(NULL, NULL, cfile);
+                       parse_address_range6(cfile, group, pond);
+                       break;
+
+                     case PREFIX6:
+                       skip_token(NULL, NULL, cfile);
+                       parse_prefix6(cfile, group, pond);
+                       break;
+
+                     case ALLOW:
+                       skip_token(NULL, NULL, cfile);
+                       get_permit(cfile, &pond->permit_list, 1,
+                                  &pond->valid_from, &pond->valid_until);
+                       break;
+
+                     case DENY:
+                       skip_token(NULL, NULL, cfile);
+                       get_permit(cfile, &pond->prohibit_list, 0,
+                                  &pond->valid_from, &pond->valid_until);
+                       break;
+                       
+                     case RBRACE:
+                       skip_token(&val, NULL, cfile);
+                       done = 1;
+                       break;
+
+                     case END_OF_FILE:
+                       /*
+                        * We can get to END_OF_FILE if, for instance,
+                        * the parse_statement() reads all available tokens
+                        * and leaves us at the end.
+                        */
+                       parse_warn(cfile, "unexpected end of file");
+                       goto cleanup;
+
+                     default:
+                       declaration = parse_statement(cfile, pond->group,
+                                                     POOL_DECL, NULL,
+                                                     declaration);
+                       break;
+               }
+       } while (!done);
+
+       /*
+        * A possible optimization is to see if this pond can be merged into
+        * an already existing pond.  But I'll pass on that for now as we need
+        * to repoint the leases to the other pond which is annoying. SAR
+        */
+
+       /* 
+        * Add this pond to the list (will need updating if we add the
+        * optimization).
+        */
+
+       p = &pond->shared_network->ipv6_pond;
+       for (; *p; p = &((*p)->next))
+               ;
+       ipv6_pond_reference(p, pond, MDL);
+
+       /* Don't allow a pool6 declaration with no addresses or
+          prefixes, since it is probably a configuration error. */
+       if (pond->ipv6_pools == NULL) {
+               parse_warn (cfile, "Pool6 declaration with no %s.",
+                           "address range6 or prefix6");
+               log_error ("Pool6 declarations must always contain at least");
+               log_error ("one range6 or prefix6 statement.");
+       }
+
+cleanup:
+       ipv6_pond_dereference(&pond, MDL);
+}
+
+
+
 #endif /* DHCPv6 */
 
 /* allow-deny-keyword :== BOOTP
index 106206b852d082a88e511d9a0563f12622e0542b..63fa798cc5beaa94be4f0aa2c129e6da53a35af2 100644 (file)
@@ -265,8 +265,8 @@ group {
 .SH ADDRESS POOLS
 .PP
 The
-.B pool
-declaration can be used to specify a pool of addresses that will be
+\fBpool\fR and \fBpool6\fR
+declarations can be used to specify a pool of addresses that will be
 treated differently than another pool of addresses, even on the same
 network segment or subnet.  For example, you may want to provide a
 large set of addresses that can be assigned to DHCP clients that are
@@ -316,6 +316,14 @@ deny list, then only those clients that do not match any entries on
 the deny list will be eligible.   If both permit and deny lists exist
 for a pool, then only clients that match the permit list and do not
 match the deny list will be allowed access.
+.PP
+The \fBpool6\fR declaration is similar to the \fBpool6\fR declaration.
+Currently it is only allowed within a \fBsubnet6\fR declaration, and
+may not be included directly in a shared network declaration.
+In addition to the \fBrange6\fR statement it allows the \fBprefix6\fR
+statement to be included.  You may include \fBrange6\fR statements
+for both NA and TA and \fBprefixy6\fR statements in a single
+\fBpool6\fR statement.
 .SH DYNAMIC ADDRESS ALLOCATION
 Address allocation is actually only done when a client is in the INIT
 state and has sent a DHCPDISCOVER message.  If the client thinks it
@@ -442,7 +450,7 @@ this last method, change the "my state" line to:
 .PP
 .nf
 .B failover peer "\fIname\fB" state {
-.B   my   state partner-down;
+.B   my   state partner-down;.
 .B   peer state \fIstate\fB at \fIdate\fB;
 .B }
 .fi
@@ -894,6 +902,10 @@ total number of clients within a particular class or subclass that may
 hold leases at one time, and it is possible to specify automatic
 subclassing based on the contents of the client packet.
 .PP
+Classing support for DHCPv6 clients was addded in 4.3.0.  It follows
+the same rules as for DHCPv4 except that support for billing classes
+has not been added yet.
+.PP
 To add clients to classes based on conditional evaluation, you can
 specify a matching expression in the class statement:
 .PP
index b47825f04b4d60ce786e4899cb381f3aa2567b23..b65026b354a8341222a918629079e4c336373416 100644 (file)
@@ -14,6 +14,8 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
+/*! \file server/dhcpv6.c */
+
 #include "dhcpd.h"
 
 #ifdef DHCPv6
@@ -989,83 +991,112 @@ try_client_v6_address(struct iasubopt **addr,
        return result;
 }
 
-/*
- * Get an IPv6 address for the client.
+
+/*!
  *
- * addr is the result (should be a pointer to NULL on entry)
- * packet is the information about the packet from the client
- * requested_iaaddr is a hint from the client
- * client_id is the DUID for the client
+ * \brief  Get an IPv6 address for the client.
+ *
+ * Attempt to find a usable address for the client.  We walk through
+ * the ponds checking for permit and deny then through the pools
+ * seeing if they have an available address.
+ *
+ * \param reply = the state structure for the current work on this request
+ *                if we create a lease we return it using reply->lease
+ *
+ * \return
+ * ISC_R_SUCCESS = we were able to find an address and are returning a
+ *                 pointer to the lease
+ * ISC_R_NORESOURCES = there don't appear to be any free addresses.  This
+ *                     is probabalistic.  We don't exhaustively try the
+ *                     address range, instead we hash the duid and if
+ *                     the address derived from the hash is in use we
+ *                     hash the address.  After a number of failures we
+ *                     conclude the pool is basically full.
  */
 static isc_result_t 
-pick_v6_address(struct iasubopt **addr, struct shared_network *shared_network,
-               const struct data_string *client_id)
+pick_v6_address(struct reply_state *reply)
 {
-       struct ipv6_pool *p;
+       struct ipv6_pool *p = NULL;
+       struct ipv6_pond *pond;
        int i;
        int start_pool;
        unsigned int attempts;
        char tmp_buf[INET6_ADDRSTRLEN];
+       struct iasubopt **addr = &reply->lease;
 
        /*
-        * No address pools, we're done.
+        * Do a quick walk through of the ponds and pools
+        * to see if we have any NA address pools
         */
-       if (shared_network->ipv6_pools == NULL) {
-               log_debug("Unable to pick client address: "
-                         "no IPv6 pools on this shared network");
-               return ISC_R_NORESOURCES;
-       }
-       for (i = 0;; i++) {
-               p = shared_network->ipv6_pools[i];
-               if (p == NULL) {
-                       log_debug("Unable to pick client address: "
-                                 "no IPv6 address pools "
-                                 "on this shared network");
-                       return ISC_R_NORESOURCES;
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (pond->ipv6_pools == NULL)
+                       continue;
+
+               for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) {
+                       if (p->pool_type == D6O_IA_NA)
+                               break;
                }
-               if (p->pool_type == D6O_IA_NA) {
+               if (p != NULL)
                        break;
-               }
        }
 
+       /* If we get here and p is NULL we have no useful pools */
+       if (p == NULL) {
+               log_debug("Unable to pick client address: "
+                         "no IPv6 pools on this shared network");
+               return ISC_R_NORESOURCES;
+       }
+               
        /*
-        * Otherwise try to get a lease from the first subnet possible.
-        *
-        * We start looking at the last pool we allocated from, unless
-        * it had a collision trying to allocate an address. This will
-        * tend to move us into less-filled pools.
-        */
-       start_pool = shared_network->last_ipv6_pool;
-       i = start_pool;
-       do {
-
-               p = shared_network->ipv6_pools[i];
-               if ((p->pool_type == D6O_IA_NA) &&
-                   (create_lease6(p, addr, &attempts, client_id,
-                                  cur_time + 120) == ISC_R_SUCCESS)) {
-                       /*
-                        * Record the pool used (or next one if there 
-                        * was a collision).
-                        */
-                       if (attempts > 1) {
-                               i++;
-                               if (shared_network->ipv6_pools[i] == NULL) {
-                                       i = 0;
+        * We have at least one pool that could provide an address
+        * Now we walk through the ponds and pools again and check
+        * to see if the client is permitted and if an address is
+        * available
+        * 
+        * Within a given pond we start looking at the last pool we
+        * allocated from, unless it had a collision trying to allocate
+        * an address. This will tend to move us into less-filled pools.
+        */
+
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (((pond->prohibit_list != NULL) &&
+                    (permitted(reply->packet, pond->prohibit_list))) ||
+                   ((pond->permit_list != NULL) &&
+                    (!permitted(reply->packet, pond->permit_list))))
+                       continue;
+
+               start_pool = pond->last_ipv6_pool;
+               i = start_pool;
+               do {
+                       p = pond->ipv6_pools[i];
+                       if ((p->pool_type == D6O_IA_NA) &&
+                           (create_lease6(p, addr, &attempts,
+                                          &reply->ia->iaid_duid,
+                                          cur_time + 120) == ISC_R_SUCCESS)) {
+                               /*
+                                * Record the pool used (or next one if there 
+                                * was a collision).
+                                */
+                               if (attempts > 1) {
+                                       i++;
+                                       if (pond->ipv6_pools[i] == NULL) {
+                                               i = 0;
+                                       }
                                }
-                       }
-                       shared_network->last_ipv6_pool = i;
+                               pond->last_ipv6_pool = i;
 
-                       log_debug("Picking pool address %s",
-                                 inet_ntop(AF_INET6, &((*addr)->addr),
-                                           tmp_buf, sizeof(tmp_buf)));
-                       return ISC_R_SUCCESS;
-               }
+                               log_debug("Picking pool address %s",
+                                         inet_ntop(AF_INET6, &((*addr)->addr),
+                                                   tmp_buf, sizeof(tmp_buf)));
+                               return (ISC_R_SUCCESS);
+                       }
 
-               i++;
-               if (shared_network->ipv6_pools[i] == NULL) {
-                       i = 0;
-               }
-       } while (i != start_pool);
+                       i++;
+                       if (pond->ipv6_pools[i] == NULL) {
+                               i = 0;
+                       }
+               } while (i != start_pool);
+       }
 
        /*
         * If we failed to pick an IPv6 address from any of the subnets.
@@ -1134,72 +1165,97 @@ try_client_v6_prefix(struct iasubopt **pref,
        return result;
 }
 
-/*
- * Get an IPv6 prefix for the client.
+/*!
  *
- * pref is the result (should be a pointer to NULL on entry)
- * packet is the information about the packet from the client
- * requested_iaprefix is a hint from the client
- * plen is -1 or the requested prefix length
- * client_id is the DUID for the client
+ * \brief  Get an IPv6 prefix for the client.
+ *
+ * Attempt to find a usable prefix for the client.  We walk through
+ * the ponds checking for permit and deny then through the pools
+ * seeing if they have an available prefix.
+ *
+ * \param reply = the state structure for the current work on this request
+ *                if we create a lease we return it using reply->lease
+ *
+ * \return
+ * ISC_R_SUCCESS = we were able to find an prefix and are returning a
+ *                 pointer to the lease
+ * ISC_R_NORESOURCES = there don't appear to be any free addresses.  This
+ *                     is probabalistic.  We don't exhaustively try the
+ *                     address range, instead we hash the duid and if
+ *                     the address derived from the hash is in use we
+ *                     hash the address.  After a number of failures we
+ *                     conclude the pool is basically full.
  */
+
 static isc_result_t 
-pick_v6_prefix(struct iasubopt **pref, int plen,
-              struct shared_network *shared_network,
-              const struct data_string *client_id)
+pick_v6_prefix(struct reply_state *reply)
 {
-       struct ipv6_pool *p;
+       struct ipv6_pool *p = NULL;
+       struct ipv6_pond *pond;
        int i;
        unsigned int attempts;
        char tmp_buf[INET6_ADDRSTRLEN];
+       struct iasubopt **pref = &reply->lease;
 
        /*
-        * No prefix pools, we're done.
+        * Do a quick walk through of the ponds and pools
+        * to see if we have any prefix pools
         */
-       if (shared_network->ipv6_pools == NULL) {
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (pond->ipv6_pools == NULL)
+                       continue;
+
+               for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) {
+                       if (p->pool_type == D6O_IA_PD)
+                               break;
+               }
+               if (p != NULL)
+                       break;
+       }
+
+       /* If we get here and p is NULL we have no useful pools */
+       if (p == NULL) {
                log_debug("Unable to pick client prefix: "
                          "no IPv6 pools on this shared network");
                return ISC_R_NORESOURCES;
        }
-       for (i = 0;; i++) {
-               p = shared_network->ipv6_pools[i];
-               if (p == NULL) {
-                       log_debug("Unable to pick client prefix: "
-                                 "no IPv6 prefix pools "
-                                 "on this shared network");
-                       return ISC_R_NORESOURCES;
-               }
-               if (p->pool_type == D6O_IA_PD) {
-                       break;
-               }
-       }
 
        /*
-        * Otherwise try to get a prefix.
-        */
-       for (i = 0;; i++) {
-               p = shared_network->ipv6_pools[i];
-               if (p == NULL) {
-                       break;
-               }
-               if (p->pool_type != D6O_IA_PD) {
+        * We have at least one pool that could provide a prefix
+        * Now we walk through the ponds and pools again and check
+        * to see if the client is permitted and if an prefix is
+        * available
+        * 
+        */
+
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (((pond->prohibit_list != NULL) &&
+                    (permitted(reply->packet, pond->prohibit_list))) ||
+                   ((pond->permit_list != NULL) &&
+                    (!permitted(reply->packet, pond->permit_list))))
                        continue;
-               }
 
-               /*
-                * Try only pools with the requested prefix length if any.
-                */
-               if ((plen >= 0) && (p->units != plen)) {
-                       continue;
-               }
+               for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) {
+                       if (p->pool_type != D6O_IA_PD) {
+                               continue;
+                       }
 
-               if (create_prefix6(p, pref, &attempts, client_id,
-                                  cur_time + 120) == ISC_R_SUCCESS) {
-                       log_debug("Picking pool prefix %s/%u",
-                                 inet_ntop(AF_INET6, &((*pref)->addr),
-                                           tmp_buf, sizeof(tmp_buf)),
-                                 (unsigned) (*pref)->plen);
-                       return ISC_R_SUCCESS;
+                       /*
+                        * Try only pools with the requested prefix length if any.
+                        */
+                       if ((reply->preflen >= 0) && (p->units != reply->preflen)) {
+                               continue;
+                       }
+
+                       if (create_prefix6(p, pref, &attempts, &reply->ia->iaid_duid,
+                                          cur_time + 120) == ISC_R_SUCCESS) {
+                               log_debug("Picking pool prefix %s/%u",
+                                         inet_ntop(AF_INET6, &((*pref)->addr),
+                                                   tmp_buf, sizeof(tmp_buf)),
+                                         (unsigned) (*pref)->plen);
+
+                               return (ISC_R_SUCCESS);
+                       }
                }
        }
 
@@ -1258,6 +1314,7 @@ lease_to_client(struct data_string *reply_ret,
 #if defined (RFC3315_PRE_ERRATA_2010_08)
        isc_boolean_t no_resources_avail = ISC_FALSE;
 #endif
+       int i;
 
        memset(&packet_oro, 0, sizeof(packet_oro));
 
@@ -1298,20 +1355,26 @@ lease_to_client(struct data_string *reply_ret,
         * valid for the shared network the client is on.
         */
        if (find_hosts_by_uid(&reply.host, client_id->data, client_id->len,
-                             MDL))
+                             MDL)) {
+               packet->known = 1;
                seek_shared_host(&reply.host, reply.shared);
+       }
 
        if ((reply.host == NULL) &&
-           find_hosts_by_option(&reply.host, packet, packet->options, MDL))
+           find_hosts_by_option(&reply.host, packet, packet->options, MDL)) {
+               packet->known = 1;
                seek_shared_host(&reply.host, reply.shared);
+       }
 
        /*
         * Check for 'hardware' matches last, as some of the synthesis methods
         * are not considered to be as reliable.
         */
        if ((reply.host == NULL) &&
-           find_hosts_by_duid_chaddr(&reply.host, client_id))
+           find_hosts_by_duid_chaddr(&reply.host, client_id)) {
+               packet->known = 1;
                seek_shared_host(&reply.host, reply.shared);
+       }
 
        /* Process the client supplied IA's onto the reply buffer. */
        reply.ia_count = 0;
@@ -1412,6 +1475,17 @@ lease_to_client(struct data_string *reply_ret,
                                            reply.shared->group, root_group,
                                            NULL);
 
+               /* Execute statements from class scopes. */
+               for (i = reply.packet->class_count; i > 0; i--) {
+                       execute_statements_in_scope(NULL, reply.packet,
+                                                   NULL, NULL,
+                                                   reply.packet->options,
+                                                   reply.opt_state,
+                                                   &global_scope,
+                                                   reply.packet->classes[i - 1]->group,
+                                                   reply.shared->group, NULL);
+               }
+
                /* Bring in any configuration from a host record. */
                if (reply.host != NULL)
                        execute_statements_in_scope(NULL, reply.packet,
@@ -2183,7 +2257,7 @@ reply_process_addr(struct reply_state *reply, struct option_cache *addr) {
                        log_fatal("Impossible condition at %s:%d.", MDL);
 
                scope = &reply->lease->scope;
-               group = reply->lease->ipv6_pool->subnet->group;
+               group = reply->lease->ipv6_pool->ipv6_pond->group;
        }
 
        /*
@@ -2254,6 +2328,7 @@ reply_process_addr(struct reply_state *reply, struct option_cache *addr) {
 static isc_boolean_t
 address_is_owned(struct reply_state *reply, struct iaddr *addr) {
        int i;
+       struct ipv6_pond *pond;
 
        /*
         * This faults out addresses that don't match fixed addresses.
@@ -2280,7 +2355,16 @@ address_is_owned(struct reply_state *reply, struct iaddr *addr) {
                        if (lease6_usable(tmp) == ISC_FALSE) {
                                return (ISC_FALSE);
                        }
+
+                       pond = tmp->ipv6_pool->ipv6_pond;
+                       if (((pond->prohibit_list != NULL) &&
+                            (permitted(reply->packet, pond->prohibit_list))) ||
+                           ((pond->permit_list != NULL) &&
+                            (!permitted(reply->packet, pond->permit_list))))
+                               return (ISC_FALSE);
+
                        iasubopt_reference(&reply->lease, tmp, MDL);
+
                        return (ISC_TRUE);
                }
        }
@@ -2416,7 +2500,7 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
                        goto bad_temp;
                status = reply_process_is_addressed(reply,
                                                    &reply->lease->scope,
-                                                   reply->shared->group);
+                                                   reply->lease->ipv6_pool->ipv6_pond->group);
                if (status != ISC_R_SUCCESS)
                        goto bad_temp;
                status = reply_process_send_addr(reply, &tmp_addr);
@@ -2621,7 +2705,8 @@ static isc_boolean_t
 temporary_is_available(struct reply_state *reply, struct iaddr *addr) {
        struct in6_addr tmp_addr;
        struct subnet *subnet;
-       struct ipv6_pool *pool;
+       struct ipv6_pool *pool = NULL;
+       struct ipv6_pond *pond = NULL;
        int i;
 
        memcpy(&tmp_addr, addr->iabuf, sizeof(tmp_addr));
@@ -2656,14 +2741,25 @@ temporary_is_available(struct reply_state *reply, struct iaddr *addr) {
        /*
         * Verify that this address is in a temporary pool and try to get it.
         */
-       if (reply->shared->ipv6_pools == NULL)
-               return ISC_FALSE;
-       for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) {
-               if (pool->pool_type != D6O_IA_TA)
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (((pond->prohibit_list != NULL) &&
+                    (permitted(reply->packet, pond->prohibit_list))) ||
+                   ((pond->permit_list != NULL) &&
+                    (!permitted(reply->packet, pond->permit_list))))
                        continue;
-               if (ipv6_in_pool(&tmp_addr, pool))
+
+               for (i = 0 ; (pool = pond->ipv6_pools[i]) != NULL ; i++) {
+                       if (pool->pool_type != D6O_IA_TA)
+                               continue;
+
+                       if (ipv6_in_pool(&tmp_addr, pool))
+                               break;
+               }
+
+               if (pool != NULL)
                        break;
        }
+
        if (pool == NULL)
                return ISC_FALSE;
        if (lease6_exists(pool, &tmp_addr))
@@ -2684,60 +2780,83 @@ temporary_is_available(struct reply_state *reply, struct iaddr *addr) {
  */
 static isc_result_t
 find_client_temporaries(struct reply_state *reply) {
-       struct shared_network *shared;
        int i;
        struct ipv6_pool *p;
-       isc_result_t status;
+       struct ipv6_pond *pond;
+       isc_result_t status = ISC_R_NORESOURCES;;
        unsigned int attempts;
        struct iaddr send_addr;
 
        /*
-        * No pools, we're done.
+        * Do a quick walk through of the ponds and pools
+        * to see if we have any prefix pools
         */
-       shared = reply->shared;
-       if (shared->ipv6_pools == NULL) {
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (pond->ipv6_pools == NULL)
+                       continue;
+
+               for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) {
+                       if (p->pool_type == D6O_IA_TA)
+                               break;
+               }
+               if (p != NULL)
+                       break;
+       }
+
+       /* If we get here and p is NULL we have no useful pools */
+       if (p == NULL) {
                log_debug("Unable to get client addresses: "
                          "no IPv6 pools on this shared network");
                return ISC_R_NORESOURCES;
        }
 
-       status = ISC_R_NORESOURCES;
-       for (i = 0;; i++) {
-               p = shared->ipv6_pools[i];
-               if (p == NULL) {
-                       break;
-               }
-               if (p->pool_type != D6O_IA_TA) {
+       /*
+        * We have at least one pool that could provide an address
+        * Now we walk through the ponds and pools again and check
+        * to see if the client is permitted and if an address is
+        * available
+        */
+
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (((pond->prohibit_list != NULL) &&
+                    (permitted(reply->packet, pond->prohibit_list))) ||
+                   ((pond->permit_list != NULL) &&
+                    (!permitted(reply->packet, pond->permit_list))))
                        continue;
-               }
 
-               /*
-                * Get an address in this temporary pool.
-                */
-               status = create_lease6(p, &reply->lease, &attempts,
-                                      &reply->client_id, cur_time + 120);
-               if (status != ISC_R_SUCCESS) {
-                       log_debug("Unable to get a temporary address.");
-                       goto cleanup;
-               }
+               for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) {
+                       if (p->pool_type != D6O_IA_TA) {
+                               continue;
+                       }
 
-               status = reply_process_is_addressed(reply,
-                                                   &reply->lease->scope,
-                                     reply->lease->ipv6_pool->subnet->group);
-               if (status != ISC_R_SUCCESS) {
-                       goto cleanup;
-               }
-               send_addr.len = 16;
-               memcpy(send_addr.iabuf, &reply->lease->addr, 16);
-               status = reply_process_send_addr(reply, &send_addr);
-               if (status != ISC_R_SUCCESS) {
-                       goto cleanup;
+                       /*
+                        * Get an address in this temporary pool.
+                        */
+                       status = create_lease6(p, &reply->lease, &attempts,
+                                              &reply->client_id, cur_time + 120);
+                       if (status != ISC_R_SUCCESS) {
+                               log_debug("Unable to get a temporary address.");
+                               goto cleanup;
+                       }
+
+                       status = reply_process_is_addressed(reply,
+                                                           &reply->lease->scope,
+                                                           pond->group);
+                       if (status != ISC_R_SUCCESS) {
+                               goto cleanup;
+                       }
+                       send_addr.len = 16;
+                       memcpy(send_addr.iabuf, &reply->lease->addr, 16);
+                       status = reply_process_send_addr(reply, &send_addr);
+                       if (status != ISC_R_SUCCESS) {
+                               goto cleanup;
+                       }
+                       /*
+                        * reply->lease can't be null as we use it above
+                        * add check if that changes
+                        */
+                       iasubopt_dereference(&reply->lease, MDL);
                }
-               /*
-                * reply->lease can't be null as we use it above
-                * add check if that changes
-                */
-               iasubopt_dereference(&reply->lease, MDL);
        }
 
       cleanup:
@@ -2754,7 +2873,8 @@ find_client_temporaries(struct reply_state *reply) {
 static isc_result_t
 reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) {
        isc_result_t status = ISC_R_ADDRNOTAVAIL;
-       struct ipv6_pool *pool;
+       struct ipv6_pool *pool = NULL;
+       struct ipv6_pond *pond = NULL;
        int i;
        struct data_string data_addr;
 
@@ -2762,18 +2882,61 @@ reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) {
            (addr == NULL) || (reply->lease != NULL))
                return (DHCP_R_INVALIDARG);
 
-       if  (reply->shared->ipv6_pools == NULL)
+       /*
+        * Do a quick walk through of the ponds and pools
+        * to see if we have any NA address pools
+        */
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (pond->ipv6_pools == NULL)
+                       continue;
+
+               for (i = 0; ; i++) {
+                       pool = pond->ipv6_pools[i];
+                       if ((pool == NULL) ||
+                           (pool->pool_type == D6O_IA_NA))
+                               break;
+               }
+               if (pool != NULL)
+                       break;
+       }
+
+       /* If we get here and p is NULL we have no useful pools */
+       if (pool == NULL) {
                return (ISC_R_ADDRNOTAVAIL);
+       }
 
        memset(&data_addr, 0, sizeof(data_addr));
        data_addr.len = addr->len;
        data_addr.data = addr->iabuf;
 
-       for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) {
-               if (pool->pool_type != D6O_IA_NA)
+       /*
+        * We have at least one pool that could provide an address
+        * Now we walk through the ponds and pools again and check
+        * to see if the client is permitted and if an address is
+        * available
+        * 
+        * Within a given pond we start looking at the last pool we
+        * allocated from, unless it had a collision trying to allocate
+        * an address. This will tend to move us into less-filled pools.
+        */
+
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (((pond->prohibit_list != NULL) &&
+                    (permitted(reply->packet, pond->prohibit_list))) ||
+                   ((pond->permit_list != NULL) &&
+                    (!permitted(reply->packet, pond->permit_list))))
                        continue;
-               status = try_client_v6_address(&reply->lease, pool,
-                                              &data_addr);
+
+               for (i = 0 ; (pool = pond->ipv6_pools[i]) != NULL ; i++) {
+                       if (pool->pool_type != D6O_IA_NA)
+                               continue;
+
+                       status = try_client_v6_address(&reply->lease, pool,
+                                                      &data_addr);
+                       if (status == ISC_R_SUCCESS)
+                               break;
+               }
+
                if (status == ISC_R_SUCCESS)
                        break;
        }
@@ -2812,18 +2975,28 @@ find_client_address(struct reply_state *reply) {
        if (reply->old_ia != NULL)  {
                for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) {
                        struct shared_network *candidate_shared;
+                       struct ipv6_pond *pond;
 
                        lease = reply->old_ia->iasubopt[i];
                        candidate_shared = lease->ipv6_pool->shared_network;
+                       pond = lease->ipv6_pool->ipv6_pond;
 
                        /*
                         * Look for the best lease on the client's shared
-                        * network.
+                        * network, that is still permitted
                         */
-                       if ((candidate_shared == reply->shared) && 
-                           (lease6_usable(lease) == ISC_TRUE)) {
-                               best_lease = lease_compare(lease, best_lease);
-                       }
+
+                       if ((candidate_shared != reply->shared) ||
+                           (lease6_usable(lease) != ISC_TRUE))
+                               continue;
+
+                       if (((pond->prohibit_list != NULL) &&
+                            (permitted(reply->packet, pond->prohibit_list))) ||
+                           ((pond->permit_list != NULL) &&
+                            (!permitted(reply->packet, pond->permit_list))))
+                               continue;
+
+                       best_lease = lease_compare(lease, best_lease);
                }
        }
 
@@ -2831,8 +3004,7 @@ find_client_address(struct reply_state *reply) {
         * abandoned lease.
         */
        if ((best_lease == NULL) || (best_lease->state == FTS_ABANDONED)) {
-               status = pick_v6_address(&reply->lease, reply->shared,
-                                        &reply->ia->iaid_duid);
+               status = pick_v6_address(reply);
        } else if (best_lease != NULL) {
                iasubopt_reference(&reply->lease, best_lease, MDL);
                status = ISC_R_SUCCESS;
@@ -2859,7 +3031,7 @@ find_client_address(struct reply_state *reply) {
         * be desirable to place the group attachment directly in the pool.
         */
        scope = &reply->lease->scope;
-       group = reply->lease->ipv6_pool->subnet->group;
+       group = reply->lease->ipv6_pool->ipv6_pond->group;
 
        send_addr.len = 16;
        memcpy(send_addr.iabuf, &reply->lease->addr, 16);
@@ -2886,6 +3058,7 @@ reply_process_is_addressed(struct reply_state *reply,
        struct option_cache *oc;
        struct option_state *tmp_options = NULL;
        struct on_star *on_star;
+       int i;
 
        /* Initialize values we will cleanup. */
        memset(&data, 0, sizeof(data));
@@ -2925,6 +3098,15 @@ reply_process_is_addressed(struct reply_state *reply,
                                    reply->packet->options, reply->opt_state,
                                    scope, group, root_group, on_star);
 
+       /* Execute statements from class scopes. */
+       for (i = reply->packet->class_count; i > 0; i--) {
+               execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+                                           reply->packet->options,
+                                           reply->opt_state, scope,
+                                           reply->packet->classes[i - 1]->group,
+                                           group, on_star);
+       }
+
        /*
         * If there is a host record, over-ride with values configured there,
         * without re-evaluating configuration from the previously executed
@@ -3043,6 +3225,15 @@ reply_process_is_addressed(struct reply_state *reply,
                                    reply->packet->options, reply->reply_ia,
                                    scope, group, root_group, NULL);
 
+       /* Execute statements from class scopes. */
+       for (i = reply->packet->class_count; i > 0; i--) {
+               execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+                                           reply->packet->options,
+                                           reply->reply_ia, scope,
+                                           reply->packet->classes[i - 1]->group,
+                                           group, NULL);
+       }
+         
        /*
         * And bring in host record configuration, if any, but not to overlap
         * the previous group or its common enclosers.
@@ -3540,6 +3731,7 @@ reply_process_prefix(struct reply_state *reply, struct option_cache *pref) {
        struct option_cache *oc;
        struct data_string iapref, data;
        isc_result_t status = ISC_R_SUCCESS;
+       struct group *group;
 
        /* Initializes values that will be cleaned up. */
        memset(&iapref, 0, sizeof(iapref));
@@ -3681,11 +3873,19 @@ reply_process_prefix(struct reply_state *reply, struct option_cache *pref) {
                        log_fatal("Impossible condition at %s:%d.", MDL);
 
                scope = &global_scope;
+
+               /* Find the static prefixe's subnet. */
+               if (find_grouped_subnet(&reply->subnet, reply->shared,
+                                       tmp_pref.lo_addr, MDL) == 0)
+                       log_fatal("Impossible condition at %s:%d.", MDL);
+               group = reply->subnet->group;
+               subnet_dereference(&reply->subnet, MDL);
        } else {
                if (reply->lease == NULL)
                        log_fatal("Impossible condition at %s:%d.", MDL);
 
                scope = &reply->lease->scope;
+               group = reply->lease->ipv6_pool->ipv6_pond->group;
        }
 
        /*
@@ -3729,7 +3929,7 @@ reply_process_prefix(struct reply_state *reply, struct option_cache *pref) {
                        goto cleanup;
        }
 
-       status = reply_process_is_prefixed(reply, scope, reply->shared->group);
+       status = reply_process_is_prefixed(reply, scope, group);
        if (status != ISC_R_SUCCESS)
                goto cleanup;
 
@@ -3757,6 +3957,7 @@ static isc_boolean_t
 prefix_is_owned(struct reply_state *reply, struct iaddrcidrnet *pref) {
        struct iaddrcidrnetlist *l;
        int i;
+       struct ipv6_pond *pond;
 
        /*
         * This faults out prefixes that don't match fixed prefixes.
@@ -3785,6 +3986,14 @@ prefix_is_owned(struct reply_state *reply, struct iaddrcidrnet *pref) {
                        if (lease6_usable(tmp) == ISC_FALSE) {
                                return (ISC_FALSE);
                        }
+
+                       pond = tmp->ipv6_pool->ipv6_pond;
+                       if (((pond->prohibit_list != NULL) &&
+                            (permitted(reply->packet, pond->prohibit_list))) ||
+                           ((pond->permit_list != NULL) &&
+                            (!permitted(reply->packet, pond->permit_list))))
+                               return (ISC_FALSE);
+
                        iasubopt_reference(&reply->lease, tmp, MDL);
                        return (ISC_TRUE);
                }
@@ -3801,7 +4010,8 @@ static isc_result_t
 reply_process_try_prefix(struct reply_state *reply,
                         struct iaddrcidrnet *pref) {
        isc_result_t status = ISC_R_ADDRNOTAVAIL;
-       struct ipv6_pool *pool;
+       struct ipv6_pool *pool = NULL;
+       struct ipv6_pond *pond = NULL;
        int i;
        struct data_string data_pref;
 
@@ -3809,8 +4019,26 @@ reply_process_try_prefix(struct reply_state *reply,
            (pref == NULL) || (reply->lease != NULL))
                return (DHCP_R_INVALIDARG);
 
-       if (reply->shared->ipv6_pools == NULL)
+       /*
+        * Do a quick walk through of the ponds and pools
+        * to see if we have any prefix pools
+        */
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (pond->ipv6_pools == NULL)
+                       continue;
+
+               for (i = 0; (pool = pond->ipv6_pools[i]) != NULL; i++) {
+                       if (pool->pool_type == D6O_IA_PD)
+                               break;
+               }
+               if (pool != NULL)
+                       break;
+       }
+
+       /* If we get here and p is NULL we have no useful pools */
+       if (pool == NULL) {
                return (ISC_R_ADDRNOTAVAIL);
+       }
 
        memset(&data_pref, 0, sizeof(data_pref));
        data_pref.len = 17;
@@ -3822,13 +4050,33 @@ reply_process_try_prefix(struct reply_state *reply,
        data_pref.buffer->data[0] = (u_int8_t) pref->bits;
        memcpy(data_pref.buffer->data + 1, pref->lo_addr.iabuf, 16);
 
-       for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) {
-               if (pool->pool_type != D6O_IA_PD)
+       /*
+        * We have at least one pool that could provide a prefix
+        * Now we walk through the ponds and pools again and check
+        * to see if the client is permitted and if an prefix is
+        * available
+        * 
+        */
+
+       for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               if (((pond->prohibit_list != NULL) &&
+                    (permitted(reply->packet, pond->prohibit_list))) ||
+                   ((pond->permit_list != NULL) &&
+                    (!permitted(reply->packet, pond->permit_list))))
                        continue;
-               status = try_client_v6_prefix(&reply->lease, pool,
-                                             &data_pref);
-                /* If we found it in this pool (either in use or available), 
-                   there is no need to look further. */
+
+               for (i = 0; (pool = pond->ipv6_pools[i]) != NULL; i++) {
+                       if (pool->pool_type != D6O_IA_PD) {
+                               continue;
+                       }
+
+                       status = try_client_v6_prefix(&reply->lease, pool,
+                                                     &data_pref);
+                       /* If we found it in this pool (either in use or available), 
+                          there is no need to look further. */
+                       if ( (status == ISC_R_SUCCESS) || (status == ISC_R_ADDRINUSE) )
+                               break;
+                       }
                if ( (status == ISC_R_SUCCESS) || (status == ISC_R_ADDRINUSE) )
                        break;
        }
@@ -3849,6 +4097,7 @@ find_client_prefix(struct reply_state *reply) {
        struct iasubopt *prefix, *best_prefix = NULL;
        struct binding_scope **scope;
        int i;
+       struct group *group;
 
        if (reply->static_prefixes > 0) {
                struct iaddrcidrnetlist *l;
@@ -3870,27 +4119,48 @@ find_client_prefix(struct reply_state *reply) {
                memcpy(&send_pref, &l->cidrnet, sizeof(send_pref));
 
                scope = &global_scope;
+
+               /* Find the static prefixe's subnet. */
+               if (find_grouped_subnet(&reply->subnet, reply->shared,
+                                       send_pref.lo_addr, MDL) == 0)
+                       log_fatal("Impossible condition at %s:%d.", MDL);
+               group = reply->subnet->group;
+               subnet_dereference(&reply->subnet, MDL);
+
                goto send_pref;
        }
 
        if (reply->old_ia != NULL)  {
                for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) {
                        struct shared_network *candidate_shared;
+                       struct ipv6_pond *pond;
 
                        prefix = reply->old_ia->iasubopt[i];
                        candidate_shared = prefix->ipv6_pool->shared_network;
+                       pond = prefix->ipv6_pool->ipv6_pond;
 
                        /*
                         * Consider this prefix if it is in a global pool or
                         * if it is scoped in a pool under the client's shared
                         * network.
                         */
-                       if (((candidate_shared == NULL) ||
-                            (candidate_shared == reply->shared)) &&
-                           (lease6_usable(prefix) == ISC_TRUE)) {
-                               best_prefix = prefix_compare(reply, prefix,
-                                                            best_prefix);
-                       }
+                       if (((candidate_shared != NULL) &&
+                            (candidate_shared != reply->shared)) ||
+                           (lease6_usable(prefix) != ISC_TRUE))
+                               continue;
+
+                       /*
+                        * And check if the prefix is still permitted
+                        */
+
+                       if (((pond->prohibit_list != NULL) &&
+                            (permitted(reply->packet, pond->prohibit_list))) ||
+                           ((pond->permit_list != NULL) &&
+                            (!permitted(reply->packet, pond->permit_list))))
+                               continue;
+
+                       best_prefix = prefix_compare(reply, prefix,
+                                                    best_prefix);
                }
        }
 
@@ -3898,8 +4168,7 @@ find_client_prefix(struct reply_state *reply) {
         * abandoned prefix.
         */
        if ((best_prefix == NULL) || (best_prefix->state == FTS_ABANDONED)) {
-               status = pick_v6_prefix(&reply->lease, reply->preflen,
-                                       reply->shared, &reply->client_id);
+               status = pick_v6_prefix(reply);
        } else if (best_prefix != NULL) {
                iasubopt_reference(&reply->lease, best_prefix, MDL);
                status = ISC_R_SUCCESS;
@@ -3922,13 +4191,14 @@ find_client_prefix(struct reply_state *reply) {
                log_fatal("Impossible condition at %s:%d.", MDL);
 
        scope = &reply->lease->scope;
+       group = reply->lease->ipv6_pool->ipv6_pond->group;
 
        send_pref.lo_addr.len = 16;
        memcpy(send_pref.lo_addr.iabuf, &reply->lease->addr, 16);
        send_pref.bits = (int) reply->lease->plen;
 
       send_pref:
-       status = reply_process_is_prefixed(reply, scope, reply->shared->group);
+       status = reply_process_is_prefixed(reply, scope, group);
        if (status != ISC_R_SUCCESS)
                return status;
 
@@ -3949,6 +4219,7 @@ reply_process_is_prefixed(struct reply_state *reply,
        struct option_cache *oc;
        struct option_state *tmp_options = NULL;
        struct on_star *on_star;
+       int i;
 
        /* Initialize values we will cleanup. */
        memset(&data, 0, sizeof(data));
@@ -3988,6 +4259,15 @@ reply_process_is_prefixed(struct reply_state *reply,
                                    reply->packet->options, reply->opt_state,
                                    scope, group, root_group, on_star);
 
+       /* Execute statements from class scopes. */
+       for (i = reply->packet->class_count; i > 0; i--) {
+               execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+                                           reply->packet->options,
+                                           reply->opt_state, scope,
+                                           reply->packet->classes[i - 1]->group,
+                                           group, on_star);
+       }
+         
        /*
         * If there is a host record, over-ride with values configured there,
         * without re-evaluating configuration from the previously executed
@@ -4091,6 +4371,15 @@ reply_process_is_prefixed(struct reply_state *reply,
                                    reply->packet->options, reply->reply_ia,
                                    scope, group, root_group, NULL);
 
+       /* Execute statements from class scopes. */
+       for (i = reply->packet->class_count; i > 0; i--) {
+               execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+                                           reply->packet->options,
+                                           reply->reply_ia, scope,
+                                           reply->packet->classes[i - 1]->group,
+                                           group, NULL);
+       }
+         
        /*
         * And bring in host record configuration, if any, but not to overlap
         * the previous group or its common enclosers.
@@ -5888,6 +6177,10 @@ dhcpv6_discard(struct packet *packet) {
 static void 
 build_dhcpv6_reply(struct data_string *reply, struct packet *packet) {
        memset(reply, 0, sizeof(*reply));
+
+       /* Classify the client */
+       classify_client(packet);
+
        switch (packet->dhcpv6_msg_type) {
                case DHCPV6_SOLICIT:
                        dhcpv6_solicit(reply, packet);
index b98db42889d5fd68068d75aecb2b23345fd2ff02..419e58e35b78a84536bff455b4313c9d8afaf623 100644 (file)
  *
  * A brief description of the IPv6 structures as reverse engineered.
  *
- * There are three major data strucutes involved in the database:
+ * There are four major data structures in the lease configuraion.
+ *
+ * - shared_network - The shared network is the outer enclosing scope for a
+ *                    network region that shares a broadcast domain.  It is
+ *                    composed of one or more subnets all of which are valid
+ *                    in the given region.  The share network may be
+ *                    explicitly defined or implicitly created if there is
+ *                    only a subnet statement.  This structrure is shared
+ *                    with v4.  Each shared network statment or naked subnet
+ *                    will map to one of these structures
+ *
+ * - subnet     - The subnet structure mostly specifies the address range
+ *                that could be valid in a given region.  This structute
+ *                doesn't include the addresses that the server can delegate
+ *                those are in the ipv6_pool.  This structure is also shared
+ *                with v4.  Each subnet statement will map to one of these
+ *                structures.
+ *
+ * - ipv6_pond  - The pond structure is a grouping of the address and prefix
+ *                information via the pointers to the ipv6_pool and the
+ *                allowability of this pool for given clinets via the permit
+ *                lists and the valid TIMEs.  This is equivilent to the v4
+ *                pool structure and would have been named ip6_pool except
+ *                that the name was already in use.  Generally each pool6
+ *                statement will map to one of these structures. In addition
+ *                there may be one or for each group of naked range6 and
+ *                prefix6 statements within a shared network that share
+ *                the same group of statements.
  *
  * - ipv6_pool - this contains information about a pool of addresses or prefixes
- *             that the server is using.  This includes a hash table that
- *             tracks the active items and a pair of heap tables one for
- *             active items and one for non-active items.  The heap tables
- *             are used to determine the next items to be modified due to
- *             timing events (expire mostly).
+ *               that the server is using.  This includes a hash table that
+ *               tracks the active items and a pair of heap tables one for
+ *               active items and one for non-active items.  The heap tables
+ *               are used to determine the next items to be modified due to
+ *               timing events (expire mostly).  
+ * 
+ * The linkages then look like this:
+ * \verbatim
+ *+--------------+   +-------------+
+ *|Shared Network|   | ipv6_pond   |
+ *|   group      |   |   group     |
+ *|              |   | permit info |
+ *|              |   |    next    ---->
+ *|    ponds    ---->|             |
+ *|              |<----  shared    |
+ *|   Subnets    |   |    pools    |
+ *+-----|--------+   +------|------+
+ *      |  ^                |    ^
+ *      |  |                v    |
+ *      |  |         +-----------|-+
+ *      |  |         | ipv6_pool | |
+ *      |  |         |    type   | |
+ *      |  |         |   ipv6_pond |
+ *      |  |         |             |
+ *      |  |         |    next    ---->    
+ *      |  |         |             |
+ *      |  |         |   subnet    |
+ *      |  |         +-----|-------+
+ *      |  |               |
+ *      |  |               v
+ *      |  |         +-------------+
+ *      |  |         |   subnet    |
+ *      |  +----------   shared    |
+ *      +----------->|             |
+ *                   |   group     |
+ *                   +-------------+
+ *
+ * The shared network contains a list of all the subnets that are on a broadcast
+ * doamin.  These can be used to determine if an address makes sense in a given
+ * domain, but the subnets do not contain the addresses the server can delegate.
+ * Those are stored in the ponds and pools.
+ *
+ * In the simple case to find an acceptable address the server would first find
+ * the shared network the client is on based on either the interface used to 
+ * receive the request or the relay agent's information.  From the shared 
+ * network the server will walk through it's list of ponds.  For each pond it 
+ * will evaluate the permit information against the (already done) classification.
+ * If it finds an acceptable pond it will then walk through the pools for that
+ * pond.  The server first checks the type of the pool (NA, TA and PD) agaisnt the
+ * request and if they match it attemps to find an address within that pool.  On
+ * success the address is used, on failure the server steps to the next pool and
+ * if necessary to the next pond.
+ *
+ * When the server is successful in finding an address it will execute any
+ * statements assocaited with the pond, then the subnet, then the shared
+ * network the group field is for in the above picture).
+ *
+ * In configurations that don't include either a shared network or a pool6
+ * statement (or both) the missing pieces are created.
+ * 
+ *
+ * There are three major data structuress involved in the lease database:
+ *
+ * - ipv6_pool - see above
  * - ia_xx   - this contains information about a single IA from a request
  *             normally it will contain one pointer to a lease for the client
  *             but it may contain more in some circumstances.  There are 3
  *             hash tables to aid in accessing these one each for NA, TA and PD.
- * - iasubopt- the v6 lease structure.  These are created dynamically when
- *             a client asks for something and will eventually be destroyed
- *             if the client doesn't re-ask for that item.  A lease has space
- *             for backpointers to the IA and to the pool to which it belongs.
- *             The pool backpointer is always filled, the IA pointer may not be.
+ * - iasubopt - the v6 lease structure.  These are created dynamically when
+ *              a client asks for something and will eventually be destroyed
+ *              if the client doesn't re-ask for that item.  A lease has space
+ *              for backpointers to the IA and to the pool to which it belongs.
+ *              The pool backpointer is always filled, the IA pointer may not be.
  *
  * In normal use we then have something like this:
  *
@@ -523,11 +609,27 @@ lease_index_changed(void *iasubopt, unsigned int new_heap_index) {
 }
 
 
-/*
- * Create a new IPv6 lease pool structure.
+/*!
  *
- * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
- *   initialized to NULL
+ * \brief Create a new IPv6 lease pool structure
+ *
+ * Allocate space for a new ipv6_pool structure and return a reference
+ * to it, includes setting the reference count to 1.
+ *
+ * \param     pool       = space for returning a referenced pointer to the pool.
+ *                        This must point to a space that has been initialzied
+ *                        to NULL by the caller.
+ * \param[in] type       = The type of the pool NA, TA or PD
+ * \param[in] start_addr = The first address in the range for the pool
+ * \param[in] bits       = The contiguous bits of the pool
+
+ * 
+ * \return
+ * ISC_R_SUCCESS     = The pool was successfully created, pool points to it.
+ * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been
+ *                    modified
+ * ISC_R_NOMEMORY    = The system wasn't able to allocate memory, pool has
+ *                    not been modified.
  */
 isc_result_t
 ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type,
@@ -576,11 +678,24 @@ ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type,
        return ISC_R_SUCCESS;
 }
 
-/*
- * Reference an IPv6 pool structure.
+/*!
  *
- * - pool must be a pointer to a (struct pool *) pointer previously
- *   initialized to NULL
+ * \brief reference an IPv6 pool structure.
+ *
+ * This function genreates a reference to an ipv6_pool structure
+ * and increments the reference count on the structure.
+ *
+ * \param[out] pool = space for returning a referenced pointer to the pool.
+ *                   This must point to a space that has been initialzied
+ *                   to NULL by the caller.
+ * \param[in]  src  = A pointer to the pool to reference.  This must not be
+ *                   NULL.
+ *
+ * \return
+ * ISC_R_SUCCESS     = The pool was successfully referenced, pool now points
+ *                    to src.
+ * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been
+ *                    modified.
  */
 isc_result_t
 ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
@@ -634,12 +749,24 @@ dereference_heap_entry(void *value, void *dummy) {
        iasubopt_dereference(&iasubopt, MDL);
 }
 
-
-/*
- * Dereference an IPv6 pool structure.
+/*!
  *
- * If it is the last reference, then the memory for the 
- * structure is freed.
+ * \brief de-reference an IPv6 pool structure.
+ *
+ * This function decrements the reference count in an ipv6_pool structure.
+ * If this was the last reference then the memory for the structure is
+ * freed.
+ *
+ * \param[in] pool = A pointer to the pointer to the pool that should be
+ *                  de-referenced.  On success the pointer to the pool
+ *                  is cleared.  It must not be NULL and must not point
+ *                  to NULL.
+ *
+ * \return
+ * ISC_R_SUCCESS     = The pool was successfully de-referenced, pool now points
+ *                    to NULL
+ * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been
+ *                    modified.
  */
 isc_result_t
 ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
@@ -804,7 +931,7 @@ static struct in6_addr resany;
 /*
  * Create a lease for the given address and client duid.
  *
- * - pool must be a pointer to a (struct pool *) pointer previously
+ * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
  *   initialized to NULL
  *
  * Right now we simply hash the DUID, and if we get a collision, we hash 
@@ -1255,6 +1382,7 @@ move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) {
 }
 
 /*!
+ *
  * \brief Renew a lease in the pool.
  *
  * The hard_lifetime_end_time of the lease should be set to
@@ -1277,8 +1405,8 @@ move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) {
  * If the lease is moving to active we call that routine
  * which will move it from the inactive list to the active list.
  *
- * \param pool a pool the lease belongs to
- * \param lease the lease to be renewed
+ * \param pool  = a pool the lease belongs to
+ * \param lease the lease to be renewed
  *
  * \return result of the renew operation (ISC_R_SUCCESS if successful,
            ISC_R_NOMEMORY when run out of memory)
@@ -1518,7 +1646,7 @@ build_prefix6(struct in6_addr *pref,
 /*
  * Create a lease for the given prefix and client duid.
  *
- * - pool must be a pointer to a (struct pool *) pointer previously
+ * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
  *   initialized to NULL
  *
  * Right now we simply hash the DUID, and if we get a collision, we hash 
@@ -2190,4 +2318,128 @@ mark_interfaces_unavailable(void) {
        }
 }
 
+/*!
+ * \brief Create a new IPv6 pond structure.
+ *
+ * Allocate space for a new ipv6_pond structure and return a reference
+ * to it, includes setting the reference count to 1.
+ *
+ * \param pond = space for returning a referenced pointer to the pond.
+ *              This must point to a space that has been initialzied
+ *              to NULL by the caller.
+ *
+ * \return
+ * ISC_R_SUCCESS     = The pond was successfully created, pond points to it.
+ * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been
+ *                    modified
+ * ISC_R_NOMEMORY    = The system wasn't able to allocate memory, pond has
+ *                    not been modified.
+ */
+isc_result_t
+ipv6_pond_allocate(struct ipv6_pond **pond, const char *file, int line) {
+       struct ipv6_pond *tmp;
+
+       if (pond == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return DHCP_R_INVALIDARG;
+       }
+       if (*pond != NULL) {
+               log_error("%s(%d): non-NULL pointer", file, line);
+               return DHCP_R_INVALIDARG;
+       }
+
+       tmp = dmalloc(sizeof(*tmp), file, line);
+       if (tmp == NULL) {
+               return ISC_R_NOMEMORY;
+       }
+
+       tmp->refcnt = 1;
+
+       *pond = tmp;
+       return ISC_R_SUCCESS;
+}
+
+/*!
+ *
+ * \brief reference an IPv6 pond structure.
+ *
+ * This function genreates a reference to an ipv6_pond structure
+ * and increments the reference count on the structure.
+ *
+ * \param[out] pond = space for returning a referenced pointer to the pond.
+ *                   This must point to a space that has been initialzied
+ *                   to NULL by the caller.
+ * \param[in]  src  = A pointer to the pond to reference.  This must not be
+ *                   NULL.
+ *
+ * \return
+ * ISC_R_SUCCESS     = The pond was successfully referenced, pond now points
+ *                    to src.
+ * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been
+ *                    modified.
+ */
+isc_result_t
+ipv6_pond_reference(struct ipv6_pond **pond, struct ipv6_pond *src,
+                   const char *file, int line) {
+       if (pond == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return DHCP_R_INVALIDARG;
+       }
+       if (*pond != NULL) {
+               log_error("%s(%d): non-NULL pointer", file, line);
+               return DHCP_R_INVALIDARG;
+       }
+       if (src == NULL) {
+               log_error("%s(%d): NULL pointer reference", file, line);
+               return DHCP_R_INVALIDARG;
+       }
+       *pond = src;
+       src->refcnt++;
+       return ISC_R_SUCCESS;
+}
+
+/*!
+ *
+ * \brief de-reference an IPv6 pond structure.
+ *
+ * This function decrements the reference count in an ipv6_pond structure.
+ * If this was the last reference then the memory for the structure is
+ * freed.
+ *
+ * \param[in] pond = A pointer to the pointer to the pond that should be
+ *                  de-referenced.  On success the pointer to the pond
+ *                  is cleared.  It must not be NULL and must not point
+ *                  to NULL.
+ *
+ * \return
+ * ISC_R_SUCCESS     = The pond was successfully de-referenced, pond now points
+ *                    to NULL
+ * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been
+ *                    modified.
+ */
+
+isc_result_t
+ipv6_pond_dereference(struct ipv6_pond **pond, const char *file, int line) {
+       struct ipv6_pond *tmp;
+
+       if ((pond == NULL) || (*pond == NULL)) {
+               log_error("%s(%d): NULL pointer", file, line);
+               return DHCP_R_INVALIDARG;
+       }
+
+       tmp = *pond;
+       *pond = NULL;
+
+       tmp->refcnt--;
+       if (tmp->refcnt < 0) {
+               log_error("%s(%d): negative refcnt", file, line);
+               tmp->refcnt = 0;
+       }
+       if (tmp->refcnt == 0) {
+               dfree(tmp, file, line);
+       }
+
+       return ISC_R_SUCCESS;
+}
+
 /* unittest moved to server/tests/mdb6_unittest.c */
index c559de24e2c0beb52a232bc37363fc3e7bdf6a0d..c6e431f519e1f1ddeb2d9cbb830f1990c07140c1 100644 (file)
@@ -28,7 +28,7 @@ statement.
 Then run the server as root, in debug mode:
 
 # touch /tmp/test.leases
-# dhcpd -cf test-a.conf -lf /tmp/test.leases -d
+# dhcpd -6 -cf test-a.conf -lf /tmp/test.leases -d
 
 You can invoke the scripts then: