Commit 80c9fdb0 authored by Francis Dupont's avatar Francis Dupont
Browse files

Add IA_TA and IA_PD support in server

parent 892379ec
......@@ -1012,12 +1012,12 @@ dhc6_parse_prefs(struct dhc6_addr **ppref, struct packet *packet,
oc, MDL) &&
(ds.len >= 25)) {
pref->plen = getUChar(ds.data);
pref->preferred_life = getULong(ds.data);
pref->max_life = getULong(ds.data + 4);
pref->plen = getUChar(ds.data + 8);
pref->address.len = 16;
memcpy(pref->address.iabuf, ds.data + 1, 16);
memcpy(pref->address.iabuf, ds.data + 9, 16);
pref->starts = cur_time;
pref->preferred_life = getULong(ds.data + 17);
pref->max_life = getULong(ds.data + 21);
log_debug("RCV: | | X-- IAPREFIX %s/%d",
piaddr(pref->address), (int)pref->plen);
......
......@@ -918,6 +918,8 @@ intern(char *atom, enum dhcp_token dfv) {
return FIXED_ADDR;
if (!strcasecmp (atom + 1, "ixed-address6"))
return FIXED_ADDR6;
if (!strcasecmp (atom + 1, "ixed-prefix6"))
return FIXED_PREFIX6;
if (!strcasecmp (atom + 1, "ddi"))
return TOKEN_FDDI;
if (!strcasecmp (atom + 1, "ormerr"))
......@@ -1145,6 +1147,8 @@ intern(char *atom, enum dhcp_token dfv) {
return PACKET;
if (!strcasecmp (atom + 1, "ool"))
return POOL;
if (!strcasecmp (atom + 1, "refix6"))
return PREFIX6;
if (!strcasecmp (atom + 1, "seudo"))
return PSEUDO;
if (!strcasecmp (atom + 1, "eer"))
......@@ -1365,6 +1369,8 @@ intern(char *atom, enum dhcp_token dfv) {
return TSFP;
if (!strcasecmp (atom + 1, "ransmission"))
return TRANSMISSION;
if (!strcasecmp(atom + 1, "emporary"))
return TEMPORARY;
break;
case 'u':
if (!strcasecmp (atom + 1, "case"))
......
......@@ -622,7 +622,8 @@ struct lease_state {
#define SV_DHCPV6_LEASE_FILE_NAME 54
#define SV_DHCPV6_PID_FILE_NAME 55
#define SV_LIMIT_ADDRS_PER_IA 56
#define SV_DELAYED_ACK 57
#define SV_LIMIT_PREFS_PER_IA 57
#define SV_DELAYED_ACK 58
#if !defined (DEFAULT_PING_TIMEOUT)
# define DEFAULT_PING_TIMEOUT 1
......@@ -741,6 +742,7 @@ struct host_decl {
not an option_cache, but it's referenced in a lot of
places, so we'll leave it for now. */
struct option_cache *fixed_addr;
struct iaddrcidrnetlist *fixed_prefix;
struct group *group;
struct group_object *named_group;
struct data_string auth_key_id;
......@@ -1358,6 +1360,7 @@ extern ia_na_hash_t *ia_ta_active;
struct ipv6_pool {
int refcnt; /* reference count */
struct in6_addr start_addr; /* first IPv6 address */
#define POOL_IS_FOR_TEMP 0x8000
int bits; /* number of bits, CIDR style */
iaaddr_hash_t *addrs; /* non-free IAADDR */
int num_active; /* count of active IAADDR */
......@@ -1738,6 +1741,8 @@ int parse_ip6_prefix(struct parse *, struct iaddr *, u_int8_t *);
void parse_address_range PROTO ((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_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 *);
......@@ -3290,6 +3295,7 @@ isc_result_t ia_pd_add_iaprefix(struct ia_pd *ia_pd, struct iaprefix *iaprefix,
const char *file, int line);
void ia_pd_remove_iaprefix(struct ia_pd *ia_pd, struct iaprefix *iaprefix,
const char *file, int line);
isc_boolean_t ia_pd_equal(const struct ia_pd *a, const struct ia_pd *b);
isc_result_t ipv6_pool_allocate(struct ipv6_pool **pool,
const struct in6_addr *start_addr, int bits,
......@@ -3342,13 +3348,15 @@ isc_boolean_t prefix6_exists(const struct ipv6_ppool *ppool,
const struct in6_addr *pref, u_int8_t plen);
isc_result_t add_ipv6_pool(struct ipv6_pool *pool);
isc_result_t find_ipv6_pool(struct ipv6_pool **pool,
isc_result_t find_ipv6_pool(struct ipv6_pool **pool, int temp,
const struct in6_addr *addr);
isc_boolean_t ipv6_addr_in_pool(const struct in6_addr *addr,
const struct ipv6_pool *pool);
isc_result_t add_ipv6_ppool(struct ipv6_ppool *ppool);
isc_result_t find_ipv6_ppool(struct ipv6_ppool **pool,
const struct in6_addr *pref);
isc_boolean_t ipv6_prefix_in_ppool(const struct in6_addr *pref,
const struct ipv6_ppool *ppool);
isc_result_t renew_leases(struct ia_na *ia_na);
isc_result_t release_leases(struct ia_na *ia_na);
......@@ -3362,5 +3370,6 @@ void schedule_prefix_timeout(struct ipv6_ppool *ppool);
void schedule_all_ipv6_prefix_timeouts();
void mark_hosts_unavailable(void);
void mark_phosts_unavailable(void);
void mark_interfaces_unavailable(void);
......@@ -349,7 +349,10 @@ enum dhcp_token {
WHITESPACE = 652,
TOKEN_ALSO = 653,
AFTER = 654,
ZEROLEN = 655
ZEROLEN = 655,
TEMPORARY = 656,
PREFIX6 = 657,
FIXED_PREFIX6 = 658
};
#define is_identifier(x) ((x) >= FIRST_TOKEN && \
......
......@@ -617,6 +617,30 @@ int parse_statement (cfile, group, type, host_decl, declaration)
}
parse_address_range6(cfile, group);
return declaration;
case PREFIX6:
next_token(NULL, NULL, cfile);
if (type != ROOT_GROUP) {
parse_warn (cfile,
"prefix6 definitions may not be scoped.");
skip_to_semi(cfile);
return declaration;
}
parse_prefix6(cfile, group);
return declaration;
case FIXED_PREFIX6:
next_token(&val, NULL, cfile);
if (!host_decl) {
parse_warn (cfile,
"fixed-prefix6 declaration not "
"allowed here.");
skip_to_semi(cfile);
break;
}
parse_fixed_prefix6(cfile, host_decl);
break;
#endif /* DHCPv6 */
case TOKEN_NOT:
......@@ -3672,7 +3696,8 @@ add_ipv6_pool_to_shared_network(struct shared_network *share,
}
/* address-range6-declaration :== ip-address6 ip-address6 SEMI
| ip-address6 SLASH number SEMI */
| ip-address6 SLASH number SEMI
| ip-address6 TEMPORARY SEMI */
void
parse_address_range6(struct parse *cfile, struct group *group) {
......@@ -3707,7 +3732,7 @@ parse_address_range6(struct parse *cfile, struct group *group) {
}
/*
* See if we we're using range or CIDR notation.
* See if we we're using range or CIDR notation or TEMPORARY
*/
token = peek_token(&val, NULL, cfile);
if (token == SLASH) {
......@@ -3735,6 +3760,20 @@ parse_address_range6(struct parse *cfile, struct group *group) {
add_ipv6_pool_to_shared_network(share, &lo, bits);
} else if (token == TEMPORARY) {
/*
* temporary (RFC 4941)
*/
next_token(NULL, NULL, cfile);
bits = 64;
if (!is_cidr_mask_valid(&lo, bits)) {
parse_warn(cfile, "network mask too short");
skip_to_semi(cfile);
return;
}
bits |= POOL_IS_FOR_TEMP;
add_ipv6_pool_to_shared_network(share, &lo, bits);
} else {
/*
* No '/', so we are looking for the end address of
......@@ -3769,6 +3808,196 @@ parse_address_range6(struct parse *cfile, struct group *group) {
return;
}
}
static void
add_ipv6_ppool_to_global(struct iaddr *start_addr,
int pool_bits,
int alloc_bits) {
struct ipv6_ppool *ppool;
struct in6_addr tmp_in6_addr;
/*
* Create our prefix pool.
*/
if (start_addr->len != sizeof(tmp_in6_addr)) {
log_fatal("Internal error: Attempt to add non-IPv6 prefix.");
}
memcpy(&tmp_in6_addr, start_addr->iabuf, sizeof(tmp_in6_addr));
ppool = NULL;
if (ipv6_ppool_allocate(&ppool, &tmp_in6_addr,
(u_int8_t) pool_bits, (u_int8_t) alloc_bits,
MDL) != ISC_R_SUCCESS) {
log_fatal("Out of memory");
}
/*
* Add to our IPv6 prefix pool set.
*/
if (add_ipv6_ppool(ppool) != ISC_R_SUCCESS) {
log_fatal ("Out of memory");
}
}
/* prefix6-declaration :== ip-address6 ip-address6 SLASH number SEMI */
void
parse_prefix6(struct parse *cfile, struct group *group) {
struct iaddr lo, hi;
int bits;
enum dhcp_token token;
const char *val;
struct iaddrcidrnetlist *nets;
struct iaddrcidrnetlist *p;
/*
* Read starting and ending address.
*/
if (!parse_ip6_addr(cfile, &lo)) {
return;
}
if (!parse_ip6_addr(cfile, &hi)) {
return;
}
/*
* Next is '/' number ';'.
*/
token = next_token(NULL, NULL, cfile);
if (token != SLASH) {
parse_warn(cfile, "expecting '/'");
if (token != SEMI)
skip_to_semi(cfile);
return;
}
token = next_token(&val, NULL, cfile);
if (token != NUMBER) {
parse_warn(cfile, "expecting number");
if (token != SEMI)
skip_to_semi(cfile);
return;
}
bits = atoi(val);
if ((bits <= 0) || (bits >= 128)) {
parse_warn(cfile, "networks have 0 to 128 bits (exclusive)");
return;
}
if (!is_cidr_mask_valid(&lo, bits) ||
!is_cidr_mask_valid(&hi, bits)) {
parse_warn(cfile, "network mask too short");
return;
}
token = next_token(NULL, NULL, cfile);
if (token != SEMI) {
parse_warn(cfile, "semicolon expected.");
skip_to_semi(cfile);
return;
}
/*
* Convert our range to a set of CIDR networks.
*/
nets = NULL;
if (range2cidr(&nets, &lo, &hi) != ISC_R_SUCCESS) {
log_fatal("Error converting prefix to CIDR");
}
for (p = nets; p != NULL; p = p->next) {
/* Normalize and check. */
if (p->cidrnet.bits == 128) {
p->cidrnet.bits = bits;
}
if (p->cidrnet.bits > bits) {
parse_warn(cfile, "impossible mask length");
continue;
}
add_ipv6_ppool_to_global(&p->cidrnet.lo_addr,
p->cidrnet.bits, bits);
}
free_iaddrcidrnetlist(&nets);
}
/* fixed-prefix6 :== ip6-address SLASH number SEMI */
void
parse_fixed_prefix6(struct parse *cfile, struct host_decl *host_decl) {
struct iaddrcidrnetlist *ia, **h;
enum dhcp_token token;
const char *val;
/*
* Get the head of the fixed-prefix list.
*/
h = &host_decl->fixed_prefix;
/*
* Walk to the end.
*/
while (*h != NULL) {
h = &((*h)->next);
}
/*
* Allocate a new iaddrcidrnetlist structure.
*/
ia = dmalloc(sizeof(*ia), MDL);
if (!ia) {
log_fatal("Out of memory");
}
/*
* Parse it.
*/
if (!parse_ip6_addr(cfile, &ia->cidrnet.lo_addr)) {
dfree(ia, MDL);
return;
}
token = next_token(NULL, NULL, cfile);
if (token != SLASH) {
dfree(ia, MDL);
parse_warn(cfile, "expecting '/'");
if (token != SEMI)
skip_to_semi(cfile);
return;
}
token = next_token(&val, NULL, cfile);
if (token != NUMBER) {
dfree(ia, MDL);
parse_warn(cfile, "expecting number");
if (token != SEMI)
skip_to_semi(cfile);
return;
}
token = next_token(NULL, NULL, cfile);
if (token != SEMI) {
dfree(ia, MDL);
parse_warn(cfile, "semicolon expected.");
skip_to_semi(cfile);
return;
}
/*
* Fill it.
*/
ia->cidrnet.bits = atoi(val);
if ((ia->cidrnet.bits < 0) || (ia->cidrnet.bits > 128)) {
dfree(ia, MDL);
parse_warn(cfile, "networks have 0 to 128 bits");
return;
}
if (!is_cidr_mask_valid(&ia->cidrnet.lo_addr, ia->cidrnet.bits)) {
dfree(ia, MDL);
parse_warn(cfile, "network mask too short");
return;
}
/*
* Store it.
*/
*h = ia;
return;
}
#endif /* DHCPv6 */
/* allow-deny-keyword :== BOOTP
......@@ -4099,7 +4328,7 @@ parse_ia_na_declaration(struct parse *cfile) {
ia_na_add_iaaddr(ia, iaaddr, MDL);
ia_na_reference(&iaaddr->ia_na, ia, MDL);
pool = NULL;
if (find_ipv6_pool(&pool, &iaaddr->addr) != ISC_R_SUCCESS) {
if (find_ipv6_pool(&pool, 0, &iaaddr->addr) != ISC_R_SUCCESS) {
inet_ntop(AF_INET6, &iaaddr->addr,
addr_buf, sizeof(addr_buf));
parse_warn(cfile, "no pool found for address %s",
......@@ -4393,7 +4622,7 @@ parse_ia_ta_declaration(struct parse *cfile) {
ia_na_add_iaaddr(ia, iaaddr, MDL);
ia_na_reference(&iaaddr->ia_na, ia, MDL);
pool = NULL;
if (find_ipv6_pool(&pool, &iaaddr->addr) != ISC_R_SUCCESS) {
if (find_ipv6_pool(&pool, 1, &iaaddr->addr) != ISC_R_SUCCESS) {
inet_ntop(AF_INET6, &iaaddr->addr,
addr_buf, sizeof(addr_buf));
parse_warn(cfile, "no pool found for address %s",
......
......@@ -611,6 +611,7 @@ main(int argc, char **argv) {
*/
if (local_family == AF_INET6) {
mark_hosts_unavailable();
mark_phosts_unavailable();
mark_interfaces_unavailable();
}
#endif /* DHCPv6 */
......
......@@ -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.93 2008/01/24 02:43:05 each Exp $
.\" $Id: dhcpd.conf.5,v 1.94 2008/02/20 12:45:53 fdupont Exp $
.\"
.TH dhcpd.conf 5
.SH NAME
......@@ -1515,6 +1515,7 @@ single address, \fIhigh-address\fR can be omitted.
.nf
.B range6\fR \fIlow-address\fR \fIhigh-address\fR\fB;\fR
.B range6\fR \fIsubnet6-number\fR\fB;\fR
.B range6\fR \fIaddress\fR \fBtemporary\fR\fB;\fR
.fi
.PP
For any IPv6 subnet6 on which addresses will be assigned dynamically, there
......@@ -1524,9 +1525,33 @@ use CIDR notation, specified as ip6-address/bits. All IP addresses
in the \fIrange6\fR should be in the subnet6 in which the
\fIrange6\fR statement is declared.
.PP
The \fItemporay\fR variant makes the 64 bit prefix \fIaddress\fR available
for temporary (RFC 4941) addresses. A new address per prefix in the shared
network is computed at each request with an IA_TA option. Release and Confirm
ignores temporary addresses, Renew and Rebind are not defined and raise an
error.
.PP
Any IPv6 addresses given to hosts with \fIfixed-address6\fR are excluded
from the \fIrange6\fR, as are IPv6 addresses on the server itself.
.PP
.PP
.B The
.I prefix6
.B statement
.PP
.nf
.B prefix6\fR \fIlow-address\fR \fIhigh-address\fR \fB/\fR \fIbits\fR\fB;\fR
.fi
.PP
The \fIprefix6\fR is the \fIrange6\fR equivalent for Prefix Delegation
(RFC 3633). Prefixes of \fIbits\fR length are assigned between
\fIlow-address\fR and \fIhigh-address\fR.
.PP
Any IPv6 prefixes given to static entries (hosts) with \fIfixed-prefix6\fR
are excluded from the \fIprefix6\fR.
.PP
This statement is currently global but it should have a shared-network scope.
.PP
.B The
.I host
.B statement
......
This diff is collapsed.
......@@ -1149,6 +1149,50 @@ create_address(struct in6_addr *addr,
str[8] &= ~0x02;
}
/*
* Create a temporary address by a variant of RFC 4941 algo.
*/
static void
create_temporary(struct in6_addr *addr,
const struct in6_addr *net_start_addr,
const struct data_string *input) {
static u_int8_t history[8];
static u_int32_t counter = 0;
MD5_CTX ctx;
unsigned char md[16];
extern int dst_s_random(u_int8_t *, unsigned);
/*
* First time/time to reseed.
* Please use a good pseudo-random generator here!
*/
if (counter == 0) {
if (dst_s_random(history, 8) != 8)
log_fatal("Random failed.");
}
/*
* Use MD5 as recommended by RFC 4941.
*/
MD5_Init(&ctx);
MD5_Update(&ctx, history, 8UL);
MD5_Update(&ctx, input->data, input->len);
MD5_Final(md, &ctx);
/*
* Build the address.
*/
memcpy(&addr->s6_addr[0], &net_start_addr->s6_addr[0], 8);
memcpy(&addr->s6_addr[8], md, 8);
addr->s6_addr[8] &= ~0x02;
/*
* Save history for the next call.
*/
memcpy(history, md + 8, 8);
counter++;
}
/* Reserved Subnet Router Anycast ::0:0:0:0. */
static struct in6_addr rtany;
/* Reserved Subnet Anycasts ::fdff:ffff:ffff:ff80-::fdff:ffff:ffff:ffff. */
......@@ -1216,9 +1260,14 @@ activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
}
/*
* Create an address
* Create an address or a temporary address.
*/
create_address(&tmp, &pool->start_addr, pool->bits, &ds);
if ((pool->bits & POOL_IS_FOR_TEMP) == 0) {
create_address(&tmp, &pool->start_addr,
pool->bits, &ds);
} else {
create_temporary(&tmp, &pool->start_addr, &ds);
}
/*
* Avoid reserved interface IDs.
......@@ -1277,7 +1326,7 @@ activate_lease6(struct ipv6_pool *pool, struct iaaddr **addr,
memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
/*
* Add the lease to the pool.
* Add the lease to the pool (note state is free, not active?!).
*/
result = add_lease6(pool, iaaddr, valid_lifetime_end_time);
if (result == ISC_R_SUCCESS) {
......@@ -1578,6 +1627,7 @@ create_prefix(struct in6_addr *pref,
for (i=0; i<net_bytes; i++) {
str[i] = net_str[i];
}
i = net_bytes;
switch (pool_bits % 8) {
case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
......@@ -1594,15 +1644,16 @@ create_prefix(struct in6_addr *pref,
for (i=net_bytes+1; i<16; i++) {
str[i] = 0;
}
i = net_bytes;
switch (pref_bits % 8) {
case 0: str[i] &= 0;
case 1: str[i] &= 0x80;
case 2: str[i] &= 0xC0;
case 3: str[i] &= 0xE0;
case 4: str[i] &= 0xF0;
case 5: str[i] &= 0xF8;
case 6: str[i] &= 0xFC;
case 7: str[i] &= 0xFE;
case 0: str[i] &= 0; break;
case 1: str[i] &= 0x80; break;
case 2: str[i] &= 0xC0; break;
case 3: str[i] &= 0xE0; break;
case 4: str[i] &= 0xF0; break;
case 5: str[i] &= 0xF8; break;
case 6: str[i] &= 0xFC; break;
case 7: str[i] &= 0xFE; break;
}
}
......@@ -1628,10 +1679,10 @@ create_prefix(struct in6_addr *pref,
* the long term.
*/
isc_result_t
activate_prefix(struct ipv6_ppool *ppool, struct iaprefix **pref,
unsigned int *attempts,
const struct data_string *uid,
time_t valid_lifetime_end_time) {
activate_prefix6(struct ipv6_ppool *ppool, struct iaprefix **pref,
unsigned int *attempts,
const struct data_string *uid,
time_t valid_lifetime_end_time) {
struct data_string ds;
struct in6_addr tmp;
struct iaprefix *test_iapref;
......@@ -1700,10 +1751,11 @@ activate_prefix(struct ipv6_ppool *ppool, struct iaprefix **pref,
if (result != ISC_R_SUCCESS) {
return result;
}
iapref->plen = ppool->alloc_plen;
memcpy(&iapref->pref, &tmp, sizeof(iapref->pref));
/*
* Add the prefix to the pool.
* Add the prefix to the pool (note state is free, not active?!).
*/
result = add_prefix6(ppool, iapref, valid_lifetime_end_time);
if (result == ISC_R_SUCCESS) {
......@@ -1976,6 +2028,27 @@ mark_address_unavailable(struct ipv6_pool *pool, const struct in6_addr *addr) {
return result;
}
/*
* Mark an IPv6 prefix as unavailable from a prefix pool.
*
* This is used for host entries.
*/
isc_result_t
mark_prefix_unavailable(struct ipv6_ppool *ppool,
const struct in6_addr *pref) {
struct iaprefix *dummy_iapref;
isc_result_t result;
dummy_iapref = NULL;
result = iaprefix_allocate(&dummy_iapref, MDL);
if (result == ISC_R_SUCCESS) {
dummy_iapref->pref = *pref;
iaprefix_hash_add(ppool->prefs, &dummy_iapref->pref,
sizeof(*pref), dummy_iapref, MDL);