diff --git a/RELNOTES b/RELNOTES index 04928782..d9f71f7a 100644 --- a/RELNOTES +++ b/RELNOTES @@ -48,7 +48,7 @@ work on other platforms. Please report any problems and suggested fixes to . - Changes since 4.1.0a2 + Changes since 4.1.0a1 - Corrected list of failover state values in dhcpd man page. @@ -60,8 +60,6 @@ work on other platforms. Please report any problems and suggested fixes to - The server wasn't always sending the FQDN option when it should. - Changes since 4.1.0a1 - - Fixed a coredump when adding a class via OMAPI. - Check whether files are zero length before trying to parse them. @@ -111,6 +109,27 @@ work on other platforms. Please report any problems and suggested fixes to - Fix handling of -A and -a flags in dhcrelay; it was failing to expand packet size as needed to add relay agent options. +- A bug in subnet6 parsing where options contained in subnet6 clauses would + not be applied to clients addressed within that network was repaired. + +- When configuring a "subnet {}" or "subnet6 {}" without an explicit + shared-network enclosing it, the DHCP software would synthesize a + shared-network to contain the subnet. However, all configuration + parameters within the subnet more intuitively belong "to any client + on that interface", or rather the synthesized shared-network. So, + when a shared-network is synthesized, it is used to contain the + configuration present inside the subnet {} clause. This means that + the configuration will be valid for all clients on that network, not + just those addressed out of the stated subnet. If you intended the + opposite, the workaround is to explicitly configure an empty + shared-network. + +- A bug was fixed where Information-Request processing was not sourcing + configured option values. + +- A warning was added since the DHCPv6 processing software does not yet + support class statements. + Changes since 4.0.0 (new features) - Added DHCPv6 rapid commit support. diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 98ee91ef..41b0b01b 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -807,6 +807,10 @@ struct shared_network { OMAPI_OBJECT_PREAMBLE; struct shared_network *next; char *name; + +#define SHARED_IMPLICIT 1 /* This network was synthesized. */ + int flags; + struct subnet *subnets; struct interface_info *interface; struct pool *pools; @@ -1395,6 +1399,7 @@ struct ipv6_pool { released leases */ struct shared_network *shared_network; /* shared_network for this pool */ + struct subnet *subnet; /* subnet for this pool */ }; extern struct ipv6_pool **pools; diff --git a/server/confpars.c b/server/confpars.c index 747779a9..0a25cc44 100644 --- a/server/confpars.c +++ b/server/confpars.c @@ -35,6 +35,7 @@ #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); @@ -432,7 +433,7 @@ int parse_statement (cfile, group, type, host_decl, declaration) } /* If we're in a subnet declaration, just do the parse. */ - if (group->shared_network) { + if (group->shared_network != NULL) { if (token == SUBNET) { parse_subnet_declaration(cfile, group->shared_network); @@ -443,10 +444,15 @@ int parse_statement (cfile, group, type, host_decl, declaration) break; } - /* Otherwise, cons up a fake shared network structure - and populate it with the lone subnet... */ + /* + * Otherwise, cons up a fake shared network structure + * and populate it with the lone subnet...because the + * intention most likely is to refer to the entire link + * by shorthand, any configuration inside the subnet is + * actually placed in the shared-network's group. + */ - share = (struct shared_network *)0; + share = NULL; status = shared_network_allocate (&share, MDL); if (status != ISC_R_SUCCESS) log_fatal ("Can't allocate shared subnet: %s", @@ -456,6 +462,12 @@ int parse_statement (cfile, group, type, host_decl, declaration) shared_network_reference (&share -> group -> shared_network, share, MDL); + /* + * This is an implicit shared network, not explicit in + * the config. + */ + share->flags |= SHARED_IMPLICIT; + if (token == SUBNET) { parse_subnet_declaration(cfile, share); } else { @@ -2022,6 +2034,12 @@ 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, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "Expecting class name"); @@ -2547,8 +2565,22 @@ void parse_subnet_declaration (cfile, share) log_fatal ("Allocation of new subnet failed: %s", isc_result_totext (status)); shared_network_reference (&subnet -> shared_network, share, MDL); - if (!clone_group (&subnet -> group, share -> group, MDL)) - log_fatal ("allocation of group for new subnet failed."); + + /* + * If our parent shared network was implicitly created by the software, + * and not explicitly configured by the user, then we actually put all + * configuration scope in the parent (the shared network and subnet + * share the same {}-level scope). + * + * Otherwise, we clone the parent group and continue as normal. + */ + if (share->flags & SHARED_IMPLICIT) { + group_reference(&subnet->group, share->group, MDL); + } else { + if (!clone_group(&subnet->group, share->group, MDL)) { + log_fatal("Allocation of group for new subnet failed."); + } + } subnet_reference (&subnet -> group -> subnet, subnet, MDL); /* Get the network number... */ @@ -2626,8 +2658,21 @@ parse_subnet6_declaration(struct parse *cfile, struct shared_network *share) { isc_result_totext(status)); } shared_network_reference(&subnet->shared_network, share, MDL); - if (!clone_group(&subnet->group, share->group, MDL)) { - log_fatal("Allocation of group for new subnet failed."); + + /* + * If our parent shared network was implicitly created by the software, + * and not explicitly configured by the user, then we actually put all + * configuration scope in the parent (the shared network and subnet + * share the same {}-level scope). + * + * Otherwise, we clone the parent group and continue as normal. + */ + if (share->flags & SHARED_IMPLICIT) { + group_reference(&subnet->group, share->group, MDL); + } else { + if (!clone_group(&subnet->group, share->group, MDL)) { + log_fatal("Allocation of group for new subnet failed."); + } } subnet_reference(&subnet->group->subnet, subnet, MDL); @@ -3636,16 +3681,16 @@ void parse_address_range (cfile, group, type, inpool, lpchain) #ifdef DHCPv6 static void -add_ipv6_pool_to_shared_network(struct shared_network *share, - u_int16_t type, - struct iaddr *lo_addr, - int bits, - int units) { +add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type, + struct iaddr *lo_addr, int bits, int units) { 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. */ @@ -3668,8 +3713,10 @@ add_ipv6_pool_to_shared_network(struct shared_network *share, } /* - * Link our pool to our shared_network. + * Link the pool to its network. */ + pool->subnet = NULL; + subnet_reference(&pool->subnet, subnet, MDL); pool->shared_network = NULL; shared_network_reference(&pool->shared_network, share, MDL); @@ -3714,7 +3761,6 @@ parse_address_range6(struct parse *cfile, struct group *group) { int bits; enum dhcp_token token; const char *val; - struct shared_network *share; struct iaddrcidrnetlist *nets; struct iaddrcidrnetlist *p; @@ -3725,13 +3771,9 @@ parse_address_range6(struct parse *cfile, struct group *group) { return; } - /* - * We'll use the shared_network from our group. - */ - share = group->shared_network; - if (share == NULL) { - share = group->subnet->shared_network; - } + /* This is enforced by the caller, this is just a sanity check. */ + if (group->subnet == NULL) + log_fatal("Impossible condition at %s:%d.", MDL); /* * Read starting address. @@ -3767,8 +3809,8 @@ parse_address_range6(struct parse *cfile, struct group *group) { return; } - add_ipv6_pool_to_shared_network(share, D6O_IA_NA, &lo, - bits, 128); + add_ipv6_pool_to_subnet(group->subnet, D6O_IA_NA, &lo, bits, + 128); } else if (token == TEMPORARY) { /* @@ -3782,8 +3824,8 @@ parse_address_range6(struct parse *cfile, struct group *group) { return; } - add_ipv6_pool_to_shared_network(share, D6O_IA_TA, &lo, - bits, 128); + add_ipv6_pool_to_subnet(group->subnet, D6O_IA_TA, &lo, bits, + 128); } else { /* * No '/', so we are looking for the end address of @@ -3802,13 +3844,12 @@ parse_address_range6(struct parse *cfile, struct group *group) { } for (p=nets; p != NULL; p=p->next) { - add_ipv6_pool_to_shared_network(share, D6O_IA_NA, - &p->cidrnet.lo_addr, - p->cidrnet.bits, 128); + add_ipv6_pool_to_subnet(group->subnet, D6O_IA_NA, + &p->cidrnet.lo_addr, + p->cidrnet.bits, 128); } free_iaddrcidrnetlist(&nets); - } token = next_token(NULL, NULL, cfile); @@ -3827,7 +3868,6 @@ parse_prefix6(struct parse *cfile, struct group *group) { int bits; enum dhcp_token token; const char *val; - struct shared_network *share; struct iaddrcidrnetlist *nets; struct iaddrcidrnetlist *p; @@ -3838,14 +3878,10 @@ parse_prefix6(struct parse *cfile, struct group *group) { return; } - /* - * We'll use the shared_network from our group. - */ - share = group->shared_network; - if (share == NULL) { - share = group->subnet->shared_network; - } - + /* This is enforced by the caller, so it's just a sanity check. */ + if (group->subnet == NULL) + log_fatal("Impossible condition at %s:%d.", MDL); + /* * Read starting and ending address. */ @@ -3907,9 +3943,9 @@ parse_prefix6(struct parse *cfile, struct group *group) { parse_warn(cfile, "impossible mask length"); continue; } - add_ipv6_pool_to_shared_network(share, D6O_IA_PD, - &p->cidrnet.lo_addr, - p->cidrnet.bits, bits); + add_ipv6_pool_to_subnet(group->subnet, D6O_IA_PD, + &p->cidrnet.lo_addr, + p->cidrnet.bits, bits); } free_iaddrcidrnetlist(&nets); diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5 index dd71b78b..47ef2571 100644 --- a/server/dhcpd.conf.5 +++ b/server/dhcpd.conf.5 @@ -28,7 +28,7 @@ .\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see .\" ``http://www.nominum.com''. .\" -.\" $Id: dhcpd.conf.5,v 1.96 2008/06/30 17:37:47 jreed Exp $ +.\" $Id: dhcpd.conf.5,v 1.97 2008/08/19 17:55:57 dhankins Exp $ .\" .TH dhcpd.conf 5 .SH NAME @@ -86,6 +86,12 @@ ethernet until such time as a new physical network can be added. In this case, the \fIsubnet\fR declarations for these two networks must be enclosed in a \fIshared-network\fR declaration. .PP +Note that even when the \fIshared-network\fR declaration is absent, an +empty one is created by the server to contain the \fIsubnet\fR (and any scoped +parameters included in the \fIsubnet\fR). For practical purposes, this means +that "stateless" DHCP clients, which are not tied to addresses (and therefore +subnets) will receive the same configuration as stateful ones. +.PP Some sites may have departments which have clients on more than one subnet, but it may be desirable to offer those clients a uniform set of parameters which are different than what would be offered to diff --git a/server/dhcpv6.c b/server/dhcpv6.c index a2e6ac75..3aa6610e 100644 --- a/server/dhcpv6.c +++ b/server/dhcpv6.c @@ -1366,9 +1366,21 @@ lease_to_client(struct data_string *reply_ret, * Make no reply if we gave no resources and is not * for Information-Request. */ - if ((reply.ia_count == 0) && (reply.pd_count == 0) && - (reply.packet->dhcpv6_msg_type != DHCPV6_INFORMATION_REQUEST)) - goto exit; + if ((reply.ia_count == 0) && (reply.pd_count == 0)) { + if (reply.packet->dhcpv6_msg_type != + DHCPV6_INFORMATION_REQUEST) + goto exit; + + /* + * Because we only execute statements on a per-IA basis, + * we need to execute statements in any non-IA reply to + * source configuration. + */ + execute_statements_in_scope(NULL, reply.packet, NULL, NULL, + reply.packet->options, + reply.opt_state, &global_scope, + reply.shared->group, root_group); + } /* * RFC3315 section 17.2.2 (Solicit): @@ -2087,7 +2099,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->shared->group; + group = reply->lease->ipv6_pool->subnet->group; } /* @@ -2518,7 +2530,7 @@ find_client_temporaries(struct reply_state *reply) { status = reply_process_is_addressed(reply, &reply->lease->scope, - reply->shared->group); + reply->lease->ipv6_pool->subnet->group); if (status != ISC_R_SUCCESS) { goto cleanup; } @@ -2588,11 +2600,6 @@ find_client_address(struct reply_state *reply) { struct group *group; int i; - if (reply->host != NULL) - group = reply->host->group; - else - group = reply->shared->group; - if (reply->static_lease) { if (reply->host == NULL) return ISC_R_INVALIDARG; @@ -2602,6 +2609,7 @@ find_client_address(struct reply_state *reply) { status = ISC_R_SUCCESS; scope = &global_scope; + group = reply->host->group; goto send_addr; } @@ -2640,8 +2648,12 @@ find_client_address(struct reply_state *reply) { if (reply->lease == NULL) log_fatal("Impossible condition at %s:%d.", MDL); + /* Draw binding scopes from the lease's binding scope, and config + * from the lease's containing subnet and higher. Note that it may + * be desirable to place the group attachment directly in the pool. + */ scope = &reply->lease->scope; - group = reply->shared->group; + group = reply->lease->ipv6_pool->subnet->group; send_addr.len = 16; memcpy(send_addr.iabuf, &reply->lease->addr, 16);