/*
- * Copyright (C) 2006-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2006-2017 by Internet Systems Consortium, Inc. ("ISC")
*
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+ * 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/.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#include "dhcpd.h"
/*
- * TODO: RFC4388 specifies that the server SHOULD store the
- * vendor-class-id.
- *
* TODO: RFC4388 specifies that the server SHOULD return the same
* options it would for a DHCREQUEST message, if no Parameter
* Request List option (option 55) is passed. We do not do that.
unsigned char dhcpMsgType;
const char *dhcp_msg_type_name;
struct subnet *subnet;
+ struct group *relay_group;
struct option_state *options;
struct option_cache *oc;
int allow_leasequery;
u_int32_t time_rebinding;
u_int32_t time_expiry;
u_int32_t client_last_transaction_time;
+#if defined(RELAY_PORT)
+ u_int16_t relay_port = 0;
+#endif
struct sockaddr_in to;
struct in_addr siaddr;
struct data_string prl;
/*
* We can't reply if there is no giaddr field.
*/
+ /*
+ * Note: this makes DHCPv4-over-DHCPv6 always fail but it should not
+ * really be a problem because it is not a specified use case
+ * (or even one that makes sense).
+ */
if (!packet->raw->giaddr.s_addr) {
log_info("%s: missing giaddr, ciaddr is %s, no reply sent",
msgbuf, inet_ntoa(packet->raw->ciaddr));
}
/*
- * Set up our options, scope, and, um... stuff.
- * This is basically copied from dhcpinform() in dhcp.c.
+ * Initially we use the 'giaddr' subnet options scope to determine if
+ * the giaddr-identified relay agent is permitted to perform a
+ * leasequery. The subnet is not required, and may be omitted, in
+ * which case we are essentially interrogating the root options class
+ * to find a globally permit.
*/
gip.len = sizeof(packet->raw->giaddr);
memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr));
subnet = NULL;
find_subnet(&subnet, gip, MDL);
- if (subnet == NULL) {
- log_info("%s: unknown subnet for address %s",
- msgbuf, piaddr(gip));
- return;
- }
+ if (subnet != NULL)
+ relay_group = subnet->group;
+ else
+ relay_group = root_group;
+
+ subnet_dereference(&subnet, MDL);
options = NULL;
if (!option_state_allocate(&options, MDL)) {
- subnet_dereference(&subnet, MDL);
log_error("No memory for option state.");
log_info("%s: out of memory, no reply sent", msgbuf);
return;
}
- execute_statements_in_scope(NULL,
- packet,
- NULL,
- NULL,
- packet->options,
- options,
- &global_scope,
- subnet->group,
- NULL);
+ execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options,
+ options, &global_scope, relay_group,
+ NULL, NULL);
+
for (i=packet->class_count-1; i>=0; i--) {
- execute_statements_in_scope(NULL,
- packet,
- NULL,
- NULL,
- packet->options,
- options,
+ execute_statements_in_scope(NULL, packet, NULL, NULL,
+ packet->options, options,
&global_scope,
packet->classes[i]->group,
- subnet->group);
+ relay_group, NULL);
}
- subnet_dereference(&subnet, MDL);
-
/*
* Because LEASEQUERY has some privacy concerns, default to deny.
*/
option_state_dereference(&options, MDL);
return;
}
-
+
/*
* Copy out the client IP address.
if (dhcpMsgType == DHCPLEASEACTIVE)
{
+ /*
+ * RFC 4388 uses the PRL to request options for the agent to
+ * receive that are "about" the client. It is confusing
+ * because in some cases it wants to know what was sent to
+ * the client (lease times, adjusted), and in others it wants
+ * to know information the client sent. You're supposed to
+ * know this on a case-by-case basis.
+ *
+ * "Name servers", "domain name", and the like from the relay
+ * agent's scope seems less than useful. Our options are to
+ * restart the option cache from the lease's best point of view
+ * (execute statements from the lease pool's group), or to
+ * simply restart the option cache from empty.
+ *
+ * I think restarting the option cache from empty best
+ * approaches RFC 4388's intent; specific options are included.
+ */
+ option_state_dereference(&options, MDL);
+
+ if (!option_state_allocate(&options, MDL)) {
+ log_error("%s: out of memory, no reply sent", msgbuf);
+ lease_dereference(&lease, MDL);
+ return;
+ }
/*
* Set the hardware address fields.
if (time_renewal > cur_time) {
time_renewal = htonl(time_renewal - cur_time);
+
if (!add_option(options,
DHO_DHCP_RENEWAL_TIME,
&time_renewal,
if (time_rebinding > cur_time) {
time_rebinding = htonl(time_rebinding - cur_time);
+
if (!add_option(options,
DHO_DHCP_REBINDING_TIME,
&time_rebinding,
if (lease->ends > cur_time) {
time_expiry = htonl(lease->ends - cur_time);
+
if (!add_option(options,
DHO_DHCP_LEASE_TIME,
&time_expiry,
}
}
+ /* Supply the Vendor-Class-Identifier. */
+ if (lease->scope != NULL) {
+ struct data_string vendor_class;
+
+ memset(&vendor_class, 0, sizeof(vendor_class));
+
+ if (find_bound_string(&vendor_class, lease->scope,
+ "vendor-class-identifier")) {
+ if (!add_option(options,
+ DHO_VENDOR_CLASS_IDENTIFIER,
+ (void *)vendor_class.data,
+ vendor_class.len)) {
+ option_state_dereference(&options,
+ MDL);
+ lease_dereference(&lease, MDL);
+ log_error("%s: error adding vendor "
+ "class identifier, no reply "
+ "sent", msgbuf);
+ data_string_forget(&vendor_class, MDL);
+ return;
+ }
+ data_string_forget(&vendor_class, MDL);
+ }
+ }
/*
* Set the relay agent info.
+ *
+ * Note that because agent info is appended without regard
+ * to the PRL in cons_options(), this will be sent as the
+ * last option in the packet whether it is listed on PRL or
+ * not.
*/
if (lease->agent_options != NULL) {
/*
* Figure out which address to use to send from.
*/
- get_server_source_address(&siaddr, options, packet);
+ get_server_source_address(&siaddr, options, options, packet);
/*
* Set up the option buffer.
#endif
memset(to.sin_zero, 0, sizeof(to.sin_zero));
+#if defined(RELAY_PORT)
+ relay_port = dhcp_check_relayport(packet);
+#endif
+
/*
* Leasequery packets are be sent to the gateway address.
*/
to.sin_addr = packet->raw->giaddr;
if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
+#if defined(RELAY_PORT)
+ to.sin_port = relay_port ? relay_port : local_port;
+#else
to.sin_port = local_port;
+#endif
} else {
to.sin_port = remote_port; /* XXXSK: For debugging. */
}
*
* TODO: RFC5007 ORO in query-options.
*
- * TODO: RFC5007 not default preferred and valid time.
- *
- * TODO: RFC5007 not zero Client Last Transaction Time (clt-time).
- *
* TODO: RFC5007 lq-relay-data.
*
* TODO: RFC5007 lq-client-link.
* Verify our lq_query structure is empty.
*/
if ((lq_query->data != NULL) || (lq_query->len != 0)) {
- return ISC_R_INVALIDARG;
+ return DHCP_R_INVALIDARG;
}
oc = lookup_option(&dhcpv6_universe, packet->options, D6O_LQ_QUERY);
exit:
if (!ret_val) {
- if (lq->client_id.len > 0) {
- data_string_forget(&lq->client_id, MDL);
- }
- if (lq->server_id.len > 0) {
- data_string_forget(&lq->server_id, MDL);
- }
- if (lq->lq_query.len > 0) {
- data_string_forget(&lq->lq_query, MDL);
- }
+ data_string_forget(&lq->client_id, MDL);
+ data_string_forget(&lq->server_id, MDL);
+ data_string_forget(&lq->lq_query, MDL);
}
return ret_val;
}
struct ipv6_pool *pool = NULL;
struct data_string data;
struct in6_addr addr;
- struct iaaddr *iaaddr = NULL;
+ struct iasubopt *iaaddr = NULL;
struct option_state *opt_state = NULL;
u_int32_t lifetime;
unsigned opt_cursor;
* or the ia-aadr when it is :: but in any case the ia-addr has
* to be on the link, so we ignore the link-address here.
*/
- if (find_ipv6_pool(&pool, &addr) != ISC_R_SUCCESS) {
+ if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
if (!set_error(lq, STATUS_NotConfigured,
"Address not in a pool.")) {
log_error("process_lq_by_address: unable "
ret_val = 1;
goto exit;
}
- if (iaaddr_hash_lookup(&iaaddr, pool->addrs, &addr,
- sizeof(addr), MDL) == 0) {
+ if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr,
+ sizeof(addr), MDL) == 0) {
ret_val = 1;
goto exit;
}
if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) ||
- (iaaddr->ia_na == NULL) ||
- (iaaddr->ia_na->iaid_duid.len <= 4)) {
+ (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) {
ret_val = 1;
goto exit;
}
goto exit;
}
- data_string_copy(&data, &iaaddr->ia_na->iaid_duid, MDL);
+ data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL);
data.data += 4;
data.len -= 4;
if (!save_option_buffer(&dhcpv6_universe, opt_state,
}
data.data = data.buffer->data;
memcpy(data.buffer->data, &iaaddr->addr, 16);
- lifetime = DEFAULT_DEFAULT_LEASE_TIME;
- lifetime = (lifetime / 2) + (lifetime / 8);
+ lifetime = iaaddr->prefer;
putULong(data.buffer->data + 16, lifetime);
- lifetime = DEFAULT_DEFAULT_LEASE_TIME;
+ lifetime = iaaddr->valid;
putULong(data.buffer->data + 20, lifetime);
if (!save_option_buffer(&dhcpv6_universe, opt_state,
NULL, (unsigned char *)data.data, data.len,
}
data_string_forget(&data, MDL);
- lifetime = 0;
+ lifetime = htonl(iaaddr->ia->cltt);
if (!save_option_buffer(&dhcpv6_universe, opt_state,
NULL, (unsigned char *)&lifetime, 4,
D6O_CLT_TIME, 0)) {
if (pool != NULL)
ipv6_pool_dereference(&pool, MDL);
if (iaaddr != NULL)
- iaaddr_dereference(&iaaddr, MDL);
+ iasubopt_dereference(&iaaddr, MDL);
if (opt_state != NULL)
option_state_dereference(&opt_state, MDL);
return ret_val;
void
dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
static struct lq6_state lq;
- struct data_string server_duid;
struct option_cache *oc;
int allow_lq;
}
execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
lq.packet->options, lq.reply_opts,
- &global_scope, root_group, NULL);
+ &global_scope, root_group, NULL, NULL);
lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;