DHCP Protocol engine. */
/*
- * Copyright (c) 2004-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2019 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
- * 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
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
+ * PO Box 360
+ * Newmarket, NH 03857 USA
* <info@isc.org>
* https://www.isc.org/
*
#include <limits.h>
#include <sys/time.h>
-static void commit_leases_ackout(void *foo);
static void maybe_return_agent_options(struct packet *packet,
struct option_state *options);
+static int reuse_lease (struct packet* packet, struct lease* new_lease,
+ struct lease* lease, struct lease_state *state,
+ int offer, int* same_client);
+
+static int do_ping_check(struct packet* packet, struct lease_state* state,
+ struct lease* lease, TIME original_cltt,
+ int same_client);
+
+#if defined(DHCPv6) && defined(DHCP4o6)
+static int locate_network6(struct packet *packet);
+#endif
+
int outstanding_pings;
+#if defined(DELAYED_ACK)
+static void delayed_ack_enqueue(struct lease *);
+static void delayed_acks_timer(void *);
+
+
struct leasequeue *ackqueue_head, *ackqueue_tail;
static struct leasequeue *free_ackqueue;
static struct timeval max_fsync;
int max_ack_delay_secs = DEFAULT_ACK_DELAY_SECS;
int max_ack_delay_usecs = DEFAULT_ACK_DELAY_USECS;
int min_ack_delay_usecs = DEFAULT_MIN_ACK_DELAY_USECS;
+#endif
static char dhcp_message [256];
static int site_code_min;
static int find_min_site_code(struct universe *);
static isc_result_t lowest_site_code(const void *, unsigned, void *);
-static const char *dhcp_type_names [] = {
+static const char *dhcp_type_names [] = {
"DHCPDISCOVER",
"DHCPOFFER",
"DHCPREQUEST",
# define send_packet trace_packet_send
#endif
+static TIME leaseTimeCheck(TIME calculated, TIME alternate);
+
void
dhcp (struct packet *packet) {
int ms_nulltp = 0;
if (!locate_network(packet) &&
packet->packet_type != DHCPREQUEST &&
- packet->packet_type != DHCPINFORM &&
+ packet->packet_type != DHCPINFORM &&
packet->packet_type != DHCPLEASEQUERY) {
const char *s;
char typebuf[32];
errmsg = "unknown network segment";
bad_packet:
-
+
if (packet->packet_type > 0 &&
packet->packet_type <= dhcp_type_name_max) {
s = dhcp_type_names[packet->packet_type - 1];
sprintf(typebuf, "type %d", packet->packet_type);
s = typebuf;
}
-
+
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ log_info("DHCP4o6 %s from %s via %s: %s", s,
+ (packet->raw->htype
+ ? print_hw_addr(packet->raw->htype,
+ packet->raw->hlen,
+ packet->raw->chaddr)
+ : "<no identifier>"),
+ piaddr(packet->client_addr),
+ errmsg);
+ goto out;
+ }
+#endif
+
log_info("%s from %s via %s: %s", s,
(packet->raw->htype
? print_hw_addr(packet->raw->htype,
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPDISCOVER from %s %s%s%svia %s",
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(packet->client_addr));
+ } else
+#endif
snprintf (msgbuf, sizeof msgbuf, "DHCPDISCOVER from %s %s%s%svia %s",
(packet -> raw -> htype
? print_hw_addr (packet -> raw -> htype,
/* Sourceless packets don't make sense here. */
if (!packet -> shared_network) {
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ log_info ("DHCP4o6 packet from unknown subnet: %s",
+ piaddr(packet->client_addr));
+ } else
+#endif
log_info ("Packet from unknown subnet: %s",
inet_ntoa (packet -> raw -> giaddr));
goto out;
/* If we didn't find a lease, try to allocate one... */
if (!lease) {
if (!allocate_lease (&lease, packet,
- packet -> shared_network -> pools,
+ packet -> shared_network -> pools,
&peer_has_leases)) {
if (peer_has_leases)
log_error ("%s: peer holds all free leases",
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPREQUEST for %s%s from %s %s%s%svia %s",
+ piaddr (cip), smbuf,
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(packet->client_addr));
+ } else
+#endif
snprintf (msgbuf, sizeof msgbuf,
"DHCPREQUEST for %s%s from %s %s%s%svia %s",
piaddr (cip), smbuf,
goto out;
}
-#if defined(SERVER_ID_CHECK)
- /* Do a quick check on the server source address to see if
- it is ours. sip is the incoming servrer id. To avoid
- problems with confused clients we do some sanity checks
- to verify sip's length and that it isn't all zeros.
- We then get the server id we would likely use for this
- packet and compare them. If they don't match it we assume
- we didn't send the offer and so we don't process the request.
- */
-
- if ((sip.len == 4) &&
+ /* If server-id-check is enabled, verify that the client's
+ * server source address (sip from incoming packet) is ours.
+ * To avoid problems with confused clients we do some sanity
+ * checks to verify sip's length and that it isn't all zeros.
+ * We then get the server id we would likely use for this
+ * packet and compare them. If they don't match it we assume
+ * we didn't send the offer and so we don't process the
+ * request. */
+ if ((server_id_check == 1) && (sip.len == 4) &&
(memcmp(sip.iabuf, "\0\0\0\0", sip.len) != 0)) {
struct in_addr from;
struct option_state *eval_options = NULL;
eval_network_statements(&eval_options, packet, NULL);
- get_server_source_address(&from, eval_options, NULL,
- packet);
+ get_server_source_address(&from, eval_options,
+ NULL, packet);
option_state_dereference (&eval_options, MDL);
if (memcmp(sip.iabuf, &from, sip.len) != 0) {
log_debug("%s: not our server id", msgbuf);
goto out;
}
}
-#endif /* if defined(SERVER_ID_CHECK) */
/* At this point it's possible that we will get a broadcast
DHCPREQUEST for a lease that we didn't offer, because
(packet -> raw -> ciaddr.s_addr &&
packet -> raw -> giaddr.s_addr) ||
(have_requested_addr && !packet -> raw -> ciaddr.s_addr)) {
-
+
/* If we don't know where it came from but we do know
where it claims to have come from, it didn't come
from there. */
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPRELEASE of %s from %s %s%s%svia "
+ "%s (%sfound)",
+ cstr,
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(packet->client_addr),
+ lease ? "" : "not ");
+ } else
+#endif
snprintf (msgbuf, sizeof msgbuf,
"DHCPRELEASE of %s from %s %s%s%svia %s (%sfound)",
cstr,
/* If we found a lease, release it. */
if (lease && lease -> ends > cur_time) {
release_lease (lease, packet);
- }
+ }
log_info ("%s", msgbuf);
#if defined(FAILOVER_PROTOCOL)
out:
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPDECLINE of %s from %s %s%s%svia %s",
+ piaddr (cip),
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(packet->client_addr));
+ } else
+#endif
snprintf (msgbuf, sizeof msgbuf,
"DHCPDECLINE of %s from %s %s%s%svia %s",
piaddr (cip),
lease_dereference (&lease, MDL);
}
+#if defined(RELAY_PORT)
+u_int16_t dhcp_check_relayport(packet)
+ struct packet *packet;
+{
+ if (lookup_option(&agent_universe,
+ packet->options,
+ RAI_RELAY_PORT) != NULL) {
+ return (packet->client_port);
+ }
+
+ return (0);
+}
+#endif
+
void dhcpinform (packet, ms_nulltp)
struct packet *packet;
int ms_nulltp;
struct interface_info *interface;
int result, h_m_client_ip = 0;
struct host_decl *host = NULL, *hp = NULL, *h;
+#if defined(RELAY_PORT)
+ u_int16_t relay_port = 0;
+#endif
#if defined (DEBUG_INFORM_HOST)
int h_w_fixed_addr = 0;
#endif
source address if they didn't set ciaddr. */
if (!packet->raw->ciaddr.s_addr) {
zeroed_ciaddr = ISC_TRUE;
- cip.len = 4;
- memcpy(cip.iabuf, &packet->client_addr.iabuf, 4);
- addr_type = "source";
+ /* With DHCPv4-over-DHCPv6 it can be an IPv6 address
+ so we check its length. */
+ if (packet->client_addr.len == 4) {
+ cip.len = 4;
+ memcpy(cip.iabuf, &packet->client_addr.iabuf, 4);
+ addr_type = "source";
+ } else {
+ cip.len = 0;
+ memset(cip.iabuf, 0, 4);
+ addr_type = "v4o6";
+ }
} else {
zeroed_ciaddr = ISC_FALSE;
cip.len = 4;
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ snprintf(msgbuf, sizeof(msgbuf),
+ "DHCP4o6 DHCPINFORM from %s via %s",
+ piaddr(cip),
+ piaddr(packet->client_addr));
+ } else
+#endif
snprintf(msgbuf, sizeof(msgbuf), "DHCPINFORM from %s via %s",
piaddr(cip),
packet->raw->giaddr.s_addr ?
return;
}
- /* Find the subnet that the client is on.
+#if defined(RELAY_PORT)
+ relay_port = dhcp_check_relayport(packet);
+#endif
+
+ /* Find the subnet that the client is on.
* CC: Do the link selection / subnet selection
*/
if (d1.len != 4) {
log_info("%s: ignored (invalid subnet selection option).", msgbuf);
option_state_dereference(&options, MDL);
+ data_string_forget(&d1, MDL);
return;
}
option_state_dereference(&options, MDL);
return;
}
-
+
memset(&outgoing, 0, sizeof outgoing);
memset(&raw, 0, sizeof raw);
outgoing.raw = &raw;
maybe_return_agent_options(packet, options);
- /* Execute statements in scope starting with the subnet scope. */
+ /* Execute statements network statements starting at the subnet level */
execute_statements_in_scope(NULL, packet, NULL, NULL,
packet->options, options,
&global_scope, subnet->group,
NULL, NULL);
-
+
+ /* If we have ciaddr, find its lease so we can find its pool. */
+ if (zeroed_ciaddr == ISC_FALSE) {
+ struct lease* cip_lease = NULL;
+
+ find_lease_by_ip_addr (&cip_lease, cip, MDL);
+
+ /* Overlay with pool options if ciaddr mapped to a lease. */
+ if (cip_lease) {
+ if (cip_lease->pool && cip_lease->pool->group) {
+ execute_statements_in_scope(
+ NULL, packet, NULL, NULL,
+ packet->options, options,
+ &global_scope,
+ cip_lease->pool->group,
+ cip_lease->pool->shared_network->group,
+ NULL);
+ }
+
+ lease_dereference (&cip_lease, MDL);
+ }
+ }
+
/* Execute statements in the class scopes. */
for (i = packet->class_count; i > 0; i--) {
execute_statements_in_scope(NULL, packet, NULL, NULL,
}
/*
- * Process host declarations during DHCPINFORM,
+ * Process host declarations during DHCPINFORM,
* Try to find a matching host declaration by cli ID or HW addr.
*
* Look through the host decls for one that matches the
if (hp)
host_dereference (&hp, MDL);
}
-
+
#if defined (DEBUG_INFORM_HOST)
/* Hmm..: what when there is a host with a fixed-address,
* that matches by hw or id, but the fixed-addresses
execute_statements_in_scope(NULL, packet, NULL, NULL,
packet->options, options,
&global_scope, host->group,
- host->group ?
- host->group->next : NULL,
+ subnet->group,
NULL);
host_dereference (&host, MDL);
}
/* CC: end of host entry processing.... */
-
+
/* Figure out the filename. */
memset (&d1, 0, sizeof d1);
oc = lookup_option (&server_universe, options, SV_FILENAME);
i = d1.len;
if (i >= sizeof(raw.file)) {
log_info("file name longer than packet field "
- "truncated - field: %lu name: %d %.*s",
+ "truncated - field: %lu name: %d %.*s",
(unsigned long)sizeof(raw.file), i,
(int)i, d1.data);
i = sizeof(raw.file);
i = d1.len;
if (i >= sizeof(raw.sname)) {
log_info("server name longer than packet field "
- "truncated - field: %lu name: %d %.*s",
+ "truncated - field: %lu name: %d %.*s",
(unsigned long)sizeof(raw.sname), i,
(int)i, d1.data);
i = sizeof(raw.sname);
option_state_dereference (&options, MDL);
if (subnet)
subnet_dereference (&subnet, MDL);
+ data_string_forget (&d1, MDL);
return;
}
dump_raw ((unsigned char *)&raw, outgoing.packet_length);
#endif
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ /* Report what we're sending. */
+ snprintf(msgbuf, sizeof msgbuf,
+ "DHCP4o6 DHCPACK to %s (%s) via", piaddr(cip),
+ (packet->raw->htype && packet->raw->hlen) ?
+ print_hw_addr(packet->raw->htype, packet->raw->hlen,
+ packet->raw->chaddr) :
+ "<no client hardware address>");
+ log_info("%s %s", msgbuf, piaddr(packet->client_addr));
+
+ /* fill dhcp4o6_response */
+ packet->dhcp4o6_response->len = outgoing.packet_length;
+ packet->dhcp4o6_response->buffer = NULL;
+ if (!buffer_allocate(&packet->dhcp4o6_response->buffer,
+ outgoing.packet_length, MDL)) {
+ log_fatal("No memory to store DHCP4o6 reply.");
+ }
+ packet->dhcp4o6_response->data =
+ packet->dhcp4o6_response->buffer->data;
+ memcpy(packet->dhcp4o6_response->buffer->data,
+ outgoing.raw, outgoing.packet_length);
+
+ /* done */
+ if (subnet)
+ subnet_dereference (&subnet, MDL);
+ return;
+ }
+#endif
+
/* Set up the common stuff... */
to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
#endif
memset (to.sin_zero, 0, sizeof to.sin_zero);
- /* RFC2131 states the server SHOULD unciast to ciaddr.
+ /* RFC2131 states the server SHOULD unicast to ciaddr.
* There are two wrinkles - relays, and when ciaddr is zero.
* There's actually no mention of relays at all in rfc2131 in
* regard to DHCPINFORM, except to say we might get packets from
*/
if (!raw.ciaddr.s_addr && gip.len) {
memcpy(&to.sin_addr, gip.iabuf, 4);
+#if defined(RELAY_PORT)
+ to.sin_port = relay_port ? relay_port : local_port;
+#else
to.sin_port = local_port;
+#endif
raw.flags |= htons(BOOTP_BROADCAST);
} else {
gip.len = 0;
unsigned char nak = DHCPNAK;
struct packet outgoing;
unsigned i;
+#if defined(RELAY_PORT)
+ u_int16_t relay_port = 0;
+#endif
struct option_state *options = (struct option_state *)0;
struct option_cache *oc = (struct option_cache *)0;
struct option_state *eval_options = NULL;
&i, 0, MDL);
save_option (&dhcp_universe, options, oc);
option_cache_dereference (&oc, MDL);
-
+
+#if defined(RELAY_PORT)
+ relay_port = dhcp_check_relayport(packet);
+#endif
+
/* Set DHCP_MESSAGE to whatever the message is */
if (!option_cache_allocate (&oc, MDL)) {
log_error ("No memory for DHCPNAK message type.");
option_state_dereference (&options, MDL);
/* memset (&raw.ciaddr, 0, sizeof raw.ciaddr);*/
- if (packet->interface->address_count)
- raw.siaddr = packet->interface->addresses[0];
raw.giaddr = packet -> raw -> giaddr;
memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
raw.hlen = packet -> raw -> hlen;
raw.hops = packet -> raw -> hops;
raw.op = BOOTREPLY;
+ /* Make sure that the packet is at least as big as a BOOTP packet. */
+ if (outgoing.packet_length < BOOTP_MIN_LEN)
+ outgoing.packet_length = BOOTP_MIN_LEN;
+
/* Report what we're sending... */
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ log_info ("DHCP4o6 DHCPNAK on %s to %s via %s",
+ piaddr (*cip),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ piaddr(packet->client_addr));
+ } else
+#endif
log_info ("DHCPNAK on %s to %s via %s",
piaddr (*cip),
print_hw_addr (packet -> raw -> htype,
dump_raw ((unsigned char *)&raw, outgoing.packet_length);
#endif
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ /* fill dhcp4o6_response */
+ packet->dhcp4o6_response->len = outgoing.packet_length;
+ packet->dhcp4o6_response->buffer = NULL;
+ if (!buffer_allocate(&packet->dhcp4o6_response->buffer,
+ outgoing.packet_length, MDL)) {
+ log_fatal("No memory to store DHCP4o6 reply.");
+ }
+ packet->dhcp4o6_response->data =
+ packet->dhcp4o6_response->buffer->data;
+ memcpy(packet->dhcp4o6_response->buffer->data,
+ outgoing.raw, outgoing.packet_length);
+ return;
+ }
+#endif
+
/* Set up the common stuff... */
to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
#endif
memset (to.sin_zero, 0, sizeof to.sin_zero);
- /* Make sure that the packet is at least as big as a BOOTP packet. */
- if (outgoing.packet_length < BOOTP_MIN_LEN)
- outgoing.packet_length = BOOTP_MIN_LEN;
-
/* If this was gatewayed, send it back to the gateway.
Otherwise, broadcast it on the local network. */
if (raw.giaddr.s_addr) {
to.sin_addr = raw.giaddr;
if (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; /* for testing. */
TIME default_lease_time;
struct option_cache *oc;
isc_result_t result;
- TIME ping_timeout;
- TIME lease_cltt;
+ TIME original_cltt;
struct in_addr from;
TIME remaining_time;
struct iaddr cip;
isc_boolean_t enqueue = ISC_FALSE;
#endif
int use_old_lease = 0;
+ int same_client = 0;
unsigned i, j;
int s1;
int ignorep;
- struct timeval tv;
/* If we're already acking this lease, don't do it again. */
if (lease -> state)
return;
/* Save original cltt for comparison later. */
- lease_cltt = lease->cltt;
+ original_cltt = lease->cltt;
/* If the lease carries a host record, remember it. */
if (hp)
} while (1);
}
}
-
+
/* Make sure this packet satisfies the configured minimum
number of seconds. */
if (host)
host_dereference (&host, MDL);
return;
- }
+ }
/* Drop the request if it's not allowed for this client. */
if (!offer &&
if (host)
host_dereference (&host, MDL);
return;
- }
+ }
/* Drop the request if booting is specifically denied. */
oc = lookup_option (&server_universe, state -> options,
if (packet -> classes [i] ==
lease -> billing_class)
break;
- if (i == packet -> class_count)
- unbill_class (lease, lease -> billing_class);
+ if (i == packet -> class_count) {
+ unbill_class(lease);
+ /* Active lease billing change negates reuse */
+ if (lease->binding_state == FTS_ACTIVE) {
+ lease->cannot_reuse = 1;
+ }
+ }
}
/* If we don't have an active billing, see if we need
int bill = 0;
for (i = 0; i < packet->class_count; i++) {
- struct class *billclass, *subclass;
+ struct class *billclass, *superclass;
billclass = packet->classes[i];
if (billclass->lease_limit) {
if (bill_class(lease, billclass))
break;
- subclass = billclass->superclass;
- if (subclass == NULL)
- cname = subclass->name;
+ superclass = billclass->superclass;
+ if (superclass != NULL)
+ cname = superclass->name;
else
cname = billclass->name;
}
if (offer == DHCPOFFER &&
lease->billing_class != NULL &&
lease->binding_state != FTS_ACTIVE)
- unbill_class(lease, lease->billing_class);
+ unbill_class(lease);
+
+ /* Lease billing change negates reuse */
+ if (lease->billing_class != NULL) {
+ lease->cannot_reuse = 1;
+ }
}
}
host_dereference (&host, MDL);
return;
}
-
+
/* Use the ip address of the lease that we finally found in
the database. */
lt -> ip_addr = lease -> ip_addr;
if (lease_time < 0 /* XXX */
|| lease_time > max_lease_time)
lease_time = max_lease_time;
-
+
min_lease_time = DEFAULT_MIN_LEASE_TIME;
if (min_lease_time > max_lease_time)
min_lease_time = max_lease_time;
check_pool_threshold(packet, lease, state);
/* a client requests an address which is not yet active*/
- if (lease->pool && lease->pool->valid_from &&
+ if (lease->pool && lease->pool->valid_from &&
cur_time < lease->pool->valid_from) {
/* NAK leases before pool activation date */
cip.len = 4;
if (host)
host_dereference (&host, MDL);
return;
-
+
}
/* CC:
A min-lease-time of 6 seconds effectively switches over
all clients in this pool very quickly.
*/
-
+
if (lease->pool && lease->pool->valid_until) {
if (cur_time >= lease->pool->valid_until) {
/* NAK leases after pool expiration date */
if (lease_time > remaining_time)
lease_time = remaining_time;
}
-
+
if (lease_time < min_lease_time) {
if (min_lease_time)
lease_time = min_lease_time;
* the desired lease time upon renewal.
*/
if (offer == DHCPACK) {
- lt->tstp = cur_time + lease_time +
- (new_lease_time / 2);
+ if (lease_time == INFINITE_TIME) {
+ lt->tstp = MAX_TIME;
+ } else {
+ lt->tstp =
+ leaseTimeCheck(
+ (cur_time + lease_time
+ + (new_lease_time / 2)),
+ MAX_TIME - 1);
+ }
/* If we reduced the potential expiry time,
* make sure we don't offer an old-expiry-time
}
#endif /* FAILOVER_PROTOCOL */
- /* If the lease duration causes the time value to wrap,
- use the maximum expiry time. */
- if (cur_time + lease_time < cur_time)
- state -> offered_expiry = MAX_TIME - 1;
- else
- state -> offered_expiry = cur_time + lease_time;
+ if (lease_time == INFINITE_TIME) {
+ state->offered_expiry = MAX_TIME;
+ } else {
+ /* If the lease duration causes the time value to wrap,
+ use the maximum expiry time. */
+ state->offered_expiry
+ = leaseTimeCheck(cur_time + lease_time,
+ MAX_TIME - 1);
+ }
+
if (when)
lt -> ends = when;
else
!evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
packet->options, state->options,
&lease->scope, oc, MDL)) {
-
+
/* Record the uid, if given... */
oc = lookup_option (&dhcp_universe, packet -> options,
DHO_DHCP_CLIENT_IDENTIFIER);
DHO_VENDOR_CLASS_IDENTIFIER);
if (oc != NULL &&
evaluate_option_cache(&d1, packet, NULL, NULL, packet->options,
- NULL, &lease->scope, oc, MDL)) {
+ NULL, <->scope, oc, MDL)) {
if (d1.len != 0) {
- bind_ds_value(&lease->scope, "vendor-class-identifier",
+ bind_ds_value(<->scope, "vendor-class-identifier",
&d1);
}
lt -> client_hostname [d1.len] = 0;
}
data_string_forget (&d1, MDL);
+ /* hostname changed, can't reuse lease */
+ lease->cannot_reuse = 1;
}
/* Record the hardware address, if given... */
memcpy (< -> hardware_addr.hbuf [1], packet -> raw -> chaddr,
sizeof packet -> raw -> chaddr);
- lt -> flags = lease -> flags & ~PERSISTENT_FLAGS;
+ /*
+ * If client has requested the lease become infinite, then it
+ * doens't qualify for reuse even if it's younger than the
+ * dhcp-cache-threshold.
+ */
+ if ((lt->flags & RESERVED_LEASE) && !(lease->flags & RESERVED_LEASE)) {
+ log_debug ("Cannot reuse: lease is changing to RESERVED");
+ lease->cannot_reuse = 1;
+ }
+
+ lt->flags |= lease->flags & ~PERSISTENT_FLAGS;
/* If there are statements to execute when the lease is
committed, execute them. */
sizeof packet -> raw -> chaddr); /* XXX */
} else {
int commit = (!offer || (offer == DHCPACK));
- int thresh = DEFAULT_CACHE_THRESHOLD;
- /*
- * Check if the lease was issued recently, if so replay the
- * current lease and do not require a database sync event.
- * Recently is defined as being issued less than a given
- * percentage of the lease previously. The percentage can be
- * chosen either from a default value or via configuration.
- *
- */
- if ((oc = lookup_option(&server_universe, state->options,
- SV_CACHE_THRESHOLD)) &&
- evaluate_option_cache(&d1, packet, lt, NULL,
- packet->options, state->options,
- <->scope, oc, MDL)) {
- if (d1.len == 1 && (d1.data[0] < 100))
- thresh = d1.data[0];
-
- data_string_forget(&d1, MDL);
- }
-
- /*
- * We check on ddns_cb to see if the ddns code has
- * updated the lt structure. We could probably simply
- * copy the ddns_cb pointer in that case but lets be
- * simple and safe and update the entire lease.
- */
- if ((lt->ddns_cb == NULL) &&
- (thresh > 0) && (offer == DHCPACK) &&
- (lease->binding_state == FTS_ACTIVE)) {
- int limit;
- int prev_lease = lease->ends - lease->starts;
-
- /* it is better to avoid division by 0 */
- if (prev_lease <= (INT_MAX / thresh))
- limit = prev_lease * thresh / 100;
- else
- limit = prev_lease / 100 * thresh;
-
- if ((lt->starts - lease->starts) <= limit) {
- lt->starts = lease->starts;
- state->offered_expiry = lt->ends = lease->ends;
- commit = 0;
- use_old_lease = 1;
- }
+ /* If dhcp-cache-threshold is enabled, see if "lease" can
+ * be reused. */
+ use_old_lease = reuse_lease(packet, lt, lease, state, offer,
+ &same_client);
+ if (use_old_lease == 1) {
+ commit = 0;
}
#if !defined(DELAYED_ACK)
*/
if ((use_old_lease == 0) &&
!supersede_lease(lease, lt, commit,
- offer == DHCPACK, offer == DHCPACK)) {
+ offer == DHCPACK, offer == DHCPACK, 0)) {
#else /* defined(DELAYED_ACK) */
/*
* If there already isn't a need for a lease commit, and we
*/
if ((use_old_lease == 0) &&
!supersede_lease(lease, lt, 0,
- !offer || offer == DHCPACK, 0)) {
+ !offer || offer == DHCPACK, 0, 0)) {
#endif
log_info ("%s: database update failed", msg);
free_lease_state (state, MDL);
}
/* Use the name of the host declaration if there is one
- and no hostname has otherwise been provided, and if the
+ and no hostname has otherwise been provided, and if the
use-host-decl-name flag is set. */
use_host_decl_name(packet, lease, state->options);
lookup_option (&server_universe, state->options, j), MDL)) {
struct in_addr ia;
struct hostent *h;
-
+
memcpy (&ia, lease -> ip_addr.iabuf, 4);
-
+
h = gethostbyaddr ((char *)&ia, sizeof ia, AF_INET);
if (!h)
log_error ("No hostname for %s", inet_ntoa (ia));
save_option (&dhcp_universe,
state -> options, oc);
}
- option_cache_dereference (&oc, MDL);
+ option_cache_dereference (&oc, MDL);
}
}
}
(const char *)d1.data, d1.len,
MDL)) {
log_error ("unknown option space %s.", d1.data);
+ data_string_forget (&d1, MDL);
return;
}
/* Hang the packet off the lease state. */
packet_reference (&lease -> state -> packet, packet, MDL);
- /* If this is a DHCPOFFER, ping the lease address before actually
- sending the offer. */
- if (offer == DHCPOFFER && !(lease -> flags & STATIC_LEASE) &&
- ((cur_time - lease_cltt) > 60) &&
- (!(oc = lookup_option (&server_universe, state -> options,
- SV_PING_CHECKS)) ||
- evaluate_boolean_option_cache (&ignorep, packet, lease,
- (struct client_state *)0,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL))) {
- icmp_echorequest (&lease -> ip_addr);
-
- /* Determine whether to use configured or default ping timeout.
- */
- if ((oc = lookup_option (&server_universe, state -> options,
- SV_PING_TIMEOUT)) &&
- evaluate_option_cache (&d1, packet, lease, NULL,
- packet -> options,
- state -> options,
- &lease -> scope, oc, MDL)) {
- if (d1.len == sizeof (u_int32_t))
- ping_timeout = getULong (d1.data);
- else
- ping_timeout = DEFAULT_PING_TIMEOUT;
-
- data_string_forget (&d1, MDL);
- } else
- ping_timeout = DEFAULT_PING_TIMEOUT;
-
-#ifdef DEBUG
- log_debug ("Ping timeout: %ld", (long)ping_timeout);
-#endif
-
- /*
- * Set a timeout for 'ping-timeout' seconds from NOW, including
- * current microseconds. As ping-timeout defaults to 1, the
- * exclusion of current microseconds causes a value somewhere
- * /between/ zero and one.
- */
- tv.tv_sec = cur_tv.tv_sec + ping_timeout;
- tv.tv_usec = cur_tv.tv_usec;
- add_timeout (&tv, lease_ping_timeout, lease,
- (tvref_t)lease_reference,
- (tvunref_t)lease_dereference);
+ /* If this is a DHCPOFFER, send a ping (if appropriate) to the
+ * lease address before actually we send the offer. */
+ if ((offer == DHCPOFFER) &&
+ do_ping_check(packet, state, lease, original_cltt, same_client)) {
++outstanding_pings;
} else {
lease->cltt = cur_time;
#if defined(DELAYED_ACK)
if (enqueue)
delayed_ack_enqueue(lease);
- else
+ else
#endif
dhcp_reply(lease);
}
}
+/*
+ * \brief Sends a ping to the lease ip_addr when appropriate
+ *
+ * A ping will be sent if all of the following are true:
+ *
+ * 1. Ping checks are enabled
+ * 2. The lease is neither active nor static
+ * 3. Any of the following is true:
+ * a. The lease state is ABANDONED
+ * b. This is the first offer of this lease (CLTT = 0)
+ * c. The lease is being offered to a client other than its previous
+ * owner
+ * d. The lease is being offered to its previous owner and more than
+ * cltt-secs have elapsed since CLTT of the original lease.
+ *
+ * \param packet inbound packet received from the client
+ * \param state lease options state
+ * \param lease lease to be offered (if one)
+ * \param original_cltt CLTT of the original lease
+ * \param same_client flag indicating if the client to be offered the
+ * lease is its previous owner
+ * \return Returns 1 if ping has been sent, 0 otherwise
+ */
+int do_ping_check(struct packet* packet, struct lease_state* state,
+ struct lease* lease, TIME original_cltt,
+ int same_client) {
+ TIME ping_timeout = DEFAULT_PING_TIMEOUT;
+ TIME ping_timeout_ms = DEFAULT_PING_TIMEOUT_MS;
+ struct option_cache *oc = NULL;
+ struct data_string ds;
+ struct timeval tv;
+ int ignorep;
+ int timeout_secs;
+ int timeout_ms;
+
+ // Don't go any further if lease is active or static.
+ if (lease->binding_state == FTS_ACTIVE || lease->flags & STATIC_LEASE) {
+ return (0);
+ }
+
+ // If pings aren't enabled, punt.
+ oc = lookup_option (&server_universe, state -> options, SV_PING_CHECKS);
+ if (oc &&
+ !(evaluate_boolean_option_cache (&ignorep, packet, lease,
+ 0, packet->options, state->options,
+ &lease->scope, oc, MDL))) {
+ return (0);
+ }
+
+ // If it's not the first time for the same client and not an
+ // abandoned lease, we need to check the cltt threshold
+ if (same_client && original_cltt &&
+ lease->binding_state != FTS_ABANDONED) {
+ TIME cltt_secs = DEFAULT_PING_CLTT_SECS;
+ memset(&ds, 0, sizeof(ds));
+ oc = lookup_option (&server_universe, state->options,
+ SV_PING_CLTT_SECS);
+ if (oc &&
+ (evaluate_option_cache (&ds, packet, lease, 0,
+ packet->options, state->options,
+ &lease->scope, oc, MDL))) {
+ if (ds.len == sizeof (u_int32_t)) {
+ cltt_secs = getULong (ds.data);
+ }
+
+ data_string_forget (&ds, MDL);
+ }
+
+ // Punt if it is too soon.
+ if (cur_time - original_cltt < cltt_secs) {
+ return (0);
+ }
+ }
+
+ // Send the ping.
+ icmp_echorequest (&lease->ip_addr);
+
+ /* Determine whether to use configured or default ping timeout. */
+ memset(&ds, 0, sizeof(ds));
+
+ oc = lookup_option (&server_universe, state->options, SV_PING_TIMEOUT);
+ if (oc &&
+ (evaluate_option_cache (&ds, packet, lease, 0,
+ packet->options, state->options,
+ &lease->scope, oc, MDL))) {
+ if (ds.len == sizeof (u_int32_t)) {
+ ping_timeout = getULong (ds.data);
+ }
+
+ data_string_forget (&ds, MDL);
+ }
+
+ oc = lookup_option (&server_universe, state->options, SV_PING_TIMEOUT_MS);
+ if (oc &&
+ (evaluate_option_cache (&ds, packet, lease, 0,
+ packet->options, state->options,
+ &lease->scope, oc, MDL))) {
+ if (ds.len == sizeof (u_int32_t)) {
+ ping_timeout_ms = getULong (ds.data);
+ }
+
+ data_string_forget (&ds, MDL);
+ }
+
+ /*
+ * Set the timeout for the ping to the current timeval plus
+ * the configured time out. Use ping-timeout-ms if it is > 0.
+ * This overrides ping-timeout allowing users to specify it in
+ * milliseconds.
+ */
+ if (ping_timeout_ms > 0) {
+ timeout_secs = ping_timeout_ms / 1000;
+ timeout_ms = ping_timeout_ms % 1000;
+ } else {
+ timeout_secs = ping_timeout;
+ timeout_ms = 0;
+
+ }
+
+ tv.tv_sec = cur_tv.tv_sec + timeout_secs;
+ tv.tv_usec = cur_tv.tv_usec + (timeout_ms * 1000);
+
+#ifdef DEBUG
+ log_debug ("Pinging:%s, state: %d, same client? %s, "
+ " orig_cltt %s, elasped: %ld, timeout in: %d.%d secs" ,
+ piaddr(lease->ip_addr),
+ lease->binding_state,
+ (same_client ? "y" : "n"),
+ (original_cltt ? print_time(original_cltt) : "0"),
+ (original_cltt ? (long)(cur_time - original_cltt) : 0),
+ timeout_secs, timeout_ms);
+
+#endif
+
+ add_timeout (&tv, lease_ping_timeout, lease, (tvref_t)lease_reference,
+ (tvunref_t)lease_dereference);
+
+ return (1);
+}
+
+
+#if defined(DELAYED_ACK)
+
/*
* CC: queue single ACK:
* - write the lease (but do not fsync it yet)
* but only up to the max timer value.
*/
-void
+static void
delayed_ack_enqueue(struct lease *lease)
{
struct leasequeue *q;
- if (!write_lease(lease))
+ if (!write_lease(lease))
return;
if (free_ackqueue) {
q = free_ackqueue;
lease_reference(&q->lease, lease, MDL);
q->next = ackqueue_head;
ackqueue_head = q;
- if (!ackqueue_tail)
+ if (!ackqueue_tail)
ackqueue_tail = q;
else
q->next->prev = q;
outstanding_acks++;
if (outstanding_acks > max_outstanding_acks) {
- commit_leases();
-
- /* Reset max_fsync and cancel any pending timeout. */
- memset(&max_fsync, 0, sizeof(max_fsync));
- cancel_timeout(commit_leases_ackout, NULL);
+ /* Cancel any pending timeout and call handler directly */
+ cancel_timeout(delayed_acks_timer, NULL);
+ delayed_acks_timer(NULL);
} else {
struct timeval next_fsync;
next_fsync.tv_usec = max_fsync.tv_usec;
}
- add_timeout(&next_fsync, commit_leases_ackout, NULL,
+ add_timeout(&next_fsync, delayed_acks_timer, NULL,
(tvref_t) NULL, (tvunref_t) NULL);
}
}
+/* Processes any delayed acks:
+ * Commits the leases and then for each delayed ack:
+ * - Update the failover peer if we're in failover
+ * - Send the REPLY to the client
+ */
static void
-commit_leases_ackout(void *foo)
+delayed_acks_timer(void *foo)
{
- if (outstanding_acks) {
- commit_leases();
+ struct leasequeue *ack, *p;
+
+ /* Reset max fsync */
+ memset(&max_fsync, 0, sizeof(max_fsync));
- memset(&max_fsync, 0, sizeof(max_fsync));
+ if (!outstanding_acks) {
+ /* Nothing to do, so punt, shouldn't happen? */
+ return;
}
-}
-/* CC: process the delayed ACK responses:
- - send out the ACK packets
- - move the queue slots to the free list
- */
-void
-flush_ackqueue(void *foo)
-{
- struct leasequeue *ack, *p;
+ /* Commit the leases first */
+ commit_leases();
+
+ /* Now process the delayed ACKs
+ - update failover peer
+ - send out the ACK packets
+ - move the queue slots to the free list
+ */
+
/* process from bottom to retain packet order */
- for (ack = ackqueue_tail ; ack ; ack = p) {
+ for (ack = ackqueue_tail ; ack ; ack = p) {
p = ack->prev;
+#if defined(FAILOVER_PROTOCOL)
+ /* If we're in failover we need to send any deferred
+ * bind updates as well as the replies */
+ if (ack->lease->pool) {
+ dhcp_failover_state_t *fpeer;
+
+ fpeer = ack->lease->pool->failover_peer;
+ if (fpeer && fpeer->link_to_peer) {
+ dhcp_failover_send_updates(fpeer);
+ }
+ }
+#endif
+
/* dhcp_reply() requires that the reply state still be valid */
if (ack->lease->state == NULL)
log_error("delayed ack for %s has gone stale",
piaddr(ack->lease->ip_addr));
- else
+ else {
dhcp_reply(ack->lease);
+ }
lease_dereference(&ack->lease, MDL);
ack->next = free_ackqueue;
free_ackqueue = ack;
}
+
ackqueue_head = NULL;
ackqueue_tail = NULL;
outstanding_acks = 0;
relinquish_ackqueue(void)
{
struct leasequeue *q, *n;
-
+
for (q = ackqueue_head ; q ; q = n) {
n = q->next;
dfree(q, MDL);
}
#endif
+#endif /* defined(DELAYED_ACK) */
+
void dhcp_reply (lease)
struct lease *lease;
{
int result;
struct lease_state *state = lease -> state;
int nulltp, bootpp, unicastp = 1;
+#if defined(RELAY_PORT)
+ u_int16_t relay_port = 0;
+#endif
struct data_string d1;
const char *s;
if (sizeof raw.file > state -> filename.len)
memset (&raw.file [state -> filename.len], 0,
(sizeof raw.file) - state -> filename.len);
- else
+ else
log_info("file name longer than packet field "
- "truncated - field: %lu name: %d %.*s",
+ "truncated - field: %lu name: %d %.*s",
(unsigned long)sizeof(raw.file),
state->filename.len, (int)state->filename.len,
state->filename.data);
if (sizeof raw.sname > state -> server_name.len)
memset (&raw.sname [state -> server_name.len], 0,
(sizeof raw.sname) - state -> server_name.len);
- else
+ else
log_info("server name longer than packet field "
- "truncated - field: %lu name: %d %.*s",
+ "truncated - field: %lu name: %d %.*s",
(unsigned long)sizeof(raw.sname),
state->server_name.len,
(int)state->server_name.len,
} else
s = (char *)0;
+ /* Make sure outgoing packets are at least as big
+ as a BOOTP packet. */
+ if (packet_length < BOOTP_MIN_LEN)
+ packet_length = BOOTP_MIN_LEN;
+
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (state->packet->dhcp4o6_response != NULL)) {
+ /* Say what we're doing... */
+ log_info ("DHCP4o6 %s on %s to %s %s%s%svia %s",
+ (state -> offer
+ ? (state -> offer == DHCPACK
+ ? "DHCPACK" : "DHCPOFFER")
+ : "BOOTREPLY"),
+ piaddr (lease -> ip_addr),
+ (lease -> hardware_addr.hlen
+ ? print_hw_addr (lease -> hardware_addr.hbuf [0],
+ lease -> hardware_addr.hlen - 1,
+ &lease -> hardware_addr.hbuf [1])
+ : print_hex_1(lease->uid_len, lease->uid, 60)),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ piaddr(state->packet->client_addr));
+
+ /* fill dhcp4o6_response */
+ state->packet->dhcp4o6_response->len = packet_length;
+ state->packet->dhcp4o6_response->buffer = NULL;
+ if (!buffer_allocate(&state->packet->dhcp4o6_response->buffer,
+ packet_length, MDL)) {
+ log_fatal("No memory to store DHCP4o6 reply.");
+ }
+ state->packet->dhcp4o6_response->data =
+ state->packet->dhcp4o6_response->buffer->data;
+ memcpy(state->packet->dhcp4o6_response->buffer->data,
+ &raw, packet_length);
+
+ /* done */
+ free_lease_state (state, MDL);
+ lease -> state = (struct lease_state *)0;
+
+ return;
+ }
+#endif
+
/* Say what we're doing... */
log_info ("%s on %s to %s %s%s%svia %s",
(state -> offer
? inet_ntoa (state -> giaddr)
: state -> ip -> name));
+#ifdef DEBUG_PACKET
+ dump_raw ((unsigned char *)&raw, packet_length);
+#endif
+
/* Set up the hardware address... */
hto.hlen = lease -> hardware_addr.hlen;
memcpy (hto.hbuf, lease -> hardware_addr.hbuf, hto.hlen);
#endif
memset (to.sin_zero, 0, sizeof to.sin_zero);
-#ifdef DEBUG_PACKET
- dump_raw ((unsigned char *)&raw, packet_length);
+#if defined(RELAY_PORT)
+ relay_port = dhcp_check_relayport(state->packet);
#endif
- /* Make sure outgoing packets are at least as big
- as a BOOTP packet. */
- if (packet_length < BOOTP_MIN_LEN)
- packet_length = BOOTP_MIN_LEN;
-
/* If this was gatewayed, send it back to the gateway... */
if (raw.giaddr.s_addr) {
to.sin_addr = raw.giaddr;
if (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; /* For debugging. */
cip.len = 4;
memcpy (cip.iabuf, d1.data, cip.len);
data_string_forget (&d1, MDL);
- } else
+ } else
cip.len = 0;
}
* preference, so the first one is the best one.
*/
while (uid_lease) {
+ isc_boolean_t do_release = !packet->raw->ciaddr.s_addr;
#if defined (DEBUG_FIND_LEASE)
log_info ("trying next lease matching client id: %s",
piaddr (uid_lease -> ip_addr));
log_info ("wrong network segment: %s",
piaddr (uid_lease -> ip_addr));
#endif
+ /* Allow multiple leases using the same UID
+ on different subnetworks. */
+ do_release = ISC_FALSE;
goto n_uid;
}
if (uid_lease -> n_uid)
lease_reference (&next,
uid_lease -> n_uid, MDL);
- if (!packet -> raw -> ciaddr.s_addr)
+ if (do_release)
release_lease (uid_lease, packet);
lease_dereference (&uid_lease, MDL);
if (next) {
#if defined (DEBUG_FIND_LEASE)
log_info ("not choosing requested address (!).");
#endif
+ lease_dereference (&ip_lease, MDL);
} else {
#if defined (DEBUG_FIND_LEASE)
log_info ("choosing lease on requested address.");
if (lease -> host)
host_dereference (&lease -> host, MDL);
}
- lease_dereference (&ip_lease, MDL);
}
/* If we got a lease that matched the client identifier, we may want
{
struct lease *lease = (struct lease *)0;
struct host_decl *rhp = (struct host_decl *)0;
-
+
if (lease_allocate (&lease, MDL) != ISC_R_SUCCESS)
return 0;
if (host_reference (&rhp, hp, MDL) != ISC_R_SUCCESS) {
int allocate_lease (struct lease **lp, struct packet *packet,
struct pool *pool, int *peer_has_leases)
{
- struct lease *lease = (struct lease *)0;
- struct lease *candl = (struct lease *)0;
+ struct lease *lease = NULL;
+ struct lease *candl = NULL;
for (; pool ; pool = pool -> next) {
if ((pool -> prohibit_list &&
/* Skip to the most expired lease in the pool that is not
* owned by a failover peer. */
if (pool->failover_peer != NULL) {
+ struct lease *peerl = NULL;
if (pool->failover_peer->i_am == primary) {
- candl = pool->free;
+ candl = LEASE_GET_FIRST(pool->free);
/*
* In normal operation, we never want to touch
- * the peer's leases. In partner-down
+ * the peer's leases. In partner-down
* operation, we need to be able to pick up
* the peer's leases after STOS+MCLT.
*/
- if (pool->backup != NULL) {
+ peerl = LEASE_GET_FIRST(pool->backup);
+ if (peerl != NULL) {
if (((candl == NULL) ||
- (candl->ends >
- pool->backup->ends)) &&
- lease_mine_to_reallocate(
- pool->backup)) {
- candl = pool->backup;
+ (candl->ends > peerl->ends)) &&
+ lease_mine_to_reallocate(peerl)) {
+ candl = peerl;
} else {
*peer_has_leases = 1;
}
}
} else {
- candl = pool->backup;
+ candl = LEASE_GET_FIRST(pool->backup);
- if (pool->free != NULL) {
+ peerl = LEASE_GET_FIRST(pool->free);
+ if (peerl != NULL) {
if (((candl == NULL) ||
- (candl->ends >
- pool->free->ends)) &&
- lease_mine_to_reallocate(
- pool->free)) {
- candl = pool->free;
+ (candl->ends > peerl->ends)) &&
+ lease_mine_to_reallocate(peerl)) {
+ candl = peerl;
} else {
*peer_has_leases = 1;
}
}
/* Try abandoned leases as a last resort. */
- if ((candl == NULL) &&
- (pool->abandoned != NULL) &&
- lease_mine_to_reallocate(pool->abandoned))
- candl = pool->abandoned;
+ peerl = LEASE_GET_FIRST(pool->abandoned);
+ if ((candl == NULL) && (peerl != NULL) &&
+ lease_mine_to_reallocate(peerl))
+ candl = peerl;
} else
#endif
{
- if (pool -> free)
- candl = pool -> free;
+ if (LEASE_NOT_EMPTY(pool->free))
+ candl = LEASE_GET_FIRST(pool->free);
else
- candl = pool -> abandoned;
+ candl = LEASE_GET_FIRST(pool->abandoned);
}
/*
* "no free leases" error when the last lease has been
* offered, but it's not exactly broken either.
*/
- if (!candl || (candl -> ends > cur_time))
+ if (!candl ||
+ (candl->binding_state != FTS_ABANDONED &&
+ (candl->ends > cur_time))) {
continue;
+ }
if (!lease) {
lease = candl;
!packet -> packet_type)
return 1;
break;
-
+
case permit_class:
for (i = 0; i < packet -> class_count; i++) {
if (p -> class == packet -> classes [i])
return 0;
}
+#if defined(DHCPv6) && defined(DHCP4o6)
+static int locate_network6 (packet)
+ struct packet *packet;
+{
+ const struct packet *chk_packet;
+ const struct in6_addr *link_addr, *first_link_addr;
+ struct iaddr ia;
+ struct data_string data;
+ struct subnet *subnet = NULL;
+ struct option_cache *oc;
+
+ /* from locate_network() */
+
+ /* See if there's a Relay Agent Link Selection Option, or a
+ * Subnet Selection Option. The Link-Select and Subnet-Select
+ * are formatted and used precisely the same, but we must prefer
+ * the link-select over the subnet-select.
+ * BTW in DHCPv4 over DHCPv6 no cross version relay was specified
+ * so it is unlikely to see a link-select.
+ */
+ if ((oc = lookup_option(&agent_universe, packet->options,
+ RAI_LINK_SELECT)) == NULL)
+ oc = lookup_option(&dhcp_universe, packet->options,
+ DHO_SUBNET_SELECTION);
+
+ /* If there's an option indicating link connection or subnet
+ * selection, and it's valid, use it to figure out the subnet.
+ * If it's not valid, fail.
+ */
+ if (oc) {
+ memset(&data, 0, sizeof data);
+ if (!evaluate_option_cache(&data, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ return (0);
+ }
+ if (data.len == 0) {
+ return (0);
+ }
+ if (data.len != 4) {
+ data_string_forget(&data, MDL);
+ return (0);
+ }
+ ia.len = 4;
+ memcpy(ia.iabuf, data.data, 4);
+ data_string_forget(&data, MDL);
+
+ if (find_subnet(&subnet, ia, MDL)) {
+ shared_network_reference(&packet->shared_network,
+ subnet->shared_network, MDL);
+ subnet_dereference(&subnet, MDL);
+ return (1);
+ }
+ return (0);
+ }
+
+ /* See if there is a giaddr (still unlikely), if there is one
+ * use it to figure out the subnet. If it's not valid, fail.
+ */
+ if (packet->raw->giaddr.s_addr) {
+ ia.len = 4;
+ memcpy(ia.iabuf, &packet->raw->giaddr, 4);
+
+ if (find_subnet(&subnet, ia, MDL)) {
+ shared_network_reference(&packet->shared_network,
+ subnet->shared_network, MDL);
+ subnet_dereference(&subnet, MDL);
+ return (1);
+ }
+ return (0);
+ }
+
+ /* from shared_network_from_packet6() */
+
+ /* First, find the link address where the packet from the client
+ * first appeared (if this packet was relayed).
+ */
+ first_link_addr = NULL;
+ chk_packet = packet->dhcpv6_container_packet;
+ while (chk_packet != NULL) {
+ link_addr = &chk_packet->dhcpv6_link_address;
+ if (!IN6_IS_ADDR_UNSPECIFIED(link_addr) &&
+ !IN6_IS_ADDR_LINKLOCAL(link_addr)) {
+ first_link_addr = link_addr;
+ break;
+ }
+ chk_packet = chk_packet->dhcpv6_container_packet;
+ }
+
+ /* If there is a relayed link address, find the subnet associated
+ * with that, and use that to get the appropriate shared_network.
+ */
+ if (first_link_addr != NULL) {
+ ia.len = sizeof(*first_link_addr);
+ memcpy(ia.iabuf, first_link_addr, sizeof(*first_link_addr));
+ if (find_subnet (&subnet, ia, MDL)) {
+ shared_network_reference(&packet->shared_network,
+ subnet->shared_network, MDL);
+ subnet_dereference(&subnet, MDL);
+ return (1);
+ }
+ return (0);
+ }
+
+ /* If there is no link address, we will use the interface
+ * that this packet came in on to pick the shared_network.
+ */
+ if (packet->interface != NULL) {
+ if (packet->interface->shared_network == NULL)
+ return (0);
+ shared_network_reference(&packet->shared_network,
+ packet->interface->shared_network,
+ MDL);
+ return (1);
+ }
+
+ /* We shouldn't be able to get here but if there is no link
+ * address and no interface we don't know where to get the
+ * shared_network from, log an error and return an error.
+ */
+ log_error("No interface and no link address "
+ "can't determine DHCP4o6 shared network");
+ return (0);
+}
+#endif
+
int locate_network (packet)
struct packet *packet;
{
struct subnet *subnet = (struct subnet *)0;
struct option_cache *oc;
+#if defined(DHCPv6) && defined(DHCP4o6)
+ if (dhcpv4_over_dhcpv6 && (packet->dhcp4o6_response != NULL)) {
+ return (locate_network6 (packet));
+ }
+#endif
+
/* See if there's a Relay Agent Link Selection Option, or a
* Subnet Selection Option. The Link-Select and Subnet-Select
* are formatted and used precisely the same, but we must prefer
&global_scope, oc, MDL)) {
return 0;
}
+
if (data.len != 4) {
+ data_string_forget (&data, MDL);
return 0;
}
+
ia.len = 4;
memcpy (ia.iabuf, data.data, 4);
data_string_forget (&data, MDL);
option_num = DHO_DHCP_SERVER_IDENTIFIER;
oc = lookup_option(&dhcp_universe, options, option_num);
if (oc != NULL) {
- if (evaluate_option_cache(&d, packet, NULL, NULL,
- packet->options, options,
+ if (evaluate_option_cache(&d, packet, NULL, NULL,
+ packet->options, options,
&global_scope, oc, MDL)) {
if (d.len == sizeof(*from)) {
found = ISC_TRUE;
if (make_const_data(&oc->expression,
(unsigned char *)a, sizeof(*a),
0, allocate, MDL)) {
- option_code_hash_lookup(&oc->option,
+ option_code_hash_lookup(&oc->option,
dhcp_universe.code_hash,
&option_num, 0, MDL);
save_option(&dhcp_universe, out_options, oc);
}
}
}
+
+/*!
+ * \brief Checks and preps for lease resuse based on dhcp-cache-threshold
+ *
+ * If dhcp-cache-threshold is enabled (i.e. greater than zero), this function
+ * determines if the current lease is young enough to be reused. If the lease
+ * can be resused the function returns 1, O if not. This function is called
+ * by ack_lease when responding to both DISCOVERs and REQUESTS.
+ *
+ * The current lease can be reused only if all of the following are true:
+ * a. dhcp-cache-threshold is > 0
+ * b. The current lease is active
+ * c. The lease "age" is less than that allowed by the threshold
+ * d. DNS updates are not being performed on the new lease.
+ * e. Lease has not been otherwise disqualified for reuse (Ex: billing class
+ * or hostname changed)
+ * f. The host declaration has changed (either a new one was added
+ * or an older one was found due to something like a change in the uid)
+ * g. The UID or hardware address have changed.
+ *
+ * Clients may renew leases using full DORA cycles or just RAs. This means
+ * that reusability must be checked when acking both DISCOVERs and REQUESTs.
+ * When a lease cannot be reused, ack_lease() calls supersede_lease() which
+ * updates the lease start time (among other things). If this occurs on the
+ * DISCOVER, then the lease will virtually always be seen as young enough to
+ * reuse on the ensuing REQUEST and the lease updates will not get committed
+ * to the lease file. The lease.cannot_reuse flag is used to handle this
+ * this situation.
+ *
+ * \param packet inbound packet received from the client
+ * \param new_lease candidate new lease to associate with the client
+ * \param lease current lease associated with the client
+ * \param lease_state lease state to search and update
+ * \param offer type of DHCP response we're building
+ * \param[out] same_client pointer to int, that will be set to 1 if
+ * the two leases refer to the same client, 0 if not. Must NOT be null.
+ *
+ * \return 1 if the lease can be reused.
+ */
+int
+reuse_lease (struct packet* packet,
+ struct lease* new_lease,
+ struct lease* lease,
+ struct lease_state *state,
+ int offer,
+ int *same_client) {
+ int reusable = 0;
+
+ /* To even consider reuse all of the following must be true:
+ * 1 - reuse hasn't already disqualified
+ * 2 - current lease is active
+ * 3 - DNS info hasn't changed
+ * 4 - the host declaration hasn't changed
+ * 5 - the uid hasn't changed
+ * 6 - the hardware address hasn't changed */
+
+ /* Check client equality separately so we can pass the result out. */
+ *same_client =
+ (((lease->host == new_lease->host) &&
+ (lease->uid_len == new_lease->uid_len) &&
+ (memcmp(lease->uid, new_lease->uid, new_lease->uid_len) == 0) &&
+ (lease->hardware_addr.hlen == new_lease->hardware_addr.hlen) &&
+ (memcmp(&lease->hardware_addr.hbuf[0],
+ &new_lease->hardware_addr.hbuf[0],
+ lease->hardware_addr.hlen) == 0)) ? 1 : 0);
+
+ if ((lease->cannot_reuse == 0) &&
+ (lease->binding_state == FTS_ACTIVE) &&
+ (new_lease->ddns_cb == NULL) && *same_client) {
+ int thresh = DEFAULT_CACHE_THRESHOLD;
+ struct option_cache* oc = NULL;
+ struct data_string d1;
+
+ /* Look up threshold value */
+ memset(&d1, 0, sizeof(struct data_string));
+ if ((oc = lookup_option(&server_universe, state->options,
+ SV_CACHE_THRESHOLD)) &&
+ (evaluate_option_cache(&d1, packet, new_lease, NULL,
+ packet->options, state->options,
+ &new_lease->scope, oc, MDL))) {
+ if (d1.len == 1 && (d1.data[0] < 100))
+ thresh = d1.data[0];
+
+ data_string_forget(&d1, MDL);
+ }
+
+ /* If threshold is enabled, check lease age */
+ if (thresh > 0) {
+ int limit = 0;
+ int lease_length = 0;
+ long lease_age = 0;
+
+ /* Calculate limit in seconds */
+ lease_length = lease->ends - lease->starts;
+ if (lease_length <= (INT_MAX / thresh))
+ limit = lease_length * thresh / 100;
+ else
+ limit = lease_length / 100 * thresh;
+
+ /* Note new_lease->starts is really just cur_time */
+ lease_age = new_lease->starts - lease->starts;
+
+ /* Is the lease young enough to reuse? */
+ if (lease_age <= limit) {
+ /* Restore expiry to its original value */
+ state->offered_expiry = lease->ends;
+
+ /* Restore bindings. This fixes 37368. */
+ if (new_lease->scope != NULL) {
+ if (lease->scope != NULL) {
+ binding_scope_dereference(
+ &lease->scope,
+ MDL);
+ }
+
+ binding_scope_reference(&lease->scope,
+ new_lease->scope, MDL);
+ }
+
+ /* restore client hostname, fixes 42849. */
+ if (new_lease->client_hostname) {
+ lease->client_hostname =
+ new_lease->client_hostname;
+ new_lease->client_hostname = NULL;
+ }
+
+ /* We're cleared to reuse it */
+ log_debug("reuse_lease: lease age %ld (secs)"
+ " under %d%% threshold, reply with "
+ "unaltered, existing lease for %s",
+ lease_age, thresh, piaddr(lease->ip_addr));
+
+ reusable = 1;
+ }
+ }
+ }
+
+ /* If we can't reuse it and this is an offer disqualify reuse for
+ * ensuing REQUEST, otherwise clear the flag. */
+ lease->cannot_reuse = (!reusable && offer == DHCPOFFER);
+ return (reusable);
+}
+
+/* \brief Validates a proposed value for use as a lease time
+ *
+ * Convenience function used for catching calculeated lease
+ * times that overflow 4-byte times used in v4 protocol.
+ *
+ * We use variables of type TIME in lots of places, which on
+ * 64-bit systems is 8 bytes while on 32-bit OSs it is int32_t,
+ * so we have all sorts of fun places to mess things up.
+ * This function checks a calculated lease time for and if it
+ * is unsuitable for use as a lease time, the given alternate
+ * value is returned.
+ * \param calculated
+ * \param alternate
+ *
+ * \returen either the calculated value if it is valid, or
+ * the alternate value supplied
+ */
+TIME leaseTimeCheck(TIME calculated, TIME alternate) {
+ if ((sizeof(TIME) > 4 && calculated >= INFINITE_TIME) ||
+ (calculated < cur_time)) {
+ return (alternate);
+ }
+
+ return (calculated);
+}