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