- 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.
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"))
* ``http://www.nominum.com''.
*/
+/*! \file includes/dhcpd.h */
+
#include "config.h"
#ifndef __CYGWIN32__
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;
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 */
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 */
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 *,
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);
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
*
GETHOSTBYNAME = 665,
PRIMARY6 = 666,
SECONDARY6 = 667,
- TOKEN_INFINIBAND = 668
+ TOKEN_INFINIBAND = 668,
+ POOL6 = 669
};
#define is_identifier(x) ((x) >= FIRST_TOKEN && \
* ``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);
skip_to_semi(cfile);
return declaration;
}
- parse_address_range6(cfile, group);
+ parse_address_range6(cfile, group, NULL);
return declaration;
case PREFIX6:
skip_to_semi(cfile);
return declaration;
}
- parse_prefix6(cfile, group);
+ parse_prefix6(cfile, group, NULL);
return declaration;
case FIXED_PREFIX6:
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:
}
#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
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;
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.");
#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;
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;
}
/* 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
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");
#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.
*/
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++;
}
}
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 "
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
*/
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;
type = D6O_IA_TA;
}
- add_ipv6_pool_to_subnet(group->subnet, type, &lo,
- bits, 128);
+ nets = &net;
} else if (token == TEMPORARY) {
/*
*/
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
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.");
/* 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 "
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) {
}
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);
*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
.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
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
.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
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
* PERFORMANCE OF THIS SOFTWARE.
*/
+/*! \file server/dhcpv6.c */
+
#include "dhcpd.h"
#ifdef DHCPv6
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.
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);
+ }
}
}
#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));
* 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;
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,
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;
}
/*
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.
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);
}
}
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);
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));
/*
* 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))
*/
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:
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;
(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;
}
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);
}
}
* 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;
* 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);
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));
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
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.
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));
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;
}
/*
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;
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.
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);
}
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;
(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;
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;
}
struct iasubopt *prefix, *best_prefix = NULL;
struct binding_scope **scope;
int i;
+ struct group *group;
if (reply->static_prefixes > 0) {
struct iaddrcidrnetlist *l;
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);
}
}
* 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;
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;
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));
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
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.
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);
*
* 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:
*
}
-/*
- * 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,
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,
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) {
/*
* 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
}
/*!
+ *
* \brief Renew a lease in the pool.
*
* The hard_lifetime_end_time of the lease should be set to
* 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)
/*
* 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
}
}
+/*!
+ * \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 */
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: