]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[v4_1_esv] Add global DHCPv6 option, dhcpv6-set-tee-times to calculate T1/T2
authorThomas Markwalder <tmark@isc.org>
Tue, 16 Feb 2016 18:34:24 +0000 (13:34 -0500)
committerThomas Markwalder <tmark@isc.org>
Tue, 16 Feb 2016 18:34:24 +0000 (13:34 -0500)
    Merges in rt25687

RELNOTES
includes/dhcpd.h
server/dhcpd.conf.5
server/dhcpv6.c
server/stables.c

index 8d879f0d1b0217ba4bc5d81612f69a4e3bd3f788..e26d1f24ea665d8d41dc813ae7dc3e5b23827c3f 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -172,6 +172,11 @@ by Eric Young (eay@cryptsoft.com).
   incorrectly parse the new flags value from the omapi message.
   [ISC-Bugs #31179]
 
+- Add a new global DHCPv6 option, dhcpv6-set-tee-times, which when enabled
+  instructs the server to calculate T1 and T2 as recommended in RFC 3315,
+  Section 22.4.
+  [ISC-Bugs #25687]
+
                        Changes since 4.1-ESV-R12b1
 
 - None
index a5c7ff551921ca74183eec1373126871567fd446..eb048bb42a89ec2ad58698e1841432cb25d81979 100644 (file)
@@ -621,6 +621,7 @@ struct lease_state {
 #define SV_LIMIT_PREFS_PER_IA          57
 #define SV_DELAYED_ACK                 58
 #define SV_MAX_ACK_DELAY               59
+#define SV_DHCPV6_SET_TEE_TIMES                60
 
 #if !defined (DEFAULT_PING_TIMEOUT)
 # define DEFAULT_PING_TIMEOUT 1
index 1598c065e8cfd0381dab294c75eb341dc4335040..63990a7dd0fd71b39573710efa623004875f3572 100644 (file)
@@ -1,6 +1,6 @@
 .\"    dhcpd.conf.5
 .\"
-.\" Copyright (c) 2004-2015 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004-2016 by Internet Systems Consortium, Inc. ("ISC")
 .\" Copyright (c) 1996-2003 by Internet Software Consortium
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
@@ -2689,8 +2689,7 @@ longer useable.  A preferred lifetime is an advisory condition to help
 applications move off of the address and onto currently valid addresses
 (should there still be any open TCP sockets or similar).
 .PP
-The preferred lifetime defaults to the renew+rebind timers, or 3/4 the
-default lease time if none were specified.
+The preferred lifetime defaults to 5/8 the default lease time.
 .RE
 .PP
 The
@@ -2780,6 +2779,42 @@ be the name that will be provided to the client.
 .RE
 .PP
 The
+.I dhcpv6-set-tee-times
+statement
+.RS 0.25i
+.PP
+.B dhcpv6-set-tee-times\fR \fIflag\fR\fB;\fR
+.PP
+The \fIdhcpv6-set-tee-times\fR statement enables setting T1 and T2 to the
+values recommended in RFC 3315 (Section 22.4).  When setting T1 and T2, the
+server will use dhcp-renewal-time and dhcp-rebinding-time, respectively.
+A value of zero tells the client it may choose its own value.
+
+When those options are not defined then values will be set to zero unless the
+global \fIdhcpv6-set-tee-times\R is enabled.  When this option is enabled the
+times are calculated as recommended by RFC 3315, Section 22.4:
+
+      T1 will be set to 0.5 times the shortest preferred lifetime
+      in the reply.  If the "shortest" preferred lifetime is
+      0xFFFFFFFF,  T1 will set to 0xFFFFFFFF.
+
+      T2 will be set to 0.8 times the shortest preferred lifetime
+      in the reply.  If the "shortest" preferred lifetime is
+      0xFFFFFFFF,  T2 will set to 0xFFFFFFFF.
+
+Keep in mind that given sufficiently small lease lifetimes, the above
+calculations will result in the two values being equal. For example, a 9 second
+lease lifetime would yield T1 = T2 = 4 seconds, which would cause clients to
+issue rebinds only.  In such a case it would likely be better to explicitly
+define the values.
+
+Note that dhcpv6-set-tee-times is intended to be transitional and will likely
+be removed in a future release. Once removed the behavior will be to use
+the configured values when present or calculate them per the RFC. If you want
+zeros, define them as zeros.
+.RE
+.PP
+The
 .I site-option-space
 statement
 .RS 0.25i
index 7dd7e96362a9c1a23db22d48a64dd7386b09c331..8312f054b3bb69ed26f3bfcea98ae3b5f69e1d1f 100644 (file)
@@ -68,7 +68,7 @@ struct reply_state {
         * "t1", "t2", preferred, and valid lifetimes records for calculating
         * t1 and t2 (min/max).
         */
-       u_int32_t renew, rebind, prefer, valid;
+       u_int32_t renew, rebind, min_prefer, min_valid;
 
        /* Client-requested valid and preferred lifetimes. */
        u_int32_t client_valid, client_prefer;
@@ -156,6 +156,9 @@ static isc_result_t shared_network_from_requested_addr (struct shared_network
 static isc_result_t get_first_ia_addr_val (struct packet* packet, int addr_type,
                                           struct iaddr* iaddr);
 
+static void
+set_reply_tee_times(struct reply_state* reply, unsigned ia_cursor);
+
 /*
  * Schedule lease timeouts for all of the iasubopts in the reply.
  * This is currently used to schedule timeouts for soft leases.
@@ -1561,7 +1564,7 @@ lease_to_client(struct data_string *reply_ret,
                data_string_forget(&reply.client_id, MDL);
        if (packet_oro.buffer != NULL)
                data_string_forget(&packet_oro, MDL);
-       reply.renew = reply.rebind = reply.prefer = reply.valid = 0;
+       reply.renew = reply.rebind = reply.min_prefer = reply.min_valid = 0;
        reply.cursor = 0;
 }
 
@@ -1697,7 +1700,7 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
         * A not included IA ("cleanup" below) could give a Renew/Rebind.
         */
        oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAADDR);
-       reply->valid = reply->prefer = 0xffffffff;
+       reply->min_valid = reply->min_prefer = 0xffffffff;
        reply->client_valid = reply->client_prefer = 0;
        for (; oc != NULL ; oc = oc->next) {
                status = reply_process_addr(reply, oc);
@@ -1808,50 +1811,8 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
        putUShort(reply->buf.data + ia_cursor + 2,
                  reply->cursor - (ia_cursor + 4));
 
-       /*
-        * T1/T2 time selection is kind of weird.  We actually use DHCP
-        * (v4) scoped options as handy existing places where these might
-        * be configured by an administrator.  A value of zero tells the
-        * client it may choose its own renewal time.
-        */
-       reply->renew = 0;
-       oc = lookup_option(&dhcp_universe, reply->opt_state,
-                          DHO_DHCP_RENEWAL_TIME);
-       if (oc != NULL) {
-               if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
-                                          reply->packet->options,
-                                          reply->opt_state, &global_scope,
-                                          oc, MDL) ||
-                   (data.len != 4)) {
-                       log_error("Invalid renewal time.");
-               } else {
-                       reply->renew = getULong(data.data);
-               }
-
-               if (data.data != NULL)
-                       data_string_forget(&data, MDL);
-       }
-       putULong(reply->buf.data + ia_cursor + 8, reply->renew);
-
-       /* Now T2. */
-       reply->rebind = 0;
-       oc = lookup_option(&dhcp_universe, reply->opt_state,
-                          DHO_DHCP_REBINDING_TIME);
-       if (oc != NULL) {
-               if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
-                                          reply->packet->options,
-                                          reply->opt_state, &global_scope,
-                                          oc, MDL) ||
-                   (data.len != 4)) {
-                       log_error("Invalid rebinding time.");
-               } else {
-                       reply->rebind = getULong(data.data);
-               }
-
-               if (data.data != NULL)
-                       data_string_forget(&data, MDL);
-       }
-       putULong(reply->buf.data + ia_cursor + 12, reply->rebind);
+       /* Calculate T1/T2 and stuff them in the reply */
+       set_reply_tee_times(reply, ia_cursor);
 
        /*
         * yes, goto's aren't the best but we also want to avoid extra
@@ -2428,7 +2389,7 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
         * Deal with an IAADDR for lifetimes.
         * For all or none, process IAADDRs as hints.
         */
-       reply->valid = reply->prefer = 0xffffffff;
+       reply->min_valid = reply->min_prefer = 0xffffffff;
        reply->client_valid = reply->client_prefer = 0;
        oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAADDR);
        for (; oc != NULL; oc = oc->next) {
@@ -3039,11 +3000,11 @@ reply_process_is_addressed(struct reply_state *reply,
        }
 
        /* Note lowest values for later calculation of renew/rebind times. */
-       if (reply->prefer > reply->send_prefer)
-               reply->prefer = reply->send_prefer;
+       if (reply->min_prefer > reply->send_prefer)
+               reply->min_prefer = reply->send_prefer;
 
-       if (reply->valid > reply->send_valid)
-               reply->valid = reply->send_valid;
+       if (reply->min_valid > reply->send_valid)
+               reply->min_valid = reply->send_valid;
 
 #if 0
        /*
@@ -3349,7 +3310,7 @@ reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia) {
         * For each prefix in this IA_PD, decide what to do about it.
         */
        oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAPREFIX);
-       reply->valid = reply->prefer = 0xffffffff;
+       reply->min_valid = reply->min_prefer = 0xffffffff;
        reply->client_valid = reply->client_prefer = 0;
        reply->preflen = -1;
        for (; oc != NULL ; oc = oc->next) {
@@ -3438,50 +3399,8 @@ reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia) {
        putUShort(reply->buf.data + ia_cursor + 2,
                  reply->cursor - (ia_cursor + 4));
 
-       /*
-        * T1/T2 time selection is kind of weird.  We actually use DHCP
-        * (v4) scoped options as handy existing places where these might
-        * be configured by an administrator.  A value of zero tells the
-        * client it may choose its own renewal time.
-        */
-       reply->renew = 0;
-       oc = lookup_option(&dhcp_universe, reply->opt_state,
-                          DHO_DHCP_RENEWAL_TIME);
-       if (oc != NULL) {
-               if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
-                                          reply->packet->options,
-                                          reply->opt_state, &global_scope,
-                                          oc, MDL) ||
-                   (data.len != 4)) {
-                       log_error("Invalid renewal time.");
-               } else {
-                       reply->renew = getULong(data.data);
-               }
-
-               if (data.data != NULL)
-                       data_string_forget(&data, MDL);
-       }
-       putULong(reply->buf.data + ia_cursor + 8, reply->renew);
-
-       /* Now T2. */
-       reply->rebind = 0;
-       oc = lookup_option(&dhcp_universe, reply->opt_state,
-                          DHO_DHCP_REBINDING_TIME);
-       if (oc != NULL) {
-               if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
-                                          reply->packet->options,
-                                          reply->opt_state, &global_scope,
-                                          oc, MDL) ||
-                   (data.len != 4)) {
-                       log_error("Invalid rebinding time.");
-               } else {
-                       reply->rebind = getULong(data.data);
-               }
-
-               if (data.data != NULL)
-                       data_string_forget(&data, MDL);
-       }
-       putULong(reply->buf.data + ia_cursor + 12, reply->rebind);
+       /* Calculate T1/T2 and stuff them in the reply */
+       set_reply_tee_times(reply, ia_cursor);
 
        /*
         * yes, goto's aren't the best but we also want to avoid extra
@@ -4120,11 +4039,11 @@ reply_process_is_prefixed(struct reply_state *reply,
        }
 
        /* Note lowest values for later calculation of renew/rebind times. */
-       if (reply->prefer > reply->send_prefer)
-               reply->prefer = reply->send_prefer;
+       if (reply->min_prefer > reply->send_prefer)
+               reply->min_prefer = reply->send_prefer;
 
-       if (reply->valid > reply->send_valid)
-               reply->valid = reply->send_valid;
+       if (reply->min_valid > reply->send_valid)
+               reply->min_valid = reply->send_valid;
 
        /* Perform dynamic prefix related update work. */
        if (reply->lease != NULL) {
@@ -6528,4 +6447,116 @@ get_first_ia_addr_val (struct packet* packet, int addr_type,
        return (status);
 }
 
+/*
+* \brief Calculates the reply T1/T2 times and stuffs them in outbound buffer
+*
+* T1/T2 time selection is kind of weird.  We actually use DHCP * (v4) scoped
+* options, dhcp-renewal-time and dhcp-rebinding-time, as handy existing places
+* where these can be configured by an administrator.  A value of zero tells the
+* client it may choose its own value.
+*
+* When those options are not defined, the values will be set to zero unless
+* the global option, dhcpv6-set-tee-times is enabled. When this option is
+* enabled the values are calculated as recommended by RFC 3315, Section 22.4:
+*
+*      T1 will be set to 0.5 times the shortest preferred lifetime
+*      in the IA_XX option.  If the "shortest" preferred lifetime is
+*      0xFFFFFFFF,  T1 will set to 0xFFFFFFFF.
+*
+*      T2 will be set to 0.8 times the shortest preferred lifetime
+*      in the IA_XX option.  If the "shortest" preferred lifetime is
+*      0xFFFFFFFF,  T2 will set to 0xFFFFFFFF.
+*
+* Note that dhcpv6-set-tee-times is intended to be transitional and will
+* likely be removed in 4.4.0, leaving the behavior as getting the values
+* either from the configured parameters (if you want zeros, define them as
+* zeros) or by calculating them per the RFC.
+*
+* \param reply - pointer to the reply_state structure
+* \param ia_cursor - offset of the beginning of the IA_XX option within the
+* reply's outbound data buffer
+*/
+static void
+set_reply_tee_times(struct reply_state* reply, unsigned ia_cursor)
+{
+       struct option_cache *oc;
+       int set_tee_times;
+
+       /* Found out if calculated values are enabled. */
+       oc = lookup_option(&server_universe, reply->opt_state,
+                          SV_DHCPV6_SET_TEE_TIMES);
+       set_tee_times = (oc &&
+                        evaluate_boolean_option_cache(NULL, reply->packet,
+                                                      NULL, NULL,
+                                                      reply->packet->options,
+                                                      reply->opt_state,
+                                                      &global_scope, oc, MDL));
+
+       oc = lookup_option(&dhcp_universe, reply->opt_state,
+                          DHO_DHCP_RENEWAL_TIME);
+       if (oc != NULL) {
+               /* dhcp-renewal-time is defined, use it */
+               struct data_string data;
+               memset(&data, 0x00, sizeof(data));
+
+               if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+                                          reply->packet->options,
+                                          reply->opt_state, &global_scope,
+                                          oc, MDL) ||
+                   (data.len != 4)) {
+                       log_error("Invalid renewal time.");
+                       reply->renew = 0;
+               } else {
+                       reply->renew = getULong(data.data);
+               }
+
+               if (data.data != NULL)
+                       data_string_forget(&data, MDL);
+       } else if (set_tee_times) {
+               /* Setting them is enabled so T1 is either infinite or
+                * 0.5 * the shortest preferred lifetime in the IA_XX  */
+               reply->renew = (reply->min_prefer == 0xFFFFFFFF ? 0xFFFFFFFF
+                                : reply->min_prefer / 2);
+       } else {
+               /* Default is to let the client choose */
+               reply->renew = 0;
+       }
+
+       putULong(reply->buf.data + ia_cursor + 8, reply->renew);
+
+       /* Now T2. */
+       oc = lookup_option(&dhcp_universe, reply->opt_state,
+                          DHO_DHCP_REBINDING_TIME);
+       if (oc != NULL) {
+               /* dhcp-rebinding-time is defined, use it */
+               struct data_string data;
+               memset(&data, 0x00, sizeof(data));
+
+               if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+                                          reply->packet->options,
+                                          reply->opt_state, &global_scope,
+                                          oc, MDL) ||
+                   (data.len != 4)) {
+                       log_error("Invalid rebinding time.");
+                       reply->rebind = 0;
+               } else {
+                       reply->rebind = getULong(data.data);
+               }
+
+               if (data.data != NULL)
+                       data_string_forget(&data, MDL);
+       } else if (set_tee_times) {
+               /* Setting them is enabled so T2 is either infinite or
+                * 0.8 * the shortest preferred lifetime in the reply */
+               reply->rebind = (reply->min_prefer == 0xFFFFFFFF ? 0xFFFFFFFF
+                                : (reply->min_prefer / 5) * 4);
+       } else {
+               /* Default is to let the client choose */
+               reply->rebind = 0;
+       }
+
+       putULong(reply->buf.data + ia_cursor + 12, reply->rebind);
+}
+
+
 #endif /* DHCPv6 */
index 67f0ffb3dac9ec02ccf45e8ccf3e91787d835ebf..91fbb169abadf925a70e810a9dacb4c2984525cf 100644 (file)
@@ -238,6 +238,7 @@ static struct option server_options[] = {
        { "delayed-ack", "S",                   &server_universe,  58, 1 },
        { "max-ack-delay", "L",                 &server_universe,  59, 1 },
 #endif
+       { "dhcpv6-set-tee-times", "f",          &server_universe,  SV_DHCPV6_SET_TEE_TIMES, 1 },
        { NULL, NULL, NULL, 0, 0 }
 };