summaryrefslogtreecommitdiff
path: root/client/dhc6.c
diff options
context:
space:
mode:
Diffstat (limited to 'client/dhc6.c')
-rw-r--r--client/dhc6.c168
1 files changed, 148 insertions, 20 deletions
diff --git a/client/dhc6.c b/client/dhc6.c
index bb5d2000..62455950 100644
--- a/client/dhc6.c
+++ b/client/dhc6.c
@@ -27,10 +27,15 @@
#ifdef DHCPv6
struct sockaddr_in6 DHCPv6DestAddr;
+
+/* Option definition structures that are used by the software - declared
+ * here once and assigned at startup to save lookups.
+ */
struct option *clientid_option = NULL;
+struct option *elapsed_option = NULL;
struct option *ia_na_option = NULL;
struct option *iaaddr_option = NULL;
-struct option *elapsed_option = NULL;
+struct option *oro_option = NULL;
static struct dhc6_lease *dhc6_dup_lease(struct dhc6_lease *lease,
const char *file, int line);
@@ -162,6 +167,11 @@ dhcpv6_client_assignments(void)
dhcpv6_universe.code_hash, &code, 0, MDL))
log_fatal("Unable to find the CLIENTID option definition.");
+ code = D6O_ELAPSED_TIME;
+ if (!option_code_hash_lookup(&elapsed_option,
+ dhcpv6_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find the ELAPSED_TIME option definition.");
+
code = D6O_IA_NA;
if (!option_code_hash_lookup(&ia_na_option, dhcpv6_universe.code_hash,
&code, 0, MDL))
@@ -172,10 +182,10 @@ dhcpv6_client_assignments(void)
&code, 0, MDL))
log_fatal("Unable to find the IAADDR option definition.");
- code = D6O_ELAPSED_TIME;
- if (!option_code_hash_lookup(&elapsed_option,
- dhcpv6_universe.code_hash, &code, 0, MDL))
- log_fatal("Unable to find the ELAPSED_TIME option definition.");
+ code = D6O_ORO;
+ if (!option_code_hash_lookup(&oro_option, dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the ORO option definition.");
#ifndef __CYGWIN32__ /* XXX */
endservent();
@@ -790,23 +800,42 @@ insert_lease(struct dhc6_lease **head, struct dhc6_lease *new)
/* Not really clear what to do here yet.
*/
static int
-dhc6_score_lease(struct dhc6_lease *lease)
+dhc6_score_lease(struct client_state *client, struct dhc6_lease *lease)
{
struct dhc6_ia *ia;
struct dhc6_addr *addr;
+ struct option **req;
+ int i;
if (lease->score)
return lease->score;
lease->score = 1;
-#if 0 /* XXX: oro is a bit...weird...still */
- for (i = 0 ; i < oro_len ; i++) {
- if (lookup_option(&dhcpv6_universe, lease->options,
- oro[i]) != NULL)
- lease->score++;
+ /* If this lease lacks a required option, dump it. */
+ /* XXX: we should be able to cache the failure... */
+ req = client->config->required_options;
+ if (req != NULL) {
+ for (i = 0 ; req[i] != NULL ; i++) {
+ if (lookup_option(&dhcpv6_universe, lease->options,
+ req[i]->code) == NULL) {
+ lease->score = 0;
+ return lease->score;
+ }
+ }
+ }
+
+ /* If this lease contains a requested option, improve its
+ * score.
+ */
+ req = client->config->requested_options;
+ if (req != NULL) {
+ for (i = 0 ; req[i] != NULL ; i++) {
+ if (lookup_option(&dhcpv6_universe, lease->options,
+ req[i]->code) != NULL)
+ lease->score++;
+ }
}
-#endif
for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
lease->score += 50;
@@ -1708,8 +1737,8 @@ dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
/* Compare the new lease with the selected lease to make
* sure there is no risky business.
*/
- nscore = dhc6_score_lease(new);
- sscore = dhc6_score_lease(client->selected_lease);
+ nscore = dhc6_score_lease(client, new);
+ sscore = dhc6_score_lease(client, client->selected_lease);
if ((client->advertised_leases != NULL) &&
(nscore < (sscore / 2))) {
/* XXX: An attacker might reply this way to make
@@ -1799,7 +1828,8 @@ init_handler(struct packet *packet, struct client_state *client)
* should not if the advertise contains less than one IA and address.
*/
if ((client->txcount > 1) ||
- ((lease->pref == 255) && (dhc6_score_lease(lease) > 150))) {
+ ((lease->pref == 255) &&
+ (dhc6_score_lease(client, lease) > 150))) {
log_debug("RCV: Advertisement immediately selected.");
cancel_timeout(do_init6, client);
start_selecting6(client);
@@ -1837,7 +1867,7 @@ init_handler(struct packet *packet, struct client_state *client)
* server identifiers and to select the numerically lower one.
*/
static struct dhc6_lease *
-dhc6_best_lease(struct dhc6_lease **head)
+dhc6_best_lease(struct client_state *client, struct dhc6_lease **head)
{
struct dhc6_lease **rpos, *rval, **candp, *cand;
int cscore, rscore;
@@ -1847,7 +1877,7 @@ dhc6_best_lease(struct dhc6_lease **head)
rpos = head;
rval = *rpos;
- rscore = dhc6_score_lease(rval);
+ rscore = dhc6_score_lease(client, rval);
candp = &rval->next;
cand = *candp;
@@ -1858,7 +1888,7 @@ dhc6_best_lease(struct dhc6_lease **head)
rscore, (unsigned)rval->pref);
for (; cand != NULL ; candp = &cand->next, cand = *candp) {
- cscore = dhc6_score_lease(cand);
+ cscore = dhc6_score_lease(client, cand);
log_debug("PRC: X-- Candidate %s (s: %d, p: %u).",
print_hex_1(cand->server_id.len,
@@ -1942,7 +1972,7 @@ start_selecting6(struct client_state *client)
log_debug("PRC: Selecting best advertised lease.");
client->state = S_SELECTING;
- lease = dhc6_best_lease(&client->advertised_leases);
+ lease = dhc6_best_lease(client, &client->advertised_leases);
if (lease == NULL)
log_fatal("Impossible error at %s:%d.", MDL);
@@ -3120,6 +3150,9 @@ make_client6_options(struct client_state *client, struct option_state **op,
struct dhc6_lease *lease, u_int8_t message)
{
struct option_cache *oc;
+ struct option **req;
+ struct data_string oro;
+ int buflen, i;
if ((op == NULL) || (client == NULL))
return;
@@ -3127,8 +3160,10 @@ make_client6_options(struct client_state *client, struct option_state **op,
if (*op)
option_state_dereference(op, MDL);
+ /* Create a cache to carry options to transmission. */
option_state_allocate(op, MDL);
+ /* Create and store an 'elapsed time' option in the cache. */
oc = NULL;
if (option_cache_allocate(&oc, MDL)) {
const unsigned char *cdata;
@@ -3174,6 +3209,98 @@ make_client6_options(struct client_state *client, struct option_state **op,
save_option(&dhcpv6_universe, *op, oc);
}
+ /* 'send dhcp6.oro foo;' syntax we used in 4.0.0a1/a2 has been
+ * deprecated by adjustments to the 'request' syntax also used for
+ * DHCPv4.
+ */
+ if (lookup_option(&dhcpv6_universe, *op, D6O_ORO) == NULL)
+ log_error("'send dhcp6.oro' syntax is deprecated, please "
+ "use the 'request' syntax ("man dhclient.conf").");
+
+ /* Construct and store an ORO (Option Request Option). It is a
+ * fatal error to fail to send an ORO (of at least zero length).
+ *
+ * Discussion: RFC3315 appears to be inconsistent in its statements
+ * of whether or not the ORO is mandatory. In section 18.1.1
+ * ("Creation and Transmission of Request Messages"):
+ *
+ * The client MUST include an Option Request option (see section
+ * 22.7) to indicate the options the client is interested in
+ * receiving. The client MAY include options with data values as
+ * hints to the server about parameter values the client would like
+ * to have returned.
+ *
+ * This MUST is missing from the creation/transmission of other
+ * messages (such as Renew and Rebind), and the section 22.7 ("Option
+ * Request Option" format and definition):
+ *
+ * A client MAY include an Option Request option in a Solicit,
+ * Request, Renew, Rebind, Confirm or Information-request message to
+ * inform the server about options the client wants the server to
+ * send to the client. A server MAY include an Option Request
+ * option in a Reconfigure option to indicate which options the
+ * client should request from the server.
+ *
+ * seems to relax the requirement from MUST to MAY (and still other
+ * language in RFC3315 supports this).
+ *
+ * In lieu of a clarification of RFC3315, we will conform with the
+ * MUST. Instead of an absent ORO, we will if there are no options
+ * to request supply an empty ORO. Theoretically, an absent ORO is
+ * difficult to interpret (does the client want all options or no
+ * options?). A zero-length ORO is intuitively clear: requesting
+ * nothing.
+ */
+ memset(&oro, 0, sizeof(oro));
+ buflen = 32;
+ if (!buffer_allocate(&oro.buffer, buflen, MDL))
+ log_fatal("Out of memory constructing DHCPv6 ORO.");
+ oro.data = oro.buffer->data;
+ req = client->config->requested_options;
+ if (req != NULL) {
+ for (i = 0 ; req[i] != NULL ; i++) {
+ if (buflen == oro.len) {
+ struct buffer *tmpbuf = NULL;
+
+ buflen += 32;
+
+ /* Shell game. */
+ buffer_reference(&tmpbuf, oro.buffer, MDL);
+ buffer_dereference(&oro.buffer, MDL);
+
+ if (!buffer_allocate(&oro.buffer, buflen, MDL))
+ log_fatal("Out of memory resizing "
+ "DHCPv6 ORO buffer.");
+
+ oro.data = oro.buffer->data;
+
+ memcpy(oro.buffer->data, tmpbuf->data,
+ oro.len);
+
+ buffer_dereference(&tmpbuf, MDL);
+ }
+
+ if (req[i]->universe == &dhcpv6_universe) {
+ /* Append the code to the ORO. */
+ putUShort(oro.buffer->data + oro.len,
+ req[i]->code);
+ oro.len += 2;
+ }
+ }
+ }
+
+ oc = NULL;
+ if (option_cache_allocate(&oc, MDL) &&
+ make_const_data(&oc->expression, oro.data, oro.len, 0, 0, MDL)) {
+ option_reference(&oc->option, oro_option, MDL);
+ save_option(&dhcpv6_universe, *op, oc);
+ } else {
+ log_fatal("Unable to create ORO option cache.");
+ }
+
+ data_string_forget(&oro, MDL);
+
+ /* Bring in any configured options to send. */
if (client->config->on_transmission)
execute_statements_in_scope(NULL, NULL, NULL, client,
lease ? lease->options : NULL,
@@ -3186,7 +3313,8 @@ make_client6_options(struct client_state *client, struct option_state **op,
* way (may as well fail early).
*/
if (lookup_option(&dhcpv6_universe, *op, D6O_ORO) == NULL)
- log_fatal("You must configure a dhcp6.oro!");
+ log_fatal("Internal inconsistency: no dhcp6.oro in transmit "
+ "state.");
}
/* A clone of the DHCPv4 script_write_params() minus the DHCPv4-specific