2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 18:07:25 +00:00

[master] Added DHCPv6 prefix-length-mode configuration parameter

Merges in rt36780.
This commit is contained in:
Thomas Markwalder 2015-01-08 10:30:12 -05:00
parent fb98e02e12
commit 1a006ff6ed
6 changed files with 295 additions and 44 deletions

View File

@ -235,6 +235,16 @@ by Eric Young (eay@cryptsoft.com).
[ISC-Bugs #26376]
[ISC-Bugs #38131]
- Added a global parameter, prefix-length-mode, which may be used to determine
how the server uses a non-zero value for prefix-length supplied by clients
when soliciting DHCPv6 prefixes. The server supports selection modes of:
ignore, prefer, exact, minimum and maximum which are described in detail in
the server man pages. The prior behavior of the server was to only offer a
prefix whose length exactly matched the prefix-length value requested. If
no such prefixes were available, the server returned a status of none
available. Note the default mode, "exact", provides this same behavior.
[ISC-Bugs #36780]
Changes since 4.3.1b1
- Modify the linux and openwrt dhclient scripts to process information

View File

@ -741,6 +741,7 @@ struct lease_state {
#define SV_LOG_THRESHOLD_HIGH 84
#define SV_ECHO_CLIENT_ID 85
#define SV_SERVER_ID_CHECK 86
#define SV_PREFIX_LEN_MODE 87
#if !defined (DEFAULT_PING_TIMEOUT)
# define DEFAULT_PING_TIMEOUT 1
@ -789,6 +790,12 @@ struct lease_state {
# define MIN_LEASE_WRITE 15
#endif
#define PLM_IGNORE 0
#define PLM_PREFER 1
#define PLM_EXACT 2
#define PLM_MINIMUM 3
#define PLM_MAXIMUM 4
/* Client option names */
#define CL_TIMEOUT 1
@ -1962,6 +1969,8 @@ extern int ddns_update_style;
extern int dont_use_fsync;
extern int server_id_check;
extern int prefix_length_mode;
extern const char *path_dhcpd_conf;
extern const char *path_dhcpd_db;
extern const char *path_dhcpd_pid;
@ -2751,6 +2760,8 @@ extern struct enumeration ddns_styles;
extern struct enumeration syslog_enum;
void initialize_server_option_spaces (void);
extern struct enumeration prefix_length_modes;
/* inet.c */
struct iaddr subnet_number (struct iaddr, struct iaddr);
struct iaddr ip_addr (struct iaddr, struct iaddr, u_int32_t);

View File

@ -73,6 +73,7 @@ option server.ddns-rev-domainname = \"in-addr.arpa.\";";
int ddns_update_style;
int dont_use_fsync = 0; /* 0 = default, use fsync, 1 = don't use fsync */
int server_id_check = 0; /* 0 = default, don't check server id, 1 = do check */
int prefix_length_mode = PLM_EXACT;
const char *path_dhcpd_conf = _PATH_DHCPD_CONF;
const char *path_dhcpd_db = _PATH_DHCPD_DB;
@ -548,6 +549,7 @@ main(int argc, char **argv) {
dhcp_interface_setup_hook = dhcpd_interface_setup_hook;
bootp_packet_handler = do_packet;
#ifdef DHCPv6
add_enumeration (&prefix_length_modes);
dhcpv6_packet_handler = do_packet6;
#endif /* DHCPv6 */
@ -1100,6 +1102,19 @@ void postconf_initialization (int quiet)
server_id_check = 1;
}
oc = lookup_option(&server_universe, options, SV_PREFIX_LEN_MODE);
if ((oc != NULL) &&
evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
&global_scope, oc, MDL)) {
if (db.len == 1) {
prefix_length_mode = db.data[0];
} else {
log_fatal("invalid prefix-len-mode");
}
data_string_forget(&db, MDL);
}
/* Don't need the options anymore. */
option_state_dereference(&options, MDL);
}

View File

@ -1,6 +1,6 @@
.\" dhcpd.conf.5
.\"
.\" Copyright (c) 2004-2014 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 2004-2015 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium
.\"
.\" Permission to use, copy, modify, and distribute this software for any
@ -2786,6 +2786,80 @@ default lease time if none were specified.
.RE
.PP
The
.I prefix-length-mode
statement
.RS 0.25i
.PP
.B prefix-length-mode
.I mode\fR\fB;\fR
.PP
According to RFC 3633, DHCPv6 clients may specify preferences when soliciting
prefixes by including an IA_PD Prefix option within the IA_PD option. Among
the preferences that may be conveyed is the "prefix-length". When non-zero it
indicates a client's desired length for offered prefixes. The RFC states that
servers "MAY choose to use the information...to select prefix(es)" but does
not specify any particular rules for doing so. The prefix-length-mode statement
can be used to set the prefix selection rules employed by the server,
when clients send a non-zero prefix-length value. The mode parameter must
be one of \fBignore\fR, \fBprefer\fR, \fBexact\fR, \fBminimum\fR, or
\fBmaximum\fR where:
.PP
1. ignore - The requested length is ignored. The server will offer the first
available prefix.
.PP
2. prefer - The server will offer the first available prefix with the same
length as the requested length. If none are found then it will offer the
first available prefix of any length.
.PP
3. exact - The server will offer the first available prefix with the same
length as the requested length. If none are found, it will return a status
indicating no prefixes available. This is the default behavior.
.PP
4. minimum - The server will offer the first available prefix with the same
length as the requested length. If none are found, it will return the first
available prefix whose length is greater than (e.g. longer than), the
requested value. If none of those are found, it will return a status
indicating no prefixes available. For example, if client requests a length
of /60, and the server has available prefixes of lengths /56 and /64, it will
offer prefix of length /64.
.PP
5. maximum - The server will offer the first available prefix with the same
length as the requested length. If none are found, it will return the first
available prefix whose length is less than (e.g. shorter than), the
requested value. If none of those are found, it will return a status
indicating no prefixes available. For example, if client requests a length
of /60, and the server has available prefixes of lengths /56 and /64, it will
offer a prefix of length /56.
.PP
In general "first available" is determined by the order in which pools are
defined in the server's configuration. For example, if a subnet is defined
with three prefix pools A,B, and C:
.PP
.nf
subnet 3000::/64 {
# pool A
pool6 {
:
}
# pool B
pool6 {
:
}
# pool C
pool6 {
:
}
}
.fi
.PP
then the pools will be checked in the order A, B, C. For modes \fBprefer\fR,
\fBminimum\fR, and \fBmaximum\fR this may mean checking the pools in that order
twice. A first pass through is made looking for an available prefix of exactly
the preferred length. If none are found, then a second pass is performed
starting with pool A but with appropriately adjusted length criteria.
.RE
.PP
The
.I remote-port
statement
.RS 0.25i

View File

@ -150,6 +150,10 @@ static int find_hosts_by_duid_chaddr(struct host_decl **host,
const struct data_string *client_id);
static void schedule_lease_timeout_reply(struct reply_state *reply);
static int eval_prefix_mode(int thislen, int preflen, int prefix_mode);
static isc_result_t pick_v6_prefix_helper(struct reply_state *reply,
int prefix_mode);
/*
* Schedule lease timeouts for all of the iasubopts in the reply.
* This is currently used to schedule timeouts for soft leases.
@ -1319,9 +1323,27 @@ try_client_v6_prefix(struct iasubopt **pref,
*
* \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.
* Attempt to find a usable prefix for the client. Based upon the prefix
* length mode and the plen supplied by the client (if one), we make one
* or more calls to pick_v6_prefix_helper() to find a prefix as follows:
*
* PLM_IGNORE or client specifies a plen of zero, use the first available
* prefix regardless of it's length.
*
* PLM_PREFER look for an exact match to client's plen first, if none
* found, use the first available prefix of any length
*
* PLM_EXACT look for an exact match first, if none found then fail. This
* is the default behavior.
*
* PLM_MAXIMUM - look for an exact match first, then the first available whose
* prefix length is less than client's plen, otherwise fail.
*
* PLM_MINIMUM - look for an exact match first, then the first available whose
* prefix length is greater than client's plen, otherwise fail.
*
* Note that the selection mode is configurable at the global scope only via
* prefix-len-mode.
*
* \param reply = the state structure for the current work on this request
* if we create a lease we return it using reply->lease
@ -1336,16 +1358,12 @@ try_client_v6_prefix(struct iasubopt **pref,
* hash the address. After a number of failures we
* conclude the pool is basically full.
*/
static isc_result_t
pick_v6_prefix(struct reply_state *reply)
{
pick_v6_prefix(struct reply_state *reply) {
struct ipv6_pool *p = NULL;
struct ipv6_pond *pond;
int i;
unsigned int attempts;
char tmp_buf[INET6_ADDRSTRLEN];
struct iasubopt **pref = &reply->lease;
isc_result_t result;
/*
* Do a quick walk through of the ponds and pools
@ -1370,13 +1388,93 @@ pick_v6_prefix(struct reply_state *reply)
return ISC_R_NORESOURCES;
}
if (reply->preflen <= 0) {
/* If we didn't get a plen (-1) or client plen is 0, then just
* select first available (same as PLM_INGORE) */
result = pick_v6_prefix_helper(reply, PLM_IGNORE);
} else {
switch (prefix_length_mode) {
case PLM_PREFER:
/* First we look for an exact match, if not found
* then first available */
result = pick_v6_prefix_helper(reply, PLM_EXACT);
if (result != ISC_R_SUCCESS) {
result = pick_v6_prefix_helper(reply,
PLM_IGNORE);
}
break;
case PLM_EXACT:
/* Match exactly or fail */
result = pick_v6_prefix_helper(reply, PLM_EXACT);
break;
case PLM_MINIMUM:
case PLM_MAXIMUM:
/* First we look for an exact match, if not found
* then first available by mode */
result = pick_v6_prefix_helper(reply, PLM_EXACT);
if (result != ISC_R_SUCCESS) {
result = pick_v6_prefix_helper(reply,
prefix_length_mode);
}
break;
default:
/* First available */
result = pick_v6_prefix_helper(reply, PLM_IGNORE);
break;
}
}
if (result == ISC_R_SUCCESS) {
char tmp_buf[INET6_ADDRSTRLEN];
log_debug("Picking pool prefix %s/%u",
inet_ntop(AF_INET6, &(reply->lease->addr),
tmp_buf, sizeof(tmp_buf)),
(unsigned)(reply->lease->plen));
return (ISC_R_SUCCESS);
}
/*
* 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
*
* If we failed to pick an IPv6 prefix
* Presumably that means we have no prefixes for the client.
*/
log_debug("Unable to pick client prefix: no prefixes available");
return ISC_R_NORESOURCES;
}
/*!
*
* \brief Get an IPv6 prefix for the client based upon selection mode.
*
* We walk through the ponds checking for permit and deny. If a pond is
* permissable to use, loop through its PD pools checking prefix lengths
* against the client plen based on the prefix length mode, looking for
* available prefixes.
*
* \param reply = the state structure for the current work on this request
* if we create a lease we return it using reply->lease
* \prefix_mode = selection mode to use
*
* \return
* ISC_R_SUCCESS = we were able to find a 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.
*/
isc_result_t
pick_v6_prefix_helper(struct reply_state *reply, int prefix_mode) {
struct ipv6_pool *p = NULL;
struct ipv6_pond *pond;
int i;
unsigned int attempts;
struct iasubopt **pref = &reply->lease;
for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
if (((pond->prohibit_list != NULL) &&
@ -1386,37 +1484,64 @@ pick_v6_prefix(struct reply_state *reply)
continue;
for (i = 0; (p = pond->ipv6_pools[i]) != NULL; i++) {
if (p->pool_type != D6O_IA_PD) {
continue;
}
/*
* 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);
if ((p->pool_type == D6O_IA_PD) &&
(eval_prefix_mode(p->units, reply->preflen,
prefix_mode) == 1) &&
(create_prefix6(p, pref, &attempts,
&reply->ia->iaid_duid,
cur_time + 120) == ISC_R_SUCCESS)) {
return (ISC_R_SUCCESS);
}
}
}
/*
* If we failed to pick an IPv6 prefix
* Presumably that means we have no prefixes for the client.
*/
log_debug("Unable to pick client prefix: no prefixes available");
return ISC_R_NORESOURCES;
}
/*!
*
* \brief Test a prefix length against another based on prefix length mode
*
* \param len - prefix length to test
* \param preflen - preferred prefix length against which to test
* \param prefix_mode - prefix selection mode with which to test
*
* Note that the case of preferred length of 0 is not short-cut here as it
* is assumed to be done at a higher level.
*
* \return 1 if the given length is usable based upon mode and a preferred
* length, 0 if not.
*/
int
eval_prefix_mode(int len, int preflen, int prefix_mode) {
int use_it = 1;
switch (prefix_mode) {
case PLM_EXACT:
use_it = (len == preflen);
break;
case PLM_MINIMUM:
/* they asked for a prefix length no "shorter" than preflen */
use_it = (len >= preflen);
break;
case PLM_MAXIMUM:
/* they asked for a prefix length no "longer" than preflen */
use_it = (len <= preflen);
break;
default:
/* otherwise use it */
break;
}
#if defined (DEBUG)
log_debug("eval_prefix_mode: "
"len %d, preflen %d, mode %s, use_it %d",
len, preflen,
prefix_length_modes.values[prefix_mode].name, use_it);
#endif
return (use_it);
}
/*
*! \file server/dhcpv6.c
*

View File

@ -3,7 +3,7 @@
Tables of information only used by server... */
/*
* Copyright (c) 2004-2011,2013-2014 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2004-2011,2013-2015 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
@ -269,6 +269,7 @@ static struct option server_options[] = {
{ "log-threshold-high", "B", &server_universe, 84, 1 },
{ "echo-client-id", "f", &server_universe, SV_ECHO_CLIENT_ID, 1 },
{ "server-id-check", "f", &server_universe, SV_SERVER_ID_CHECK, 1 },
{ "prefix-length-mode", "Nprefix_length_modes.", &server_universe, SV_PREFIX_LEN_MODE, 1 },
{ NULL, NULL, NULL, 0, 0 }
};
@ -342,6 +343,21 @@ struct enumeration ddns_styles = {
ddns_styles_values
};
struct enumeration_value prefix_length_modes_values[] = {
{ "ignore", PLM_IGNORE },
{ "prefer", PLM_PREFER },
{ "exact", PLM_EXACT },
{ "minimum", PLM_MINIMUM },
{ "maximum", PLM_MAXIMUM },
{ (char *)0, 0 }
};
struct enumeration prefix_length_modes = {
(struct enumeration *)0,
"prefix_length_modes", 1,
prefix_length_modes_values
};
struct enumeration_value syslog_values [] = {
#if defined (LOG_KERN)
{ "kern", LOG_KERN },