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:
parent
fb98e02e12
commit
1a006ff6ed
10
RELNOTES
10
RELNOTES
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
199
server/dhcpv6.c
199
server/dhcpv6.c
@ -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
|
||||
*
|
||||
|
@ -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 },
|
||||
|
Loading…
x
Reference in New Issue
Block a user