dhcpv6.c 235 KB
Newer Older
David Hankins's avatar
David Hankins committed
1
/*
2
 * Copyright (C) 2006-2017 by Internet Systems Consortium, Inc. ("ISC")
David Hankins's avatar
David Hankins committed
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
David Hankins's avatar
David Hankins committed
7 8 9 10 11 12 13 14 15 16
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

Shawn Routhier's avatar
Shawn Routhier committed
17 18
/*! \file server/dhcpv6.c */

David Hankins's avatar
David Hankins committed
19 20
#include "dhcpd.h"

21 22
#ifdef DHCPv6

23 24 25 26 27 28 29 30 31
#ifdef DHCP4o6
static void forw_dhcpv4_query(struct packet *packet);
static void send_dhcpv4_response(struct data_string *raw);

static void recv_dhcpv4_query(struct data_string *raw);
static void dhcp4o6_dhcpv4_query(struct data_string *reply_ret,
				 struct packet *packet);
#endif

David Hankins's avatar
David Hankins committed
32
/*
33 34 35
 * We use print_hex_1() to output DUID values. We could actually output
 * the DUID with more information... MAC address if using type 1 or 3,
 * and so on. However, RFC 3315 contains Grave Warnings against actually
David Hankins's avatar
David Hankins committed
36 37 38
 * attempting to understand a DUID.
 */

39
/*
David Hankins's avatar
David Hankins committed
40 41 42
 * TODO: gettext() or other method of localization for the messages
 *       for status codes (and probably for log formats eventually)
 * TODO: refactoring (simplify, simplify, simplify)
43 44
 * TODO: support multiple shared_networks on each interface (this
 *       will allow the server to issue multiple IPv6 addresses to
David Hankins's avatar
David Hankins committed
45 46 47
 *       a single interface)
 */

48 49 50 51 52 53 54 55 56
/*
 * DHCPv6 Reply workflow assist.  A Reply packet is built by various
 * different functions; this gives us one location where we keep state
 * regarding a reply.
 */
struct reply_state {
	/* root level persistent state */
	struct shared_network *shared;
	struct host_decl *host;
57
	struct subnet *subnet; /* Used to match fixed-addrs to subnet scopes. */
58 59 60 61 62 63
	struct option_state *opt_state;
	struct packet *packet;
	struct data_string client_id;

	/* IA level persistent state */
	unsigned ia_count;
64
	unsigned pd_count;
Francis Dupont's avatar
Francis Dupont committed
65
	unsigned client_resources;
66
	isc_boolean_t resources_included;
67
	isc_boolean_t static_lease;
68
	unsigned static_prefixes;
69 70
	struct ia_xx *ia;
	struct ia_xx *old_ia;
71 72
	struct option_state *reply_ia;
	struct data_string fixed;
Shawn Routhier's avatar
Shawn Routhier committed
73
	struct iaddrcidrnet fixed_pref; /* static prefix for logging */
74

75
	/* IAADDR/PREFIX level persistent state */
Francis Dupont's avatar
Francis Dupont committed
76
	struct iasubopt *lease;
77 78 79 80 81

	/*
	 * "t1", "t2", preferred, and valid lifetimes records for calculating
	 * t1 and t2 (min/max).
	 */
82
	u_int32_t renew, rebind, min_prefer, min_valid;
83

Francis Dupont's avatar
Francis Dupont committed
84
	/* Client-requested valid and preferred lifetimes. */
85 86 87 88 89
	u_int32_t client_valid, client_prefer;

	/* Chosen values to transmit for valid and preferred lifetimes. */
	u_int32_t send_valid, send_prefer;

90 91 92
	/* Preferred prefix length (-1 is any). */
	int preflen;

Francis Dupont's avatar
Francis Dupont committed
93
	/* Index into the data field that has been consumed. */
94 95
	unsigned cursor;

Shawn Routhier's avatar
Shawn Routhier committed
96 97 98
	/* Space for the on commit statements for a fixed host */
	struct on_star on_star;

99 100 101 102 103 104
	union reply_buffer {
		unsigned char data[65536];
		struct dhcpv6_packet reply;
	} buf;
};

105
/*
David Hankins's avatar
David Hankins committed
106 107
 * Prototypes local to this file.
 */
108 109 110 111 112
static int get_encapsulated_IA_state(struct option_state **enc_opt_state,
				     struct data_string *enc_opt_data,
				     struct packet *packet,
				     struct option_cache *oc,
				     int offset);
David Hankins's avatar
David Hankins committed
113
static void build_dhcpv6_reply(struct data_string *, struct packet *);
114 115
static isc_result_t shared_network_from_packet6(struct shared_network **shared,
						struct packet *packet);
116 117 118 119
static void seek_shared_host(struct host_decl **hp,
			     struct shared_network *shared);
static isc_boolean_t fixed_matches_shared(struct host_decl *host,
					  struct shared_network *shared);
Francis Dupont's avatar
Francis Dupont committed
120 121
static isc_result_t reply_process_ia_na(struct reply_state *reply,
					struct option_cache *ia);
122 123
static isc_result_t reply_process_ia_ta(struct reply_state *reply,
					struct option_cache *ia);
124 125 126 127
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);
128 129
static isc_boolean_t temporary_is_available(struct reply_state *reply,
					    struct iaddr *addr);
130
static isc_result_t find_client_temporaries(struct reply_state *reply);
131 132 133 134 135 136 137 138
static isc_result_t reply_process_try_addr(struct reply_state *reply,
					   struct iaddr *addr);
static isc_result_t find_client_address(struct reply_state *reply);
static isc_result_t reply_process_is_addressed(struct reply_state *reply,
					       struct binding_scope **scope,
					       struct group *group);
static isc_result_t reply_process_send_addr(struct reply_state *reply,
					    struct iaddr *addr);
Francis Dupont's avatar
Francis Dupont committed
139 140
static struct iasubopt *lease_compare(struct iasubopt *alpha,
				      struct iasubopt *beta);
141 142
static isc_result_t reply_process_ia_pd(struct reply_state *reply,
					struct option_cache *ia_pd);
143
static struct group *find_group_by_prefix(struct reply_state *reply);
144 145 146 147 148 149 150 151 152 153 154 155
static isc_result_t reply_process_prefix(struct reply_state *reply,
					 struct option_cache *pref);
static isc_boolean_t prefix_is_owned(struct reply_state *reply,
				     struct iaddrcidrnet *pref);
static isc_result_t find_client_prefix(struct reply_state *reply);
static isc_result_t reply_process_try_prefix(struct reply_state *reply,
					     struct iaddrcidrnet *pref);
static isc_result_t reply_process_is_prefixed(struct reply_state *reply,
					      struct binding_scope **scope,
					      struct group *group);
static isc_result_t reply_process_send_prefix(struct reply_state *reply,
					      struct iaddrcidrnet *pref);
Francis Dupont's avatar
Francis Dupont committed
156 157 158
static struct iasubopt *prefix_compare(struct reply_state *reply,
				       struct iasubopt *alpha,
				       struct iasubopt *beta);
159 160
static void schedule_lease_timeout_reply(struct reply_state *reply);

161 162 163 164
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);

165 166 167 168 169 170 171 172 173 174 175
static void unicast_reject(struct data_string *reply_ret, struct packet *packet,
		  const struct data_string *client_id,
		  const struct data_string *server_id);

static isc_boolean_t is_unicast_option_defined(struct packet *packet);
static isc_result_t shared_network_from_requested_addr (struct shared_network
							**shared,
							struct packet* packet);
static isc_result_t get_first_ia_addr_val (struct packet* packet, int addr_type,
					   struct iaddr* iaddr);

176 177 178
static void
set_reply_tee_times(struct reply_state* reply, unsigned ia_cursor);

179 180 181
static const char *iasubopt_plen_str(struct iasubopt *lease);
static int release_on_roam(struct reply_state *reply);

182 183 184 185 186 187
static int reuse_lease6(struct reply_state *reply, struct iasubopt *lease);
static void shorten_lifetimes(struct reply_state *reply, struct iasubopt *lease,
			      time_t age, int threshold);
static void write_to_packet(struct reply_state *reply, unsigned ia_cursor);
static const char *iasubopt_plen_str(struct iasubopt *lease);

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
#ifdef DHCP4o6
/*
 * \brief Omapi I/O handler
 *
 * The inter-process communication receive handler.
 * Get the message, put it into the raw data_string
 * and call \ref send_dhcpv4_response() (DHCPv6 side) or
 * \ref recv_dhcpv4_query() (DHCPv4 side)
 *
 * \param h the OMAPI object
 * \return a result for I/O success or error (used by the I/O subsystem)
 */
isc_result_t dhcpv4o6_handler(omapi_object_t *h) {
	char buf[65536];
	struct data_string raw;
	int cc;

	if (h->type != dhcp4o6_type)
		return DHCP_R_INVALIDARG;

	cc = recv(dhcp4o6_fd, buf, sizeof(buf), 0);

	if (cc < DHCP_FIXED_NON_UDP + 32)
		return ISC_R_UNEXPECTED;
	memset(&raw, 0, sizeof(raw));
	if (!buffer_allocate(&raw.buffer, cc, MDL)) {
		log_error("dhcpv4o6_handler: no memory buffer.");
		return ISC_R_NOMEMORY;
	}
	raw.data = raw.buffer->data;
	raw.len = cc;
	memcpy(raw.buffer->data, buf, cc);

	if (local_family == AF_INET6) {
		send_dhcpv4_response(&raw);
	} else {
		recv_dhcpv4_query(&raw);
	}

	data_string_forget(&raw, MDL);

	return ISC_R_SUCCESS;
}

/*
 * \brief Send the DHCPv4-response back to the DHCPv6 side
 *  (DHCPv6 server function)
 *
 * Format: interface:16 + address:16 + DHCPv6 DHCPv4-response message
 *
 * \param raw the IPC message content
 */
static void send_dhcpv4_response(struct data_string *raw) {
	struct interface_info *ip;
	char name[16 + 1];
	struct sockaddr_in6 to_addr;
	char pbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
	int send_ret;

	memset(name, 0, sizeof(name));
	memcpy(name, raw->data, 16);
	for (ip = interfaces; ip != NULL; ip = ip->next) {
		if (!strcmp(name, ip->name))
			break;
	}
	if (ip == NULL) {
		log_error("send_dhcpv4_response: can't find interface %s.",
			  name);
		return;
	}

	memset(&to_addr, 0, sizeof(to_addr));
	to_addr.sin6_family = AF_INET6;
	memcpy(&to_addr.sin6_addr, raw->data + 16, 16);
	if ((raw->data[32] == DHCPV6_RELAY_FORW) ||
	    (raw->data[32] == DHCPV6_RELAY_REPL)) {
		to_addr.sin6_port = local_port;
	} else {
		to_addr.sin6_port = remote_port;
	}

	log_info("send_dhcpv4_response(): sending %s on %s to %s port %d",
		 dhcpv6_type_names[raw->data[32]],
		 name,
		 inet_ntop(AF_INET6, raw->data + 16, pbuf, sizeof(pbuf)),
		 ntohs(to_addr.sin6_port));

	send_ret = send_packet6(ip, raw->data + 32, raw->len - 32, &to_addr);
	if (send_ret < 0) {
		log_error("send_dhcpv4_response: send_packet6(): %m");
	} else if (send_ret != raw->len - 32) {
		log_error("send_dhcpv4_response: send_packet6() "
			  "sent %d of %d bytes",
			  send_ret, raw->len - 32);
	}
}
#endif /* DHCP4o6 */

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
/*
 * Schedule lease timeouts for all of the iasubopts in the reply.
 * This is currently used to schedule timeouts for soft leases.
 */

static void
schedule_lease_timeout_reply(struct reply_state *reply) {
	struct iasubopt *tmp;
	int i;

	/* sanity check the reply */
	if ((reply == NULL) || (reply->ia == NULL) || (reply->ia->iasubopt == NULL))
		return;

	/* walk through the list, scheduling as we go */
	for (i = 0 ; i < reply->ia->num_iasubopt ; i++) {
		tmp = reply->ia->iasubopt[i];
		schedule_lease_timeout(tmp->ipv6_pool);
	}
}

David Hankins's avatar
David Hankins committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
/*
 * This function returns the time since DUID time start for the
 * given time_t value.
 */
static u_int32_t
duid_time(time_t when) {
	/*
	 * This time is modulo 2^32.
	 */
	while ((when - DUID_TIME_EPOCH) > 4294967295u) {
		/* use 2^31 to avoid spurious compiler warnings */
		when -= 2147483648u;
		when -= 2147483648u;
	}

	return when - DUID_TIME_EPOCH;
}


326
/*
David Hankins's avatar
David Hankins committed
327 328 329 330 331 332 333
 * Server DUID.
 *
 * This must remain the same for the lifetime of this server, because
 * clients return the server DUID that we sent them in Request packets.
 *
 * We pick the server DUID like this:
 *
334
 * 1. Check dhcpd.conf - any value the administrator has configured
David Hankins's avatar
David Hankins committed
335
 *    overrides any possible values.
336
 * 2. Check the leases.txt - we want to use the previous value if
David Hankins's avatar
David Hankins committed
337 338 339 340 341 342 343 344 345 346
 *    possible.
 * 3. Check if dhcpd.conf specifies a type of server DUID to use,
 *    and generate that type.
 * 4. Generate a type 1 (time + hardware address) DUID.
 */
static struct data_string server_duid;

/*
 * Check if the server_duid has been set.
 */
347
isc_boolean_t
David Hankins's avatar
David Hankins committed
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
server_duid_isset(void) {
	return (server_duid.data != NULL);
}

/*
 * Return the server_duid.
 */
void
copy_server_duid(struct data_string *ds, const char *file, int line) {
	data_string_copy(ds, &server_duid, file, line);
}

/*
 * Set the server DUID to a specified value. This is used when
 * the server DUID is stored in persistent memory (basically the
 * leases.txt file).
 */
void
set_server_duid(struct data_string *new_duid) {
	/* INSIST(new_duid != NULL); */
	/* INSIST(new_duid->data != NULL); */

	if (server_duid_isset()) {
		data_string_forget(&server_duid, MDL);
	}
	data_string_copy(&server_duid, new_duid, MDL);
}


/*
 * Set the server DUID based on the D6O_SERVERID option. This handles
379
 * the case where the administrator explicitly put it in the dhcpd.conf
David Hankins's avatar
David Hankins committed
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
 * file.
 */
isc_result_t
set_server_duid_from_option(void) {
	struct option_state *opt_state;
	struct option_cache *oc;
	struct data_string option_duid;
	isc_result_t ret_val;

	opt_state = NULL;
	if (!option_state_allocate(&opt_state, MDL)) {
		log_fatal("No memory for server DUID.");
	}

	execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL,
Shawn Routhier's avatar
Shawn Routhier committed
395 396
				    opt_state, &global_scope, root_group,
				    NULL, NULL);
David Hankins's avatar
David Hankins committed
397 398 399 400 401 402

	oc = lookup_option(&dhcpv6_universe, opt_state, D6O_SERVERID);
	if (oc == NULL) {
		ret_val = ISC_R_NOTFOUND;
	} else {
		memset(&option_duid, 0, sizeof(option_duid));
403 404
		if (!evaluate_option_cache(&option_duid, NULL, NULL, NULL,
					   opt_state, NULL, &global_scope,
David Hankins's avatar
David Hankins committed
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
					   oc, MDL)) {
			ret_val = ISC_R_UNEXPECTED;
		} else {
			set_server_duid(&option_duid);
			data_string_forget(&option_duid, MDL);
			ret_val = ISC_R_SUCCESS;
		}
	}

	option_state_dereference(&opt_state, MDL);

	return ret_val;
}

/*
 * DUID layout, as defined in RFC 3315, section 9.
421
 *
David Hankins's avatar
David Hankins committed
422 423 424
 * We support type 1 (hardware address plus time) and type 3 (hardware
 * address).
 *
425
 * We can support type 2 for specific vendors in the future, if they
David Hankins's avatar
David Hankins committed
426 427 428 429 430
 * publish the specification. And of course there may be additional
 * types later.
 */
static int server_duid_type = DUID_LLT;

431
/*
David Hankins's avatar
David Hankins committed
432 433 434 435 436 437 438 439
 * Set the DUID type.
 */
void
set_server_duid_type(int type) {
	server_duid_type = type;
}

/*
440
 * Generate a new server DUID. This is done if there was no DUID in
David Hankins's avatar
David Hankins committed
441 442 443 444 445 446 447 448 449 450 451 452 453 454
 * the leases.txt or in the dhcpd.conf file.
 */
isc_result_t
generate_new_server_duid(void) {
	struct interface_info *p;
	u_int32_t time_val;
	struct data_string generated_duid;

	/*
	 * Verify we have a type that we support.
	 */
	if ((server_duid_type != DUID_LL) && (server_duid_type != DUID_LLT)) {
		log_error("Invalid DUID type %d specified, "
			  "only LL and LLT types supported", server_duid_type);
455
		return DHCP_R_INVALIDARG;
David Hankins's avatar
David Hankins committed
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
	}

	/*
	 * Find an interface with a hardware address.
	 * Any will do. :)
	 */
	for (p = interfaces; p != NULL; p = p->next) {
		if (p->hw_address.hlen > 0) {
			break;
		}
	}
	if (p == NULL) {
		return ISC_R_UNEXPECTED;
	}

	/*
	 * Build our DUID.
	 */
	memset(&generated_duid, 0, sizeof(generated_duid));
	if (server_duid_type == DUID_LLT) {
		time_val = duid_time(time(NULL));
		generated_duid.len = 8 + p->hw_address.hlen - 1;
478
		if (!buffer_allocate(&generated_duid.buffer,
David Hankins's avatar
David Hankins committed
479 480 481 482 483
				     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);
484
		putUShort(generated_duid.buffer->data + 2,
David Hankins's avatar
David Hankins committed
485 486
			  p->hw_address.hbuf[0]);
		putULong(generated_duid.buffer->data + 4, time_val);
487
		memcpy(generated_duid.buffer->data + 8,
David Hankins's avatar
David Hankins committed
488 489 490
		       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;
491
		if (!buffer_allocate(&generated_duid.buffer,
David Hankins's avatar
David Hankins committed
492 493 494 495 496
				     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);
497
		putUShort(generated_duid.buffer->data + 2,
David Hankins's avatar
David Hankins committed
498
			  p->hw_address.hbuf[0]);
499
		memcpy(generated_duid.buffer->data + 4,
David Hankins's avatar
David Hankins committed
500 501 502
		       p->hw_address.hbuf+1, p->hw_address.hlen-1);
	} else {
		log_fatal("Unsupported server DUID type %d.", server_duid_type);
503
	}
David Hankins's avatar
David Hankins committed
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521

	set_server_duid(&generated_duid);
	data_string_forget(&generated_duid, MDL);

	return ISC_R_SUCCESS;
}

/*
 * Get the client identifier from the packet.
 */
isc_result_t
get_client_id(struct packet *packet, struct data_string *client_id) {
	struct option_cache *oc;

	/*
	 * Verify our client_id structure is empty.
	 */
	if ((client_id->data != NULL) || (client_id->len != 0)) {
522
		return DHCP_R_INVALIDARG;
David Hankins's avatar
David Hankins committed
523 524 525 526 527 528 529
	}

	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_CLIENTID);
	if (oc == NULL) {
		return ISC_R_NOTFOUND;
	}

530
	if (!evaluate_option_cache(client_id, packet, NULL, NULL,
David Hankins's avatar
David Hankins committed
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
				   packet->options, NULL,
				   &global_scope, oc, MDL)) {
		return ISC_R_FAILURE;
	}

	return ISC_R_SUCCESS;
}

/*
 * Message validation, defined in RFC 3315, sections 15.2, 15.5, 15.7:
 *
 *    Servers MUST discard any Solicit messages that do not include a
 *    Client Identifier option or that do include a Server Identifier
 *    option.
 */
int
valid_client_msg(struct packet *packet, struct data_string *client_id) {
	int ret_val;
	struct option_cache *oc;
	struct data_string data;

	ret_val = 0;
	memset(client_id, 0, sizeof(*client_id));
	memset(&data, 0, sizeof(data));

	switch (get_client_id(packet, client_id)) {
		case ISC_R_SUCCESS:
			break;
		case ISC_R_NOTFOUND:
			log_debug("Discarding %s from %s; "
561
				  "client identifier missing",
David Hankins's avatar
David Hankins committed
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
				  dhcpv6_type_names[packet->dhcpv6_msg_type],
				  piaddr(packet->client_addr));
			goto exit;
		default:
			log_error("Error processing %s from %s; "
				  "unable to evaluate Client Identifier",
				  dhcpv6_type_names[packet->dhcpv6_msg_type],
				  piaddr(packet->client_addr));
			goto exit;
	}

	/*
	 * Required by RFC 3315, section 15.
	 */
	if (packet->unicast) {
		log_debug("Discarding %s from %s; packet sent unicast "
578
			  "(CLIENTID %s)",
David Hankins's avatar
David Hankins committed
579 580
			  dhcpv6_type_names[packet->dhcpv6_msg_type],
			  piaddr(packet->client_addr),
Shane Kerr's avatar
Shane Kerr committed
581
			  print_hex_1(client_id->len, client_id->data, 60));
David Hankins's avatar
David Hankins committed
582 583 584 585 586 587 588
		goto exit;
	}


	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
	if (oc != NULL) {
		if (evaluate_option_cache(&data, packet, NULL, NULL,
589
					  packet->options, NULL,
David Hankins's avatar
David Hankins committed
590
					  &global_scope, oc, MDL)) {
591
			log_debug("Discarding %s from %s; "
David Hankins's avatar
David Hankins committed
592
				  "server identifier found "
593
				  "(CLIENTID %s, SERVERID %s)",
David Hankins's avatar
David Hankins committed
594 595
				  dhcpv6_type_names[packet->dhcpv6_msg_type],
				  piaddr(packet->client_addr),
596
				  print_hex_1(client_id->len,
David Hankins's avatar
David Hankins committed
597 598 599 600
				  	      client_id->data, 60),
				  print_hex_2(data.len,
				  	      data.data, 60));
		} else {
601
			log_debug("Discarding %s from %s; "
David Hankins's avatar
David Hankins committed
602
				  "server identifier found "
603
				  "(CLIENTID %s)",
David Hankins's avatar
David Hankins committed
604
				  dhcpv6_type_names[packet->dhcpv6_msg_type],
605
				  print_hex_1(client_id->len,
David Hankins's avatar
David Hankins committed
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
				  	      client_id->data, 60),
				  piaddr(packet->client_addr));
		}
		goto exit;
	}

	/* looks good */
	ret_val = 1;

exit:
	if (data.len > 0) {
		data_string_forget(&data, MDL);
	}
	if (!ret_val) {
		if (client_id->len > 0) {
			data_string_forget(client_id, MDL);
		}
	}
	return ret_val;
}

/*
628
 * Response validation, defined in RFC 3315, sections 15.4, 15.6, 15.8,
David Hankins's avatar
David Hankins committed
629 630 631 632 633 634 635 636 637 638 639 640 641
 * 15.9 (slightly different wording, but same meaning):
 *
 *   Servers MUST discard any received Request message that meet any of
 *   the following conditions:
 *
 *   -  the message does not include a Server Identifier option.
 *   -  the contents of the Server Identifier option do not match the
 *      server's DUID.
 *   -  the message does not include a Client Identifier option.
 */
int
valid_client_resp(struct packet *packet,
		  struct data_string *client_id,
Shane Kerr's avatar
Shane Kerr committed
642 643
		  struct data_string *server_id)
{
David Hankins's avatar
David Hankins committed
644 645 646 647 648 649 650 651 652 653 654 655 656 657
	int ret_val;
	struct option_cache *oc;

	/* INSIST((duid.data != NULL) && (duid.len > 0)); */

	ret_val = 0;
	memset(client_id, 0, sizeof(*client_id));
	memset(server_id, 0, sizeof(*server_id));

	switch (get_client_id(packet, client_id)) {
		case ISC_R_SUCCESS:
			break;
		case ISC_R_NOTFOUND:
			log_debug("Discarding %s from %s; "
658
				  "client identifier missing",
David Hankins's avatar
David Hankins committed
659 660 661 662 663 664 665 666 667 668 669 670 671 672
				  dhcpv6_type_names[packet->dhcpv6_msg_type],
				  piaddr(packet->client_addr));
			goto exit;
		default:
			log_error("Error processing %s from %s; "
				  "unable to evaluate Client Identifier",
				  dhcpv6_type_names[packet->dhcpv6_msg_type],
				  piaddr(packet->client_addr));
			goto exit;
	}

	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
	if (oc == NULL) {
		log_debug("Discarding %s from %s: "
673
			  "server identifier missing (CLIENTID %s)",
David Hankins's avatar
David Hankins committed
674 675 676 677 678
			  dhcpv6_type_names[packet->dhcpv6_msg_type],
			  piaddr(packet->client_addr),
			  print_hex_1(client_id->len, client_id->data, 60));
		goto exit;
	}
679
	if (!evaluate_option_cache(server_id, packet, NULL, NULL,
David Hankins's avatar
David Hankins committed
680 681 682 683 684 685 686 687 688
				   packet->options, NULL,
				   &global_scope, oc, MDL)) {
		log_error("Error processing %s from %s; "
			  "unable to evaluate Server Identifier (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;
	}
689
	if ((server_duid.len != server_id->len) ||
David Hankins's avatar
David Hankins committed
690
	    (memcmp(server_duid.data, server_id->data, server_duid.len) != 0)) {
691
		log_debug("Discarding %s from %s; "
David Hankins's avatar
David Hankins committed
692
			  "not our server identifier "
693
			  "(CLIENTID %s, SERVERID %s, server DUID %s)",
David Hankins's avatar
David Hankins committed
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
			  dhcpv6_type_names[packet->dhcpv6_msg_type],
			  piaddr(packet->client_addr),
			  print_hex_1(client_id->len, client_id->data, 60),
			  print_hex_2(server_id->len, server_id->data, 60),
			  print_hex_3(server_duid.len, server_duid.data, 60));
		goto exit;
	}

	/* looks good */
	ret_val = 1;

exit:
	if (!ret_val) {
		if (server_id->len > 0) {
			data_string_forget(server_id, MDL);
		}
		if (client_id->len > 0) {
			data_string_forget(client_id, MDL);
		}
	}
	return ret_val;
}

/*
 * Information request validation, defined in RFC 3315, section 15.12:
 *
 *   Servers MUST discard any received Information-request message that
 *   meets any of the following conditions:
 *
 *   -  The message includes a Server Identifier option and the DUID in
 *      the option does not match the server's DUID.
 *
 *   -  The message includes an IA option.
 */
int
valid_client_info_req(struct packet *packet, struct data_string *server_id) {
	int ret_val;
	struct option_cache *oc;
Shane Kerr's avatar
Shane Kerr committed
732 733 734
	struct data_string client_id;
	char client_id_str[80];	/* print_hex_1() uses maximum 60 characters,
				   plus a few more for extra information */
David Hankins's avatar
David Hankins committed
735 736 737

	ret_val = 0;
	memset(server_id, 0, sizeof(*server_id));
Shawn Routhier's avatar
Shawn Routhier committed
738
	memset(&client_id, 0, sizeof(client_id));
David Hankins's avatar
David Hankins committed
739

Shane Kerr's avatar
Shane Kerr committed
740
	/*
741
	 * Make a string that we can print out to give more
Shane Kerr's avatar
Shane Kerr committed
742 743
	 * information about the client if we need to.
	 *
744 745
	 * By RFC 3315, Section 18.1.5 clients SHOULD have a
	 * client-id on an Information-request packet, but it
Shane Kerr's avatar
Shane Kerr committed
746 747 748 749 750 751 752 753 754 755
	 * is not strictly necessary.
	 */
	if (get_client_id(packet, &client_id) == ISC_R_SUCCESS) {
		snprintf(client_id_str, sizeof(client_id_str), " (CLIENTID %s)",
			 print_hex_1(client_id.len, client_id.data, 60));
		data_string_forget(&client_id, MDL);
	} else {
		client_id_str[0] = '\0';
	}

David Hankins's avatar
David Hankins committed
756 757 758 759
	/*
	 * Required by RFC 3315, section 15.
	 */
	if (packet->unicast) {
Shane Kerr's avatar
Shane Kerr committed
760
		log_debug("Discarding %s from %s; packet sent unicast%s",
David Hankins's avatar
David Hankins committed
761
			  dhcpv6_type_names[packet->dhcpv6_msg_type],
Shane Kerr's avatar
Shane Kerr committed
762
			  piaddr(packet->client_addr), client_id_str);
David Hankins's avatar
David Hankins committed
763 764 765 766 767 768
		goto exit;
	}

	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA);
	if (oc != NULL) {
		log_debug("Discarding %s from %s; "
769
			  "IA_NA option present%s",
David Hankins's avatar
David Hankins committed
770
			  dhcpv6_type_names[packet->dhcpv6_msg_type],
Shane Kerr's avatar
Shane Kerr committed
771
			  piaddr(packet->client_addr), client_id_str);
David Hankins's avatar
David Hankins committed
772 773 774 775 776
		goto exit;
	}
	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_TA);
	if (oc != NULL) {
		log_debug("Discarding %s from %s; "
777
			  "IA_TA option present%s",
David Hankins's avatar
David Hankins committed
778
			  dhcpv6_type_names[packet->dhcpv6_msg_type],
Shane Kerr's avatar
Shane Kerr committed
779
			  piaddr(packet->client_addr), client_id_str);
David Hankins's avatar
David Hankins committed
780 781
		goto exit;
	}
Francis Dupont's avatar
Francis Dupont committed
782 783 784
	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD);
	if (oc != NULL) {
		log_debug("Discarding %s from %s; "
785
			  "IA_PD option present%s",
Francis Dupont's avatar
Francis Dupont committed
786 787 788 789
			  dhcpv6_type_names[packet->dhcpv6_msg_type],
			  piaddr(packet->client_addr), client_id_str);
		goto exit;
	}
David Hankins's avatar
David Hankins committed
790 791 792

	oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
	if (oc != NULL) {
793
		if (!evaluate_option_cache(server_id, packet, NULL, NULL,
David Hankins's avatar
David Hankins committed
794 795 796
					   packet->options, NULL,
					   &global_scope, oc, MDL)) {
			log_error("Error processing %s from %s; "
Shane Kerr's avatar
Shane Kerr committed
797
				  "unable to evaluate Server Identifier%s",
David Hankins's avatar
David Hankins committed
798
				  dhcpv6_type_names[packet->dhcpv6_msg_type],
Shane Kerr's avatar
Shane Kerr committed
799
				  piaddr(packet->client_addr), client_id_str);
David Hankins's avatar
David Hankins committed
800 801
			goto exit;
		}
802 803
		if ((server_duid.len != server_id->len) ||
		    (memcmp(server_duid.data, server_id->data,
David Hankins's avatar
David Hankins committed
804
		    	    server_duid.len) != 0)) {
805
			log_debug("Discarding %s from %s; "
David Hankins's avatar
David Hankins committed
806
				  "not our server identifier "
807
				  "(SERVERID %s, server DUID %s)%s",
David Hankins's avatar
David Hankins committed
808 809
				  dhcpv6_type_names[packet->dhcpv6_msg_type],
				  piaddr(packet->client_addr),
810
				  print_hex_1(server_id->len,
David Hankins's avatar
David Hankins committed
811
				  	      server_id->data, 60),
812
				  print_hex_2(server_duid.len,
Shane Kerr's avatar
Shane Kerr committed
813 814
				  	      server_duid.data, 60),
				  client_id_str);
David Hankins's avatar
David Hankins committed
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
			goto exit;
		}
	}

	/* looks good */
	ret_val = 1;

exit:
	if (!ret_val) {
		if (server_id->len > 0) {
			data_string_forget(server_id, MDL);
		}
	}
	return ret_val;
}

831
/*
David Hankins's avatar
David Hankins committed
832 833 834 835 836 837 838
 * Options that we want to send, in addition to what was requested
 * via the ORO.
 */
static const int required_opts[] = {
	D6O_CLIENTID,
	D6O_SERVERID,
	D6O_STATUS_CODE,
839
	D6O_PREFERENCE,
David Hankins's avatar
David Hankins committed
840 841 842 843 844 845 846
	0
};
static const int required_opts_solicit[] = {
	D6O_CLIENTID,
	D6O_SERVERID,
	D6O_IA_NA,
	D6O_IA_TA,
Francis Dupont's avatar
Francis Dupont committed
847
	D6O_IA_PD,
David Hankins's avatar
David Hankins committed
848 849 850
	D6O_RAPID_COMMIT,
	D6O_STATUS_CODE,
	D6O_RECONF_ACCEPT,
851
	D6O_PREFERENCE,
David Hankins's avatar
David Hankins committed
852 853
	0
};
Francis Dupont's avatar
Francis Dupont committed
854 855 856 857 858
static const int required_opts_agent[] = {
	D6O_INTERFACE_ID,
	D6O_RELAY_MSG,
	0
};
859
static const int required_opts_IA[] = {
Francis Dupont's avatar
Francis Dupont committed
860 861 862 863 864 865 866 867 868
	D6O_IAADDR,
	D6O_STATUS_CODE,
	0
};
static const int required_opts_IA_PD[] = {
	D6O_IAPREFIX,
	D6O_STATUS_CODE,
	0
};
David Hankins's avatar
David Hankins committed
869 870 871 872
static const int required_opts_STATUS_CODE[] = {
	D6O_STATUS_CODE,
	0
};
873 874 875 876 877 878
#ifdef DHCP4o6
static const int required_opts_4o6[] = {
	D6O_DHCPV4_MSG,
	0
};
#endif
David Hankins's avatar
David Hankins committed
879

880 881 882 883 884 885 886 887
static const int unicast_reject_opts[] = {
	D6O_CLIENTID,
	D6O_SERVERID,
	D6O_STATUS_CODE,
	0
};


David Hankins's avatar
David Hankins committed
888
/*
889 890 891 892
 * Extracts from packet contents an IA_* option, storing the IA structure
 * in its entirety in enc_opt_data, and storing any decoded DHCPv6 options
 * in enc_opt_state for later lookup and evaluation.  The 'offset' indicates
 * where in the IA_* the DHCPv6 options commence.
David Hankins's avatar
David Hankins committed
893 894
 */
static int
895
get_encapsulated_IA_state(struct option_state **enc_opt_state,
David Hankins's avatar
David Hankins committed
896 897
			  struct data_string *enc_opt_data,
			  struct packet *packet,
898 899
			  struct option_cache *oc,
			  int offset)
Shane Kerr's avatar
Shane Kerr committed
900
{
901
	/*
David Hankins's avatar
David Hankins committed
902 903 904 905 906 907 908 909 910 911
	 * Get the raw data for the encapsulated options.
	 */
	memset(enc_opt_data, 0, sizeof(*enc_opt_data));
	if (!evaluate_option_cache(enc_opt_data, packet,
				   NULL, NULL, packet->options, NULL,
				   &global_scope, oc, MDL)) {
		log_error("get_encapsulated_IA_state: "
			  "error evaluating raw option.");
		return 0;
	}
912
	if (enc_opt_data->len < offset) {
David Hankins's avatar
David Hankins committed
913 914 915 916 917 918
		log_error("get_encapsulated_IA_state: raw option too small.");
		data_string_forget(enc_opt_data, MDL);
		return 0;
	}

	/*
919
	 * Now create the option state structure, and pass it to the
David Hankins's avatar
David Hankins committed
920 921 922 923 924 925 926 927
	 * function that parses options.
	 */
	*enc_opt_state = NULL;
	if (!option_state_allocate(enc_opt_state, MDL)) {
		log_error("get_encapsulated_IA_state: no memory for options.");
		data_string_forget(enc_opt_data, MDL);
		return 0;
	}
928
	if (!parse_option_buffer(*enc_opt_state,
929
				 enc_opt_data->data + offset,
930
				 enc_opt_data->len - offset,
David Hankins's avatar
David Hankins committed
931 932 933 934 935 936 937 938 939 940 941 942
				 &dhcpv6_universe)) {
		log_error("get_encapsulated_IA_state: error parsing options.");
		option_state_dereference(enc_opt_state, MDL);
		data_string_forget(enc_opt_data, MDL);
		return 0;
	}

	return 1;
}

static int
set_status_code(u_int16_t status_code, const char *status_message,
Shane Kerr's avatar
Shane Kerr committed
943 944
		struct option_state *opt_state)
{
David Hankins's avatar
David Hankins committed
945 946 947 948 949 950 951 952 953 954
	struct data_string d;
	int ret_val;

	memset(&d, 0, sizeof(d));
	d.len = sizeof(status_code) + strlen(status_message);
	if (!buffer_allocate(&d.buffer, d.len, MDL)) {
		log_fatal("set_status_code: no memory for status code.");
	}
	d.data = d.buffer->data;
	putUShort(d.buffer->data, status_code);
955
	memcpy(d.buffer->data + sizeof(status_code),
David Hankins's avatar
David Hankins committed
956
	       status_message, d.len - sizeof(status_code));
957 958
	if (!save_option_buffer(&dhcpv6_universe, opt_state,
				d.buffer, (unsigned char *)d.data, d.len,
David Hankins's avatar
David Hankins committed
959 960 961 962 963 964 965 966 967 968
				D6O_STATUS_CODE, 0)) {
		log_error("set_status_code: error saving status code.");
		ret_val = 0;
	} else {
		ret_val = 1;
	}
	data_string_forget(&d, MDL);
	return ret_val;
}

969 970 971 972
void check_pool6_threshold(struct reply_state *reply,
			   struct iasubopt *lease)
{
	struct ipv6_pond *pond;
973 974
	isc_uint64_t used, count, high_threshold;
	int poolhigh = 0, poollow = 0;
975 976 977 978 979 980 981
	char *shared_name = "no name";
	char tmp_addr[INET6_ADDRSTRLEN];

	if ((lease->ipv6_pool == NULL) || (lease->ipv6_pool->ipv6_pond == NULL))
		return;
	pond = lease->ipv6_pool->ipv6_pond;

982 983 984 985 986
	/* If the address range is too large to track, just skip all this. */
	if (pond->jumbo_range == 1) {
		return;
	}

987 988 989
	count = pond->num_total;
	used = pond->num_active;

990 991 992 993 994 995
	/* get network name for logging */
	if ((pond->shared_network != NULL) &&
	    (pond->shared_network->name != NULL)) {
		shared_name = pond->shared_network->name;
	}

996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
	/* The logged flag indicates if we have already crossed the high
	 * threshold and emitted a log message.  If it is set we check to
	 * see if we have re-crossed the low threshold and need to reset
	 * things.  When we cross the high threshold we determine what
	 * the low threshold is and save it into the low_threshold value.
	 * When we cross that threshold we reset the logged flag and
	 * the low_threshold to 0 which allows the high threshold message
	 * to be emitted once again.
	 * if we haven't recrossed the boundry we don't need to do anything.
	 */
	if (pond->logged !=0) {
		if (used <= pond->low_threshold) {
			pond->low_threshold = 0;
			pond->logged = 0;
			log_error("Pool threshold reset - shared subnet: %s; "
1011
				  "address: %s; low threshold %llu/%llu.",
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
				  shared_name,
				  inet_ntop(AF_INET6, &lease->addr,
					    tmp_addr, sizeof(tmp_addr)),
				  used, count);
		}
		return;
	}

	/* find the high threshold */
	if (get_option_int(&poolhigh, &server_universe, reply->packet, NULL,
			   NULL, reply->packet->options, reply->opt_state,
			   reply->opt_state, &lease->scope,
			   SV_LOG_THRESHOLD_HIGH, MDL) == 0) {
		/* no threshold bail out */
		return;
	}

	/* We do have a threshold for this pool, see if its valid */
	if ((poolhigh <= 0) || (poolhigh > 100)) {
		/* not valid */
		return;
	}

	/* we have a valid value, have we exceeded it */
1036
	high_threshold = FIND_POND6_PERCENT(count, poolhigh);
1037 1038 1039 1040 1041 1042 1043
	if (used < high_threshold) {
		/* nope, no more to do */
		return;
	}

	/* we've exceeded it, output a message */
	log_error("Pool threshold exceeded - shared subnet: %s; "
1044
		  "address: %s; high threshold %d%% %llu/%llu.",
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
		  shared_name,
		  inet_ntop(AF_INET6, &lease->addr, tmp_addr, sizeof(tmp_addr)),
		  poolhigh, used, count);

	/* handle the low threshold now, if we don't
	 * have one we default to 0. */
	if ((get_option_int(&poollow, &server_universe, reply->packet, NULL,
			    NULL, reply->packet->options, reply->opt_state,
			    reply->opt_state, &lease->scope,
			    SV_LOG_THRESHOLD_LOW, MDL) == 0) ||
	    (poollow > 100)) {
		poollow = 0;
	}

	/*
	 * If the low theshold is higher than the high threshold we continue to log
	 * If it isn't then we set the flag saying we already logged and determine
	 * what the reset threshold is.
	 */
	if (poollow < poolhigh) {
		pond->logged = 1;
1066
		pond->low_threshold = FIND_POND6_PERCENT(count, poollow);
1067 1068 1069
	}
}

David Hankins's avatar
David Hankins committed
1070 1071 1072 1073 1074 1075
/*
 * We have a set of operations we do to set up the reply packet, which
 * is the same for many message types.
 */
static int
start_reply(struct packet *packet,
1076
	    const struct data_string *client_id,
David Hankins's avatar
David Hankins committed
1077 1078
	    const struct data_string *server_id,
	    struct option_state **opt_state,
Shane Kerr's avatar
Shane Kerr committed
1079 1080
	    struct dhcpv6_packet *reply)
{
David Hankins's avatar
David Hankins committed
1081
	struct option_cache *oc;
1082
	const unsigned char *server_id_data;
David Hankins's avatar
David Hankins committed
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
	int server_id_len;

	/*
	 * Build our option state for reply.
	 */
	*opt_state = NULL;
	if (!option_state_allocate(opt_state, MDL)) {
		log_error("start_reply: no memory for option_state.");
		return 0;
	}
1093 1094
	execute_statements_in_scope(NULL, packet, NULL, NULL,
				    packet->options, *opt_state,
Shawn Routhier's avatar
Shawn Routhier committed
1095
				    &global_scope, root_group, NULL, NULL);
David Hankins's avatar
David Hankins committed
1096

1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
	/*
	 * A small bit of special handling for Solicit messages.
	 *
	 * We could move the logic into a flag, but for now just check
	 * explicitly.
	 */
	if (packet->dhcpv6_msg_type == DHCPV6_SOLICIT) {
		reply->msg_type = DHCPV6_ADVERTISE;

		/*
		 * If:
		 * - this message type supports rapid commit (Solicit), and
		 * - the server is configured to supply a rapid commit, and
		 * - the client requests a rapid commit,
		 * Then we add a rapid commit option, and send Reply (instead
		 * of an Advertise).
		 */
		oc = lookup_option(&dhcpv6_universe,
				   *opt_state, D6O_RAPID_COMMIT);
		if (oc != NULL) {
			oc = lookup_option(&dhcpv6_universe,
					   packet->options, D6O_RAPID_COMMIT);
			if (oc != NULL) {
1120
				/* Rapid-commit in action. */
1121
				reply->msg_type = DHCPV6_REPLY;
1122 1123 1124 1125
			} else {
				/* Don't want a rapid-commit in advertise. */
				delete_option(&dhcpv6_universe,
					      *opt_state, D6O_RAPID_COMMIT);
1126 1127
			}
		}
1128
	} else {
1129
		reply->msg_type = DHCPV6_REPLY;
1130 1131 1132 1133 1134 1135 1136 1137
		/* Delete the rapid-commit from the sent options. */
		oc = lookup_option(&dhcpv6_universe,
				   *opt_state, D6O_RAPID_COMMIT);
		if (oc != NULL) {
			delete_option(&dhcpv6_universe,
				      *opt_state, D6O_RAPID_COMMIT);
		}
	}
1138

1139
	/*
1140 1141
	 * Use the client's transaction identifier for the reply.
	 */
1142
	memcpy(reply->transaction_id, packet->dhcpv6_transaction_id,
1143 1144
	       sizeof(reply->transaction_id));

1145
	/*
David Hankins's avatar
David Hankins committed
1146 1147 1148 1149
	 * RFC 3315, section 18.2 says we need server identifier and
	 * client identifier.
	 *
	 * If the server ID is defined via the configuration file, then
1150
	 * it will already be present in the option state at this point,
David Hankins's avatar
David Hankins committed
1151 1152
	 * so we don't need to set it.
	 *
1153
	 * If we have a server ID passed in from the caller,
David Hankins's avatar
David Hankins committed
1154 1155 1156 1157 1158
	 * use that, otherwise use the global DUID.
	 */
	oc = lookup_option(&dhcpv6_universe, *opt_state, D6O_SERVERID);
	if (oc == NULL) {
		if (server_id == NULL) {
1159
			server_id_data = server_duid.data;
David Hankins's avatar
David Hankins committed
1160 1161
			server_id_len = server_duid.len;
		} else {
1162
			server_id_data = server_id->data;
David Hankins's avatar
David Hankins committed
1163 1164
			server_id_len = server_id->len;
		}
1165
		if (!save_option_buffer(&dhcpv6_universe, *opt_state,
1166 1167
					NULL, (unsigned char *)server_id_data,
					server_id_len, D6O_SERVERID, 0)) {
David Hankins's avatar
David Hankins committed
1168 1169 1170 1171 1172 1173 1174
				log_error("start_reply: "
					  "error saving server identifier.");
				return 0;
		}
	}

	if (client_id->buffer != NULL) {
1175 1176 1177 1178
		if (!save_option_buffer(&dhcpv6_universe, *opt_state,
					client_id->buffer,
					(unsigned char *)client_id->data,
					client_id->len,
David Hankins's avatar
David Hankins committed
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
					D6O_CLIENTID, 0)) {
			log_error("start_reply: error saving "
				  "client identifier.");
			return 0;
		}
	}

	/*
	 * If the client accepts reconfiguration, let it know that we
	 * will send them.
	 *
	 * Note: we don't actually do this yet, but DOCSIS requires we
	 *       claim to.
	 */
	oc = lookup_option(&dhcpv6_universe, packet->options,
			   D6O_RECONF_ACCEPT);
	if (oc != NULL) {
		if (!save_option_buffer(&dhcpv6_universe, *opt_state,
1197
					NULL, (unsigned char *)"", 0,
1198
					D6O_RECONF_ACCEPT, 0)) {
David Hankins's avatar
David Hankins committed
1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
			log_error("start_reply: "
				  "error saving RECONF_ACCEPT option.");
			option_state_dereference(opt_state, MDL);
			return 0;
		}
	}

	return 1;
}

/*
1210
 * Try to get the IPv6 address the client asked for from the
David Hankins's avatar
David Hankins committed
1211 1212 1213 1214 1215 1216 1217
 * pool.
 *
 * addr is the result (should be a pointer to NULL on entry)
 * pool is the pool to search in
 * requested_addr is the address the client wants
 */
static isc_result_t
Francis Dupont's avatar
Francis Dupont committed
1218
try_client_v6_address(struct iasubopt **addr,
David Hankins's avatar
David Hankins committed
1219
		      struct ipv6_pool *pool,
Shane Kerr's avatar
Shane Kerr committed
1220 1221
		      const struct data_string *requested_addr)
{
David Hankins's avatar
David Hankins committed
1222 1223 1224 1225
	struct in6_addr tmp_addr;
	isc_result_t result;

	if (requested_addr->len < sizeof(tmp_addr)) {
1226
		return DHCP_R_INVALIDARG;
David Hankins's avatar