2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-28 21:07:43 +00:00

-n [master]

26510
 Add support for classes in the IPv6 code
This commit is contained in:
Shawn Routhier 2013-08-27 14:20:09 -07:00
parent a7341359cc
commit 01fa619fab
9 changed files with 1403 additions and 456 deletions

View File

@ -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. - Add support in v6 for on-commit, on-expire and on-release.
[ISC-Bugs #27912 [ISC-Bugs #27912
- Add support for using classes with v6.
[ISC-Bugs #26510]
Changes since 4.2.5 Changes since 4.2.5
- Address static analysis warnings. - Address static analysis warnings.

View File

@ -1231,6 +1231,8 @@ intern(char *atom, enum dhcp_token dfv) {
return PACKET; return PACKET;
if (!strcasecmp (atom + 1, "ool")) if (!strcasecmp (atom + 1, "ool"))
return POOL; return POOL;
if (!strcasecmp (atom + 1, "ool6"))
return POOL6;
if (!strcasecmp (atom + 1, "refix6")) if (!strcasecmp (atom + 1, "refix6"))
return PREFIX6; return PREFIX6;
if (!strcasecmp (atom + 1, "seudo")) if (!strcasecmp (atom + 1, "seudo"))

View File

@ -32,6 +32,8 @@
* ``http://www.nominum.com''. * ``http://www.nominum.com''.
*/ */
/*! \file includes/dhcpd.h */
#include "config.h" #include "config.h"
#ifndef __CYGWIN32__ #ifndef __CYGWIN32__
@ -933,9 +935,7 @@ struct shared_network {
struct subnet *subnets; struct subnet *subnets;
struct interface_info *interface; struct interface_info *interface;
struct pool *pools; struct pool *pools;
struct ipv6_pool **ipv6_pools; /* NULL-terminated array */ struct ipv6_pond *ipv6_pond;
int last_ipv6_pool; /* offset of last IPv6 pool
used to issue a lease */
struct group *group; struct group *group;
#if defined (FAILOVER_PROTOCOL) #if defined (FAILOVER_PROTOCOL)
dhcp_failover_state_t *failover_peer; 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_ta_active;
extern ia_hash_t *ia_pd_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 { struct ipv6_pool {
int refcnt; /* reference count */ int refcnt; /* reference count */
u_int16_t pool_type; /* IA_xx */ u_int16_t pool_type; /* IA_xx */
@ -1545,6 +1565,34 @@ struct ipv6_pool {
struct shared_network *shared_network; /* shared_network for struct shared_network *shared_network; /* shared_network for
this pool */ this pool */
struct subnet *subnet; /* subnet 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 */ /* 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 *); int parse_ip6_prefix(struct parse *, struct iaddr *, u_int8_t *);
void parse_address_range (struct parse *, struct group *, int, void parse_address_range (struct parse *, struct group *, int,
struct pool *, struct lease **); struct pool *, struct lease **);
void parse_address_range6(struct parse *cfile, struct group *group); void parse_address_range6(struct parse *cfile, struct group *group,
void parse_prefix6(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_fixed_prefix6(struct parse *cfile, struct host_decl *host_decl);
void parse_ia_na_declaration(struct parse *); void parse_ia_na_declaration(struct parse *);
void parse_ia_ta_declaration(struct parse *); void parse_ia_ta_declaration(struct parse *);
void parse_ia_pd_declaration(struct parse *); void parse_ia_pd_declaration(struct parse *);
void parse_server_duid(struct parse *cfile); void parse_server_duid(struct parse *cfile);
void parse_server_duid_conf(struct parse *cfile); void parse_server_duid_conf(struct parse *cfile);
void parse_pool6_statement (struct parse *, struct group *, int);
/* ddns.c */ /* ddns.c */
int ddns_updates(struct packet *, struct lease *, struct lease *, 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); const struct in6_addr *addr);
isc_boolean_t ipv6_in_pool(const struct in6_addr *addr, isc_boolean_t ipv6_in_pool(const struct in6_addr *addr,
const struct ipv6_pool *pool); 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 renew_leases(struct ia_xx *ia);
isc_result_t release_leases(struct ia_xx *ia); isc_result_t release_leases(struct ia_xx *ia);

View File

@ -3,7 +3,7 @@
Tokens for config file lexer and parser. */ 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) 2004,2007-2009 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-2003 by Internet Software Consortium * Copyright (c) 1996-2003 by Internet Software Consortium
* *
@ -365,7 +365,8 @@ enum dhcp_token {
GETHOSTBYNAME = 665, GETHOSTBYNAME = 665,
PRIMARY6 = 666, PRIMARY6 = 666,
SECONDARY6 = 667, SECONDARY6 = 667,
TOKEN_INFINIBAND = 668 TOKEN_INFINIBAND = 668,
POOL6 = 669
}; };
#define is_identifier(x) ((x) >= FIRST_TOKEN && \ #define is_identifier(x) ((x) >= FIRST_TOKEN && \

View File

@ -32,10 +32,11 @@
* ``http://www.nominum.com''. * ``http://www.nominum.com''.
*/ */
/*! \file server/confpars.c */
#include "dhcpd.h" #include "dhcpd.h"
static unsigned char global_host_once = 1; static unsigned char global_host_once = 1;
static unsigned char dhcpv6_class_once = 1;
static int parse_binding_value(struct parse *cfile, static int parse_binding_value(struct parse *cfile,
struct binding_value *value); struct binding_value *value);
@ -634,7 +635,7 @@ int parse_statement (cfile, group, type, host_decl, declaration)
skip_to_semi(cfile); skip_to_semi(cfile);
return declaration; return declaration;
} }
parse_address_range6(cfile, group); parse_address_range6(cfile, group, NULL);
return declaration; return declaration;
case PREFIX6: case PREFIX6:
@ -645,7 +646,7 @@ int parse_statement (cfile, group, type, host_decl, declaration)
skip_to_semi(cfile); skip_to_semi(cfile);
return declaration; return declaration;
} }
parse_prefix6(cfile, group); parse_prefix6(cfile, group, NULL);
return declaration; return declaration;
case FIXED_PREFIX6: case FIXED_PREFIX6:
@ -660,6 +661,19 @@ int parse_statement (cfile, group, type, host_decl, declaration)
parse_fixed_prefix6(cfile, host_decl); parse_fixed_prefix6(cfile, host_decl);
break; 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 */ #endif /* DHCPv6 */
case TOKEN_NOT: case TOKEN_NOT:
@ -1377,6 +1391,163 @@ void parse_failover_state (cfile, state, stos)
} }
#endif /* defined (FAILOVER_PROTOCOL) */ #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 /* 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 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 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; 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) void parse_pool_statement (cfile, group, type)
struct parse *cfile; struct parse *cfile;
struct group *group; struct group *group;
@ -1416,15 +1606,11 @@ void parse_pool_statement (cfile, group, type)
const char *val; const char *val;
int done = 0; int done = 0;
struct pool *pool, **p, *pp; struct pool *pool, **p, *pp;
struct permit *permit;
struct permit **permit_head;
int declaration = 0; int declaration = 0;
isc_result_t status; isc_result_t status;
struct lease *lpchain = (struct lease *)0, *lp; struct lease *lpchain = NULL, *lp;
TIME t;
int is_allow = 0;
pool = (struct pool *)0; pool = NULL;
status = pool_allocate(&pool, MDL); status = pool_allocate(&pool, MDL);
if (status != ISC_R_SUCCESS) if (status != ISC_R_SUCCESS)
log_fatal ("no memory for pool: %s", log_fatal ("no memory for pool: %s",
@ -1462,14 +1648,13 @@ void parse_pool_statement (cfile, group, type)
} }
do { do {
token = peek_token (&val, (unsigned *)0, cfile); token = peek_token(&val, NULL, cfile);
switch (token) { switch (token) {
case TOKEN_NO: case TOKEN_NO:
skip_token(&val, (unsigned *)0, cfile); skip_token(&val, NULL, cfile);
token = next_token (&val, (unsigned *)0, cfile); token = next_token(&val, NULL, cfile);
if (token != FAILOVER || if (token != FAILOVER ||
(token = next_token (&val, (unsigned *)0, (token = next_token(&val, NULL, cfile)) != PEER) {
cfile)) != PEER) {
parse_warn(cfile, parse_warn(cfile,
"expecting \"failover peer\"."); "expecting \"failover peer\".");
skip_to_semi(cfile); skip_to_semi(cfile);
@ -1484,14 +1669,14 @@ void parse_pool_statement (cfile, group, type)
#if defined (FAILOVER_PROTOCOL) #if defined (FAILOVER_PROTOCOL)
case FAILOVER: case FAILOVER:
skip_token(&val, (unsigned *)0, cfile); skip_token(&val, NULL, cfile);
token = next_token (&val, (unsigned *)0, cfile); token = next_token (&val, NULL, cfile);
if (token != PEER) { if (token != PEER) {
parse_warn(cfile, "expecting 'peer'."); parse_warn(cfile, "expecting 'peer'.");
skip_to_semi(cfile); skip_to_semi(cfile);
break; break;
} }
token = next_token (&val, (unsigned *)0, cfile); token = next_token(&val, NULL, cfile);
if (token != STRING) { if (token != STRING) {
parse_warn(cfile, "expecting string."); parse_warn(cfile, "expecting string.");
skip_to_semi(cfile); skip_to_semi(cfile);
@ -1513,133 +1698,24 @@ void parse_pool_statement (cfile, group, type)
#endif #endif
case RANGE: case RANGE:
skip_token(&val, (unsigned *)0, cfile); skip_token(&val, NULL, cfile);
parse_address_range (cfile, group, type, parse_address_range (cfile, group, type,
pool, &lpchain); pool, &lpchain);
break; break;
case ALLOW: case ALLOW:
permit_head = &pool -> permit_list; skip_token(&val, NULL, cfile);
/* remember the clause which leads to get_permit */ get_permit(cfile, &pool->permit_list, 1,
is_allow = 1; &pool->valid_from, &pool->valid_until);
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);
break; break;
case DENY: case DENY:
permit_head = &pool -> prohibit_list; skip_token(&val, NULL, cfile);
/* remember the clause which leads to get_permit */ get_permit(cfile, &pool->prohibit_list, 0,
is_allow = 0; &pool->valid_from, &pool->valid_until);
goto get_permit; break;
case RBRACE: case RBRACE:
skip_token(&val, (unsigned *)0, cfile); skip_token(&val, NULL, cfile);
done = 1; done = 1;
break; break;
@ -1654,8 +1730,7 @@ void parse_pool_statement (cfile, group, type)
default: default:
declaration = parse_statement(cfile, pool->group, declaration = parse_statement(cfile, pool->group,
POOL_DECL, POOL_DECL, NULL,
(struct host_decl *)0,
declaration); declaration);
break; break;
} }
@ -1707,7 +1782,7 @@ void parse_pool_statement (cfile, group, type)
cleanup: cleanup:
/* Dereference the lease chain. */ /* Dereference the lease chain. */
lp = (struct lease *)0; lp = NULL;
while (lpchain) { while (lpchain) {
lease_reference(&lp, lpchain, MDL); lease_reference(&lp, lpchain, MDL);
lease_dereference(&lpchain, MDL); lease_dereference(&lpchain, MDL);
@ -2008,12 +2083,6 @@ int parse_class_declaration (cp, cfile, group, type)
int submatchedonce = 0; int submatchedonce = 0;
unsigned code; 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); token = next_token (&val, NULL, cfile);
if (token != STRING) { if (token != STRING) {
parse_warn (cfile, "Expecting class name"); parse_warn (cfile, "Expecting class name");
@ -3688,15 +3757,13 @@ void parse_address_range (cfile, group, type, inpool, lpchain)
#ifdef DHCPv6 #ifdef DHCPv6
static void static void
add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type, 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 ipv6_pool *pool;
struct shared_network *share;
struct in6_addr tmp_in6_addr; struct in6_addr tmp_in6_addr;
int num_pools; int num_pools;
struct ipv6_pool **tmp; struct ipv6_pool **tmp;
share = subnet->shared_network;
/* /*
* Create our pool. * Create our pool.
*/ */
@ -3724,16 +3791,19 @@ add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type,
pool->subnet = NULL; pool->subnet = NULL;
subnet_reference(&pool->subnet, subnet, MDL); subnet_reference(&pool->subnet, subnet, MDL);
pool->shared_network = NULL; 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; num_pools = 0;
} else { } else {
num_pools = 0; num_pools = 0;
while (share->ipv6_pools[num_pools] != NULL) { while (pond->ipv6_pools[num_pools] != NULL) {
num_pools++; num_pools++;
} }
} }
@ -3742,34 +3812,114 @@ add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type,
log_fatal("Out of memory"); log_fatal("Out of memory");
} }
if (num_pools > 0) { if (num_pools > 0) {
memcpy(tmp, share->ipv6_pools, memcpy(tmp, pond->ipv6_pools,
sizeof(struct ipv6_pool *) * num_pools); sizeof(struct ipv6_pool *) * num_pools);
} }
if (share->ipv6_pools != NULL) { if (pond->ipv6_pools != NULL) {
dfree(share->ipv6_pools, MDL); 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. * Record this pool in our array of pools for this shared network.
*/ */
ipv6_pool_reference(&share->ipv6_pools[num_pools], pool, MDL); ipv6_pool_reference(&pond->ipv6_pools[num_pools], pool, MDL);
share->ipv6_pools[num_pools+1] = NULL; 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 /* address-range6-declaration :== ip-address6 ip-address6 SEMI
| ip-address6 SLASH number SEMI | ip-address6 SLASH number SEMI
| ip-address6 [SLASH number] TEMPORARY SEMI */ | ip-address6 [SLASH number] TEMPORARY SEMI */
void 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; struct iaddr lo, hi;
int bits; int bits;
enum dhcp_token token; enum dhcp_token token;
const char *val; const char *val;
struct iaddrcidrnetlist *nets; struct iaddrcidrnetlist *nets, net;
struct iaddrcidrnetlist *p; struct iaddrcidrnetlist *p;
u_int16_t type = D6O_IA_NA; u_int16_t type = D6O_IA_NA;
struct ipv6_pond *pond = NULL;
if (local_family != AF_INET6) { if (local_family != AF_INET6) {
parse_warn(cfile, "range6 statement is only supported " parse_warn(cfile, "range6 statement is only supported "
@ -3789,6 +3939,12 @@ parse_address_range6(struct parse *cfile, struct group *group) {
return; 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 * 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); skip_to_semi(cfile);
return; return;
} }
bits = atoi(val); net.cidrnet.bits = atoi(val);
bits = net.cidrnet.bits;
if ((bits < 0) || (bits > 128)) { if ((bits < 0) || (bits > 128)) {
parse_warn(cfile, "networks have 0 to 128 bits"); parse_warn(cfile, "networks have 0 to 128 bits");
skip_to_semi(cfile); skip_to_semi(cfile);
return; 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"); parse_warn(cfile, "network mask too short");
skip_to_semi(cfile); skip_to_semi(cfile);
return; return;
@ -3829,8 +3987,7 @@ parse_address_range6(struct parse *cfile, struct group *group) {
type = D6O_IA_TA; type = D6O_IA_TA;
} }
add_ipv6_pool_to_subnet(group->subnet, type, &lo, nets = &net;
bits, 128);
} else if (token == TEMPORARY) { } else if (token == TEMPORARY) {
/* /*
@ -3838,15 +3995,16 @@ parse_address_range6(struct parse *cfile, struct group *group) {
*/ */
type = D6O_IA_TA; type = D6O_IA_TA;
skip_token(NULL, NULL, cfile); skip_token(NULL, NULL, cfile);
bits = 64; net.cidrnet.bits = 64;
if (!is_cidr_mask_valid(&lo, bits)) { if (!is_cidr_mask_valid(&net.cidrnet.lo_addr,
net.cidrnet.bits)) {
parse_warn(cfile, "network mask too short"); parse_warn(cfile, "network mask too short");
skip_to_semi(cfile); skip_to_semi(cfile);
return; return;
} }
add_ipv6_pool_to_subnet(group->subnet, type, &lo, nets = &net;
bits, 128);
} else { } else {
/* /*
* No '/', so we are looking for the end address of * No '/', so we are looking for the end address of
@ -3864,14 +4022,32 @@ parse_address_range6(struct parse *cfile, struct group *group) {
log_fatal("Error converting range to CIDR networks"); log_fatal("Error converting range to CIDR networks");
} }
}
/*
* 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) { for (p=nets; p != NULL; p=p->next) {
add_ipv6_pool_to_subnet(group->subnet, type, add_ipv6_pool_to_subnet(group->subnet, type,
&p->cidrnet.lo_addr, &p->cidrnet.lo_addr,
p->cidrnet.bits, 128); p->cidrnet.bits, 128, pond);
} }
/* if we allocated a list free it now */
if (nets != &net)
free_iaddrcidrnetlist(&nets); free_iaddrcidrnetlist(&nets);
}
ipv6_pond_dereference(&pond, MDL);
token = next_token(NULL, NULL, cfile); token = next_token(NULL, NULL, cfile);
if (token != SEMI) { if (token != SEMI) {
@ -3884,13 +4060,16 @@ parse_address_range6(struct parse *cfile, struct group *group) {
/* prefix6-declaration :== ip-address6 ip-address6 SLASH number SEMI */ /* prefix6-declaration :== ip-address6 ip-address6 SLASH number SEMI */
void 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; struct iaddr lo, hi;
int bits; int bits;
enum dhcp_token token; enum dhcp_token token;
const char *val; const char *val;
struct iaddrcidrnetlist *nets; struct iaddrcidrnetlist *nets;
struct iaddrcidrnetlist *p; struct iaddrcidrnetlist *p;
struct ipv6_pond *pond = NULL;
if (local_family != AF_INET6) { if (local_family != AF_INET6) {
parse_warn(cfile, "prefix6 statement is only supported " 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"); 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) { for (p = nets; p != NULL; p = p->next) {
/* Normalize and check. */ /* Normalize and check. */
if (p->cidrnet.bits == 128) { 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, add_ipv6_pool_to_subnet(group->subnet, D6O_IA_PD,
&p->cidrnet.lo_addr, &p->cidrnet.lo_addr,
p->cidrnet.bits, bits); p->cidrnet.bits, bits, pond);
} }
free_iaddrcidrnetlist(&nets); free_iaddrcidrnetlist(&nets);
@ -4052,6 +4243,141 @@ parse_fixed_prefix6(struct parse *cfile, struct host_decl *host_decl) {
*h = ia; *h = ia;
return; 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 */ #endif /* DHCPv6 */
/* allow-deny-keyword :== BOOTP /* allow-deny-keyword :== BOOTP

View File

@ -265,8 +265,8 @@ group {
.SH ADDRESS POOLS .SH ADDRESS POOLS
.PP .PP
The The
.B pool \fBpool\fR and \fBpool6\fR
declaration can be used to specify a pool of addresses that will be declarations can be used to specify a pool of addresses that will be
treated differently than another pool of addresses, even on the same treated differently than another pool of addresses, even on the same
network segment or subnet. For example, you may want to provide a 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 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 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 for a pool, then only clients that match the permit list and do not
match the deny list will be allowed access. 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 .SH DYNAMIC ADDRESS ALLOCATION
Address allocation is actually only done when a client is in the INIT 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 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 .PP
.nf .nf
.B failover peer "\fIname\fB" state { .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 peer state \fIstate\fB at \fIdate\fB;
.B } .B }
.fi .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 hold leases at one time, and it is possible to specify automatic
subclassing based on the contents of the client packet. subclassing based on the contents of the client packet.
.PP .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 To add clients to classes based on conditional evaluation, you can
specify a matching expression in the class statement: specify a matching expression in the class statement:
.PP .PP

View File

@ -14,6 +14,8 @@
* PERFORMANCE OF THIS SOFTWARE. * PERFORMANCE OF THIS SOFTWARE.
*/ */
/*! \file server/dhcpv6.c */
#include "dhcpd.h" #include "dhcpd.h"
#ifdef DHCPv6 #ifdef DHCPv6
@ -989,59 +991,87 @@ try_client_v6_address(struct iasubopt **addr,
return result; return result;
} }
/*
* Get an IPv6 address for the client. /*!
* *
* addr is the result (should be a pointer to NULL on entry) * \brief Get an IPv6 address for the client.
* packet is the information about the packet from the client *
* requested_iaaddr is a hint from the client * Attempt to find a usable address for the client. We walk through
* client_id is the DUID for the client * 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 static isc_result_t
pick_v6_address(struct iasubopt **addr, struct shared_network *shared_network, pick_v6_address(struct reply_state *reply)
const struct data_string *client_id)
{ {
struct ipv6_pool *p; struct ipv6_pool *p = NULL;
struct ipv6_pond *pond;
int i; int i;
int start_pool; int start_pool;
unsigned int attempts; unsigned int attempts;
char tmp_buf[INET6_ADDRSTRLEN]; 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) { 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 != 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: " log_debug("Unable to pick client address: "
"no IPv6 pools on this shared network"); "no IPv6 pools on this shared network");
return ISC_R_NORESOURCES; 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;
}
if (p->pool_type == D6O_IA_NA) {
break;
}
}
/* /*
* Otherwise try to get a lease from the first subnet possible. * 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
* *
* We start looking at the last pool we allocated from, unless * Within a given pond we start looking at the last pool we
* it had a collision trying to allocate an address. This will * allocated from, unless it had a collision trying to allocate
* tend to move us into less-filled pools. * an address. This will tend to move us into less-filled pools.
*/ */
start_pool = shared_network->last_ipv6_pool;
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; i = start_pool;
do { do {
p = pond->ipv6_pools[i];
p = shared_network->ipv6_pools[i];
if ((p->pool_type == D6O_IA_NA) && if ((p->pool_type == D6O_IA_NA) &&
(create_lease6(p, addr, &attempts, client_id, (create_lease6(p, addr, &attempts,
&reply->ia->iaid_duid,
cur_time + 120) == ISC_R_SUCCESS)) { cur_time + 120) == ISC_R_SUCCESS)) {
/* /*
* Record the pool used (or next one if there * Record the pool used (or next one if there
@ -1049,23 +1079,24 @@ pick_v6_address(struct iasubopt **addr, struct shared_network *shared_network,
*/ */
if (attempts > 1) { if (attempts > 1) {
i++; i++;
if (shared_network->ipv6_pools[i] == NULL) { if (pond->ipv6_pools[i] == NULL) {
i = 0; i = 0;
} }
} }
shared_network->last_ipv6_pool = i; pond->last_ipv6_pool = i;
log_debug("Picking pool address %s", log_debug("Picking pool address %s",
inet_ntop(AF_INET6, &((*addr)->addr), inet_ntop(AF_INET6, &((*addr)->addr),
tmp_buf, sizeof(tmp_buf))); tmp_buf, sizeof(tmp_buf)));
return ISC_R_SUCCESS; return (ISC_R_SUCCESS);
} }
i++; i++;
if (shared_network->ipv6_pools[i] == NULL) { if (pond->ipv6_pools[i] == NULL) {
i = 0; i = 0;
} }
} while (i != start_pool); } while (i != start_pool);
}
/* /*
* If we failed to pick an IPv6 address from any of the subnets. * If we failed to pick an IPv6 address from any of the subnets.
@ -1134,54 +1165,77 @@ try_client_v6_prefix(struct iasubopt **pref,
return result; return result;
} }
/* /*!
* Get an IPv6 prefix for the client.
* *
* pref is the result (should be a pointer to NULL on entry) * \brief Get an IPv6 prefix for the client.
* packet is the information about the packet from the client *
* requested_iaprefix is a hint from the client * Attempt to find a usable prefix for the client. We walk through
* plen is -1 or the requested prefix length * the ponds checking for permit and deny then through the pools
* client_id is the DUID for the client * 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 static isc_result_t
pick_v6_prefix(struct iasubopt **pref, int plen, pick_v6_prefix(struct reply_state *reply)
struct shared_network *shared_network,
const struct data_string *client_id)
{ {
struct ipv6_pool *p; struct ipv6_pool *p = NULL;
struct ipv6_pond *pond;
int i; int i;
unsigned int attempts; unsigned int attempts;
char tmp_buf[INET6_ADDRSTRLEN]; 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: " log_debug("Unable to pick client prefix: "
"no IPv6 pools on this shared network"); "no IPv6 pools on this shared network");
return ISC_R_NORESOURCES; 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. * 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 (i = 0;; i++) {
p = shared_network->ipv6_pools[i]; for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
if (p == NULL) { if (((pond->prohibit_list != NULL) &&
break; (permitted(reply->packet, pond->prohibit_list))) ||
} ((pond->permit_list != NULL) &&
(!permitted(reply->packet, pond->permit_list))))
continue;
for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) {
if (p->pool_type != D6O_IA_PD) { if (p->pool_type != D6O_IA_PD) {
continue; continue;
} }
@ -1189,17 +1243,19 @@ pick_v6_prefix(struct iasubopt **pref, int plen,
/* /*
* Try only pools with the requested prefix length if any. * Try only pools with the requested prefix length if any.
*/ */
if ((plen >= 0) && (p->units != plen)) { if ((reply->preflen >= 0) && (p->units != reply->preflen)) {
continue; continue;
} }
if (create_prefix6(p, pref, &attempts, client_id, if (create_prefix6(p, pref, &attempts, &reply->ia->iaid_duid,
cur_time + 120) == ISC_R_SUCCESS) { cur_time + 120) == ISC_R_SUCCESS) {
log_debug("Picking pool prefix %s/%u", log_debug("Picking pool prefix %s/%u",
inet_ntop(AF_INET6, &((*pref)->addr), inet_ntop(AF_INET6, &((*pref)->addr),
tmp_buf, sizeof(tmp_buf)), tmp_buf, sizeof(tmp_buf)),
(unsigned) (*pref)->plen); (unsigned) (*pref)->plen);
return ISC_R_SUCCESS;
return (ISC_R_SUCCESS);
}
} }
} }
@ -1258,6 +1314,7 @@ lease_to_client(struct data_string *reply_ret,
#if defined (RFC3315_PRE_ERRATA_2010_08) #if defined (RFC3315_PRE_ERRATA_2010_08)
isc_boolean_t no_resources_avail = ISC_FALSE; isc_boolean_t no_resources_avail = ISC_FALSE;
#endif #endif
int i;
memset(&packet_oro, 0, sizeof(packet_oro)); 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. * valid for the shared network the client is on.
*/ */
if (find_hosts_by_uid(&reply.host, client_id->data, client_id->len, 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); seek_shared_host(&reply.host, reply.shared);
}
if ((reply.host == NULL) && 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); seek_shared_host(&reply.host, reply.shared);
}
/* /*
* Check for 'hardware' matches last, as some of the synthesis methods * Check for 'hardware' matches last, as some of the synthesis methods
* are not considered to be as reliable. * are not considered to be as reliable.
*/ */
if ((reply.host == NULL) && 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); seek_shared_host(&reply.host, reply.shared);
}
/* Process the client supplied IA's onto the reply buffer. */ /* Process the client supplied IA's onto the reply buffer. */
reply.ia_count = 0; reply.ia_count = 0;
@ -1412,6 +1475,17 @@ lease_to_client(struct data_string *reply_ret,
reply.shared->group, root_group, reply.shared->group, root_group,
NULL); 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. */ /* Bring in any configuration from a host record. */
if (reply.host != NULL) if (reply.host != NULL)
execute_statements_in_scope(NULL, reply.packet, 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); log_fatal("Impossible condition at %s:%d.", MDL);
scope = &reply->lease->scope; 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 static isc_boolean_t
address_is_owned(struct reply_state *reply, struct iaddr *addr) { address_is_owned(struct reply_state *reply, struct iaddr *addr) {
int i; int i;
struct ipv6_pond *pond;
/* /*
* This faults out addresses that don't match fixed addresses. * 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) { if (lease6_usable(tmp) == ISC_FALSE) {
return (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); iasubopt_reference(&reply->lease, tmp, MDL);
return (ISC_TRUE); return (ISC_TRUE);
} }
} }
@ -2416,7 +2500,7 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
goto bad_temp; goto bad_temp;
status = reply_process_is_addressed(reply, status = reply_process_is_addressed(reply,
&reply->lease->scope, &reply->lease->scope,
reply->shared->group); reply->lease->ipv6_pool->ipv6_pond->group);
if (status != ISC_R_SUCCESS) if (status != ISC_R_SUCCESS)
goto bad_temp; goto bad_temp;
status = reply_process_send_addr(reply, &tmp_addr); 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) { temporary_is_available(struct reply_state *reply, struct iaddr *addr) {
struct in6_addr tmp_addr; struct in6_addr tmp_addr;
struct subnet *subnet; struct subnet *subnet;
struct ipv6_pool *pool; struct ipv6_pool *pool = NULL;
struct ipv6_pond *pond = NULL;
int i; int i;
memcpy(&tmp_addr, addr->iabuf, sizeof(tmp_addr)); 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. * Verify that this address is in a temporary pool and try to get it.
*/ */
if (reply->shared->ipv6_pools == NULL) for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
return ISC_FALSE; if (((pond->prohibit_list != NULL) &&
for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) { (permitted(reply->packet, pond->prohibit_list))) ||
((pond->permit_list != NULL) &&
(!permitted(reply->packet, pond->permit_list))))
continue;
for (i = 0 ; (pool = pond->ipv6_pools[i]) != NULL ; i++) {
if (pool->pool_type != D6O_IA_TA) if (pool->pool_type != D6O_IA_TA)
continue; continue;
if (ipv6_in_pool(&tmp_addr, pool)) if (ipv6_in_pool(&tmp_addr, pool))
break; break;
} }
if (pool != NULL)
break;
}
if (pool == NULL) if (pool == NULL)
return ISC_FALSE; return ISC_FALSE;
if (lease6_exists(pool, &tmp_addr)) if (lease6_exists(pool, &tmp_addr))
@ -2684,29 +2780,51 @@ temporary_is_available(struct reply_state *reply, struct iaddr *addr) {
*/ */
static isc_result_t static isc_result_t
find_client_temporaries(struct reply_state *reply) { find_client_temporaries(struct reply_state *reply) {
struct shared_network *shared;
int i; int i;
struct ipv6_pool *p; struct ipv6_pool *p;
isc_result_t status; struct ipv6_pond *pond;
isc_result_t status = ISC_R_NORESOURCES;;
unsigned int attempts; unsigned int attempts;
struct iaddr send_addr; 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; for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
if (shared->ipv6_pools == NULL) { 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: " log_debug("Unable to get client addresses: "
"no IPv6 pools on this shared network"); "no IPv6 pools on this shared network");
return ISC_R_NORESOURCES; return ISC_R_NORESOURCES;
} }
status = ISC_R_NORESOURCES; /*
for (i = 0;; i++) { * We have at least one pool that could provide an address
p = shared->ipv6_pools[i]; * Now we walk through the ponds and pools again and check
if (p == NULL) { * to see if the client is permitted and if an address is
break; * 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;
for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) {
if (p->pool_type != D6O_IA_TA) { if (p->pool_type != D6O_IA_TA) {
continue; continue;
} }
@ -2723,7 +2841,7 @@ find_client_temporaries(struct reply_state *reply) {
status = reply_process_is_addressed(reply, status = reply_process_is_addressed(reply,
&reply->lease->scope, &reply->lease->scope,
reply->lease->ipv6_pool->subnet->group); pond->group);
if (status != ISC_R_SUCCESS) { if (status != ISC_R_SUCCESS) {
goto cleanup; goto cleanup;
} }
@ -2739,6 +2857,7 @@ find_client_temporaries(struct reply_state *reply) {
*/ */
iasubopt_dereference(&reply->lease, MDL); iasubopt_dereference(&reply->lease, MDL);
} }
}
cleanup: cleanup:
if (reply->lease != NULL) { if (reply->lease != NULL) {
@ -2754,7 +2873,8 @@ find_client_temporaries(struct reply_state *reply) {
static isc_result_t static isc_result_t
reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) { reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) {
isc_result_t status = ISC_R_ADDRNOTAVAIL; isc_result_t status = ISC_R_ADDRNOTAVAIL;
struct ipv6_pool *pool; struct ipv6_pool *pool = NULL;
struct ipv6_pond *pond = NULL;
int i; int i;
struct data_string data_addr; struct data_string data_addr;
@ -2762,22 +2882,65 @@ reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) {
(addr == NULL) || (reply->lease != NULL)) (addr == NULL) || (reply->lease != NULL))
return (DHCP_R_INVALIDARG); 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); return (ISC_R_ADDRNOTAVAIL);
}
memset(&data_addr, 0, sizeof(data_addr)); memset(&data_addr, 0, sizeof(data_addr));
data_addr.len = addr->len; data_addr.len = addr->len;
data_addr.data = addr->iabuf; data_addr.data = addr->iabuf;
for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) { /*
* 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;
for (i = 0 ; (pool = pond->ipv6_pools[i]) != NULL ; i++) {
if (pool->pool_type != D6O_IA_NA) if (pool->pool_type != D6O_IA_NA)
continue; continue;
status = try_client_v6_address(&reply->lease, pool, status = try_client_v6_address(&reply->lease, pool,
&data_addr); &data_addr);
if (status == ISC_R_SUCCESS) if (status == ISC_R_SUCCESS)
break; break;
} }
if (status == ISC_R_SUCCESS)
break;
}
/* Note that this is just pedantry. There is no allocation to free. */ /* Note that this is just pedantry. There is no allocation to free. */
data_string_forget(&data_addr, MDL); data_string_forget(&data_addr, MDL);
/* Return just the most recent status... */ /* Return just the most recent status... */
@ -2812,27 +2975,36 @@ find_client_address(struct reply_state *reply) {
if (reply->old_ia != NULL) { if (reply->old_ia != NULL) {
for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) { for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) {
struct shared_network *candidate_shared; struct shared_network *candidate_shared;
struct ipv6_pond *pond;
lease = reply->old_ia->iasubopt[i]; lease = reply->old_ia->iasubopt[i];
candidate_shared = lease->ipv6_pool->shared_network; candidate_shared = lease->ipv6_pool->shared_network;
pond = lease->ipv6_pool->ipv6_pond;
/* /*
* Look for the best lease on the client's shared * 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)) { 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); best_lease = lease_compare(lease, best_lease);
} }
} }
}
/* Try to pick a new address if we didn't find one, or if we found an /* Try to pick a new address if we didn't find one, or if we found an
* abandoned lease. * abandoned lease.
*/ */
if ((best_lease == NULL) || (best_lease->state == FTS_ABANDONED)) { if ((best_lease == NULL) || (best_lease->state == FTS_ABANDONED)) {
status = pick_v6_address(&reply->lease, reply->shared, status = pick_v6_address(reply);
&reply->ia->iaid_duid);
} else if (best_lease != NULL) { } else if (best_lease != NULL) {
iasubopt_reference(&reply->lease, best_lease, MDL); iasubopt_reference(&reply->lease, best_lease, MDL);
status = ISC_R_SUCCESS; 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. * be desirable to place the group attachment directly in the pool.
*/ */
scope = &reply->lease->scope; scope = &reply->lease->scope;
group = reply->lease->ipv6_pool->subnet->group; group = reply->lease->ipv6_pool->ipv6_pond->group;
send_addr.len = 16; send_addr.len = 16;
memcpy(send_addr.iabuf, &reply->lease->addr, 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_cache *oc;
struct option_state *tmp_options = NULL; struct option_state *tmp_options = NULL;
struct on_star *on_star; struct on_star *on_star;
int i;
/* Initialize values we will cleanup. */ /* Initialize values we will cleanup. */
memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(data));
@ -2925,6 +3098,15 @@ reply_process_is_addressed(struct reply_state *reply,
reply->packet->options, reply->opt_state, reply->packet->options, reply->opt_state,
scope, group, root_group, on_star); 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, * If there is a host record, over-ride with values configured there,
* without re-evaluating configuration from the previously executed * 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, reply->packet->options, reply->reply_ia,
scope, group, root_group, NULL); 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 * And bring in host record configuration, if any, but not to overlap
* the previous group or its common enclosers. * 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 option_cache *oc;
struct data_string iapref, data; struct data_string iapref, data;
isc_result_t status = ISC_R_SUCCESS; isc_result_t status = ISC_R_SUCCESS;
struct group *group;
/* Initializes values that will be cleaned up. */ /* Initializes values that will be cleaned up. */
memset(&iapref, 0, sizeof(iapref)); 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); log_fatal("Impossible condition at %s:%d.", MDL);
scope = &global_scope; 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 { } else {
if (reply->lease == NULL) if (reply->lease == NULL)
log_fatal("Impossible condition at %s:%d.", MDL); log_fatal("Impossible condition at %s:%d.", MDL);
scope = &reply->lease->scope; 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; 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) if (status != ISC_R_SUCCESS)
goto cleanup; goto cleanup;
@ -3757,6 +3957,7 @@ static isc_boolean_t
prefix_is_owned(struct reply_state *reply, struct iaddrcidrnet *pref) { prefix_is_owned(struct reply_state *reply, struct iaddrcidrnet *pref) {
struct iaddrcidrnetlist *l; struct iaddrcidrnetlist *l;
int i; int i;
struct ipv6_pond *pond;
/* /*
* This faults out prefixes that don't match fixed prefixes. * 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) { if (lease6_usable(tmp) == ISC_FALSE) {
return (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); iasubopt_reference(&reply->lease, tmp, MDL);
return (ISC_TRUE); return (ISC_TRUE);
} }
@ -3801,7 +4010,8 @@ static isc_result_t
reply_process_try_prefix(struct reply_state *reply, reply_process_try_prefix(struct reply_state *reply,
struct iaddrcidrnet *pref) { struct iaddrcidrnet *pref) {
isc_result_t status = ISC_R_ADDRNOTAVAIL; isc_result_t status = ISC_R_ADDRNOTAVAIL;
struct ipv6_pool *pool; struct ipv6_pool *pool = NULL;
struct ipv6_pond *pond = NULL;
int i; int i;
struct data_string data_pref; struct data_string data_pref;
@ -3809,8 +4019,26 @@ reply_process_try_prefix(struct reply_state *reply,
(pref == NULL) || (reply->lease != NULL)) (pref == NULL) || (reply->lease != NULL))
return (DHCP_R_INVALIDARG); 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); return (ISC_R_ADDRNOTAVAIL);
}
memset(&data_pref, 0, sizeof(data_pref)); memset(&data_pref, 0, sizeof(data_pref));
data_pref.len = 17; data_pref.len = 17;
@ -3822,9 +4050,26 @@ reply_process_try_prefix(struct reply_state *reply,
data_pref.buffer->data[0] = (u_int8_t) pref->bits; data_pref.buffer->data[0] = (u_int8_t) pref->bits;
memcpy(data_pref.buffer->data + 1, pref->lo_addr.iabuf, 16); 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; continue;
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, status = try_client_v6_prefix(&reply->lease, pool,
&data_pref); &data_pref);
/* If we found it in this pool (either in use or available), /* If we found it in this pool (either in use or available),
@ -3832,6 +4077,9 @@ reply_process_try_prefix(struct reply_state *reply,
if ( (status == ISC_R_SUCCESS) || (status == ISC_R_ADDRINUSE) ) if ( (status == ISC_R_SUCCESS) || (status == ISC_R_ADDRINUSE) )
break; break;
} }
if ( (status == ISC_R_SUCCESS) || (status == ISC_R_ADDRINUSE) )
break;
}
data_string_forget(&data_pref, MDL); data_string_forget(&data_pref, MDL);
/* Return just the most recent status... */ /* Return just the most recent status... */
@ -3849,6 +4097,7 @@ find_client_prefix(struct reply_state *reply) {
struct iasubopt *prefix, *best_prefix = NULL; struct iasubopt *prefix, *best_prefix = NULL;
struct binding_scope **scope; struct binding_scope **scope;
int i; int i;
struct group *group;
if (reply->static_prefixes > 0) { if (reply->static_prefixes > 0) {
struct iaddrcidrnetlist *l; struct iaddrcidrnetlist *l;
@ -3870,36 +4119,56 @@ find_client_prefix(struct reply_state *reply) {
memcpy(&send_pref, &l->cidrnet, sizeof(send_pref)); memcpy(&send_pref, &l->cidrnet, sizeof(send_pref));
scope = &global_scope; 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; goto send_pref;
} }
if (reply->old_ia != NULL) { if (reply->old_ia != NULL) {
for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) { for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) {
struct shared_network *candidate_shared; struct shared_network *candidate_shared;
struct ipv6_pond *pond;
prefix = reply->old_ia->iasubopt[i]; prefix = reply->old_ia->iasubopt[i];
candidate_shared = prefix->ipv6_pool->shared_network; candidate_shared = prefix->ipv6_pool->shared_network;
pond = prefix->ipv6_pool->ipv6_pond;
/* /*
* Consider this prefix if it is in a global pool or * Consider this prefix if it is in a global pool or
* if it is scoped in a pool under the client's shared * if it is scoped in a pool under the client's shared
* network. * network.
*/ */
if (((candidate_shared == NULL) || if (((candidate_shared != NULL) &&
(candidate_shared == reply->shared)) && (candidate_shared != reply->shared)) ||
(lease6_usable(prefix) == ISC_TRUE)) { (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 = prefix_compare(reply, prefix,
best_prefix); best_prefix);
} }
} }
}
/* Try to pick a new prefix if we didn't find one, or if we found an /* Try to pick a new prefix if we didn't find one, or if we found an
* abandoned prefix. * abandoned prefix.
*/ */
if ((best_prefix == NULL) || (best_prefix->state == FTS_ABANDONED)) { if ((best_prefix == NULL) || (best_prefix->state == FTS_ABANDONED)) {
status = pick_v6_prefix(&reply->lease, reply->preflen, status = pick_v6_prefix(reply);
reply->shared, &reply->client_id);
} else if (best_prefix != NULL) { } else if (best_prefix != NULL) {
iasubopt_reference(&reply->lease, best_prefix, MDL); iasubopt_reference(&reply->lease, best_prefix, MDL);
status = ISC_R_SUCCESS; status = ISC_R_SUCCESS;
@ -3922,13 +4191,14 @@ find_client_prefix(struct reply_state *reply) {
log_fatal("Impossible condition at %s:%d.", MDL); log_fatal("Impossible condition at %s:%d.", MDL);
scope = &reply->lease->scope; scope = &reply->lease->scope;
group = reply->lease->ipv6_pool->ipv6_pond->group;
send_pref.lo_addr.len = 16; send_pref.lo_addr.len = 16;
memcpy(send_pref.lo_addr.iabuf, &reply->lease->addr, 16); memcpy(send_pref.lo_addr.iabuf, &reply->lease->addr, 16);
send_pref.bits = (int) reply->lease->plen; send_pref.bits = (int) reply->lease->plen;
send_pref: 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) if (status != ISC_R_SUCCESS)
return status; return status;
@ -3949,6 +4219,7 @@ reply_process_is_prefixed(struct reply_state *reply,
struct option_cache *oc; struct option_cache *oc;
struct option_state *tmp_options = NULL; struct option_state *tmp_options = NULL;
struct on_star *on_star; struct on_star *on_star;
int i;
/* Initialize values we will cleanup. */ /* Initialize values we will cleanup. */
memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(data));
@ -3988,6 +4259,15 @@ reply_process_is_prefixed(struct reply_state *reply,
reply->packet->options, reply->opt_state, reply->packet->options, reply->opt_state,
scope, group, root_group, on_star); 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, * If there is a host record, over-ride with values configured there,
* without re-evaluating configuration from the previously executed * 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, reply->packet->options, reply->reply_ia,
scope, group, root_group, NULL); 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 * And bring in host record configuration, if any, but not to overlap
* the previous group or its common enclosers. * the previous group or its common enclosers.
@ -5888,6 +6177,10 @@ dhcpv6_discard(struct packet *packet) {
static void static void
build_dhcpv6_reply(struct data_string *reply, struct packet *packet) { build_dhcpv6_reply(struct data_string *reply, struct packet *packet) {
memset(reply, 0, sizeof(*reply)); memset(reply, 0, sizeof(*reply));
/* Classify the client */
classify_client(packet);
switch (packet->dhcpv6_msg_type) { switch (packet->dhcpv6_msg_type) {
case DHCPV6_SOLICIT: case DHCPV6_SOLICIT:
dhcpv6_solicit(reply, packet); dhcpv6_solicit(reply, packet);

View File

@ -25,7 +25,34 @@
* *
* A brief description of the IPv6 structures as reverse engineered. * 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 * - ipv6_pool - this contains information about a pool of addresses or prefixes
* that the server is using. This includes a hash table that * that the server is using. This includes a hash table that
@ -33,6 +60,65 @@
* active items and one for non-active items. The heap tables * active items and one for non-active items. The heap tables
* are used to determine the next items to be modified due to * are used to determine the next items to be modified due to
* timing events (expire mostly). * 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 * - ia_xx - this contains information about a single IA from a request
* normally it will contain one pointer to a lease for the client * normally it will contain one pointer to a lease for the client
* but it may contain more in some circumstances. There are 3 * but it may contain more in some circumstances. There are 3
@ -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 * \brief Create a new IPv6 lease pool structure
* initialized to NULL *
* 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 isc_result_t
ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type, 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; return ISC_R_SUCCESS;
} }
/* /*!
* Reference an IPv6 pool structure.
* *
* - pool must be a pointer to a (struct pool *) pointer previously * \brief reference an IPv6 pool structure.
* initialized to NULL *
* 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 isc_result_t
ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src, 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); iasubopt_dereference(&iasubopt, MDL);
} }
/*!
/*
* Dereference an IPv6 pool structure.
* *
* If it is the last reference, then the memory for the * \brief de-reference an IPv6 pool structure.
* structure is freed. *
* 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 isc_result_t
ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) { 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. * 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 * initialized to NULL
* *
* Right now we simply hash the DUID, and if we get a collision, we hash * 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. * \brief Renew a lease in the pool.
* *
* The hard_lifetime_end_time of the lease should be set to * 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 * If the lease is moving to active we call that routine
* which will move it from the inactive list to the active list. * which will move it from the inactive list to the active list.
* *
* \param pool a pool the lease belongs to * \param pool = a pool the lease belongs to
* \param lease the lease to be renewed * \param lease = the lease to be renewed
* *
* \return result of the renew operation (ISC_R_SUCCESS if successful, * \return result of the renew operation (ISC_R_SUCCESS if successful,
ISC_R_NOMEMORY when run out of memory) 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. * 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 * initialized to NULL
* *
* Right now we simply hash the DUID, and if we get a collision, we hash * 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 */ /* unittest moved to server/tests/mdb6_unittest.c */

View File

@ -28,7 +28,7 @@ statement.
Then run the server as root, in debug mode: Then run the server as root, in debug mode:
# touch /tmp/test.leases # 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: You can invoke the scripts then: