summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorFrancis Dupont <fdupont@isc.org>2008-10-22 11:41:58 +0000
committerFrancis Dupont <fdupont@isc.org>2008-10-22 11:41:58 +0000
commit783259b1cced2dc577cc2159990389235b36f858 (patch)
treebedd950f7aa63eb4030cd51f5e0a115b455f2067 /server
parent399d3dbe291579f9cda4407bebaf0188724e48a2 (diff)
downloadisc-dhcp-783259b1cced2dc577cc2159990389235b36f858.tar.gz
Improve IA_TA server support (17586)
Diffstat (limited to 'server')
-rw-r--r--server/confpars.c27
-rw-r--r--server/dhcpd.conf.58
-rw-r--r--server/dhcpv6.c210
-rw-r--r--server/mdb6.c46
4 files changed, 216 insertions, 75 deletions
diff --git a/server/confpars.c b/server/confpars.c
index 0a25cc44..f8e82266 100644
--- a/server/confpars.c
+++ b/server/confpars.c
@@ -3753,7 +3753,7 @@ add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type,
/* address-range6-declaration :== ip-address6 ip-address6 SEMI
| ip-address6 SLASH number SEMI
- | ip-address6 TEMPORARY SEMI */
+ | ip-address6 [SLASH number] TEMPORARY SEMI */
void
parse_address_range6(struct parse *cfile, struct group *group) {
@@ -3763,6 +3763,7 @@ parse_address_range6(struct parse *cfile, struct group *group) {
const char *val;
struct iaddrcidrnetlist *nets;
struct iaddrcidrnetlist *p;
+ u_int16_t type = D6O_IA_NA;
if (local_family != AF_INET6) {
parse_warn(cfile, "range6 statement is only supported "
@@ -3809,13 +3810,27 @@ parse_address_range6(struct parse *cfile, struct group *group) {
return;
}
- add_ipv6_pool_to_subnet(group->subnet, D6O_IA_NA, &lo, bits,
- 128);
+ /*
+ * can be temporary (RFC 4941 like)
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == TEMPORARY) {
+ if (bits < 64)
+ parse_warn(cfile, "temporary mask too short");
+ if (bits == 128)
+ parse_warn(cfile, "temporary singleton?");
+ token = next_token(NULL, NULL, cfile);
+ type = D6O_IA_TA;
+ }
+
+ add_ipv6_pool_to_subnet(group->subnet, type, &lo,
+ bits, 128);
} else if (token == TEMPORARY) {
/*
* temporary (RFC 4941)
*/
+ type = D6O_IA_TA;
next_token(NULL, NULL, cfile);
bits = 64;
if (!is_cidr_mask_valid(&lo, bits)) {
@@ -3824,8 +3839,8 @@ parse_address_range6(struct parse *cfile, struct group *group) {
return;
}
- add_ipv6_pool_to_subnet(group->subnet, D6O_IA_TA, &lo, bits,
- 128);
+ add_ipv6_pool_to_subnet(group->subnet, type, &lo,
+ bits, 128);
} else {
/*
* No '/', so we are looking for the end address of
@@ -3844,7 +3859,7 @@ parse_address_range6(struct parse *cfile, struct group *group) {
}
for (p=nets; p != NULL; p=p->next) {
- add_ipv6_pool_to_subnet(group->subnet, D6O_IA_NA,
+ add_ipv6_pool_to_subnet(group->subnet, type,
&p->cidrnet.lo_addr,
p->cidrnet.bits, 128);
}
diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5
index 47ef2571..238b8d74 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.97 2008/08/19 17:55:57 dhankins Exp $
+.\" $Id: dhcpd.conf.5,v 1.98 2008/10/22 11:41:58 fdupont Exp $
.\"
.TH dhcpd.conf 5
.SH NAME
@@ -1521,6 +1521,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 \fIsubnet6-number\fR \fBtemporary\fR\fB;\fR
.B range6\fR \fIaddress\fR \fBtemporary\fR\fB;\fR
.fi
.PP
@@ -1531,11 +1532,10 @@ 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 \fItemporary\fR variant makes the 64 bit prefix \fIaddress\fR available
+The \fItemporay\fR variant makes the prefix (by default on 64 bits) 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.
+ignores temporary addresses.
.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.
diff --git a/server/dhcpv6.c b/server/dhcpv6.c
index 3aa6610e..054e5c1d 100644
--- a/server/dhcpv6.c
+++ b/server/dhcpv6.c
@@ -109,6 +109,8 @@ static isc_result_t 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);
+static isc_boolean_t temporary_is_available(struct reply_state *reply,
+ struct iaddr *addr);
static isc_result_t find_client_temporaries(struct reply_state *reply);
static isc_result_t reply_process_try_addr(struct reply_state *reply,
struct iaddr *addr);
@@ -178,7 +180,7 @@ static struct data_string server_duid;
/*
* Check if the server_duid has been set.
*/
-isc_boolean_t
+isc_boolean_t
server_duid_isset(void) {
return (server_duid.data != NULL);
}
@@ -233,8 +235,8 @@ set_server_duid_from_option(void) {
ret_val = ISC_R_NOTFOUND;
} else {
memset(&option_duid, 0, sizeof(option_duid));
- if (!evaluate_option_cache(&option_duid, NULL, NULL, NULL,
- opt_state, NULL, &global_scope,
+ if (!evaluate_option_cache(&option_duid, NULL, NULL, NULL,
+ opt_state, NULL, &global_scope,
oc, MDL)) {
ret_val = ISC_R_UNEXPECTED;
} else {
@@ -308,32 +310,32 @@ generate_new_server_duid(void) {
if (server_duid_type == DUID_LLT) {
time_val = duid_time(time(NULL));
generated_duid.len = 8 + p->hw_address.hlen - 1;
- if (!buffer_allocate(&generated_duid.buffer,
+ if (!buffer_allocate(&generated_duid.buffer,
generated_duid.len, MDL)) {
log_fatal("No memory for server DUID.");
}
generated_duid.data = generated_duid.buffer->data;
putUShort(generated_duid.buffer->data, DUID_LLT);
- putUShort(generated_duid.buffer->data + 2,
+ putUShort(generated_duid.buffer->data + 2,
p->hw_address.hbuf[0]);
putULong(generated_duid.buffer->data + 4, time_val);
- memcpy(generated_duid.buffer->data + 8,
+ memcpy(generated_duid.buffer->data + 8,
p->hw_address.hbuf+1, p->hw_address.hlen-1);
} else if (server_duid_type == DUID_LL) {
generated_duid.len = 4 + p->hw_address.hlen - 1;
- if (!buffer_allocate(&generated_duid.buffer,
+ if (!buffer_allocate(&generated_duid.buffer,
generated_duid.len, MDL)) {
log_fatal("No memory for server DUID.");
}
generated_duid.data = generated_duid.buffer->data;
putUShort(generated_duid.buffer->data, DUID_LL);
- putUShort(generated_duid.buffer->data + 2,
+ putUShort(generated_duid.buffer->data + 2,
p->hw_address.hbuf[0]);
- memcpy(generated_duid.buffer->data +4,
+ memcpy(generated_duid.buffer->data + 4,
p->hw_address.hbuf+1, p->hw_address.hlen-1);
} else {
log_fatal("Unsupported server DUID type %d.", server_duid_type);
- }
+ }
set_server_duid(&generated_duid);
data_string_forget(&generated_duid, MDL);
@@ -360,7 +362,7 @@ get_client_id(struct packet *packet, struct data_string *client_id) {
return ISC_R_NOTFOUND;
}
- if (!evaluate_option_cache(client_id, packet, NULL, NULL,
+ if (!evaluate_option_cache(client_id, packet, NULL, NULL,
packet->options, NULL,
&global_scope, oc, MDL)) {
return ISC_R_FAILURE;
@@ -391,7 +393,7 @@ valid_client_msg(struct packet *packet, struct data_string *client_id) {
break;
case ISC_R_NOTFOUND:
log_debug("Discarding %s from %s; "
- "client identifier missing",
+ "client identifier missing",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr));
goto exit;
@@ -408,7 +410,7 @@ valid_client_msg(struct packet *packet, struct data_string *client_id) {
*/
if (packet->unicast) {
log_debug("Discarding %s from %s; packet sent unicast "
- "(CLIENTID %s)",
+ "(CLIENTID %s)",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr),
print_hex_1(client_id->len, client_id->data, 60));
@@ -419,23 +421,23 @@ valid_client_msg(struct packet *packet, struct data_string *client_id) {
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
if (oc != NULL) {
if (evaluate_option_cache(&data, packet, NULL, NULL,
- packet->options, NULL,
+ packet->options, NULL,
&global_scope, oc, MDL)) {
- log_debug("Discarding %s from %s; "
+ log_debug("Discarding %s from %s; "
"server identifier found "
- "(CLIENTID %s, SERVERID %s)",
+ "(CLIENTID %s, SERVERID %s)",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr),
- print_hex_1(client_id->len,
+ print_hex_1(client_id->len,
client_id->data, 60),
print_hex_2(data.len,
data.data, 60));
} else {
- log_debug("Discarding %s from %s; "
+ log_debug("Discarding %s from %s; "
"server identifier found "
- "(CLIENTID %s)",
+ "(CLIENTID %s)",
dhcpv6_type_names[packet->dhcpv6_msg_type],
- print_hex_1(client_id->len,
+ print_hex_1(client_id->len,
client_id->data, 60),
piaddr(packet->client_addr));
}
@@ -488,7 +490,7 @@ valid_client_resp(struct packet *packet,
break;
case ISC_R_NOTFOUND:
log_debug("Discarding %s from %s; "
- "client identifier missing",
+ "client identifier missing",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr));
goto exit;
@@ -503,13 +505,13 @@ valid_client_resp(struct packet *packet,
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
if (oc == NULL) {
log_debug("Discarding %s from %s: "
- "server identifier missing (CLIENTID %s)",
+ "server identifier missing (CLIENTID %s)",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr),
print_hex_1(client_id->len, client_id->data, 60));
goto exit;
}
- if (!evaluate_option_cache(server_id, packet, NULL, NULL,
+ if (!evaluate_option_cache(server_id, packet, NULL, NULL,
packet->options, NULL,
&global_scope, oc, MDL)) {
log_error("Error processing %s from %s; "
@@ -519,11 +521,11 @@ valid_client_resp(struct packet *packet,
print_hex_1(client_id->len, client_id->data, 60));
goto exit;
}
- if ((server_duid.len != server_id->len) ||
+ if ((server_duid.len != server_id->len) ||
(memcmp(server_duid.data, server_id->data, server_duid.len) != 0)) {
- log_debug("Discarding %s from %s; "
+ log_debug("Discarding %s from %s; "
"not our server identifier "
- "(CLIENTID %s, SERVERID %s, server DUID %s)",
+ "(CLIENTID %s, SERVERID %s, server DUID %s)",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr),
print_hex_1(client_id->len, client_id->data, 60),
@@ -598,7 +600,7 @@ valid_client_info_req(struct packet *packet, struct data_string *server_id) {
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA);
if (oc != NULL) {
log_debug("Discarding %s from %s; "
- "IA_NA option present%s",
+ "IA_NA option present%s",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr), client_id_str);
goto exit;
@@ -606,7 +608,7 @@ valid_client_info_req(struct packet *packet, struct data_string *server_id) {
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_TA);
if (oc != NULL) {
log_debug("Discarding %s from %s; "
- "IA_TA option present%s",
+ "IA_TA option present%s",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr), client_id_str);
goto exit;
@@ -614,7 +616,7 @@ valid_client_info_req(struct packet *packet, struct data_string *server_id) {
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD);
if (oc != NULL) {
log_debug("Discarding %s from %s; "
- "IA_PD option present%s",
+ "IA_PD option present%s",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr), client_id_str);
goto exit;
@@ -622,7 +624,7 @@ valid_client_info_req(struct packet *packet, struct data_string *server_id) {
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
if (oc != NULL) {
- if (!evaluate_option_cache(server_id, packet, NULL, NULL,
+ if (!evaluate_option_cache(server_id, packet, NULL, NULL,
packet->options, NULL,
&global_scope, oc, MDL)) {
log_error("Error processing %s from %s; "
@@ -631,17 +633,17 @@ valid_client_info_req(struct packet *packet, struct data_string *server_id) {
piaddr(packet->client_addr), client_id_str);
goto exit;
}
- if ((server_duid.len != server_id->len) ||
- (memcmp(server_duid.data, server_id->data,
+ if ((server_duid.len != server_id->len) ||
+ (memcmp(server_duid.data, server_id->data,
server_duid.len) != 0)) {
- log_debug("Discarding %s from %s; "
+ log_debug("Discarding %s from %s; "
"not our server identifier "
- "(SERVERID %s, server DUID %s)%s",
+ "(SERVERID %s, server DUID %s)%s",
dhcpv6_type_names[packet->dhcpv6_msg_type],
piaddr(packet->client_addr),
- print_hex_1(server_id->len,
+ print_hex_1(server_id->len,
server_id->data, 60),
- print_hex_2(server_duid.len,
+ print_hex_2(server_duid.len,
server_duid.data, 60),
client_id_str);
goto exit;
@@ -716,7 +718,7 @@ static const int required_opts_STATUS_CODE[] = {
* where in the IA_* the DHCPv6 options commence.
*/
static int
-get_encapsulated_IA_state(struct option_state **enc_opt_state,
+get_encapsulated_IA_state(struct option_state **enc_opt_state,
struct data_string *enc_opt_data,
struct packet *packet,
struct option_cache *oc,
@@ -749,7 +751,7 @@ get_encapsulated_IA_state(struct option_state **enc_opt_state,
data_string_forget(enc_opt_data, MDL);
return 0;
}
- if (!parse_option_buffer(*enc_opt_state,
+ if (!parse_option_buffer(*enc_opt_state,
enc_opt_data->data + offset,
enc_opt_data->len - offset,
&dhcpv6_universe)) {
@@ -2214,6 +2216,7 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
struct data_string ia_data, data;
struct data_string iaaddr;
u_int32_t pref_life, valid_life;
+ struct iaddr tmp_addr;
/* Initialize values that will get cleaned up on return. */
packet_ia = NULL;
@@ -2240,7 +2243,7 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
iaid = getULong(ia_data.data);
/* Create an IA_TA structure. */
- if (ia_allocate(&reply->ia, iaid, (char *)reply->client_id.data,
+ if (ia_allocate(&reply->ia, iaid, (char *)reply->client_id.data,
reply->client_id.len, MDL) != ISC_R_SUCCESS) {
log_error("reply_process_ia_ta: no memory for ia.");
status = ISC_R_NOMEMORY;
@@ -2289,11 +2292,13 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
/*
* Deal with an IAADDR for lifetimes.
+ * For all or none, process IAADDRs as hints.
*/
reply->valid = reply->prefer = 0xffffffff;
reply->client_valid = reply->client_prefer = 0;
oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAADDR);
- if (oc != NULL) {
+ for (; oc != NULL; oc = oc->next) {
+ memset(&iaaddr, 0, sizeof(iaaddr));
if (!evaluate_option_cache(&iaaddr, reply->packet,
NULL, NULL,
reply->packet->options, NULL,
@@ -2315,29 +2320,47 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
if ((reply->client_prefer == 0) ||
(reply->client_prefer > pref_life))
reply->client_prefer = pref_life;
- }
- reply->ia_count++;
- /*
- * Cancel if not Solicit or Request.
- */
- if ((reply->packet->dhcpv6_msg_type != DHCPV6_SOLICIT) &&
- (reply->packet->dhcpv6_msg_type != DHCPV6_REQUEST)) {
- if (!set_status_code(STATUS_UnspecFail,
- "Unsupported message for temporary.",
- reply->reply_ia)) {
- log_error("reply_process_ia_ta: Unable to set "
- "UnspecFail status code.");
- status = ISC_R_FAILURE;
+ /* Nothing more if something has failed. */
+ if (status == ISC_R_CANCELED)
+ continue;
+
+ tmp_addr.len = 16;
+ memcpy(tmp_addr.iabuf, iaaddr.data, 16);
+ if (!temporary_is_available(reply, &tmp_addr))
+ goto bad_temp;
+ status = reply_process_is_addressed(reply,
+ &reply->lease->scope,
+ reply->shared->group);
+ if (status != ISC_R_SUCCESS)
+ goto bad_temp;
+ status = reply_process_send_addr(reply, &tmp_addr);
+ if (status != ISC_R_SUCCESS)
+ goto bad_temp;
+ if (reply->lease != NULL)
+ iasubopt_dereference(&reply->lease, MDL);
+ continue;
+
+ bad_temp:
+ /* Rewind the IA_TA to empty. */
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (!option_state_allocate(&reply->reply_ia, MDL)) {
+ status = ISC_R_NOMEMORY;
goto cleanup;
}
status = ISC_R_CANCELED;
- goto store;
+ reply->client_resources = 0;
+ reply->resources_included = ISC_FALSE;
+ if (reply->lease != NULL)
+ iasubopt_dereference(&reply->lease, MDL);
}
+ reply->ia_count++;
/*
* Give the client temporary addresses.
*/
+ if (reply->client_resources != 0)
+ goto store;
status = find_client_temporaries(reply);
if (status == ISC_R_NORESOURCES) {
switch (reply->packet->dhcpv6_msg_type) {
@@ -2380,8 +2403,16 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
break;
default:
- /* Should not happen! */
- goto cleanup;
+ /*
+ * We don't want to include the IA if we
+ * provide zero addresses including zeroed
+ * lifetimes.
+ */
+ if (reply->resources_included)
+ status = ISC_R_SUCCESS;
+ else
+ goto cleanup;
+ break;
}
} else if (status != ISC_R_SUCCESS)
goto cleanup;
@@ -2480,13 +2511,78 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
/*
* ISC_R_CANCELED is a status code used by the addr processing to
- * indicate we're replying with a status code. This is still a
+ * indicate we're replying with other addresses. This is still a
* success at higher layers.
*/
return((status == ISC_R_CANCELED) ? ISC_R_SUCCESS : status);
}
/*
+ * Verify the temporary address is available.
+ */
+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;
+ int i;
+
+ memcpy(&tmp_addr, addr->iabuf, sizeof(tmp_addr));
+ /*
+ * Clients may choose to send :: as an address, with the idea to give
+ * hints about preferred-lifetime or valid-lifetime.
+ * So this is not a request for this address.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&tmp_addr))
+ return ISC_FALSE;
+
+ /*
+ * Verify that this address is on the client's network.
+ */
+ for (subnet = reply->shared->subnets ; subnet != NULL ;
+ subnet = subnet->next_sibling) {
+ if (addr_eq(subnet_number(*addr, subnet->netmask),
+ subnet->net))
+ break;
+ }
+
+ /* Address not found on shared network. */
+ if (subnet == NULL)
+ return ISC_FALSE;
+
+ /*
+ * Check if this address is owned (must be before next step).
+ */
+ if (address_is_owned(reply, addr))
+ return ISC_TRUE;
+
+ /*
+ * 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)
+ continue;
+ if (ipv6_in_pool(&tmp_addr, pool))
+ break;
+ }
+ if (pool == NULL)
+ return ISC_FALSE;
+ if (lease6_exists(pool, &tmp_addr))
+ return ISC_FALSE;
+ if (iasubopt_allocate(&reply->lease, MDL) != ISC_R_SUCCESS)
+ return ISC_FALSE;
+ reply->lease->addr = tmp_addr;
+ reply->lease->plen = 0;
+ /* Default is soft binding for 2 minutes. */
+ if (add_lease6(pool, reply->lease, cur_time + 120) != ISC_R_SUCCESS)
+ return ISC_FALSE;
+
+ return ISC_TRUE;
+}
+
+/*
* Get a temporary address per prefix.
*/
static isc_result_t
diff --git a/server/mdb6.c b/server/mdb6.c
index 3c689913..a5ae4eb7 100644
--- a/server/mdb6.c
+++ b/server/mdb6.c
@@ -619,12 +619,12 @@ build_address6(struct in6_addr *addr,
MD5_Final((unsigned char *)addr, &ctx);
/*
- * Copy the network bits over.
+ * Copy the [0..128] network bits over.
*/
str = (char *)addr;
net_str = (const char *)net_start_addr;
net_bytes = net_bits / 8;
- for (i=0; i<net_bytes; i++) {
+ for (i = 0; i < net_bytes; i++) {
str[i] = net_str[i];
}
switch (net_bits % 8) {
@@ -643,10 +643,11 @@ build_address6(struct in6_addr *addr,
/*
* Create a temporary address by a variant of RFC 4941 algo.
+ * Note: this should not be used for prefixes shorter than 64 bits.
*/
static void
build_temporary6(struct in6_addr *addr,
- const struct in6_addr *net_start_addr,
+ const struct in6_addr *net_start_addr, int net_bits,
const struct data_string *input) {
static u_int8_t history[8];
static u_int32_t counter = 0;
@@ -674,9 +675,37 @@ build_temporary6(struct in6_addr *addr,
/*
* 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;
+ if (net_bits == 64) {
+ memcpy(&addr->s6_addr[0], &net_start_addr->s6_addr[0], 8);
+ memcpy(&addr->s6_addr[8], md, 8);
+ addr->s6_addr[8] &= ~0x02;
+ } else {
+ int net_bytes;
+ int i;
+ char *str;
+ const char *net_str;
+
+ /*
+ * Copy the [0..128] network bits over.
+ */
+ str = (char *)addr;
+ net_str = (const char *)net_start_addr;
+ net_bytes = net_bits / 8;
+ for (i = 0; i < net_bytes; i++) {
+ str[i] = net_str[i];
+ }
+ memcpy(str + net_bytes, md, 16 - net_bytes);
+ switch (net_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;
+ case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
+ case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
+ case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
+ case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
+ case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+ }
+ }
+
/*
* Save history for the next call.
@@ -762,7 +791,8 @@ create_lease6(struct ipv6_pool *pool, struct iasubopt **addr,
break;
case D6O_IA_TA:
/* temporary address */
- build_temporary6(&tmp, &pool->start_addr, &ds);
+ build_temporary6(&tmp, &pool->start_addr,
+ pool->bits, &ds);
break;
case D6O_IA_PD:
/* prefix */
@@ -1133,7 +1163,7 @@ build_prefix6(struct in6_addr *pref,
str = (char *)pref;
net_str = (const char *)net_start_pref;
net_bytes = pool_bits / 8;
- for (i=0; i<net_bytes; i++) {
+ for (i = 0; i < net_bytes; i++) {
str[i] = net_str[i];
}
i = net_bytes;