]> git.ipfire.org Git - thirdparty/dhcp.git/blobdiff - server/dhcp.c
Update RELNOTES
[thirdparty/dhcp.git] / server / dhcp.c
index f055c4738b9f624a978c4d430fd51d689906a28b..8852e9e3cfc596f4029225595c09301b55124a9f 100644 (file)
@@ -3,12 +3,12 @@
    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
@@ -19,8 +19,8 @@
  * 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;
@@ -46,6 +62,7 @@ int max_outstanding_acks = DEFAULT_DELAYED_ACK;
 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;
@@ -53,7 +70,7 @@ 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",
@@ -74,6 +91,8 @@ const int dhcp_type_name_max = ((sizeof dhcp_type_names) / sizeof (char *));
 # define send_packet trace_packet_send
 #endif
 
+static TIME leaseTimeCheck(TIME calculated, TIME alternate);
+
 void
 dhcp (struct packet *packet) {
        int ms_nulltp = 0;
@@ -84,13 +103,13 @@ dhcp (struct packet *packet) {
 
        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];
@@ -99,7 +118,21 @@ dhcp (struct packet *packet) {
                        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,
@@ -284,6 +317,21 @@ void dhcpdiscover (packet, ms_nulltp)
        /* %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,
@@ -299,6 +347,12 @@ void dhcpdiscover (packet, ms_nulltp)
 
        /* 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;
@@ -339,7 +393,7 @@ void dhcpdiscover (packet, ms_nulltp)
        /* 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",
@@ -474,6 +528,22 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
        /* %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,
@@ -550,31 +620,28 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
                        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
@@ -649,7 +716,7 @@ void dhcprequest (packet, ms_nulltp, ip_lease)
            (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. */
@@ -796,6 +863,24 @@ void dhcprelease (packet, ms_nulltp)
        /* %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,
@@ -834,7 +919,7 @@ void dhcprelease (packet, ms_nulltp)
        /* 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:
@@ -887,6 +972,22 @@ void dhcpdecline (packet, ms_nulltp)
        /* %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),
@@ -968,6 +1069,20 @@ void dhcpdecline (packet, ms_nulltp)
                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;
@@ -989,6 +1104,9 @@ void dhcpinform (packet, 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
@@ -998,9 +1116,17 @@ void dhcpinform (packet, ms_nulltp)
           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;
@@ -1023,6 +1149,14 @@ void dhcpinform (packet, ms_nulltp)
        /* %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 ?
@@ -1035,7 +1169,11 @@ void dhcpinform (packet, ms_nulltp)
                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
         */
 
@@ -1055,6 +1193,7 @@ void dhcpinform (packet, ms_nulltp)
                if (d1.len != 4) {
                        log_info("%s: ignored (invalid subnet selection option).", msgbuf);
                        option_state_dereference(&options, MDL);
+                       data_string_forget(&d1, MDL);
                        return;
                }
 
@@ -1115,19 +1254,41 @@ void dhcpinform (packet, ms_nulltp)
                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,
@@ -1139,7 +1300,7 @@ void dhcpinform (packet, ms_nulltp)
        }
 
        /*
-        * 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
@@ -1282,7 +1443,7 @@ void dhcpinform (packet, ms_nulltp)
                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
@@ -1305,14 +1466,13 @@ void dhcpinform (packet, ms_nulltp)
                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);
@@ -1324,7 +1484,7 @@ void dhcpinform (packet, ms_nulltp)
                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);
@@ -1344,7 +1504,7 @@ void dhcpinform (packet, ms_nulltp)
                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);
@@ -1415,6 +1575,7 @@ void dhcpinform (packet, ms_nulltp)
                        option_state_dereference (&options, MDL);
                        if (subnet)
                                subnet_dereference (&subnet, MDL);
+                       data_string_forget (&d1, MDL);
                        return;
                }
 
@@ -1507,6 +1668,36 @@ void dhcpinform (packet, ms_nulltp)
        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
@@ -1514,7 +1705,7 @@ void dhcpinform (packet, ms_nulltp)
 #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
@@ -1534,7 +1725,11 @@ void dhcpinform (packet, ms_nulltp)
         */
        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;
@@ -1591,6 +1786,9 @@ void nak_lease (packet, cip, network_group)
        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;
@@ -1618,7 +1816,11 @@ void nak_lease (packet, cip, network_group)
                                &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.");
@@ -1696,8 +1898,6 @@ void nak_lease (packet, cip, network_group)
        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;
@@ -1709,7 +1909,21 @@ void nak_lease (packet, cip, network_group)
        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,
@@ -1726,6 +1940,23 @@ void nak_lease (packet, cip, network_group)
        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
@@ -1733,16 +1964,16 @@ void nak_lease (packet, cip, network_group)
 #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. */
 
@@ -1957,8 +2188,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
        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;
@@ -1967,18 +2197,18 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
        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)
@@ -2156,7 +2386,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                } while (1);
            }
        }
-       
+
 
        /* Make sure this packet satisfies the configured minimum
           number of seconds. */
@@ -2270,7 +2500,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                if (host)
                        host_dereference (&host, MDL);
                return;
-       } 
+       }
 
        /* Drop the request if it's not allowed for this client. */
        if (!offer &&
@@ -2288,7 +2518,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                if (host)
                        host_dereference (&host, MDL);
                return;
-       } 
+       }
 
        /* Drop the request if booting is specifically denied. */
        oc = lookup_option (&server_universe, state -> options,
@@ -2318,8 +2548,13 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                                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
@@ -2329,7 +2564,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                        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) {
@@ -2337,9 +2572,9 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                                        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;
                                }
@@ -2366,7 +2601,12 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                        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;
+                       }
                }
        }
 
@@ -2400,7 +2640,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                        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;
@@ -2507,7 +2747,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                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;
@@ -2590,7 +2830,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                        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;
@@ -2601,7 +2841,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                        if (host)
                                host_dereference (&host, MDL);
                        return;
-                       
+
                }
 
                /* CC:
@@ -2619,7 +2859,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                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 */
@@ -2636,7 +2876,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                        if (lease_time > remaining_time)
                                lease_time = remaining_time;
                }
+
                if (lease_time < min_lease_time) {
                        if (min_lease_time)
                                lease_time = min_lease_time;
@@ -2683,8 +2923,15 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                         * 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
@@ -2701,12 +2948,16 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                }
 #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
@@ -2764,7 +3015,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
            !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);
@@ -2824,9 +3075,9 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                           DHO_VENDOR_CLASS_IDENTIFIER);
        if (oc != NULL &&
            evaluate_option_cache(&d1, packet, NULL, NULL, packet->options,
-                                 NULL, &lease->scope, oc, MDL)) {
+                                 NULL, &lt->scope, oc, MDL)) {
                if (d1.len != 0) {
-                       bind_ds_value(&lease->scope, "vendor-class-identifier",
+                       bind_ds_value(&lt->scope, "vendor-class-identifier",
                                      &d1);
                }
 
@@ -2889,6 +3140,8 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                        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... */
@@ -2897,7 +3150,17 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
        memcpy (&lt -> 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. */
@@ -2935,51 +3198,13 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                        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,
-                                         &lt->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)
@@ -2993,7 +3218,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                 */
                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
@@ -3013,7 +3238,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                 */
                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);
@@ -3217,7 +3442,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
        }
 
        /* 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);
 
@@ -3235,9 +3460,9 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
              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));
@@ -3284,7 +3509,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                                        save_option (&dhcp_universe,
                                                     state -> options, oc);
                                }
-                               option_cache_dereference (&oc, MDL);    
+                               option_cache_dereference (&oc, MDL);
                        }
                }
        }
@@ -3303,6 +3528,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
                                           (const char *)d1.data, d1.len,
                                           MDL)) {
                        log_error ("unknown option space %s.", d1.data);
+                       data_string_forget (&d1, MDL);
                        return;
                }
 
@@ -3343,63 +3569,165 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
        /* 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)
@@ -3409,12 +3737,12 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
  *   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;
@@ -3430,18 +3758,16 @@ delayed_ack_enqueue(struct lease *lease)
        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;
 
@@ -3472,44 +3798,68 @@ delayed_ack_enqueue(struct lease *lease)
                        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;
@@ -3520,7 +3870,7 @@ void
 relinquish_ackqueue(void)
 {
        struct leasequeue *q, *n;
-       
+
        for (q = ackqueue_head ; q ; q = n) {
                n = q->next;
                dfree(q, MDL);
@@ -3532,6 +3882,8 @@ relinquish_ackqueue(void)
 }
 #endif
 
+#endif /* defined(DELAYED_ACK) */
+
 void dhcp_reply (lease)
        struct lease *lease;
 {
@@ -3544,6 +3896,9 @@ void dhcp_reply (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;
 
@@ -3564,9 +3919,9 @@ void dhcp_reply (lease)
                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);
@@ -3583,9 +3938,9 @@ void dhcp_reply (lease)
                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,
@@ -3641,6 +3996,48 @@ void dhcp_reply (lease)
        } 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
@@ -3657,6 +4054,10 @@ void dhcp_reply (lease)
                   ? 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);
@@ -3667,20 +4068,19 @@ void dhcp_reply (lease)
 #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. */
 
@@ -3829,7 +4229,7 @@ int find_lease (struct lease **lp,
                        cip.len = 4;
                        memcpy (cip.iabuf, d1.data, cip.len);
                        data_string_forget (&d1, MDL);
-               } else 
+               } else
                        cip.len = 0;
        }
 
@@ -3942,6 +4342,7 @@ int find_lease (struct lease **lp,
         * 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));
@@ -3972,6 +4373,9 @@ int find_lease (struct lease **lp,
                        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;
                }
 
@@ -3987,7 +4391,7 @@ int find_lease (struct lease **lp,
                        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) {
@@ -4352,6 +4756,7 @@ int find_lease (struct lease **lp,
 #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.");
@@ -4360,7 +4765,6 @@ int find_lease (struct lease **lp,
                        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
@@ -4532,7 +4936,7 @@ int mockup_lease (struct lease **lp, struct packet *packet,
 {
        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) {
@@ -4580,8 +4984,8 @@ int mockup_lease (struct lease **lp, struct packet *packet,
 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 &&
@@ -4606,36 +5010,35 @@ int allocate_lease (struct lease **lp, struct packet *packet,
                /* 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;
                                        }
@@ -4643,17 +5046,17 @@ int allocate_lease (struct lease **lp, struct packet *packet,
                        }
 
                        /* 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);
                }
 
                /*
@@ -4665,8 +5068,11 @@ int allocate_lease (struct lease **lp, struct packet *packet,
                 * "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;
@@ -4771,7 +5177,7 @@ int permitted (packet, permit_list)
                            !packet -> packet_type)
                                return 1;
                        break;
-                       
+
                      case permit_class:
                        for (i = 0; i < packet -> class_count; i++) {
                                if (p -> class == packet -> classes [i])
@@ -4793,6 +5199,132 @@ int permitted (packet, permit_list)
        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;
 {
@@ -4801,6 +5333,12 @@ int locate_network (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
@@ -4835,9 +5373,12 @@ int locate_network (packet)
                                            &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);
@@ -4906,8 +5447,8 @@ get_server_source_address(struct in_addr *from,
                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;
@@ -4942,7 +5483,7 @@ get_server_source_address(struct in_addr *from,
                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);
@@ -5162,3 +5703,171 @@ void use_host_decl_name(struct packet* packet,
                 }
         }
 }
+
+/*!
+ * \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);
+}