]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[master] dhcpd (-6) now supports update-static-leases
authorThomas Markwalder <tmark@isc.org>
Tue, 19 Dec 2017 18:29:32 +0000 (13:29 -0500)
committerThomas Markwalder <tmark@isc.org>
Tue, 19 Dec 2017 18:29:32 +0000 (13:29 -0500)
    Merges in rt34097.

RELNOTES
common/dns.c
includes/dhcpd.h
server/ddns.c
server/dhcpd.conf.5
server/dhcpv6.c

index f0b04ca3f8a81c9abc1096f91efcd48bcde2a9f0..ceb44e9b1bbce7beb3c2ea3e4c1c7220012b104a 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -284,6 +284,18 @@ dhcp-users@lists.isc.org.
 - Corrected some minor coverity issues: CID 1426059, 1426058, and 1426057.
   [ISC-Bugs #46836]
 
+- The server (-6) now honors the parameter, update-static-leases, for static
+  (fixed-address6) DHCPv6 leases.  It is worth noting that because stateful
+  data is not retained by the server for static leases, each time a client
+  requests or renews a static lease, the server will perform DDNS updates for
+  it. This may have significant performance implications for environments
+  with many clients that request or renew static leases often. Similarly,
+  the DNS entries will not be removed by server when a client issues a RELEASE
+  nor if the lease is deleted from the configuration. In such cases the DNS
+  entries must be removed manually. This feature is disabled by default.
+  [ISC-Bugs #34097]
+  [ISC-Bugs #41054]
+
                        Changes since 4.3.6 (Bugs):
 
 - Corrected an issue where the server would return a client's previously
index 86a1ecd1d4049927b9642168699a3cbbfe73de23..44c42582ccfa0f388c74a85c5adcb793afc6eb70 100644 (file)
@@ -594,9 +594,16 @@ ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
        }
 
        /* Should be freed by now, check just in case. */
-       if (ddns_cb->transaction != NULL)
+       if (ddns_cb->transaction != NULL) {
                log_error("Impossible memory leak at %s:%d (attempt to free "
                          "DDNS Control Block before transaction).", MDL);
+       }
+
+       /* Should be freed by now, check just in case. */
+       if (ddns_cb->fixed6_ia) {
+               log_error("Possible memory leak at %s:%d (attempt to free "
+                         "DDNS Control Block before fxed6_ia).", MDL);
+       }
 
        dfree(ddns_cb, file, line);
 }
index 95af8a2141ccf612077b6709e69a9430d9dcd041..2130f21b9cb3570eea6f7d44f5b8290ce0bdc0ce 100644 (file)
@@ -1646,6 +1646,7 @@ struct iasubopt {
 
        /* space for the on * executable statements */
        struct on_star on_star;
+       int static_lease;
 };
 
 struct ia_xx {
@@ -1818,6 +1819,7 @@ typedef struct dhcp_ddns_cb {
        dns_rdataclass_t dhcid_class;
        dns_rdataclass_t other_dhcid_class;
        char *lease_tag;
+       struct ia_xx *fixed6_ia;
 } dhcp_ddns_cb_t;
 
 extern struct ipv6_pool **pools;
index 6ac79839abe5d01efb6c07c2352324b2485afc31..1bd72f6bca57a315c7b3920f87ec09bccbaa2cda 100644 (file)
@@ -57,6 +57,25 @@ static void copy_conflict_flags(u_int16_t *target, u_int16_t source);
 
 static void ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb, isc_result_t eresult);
 
+/*
+ * ddns_cb_free() is part of common lib, while ia_* routines are known
+ * only in the server.  Use this wrapper instead of ddns_cb_free() directly.
+ */
+static void
+destroy_ddns_cb(struct dhcp_ddns_cb *ddns_cb, char* file, int line) {
+       if (!ddns_cb) {
+               return;
+       }
+
+        if (ddns_cb->fixed6_ia) {
+                ia_dereference(&ddns_cb->fixed6_ia, MDL);
+        }
+
+       ddns_cb_free(ddns_cb, file, line);
+
+}
+
+
 /* DN: No way of checking that there is enough space in a data_string's
    buffer.  Be certain to allocate enough!
    TL: This is why the expression evaluation code allocates a *new*
@@ -153,6 +172,13 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
                scope = &(lease6->scope);
                memcpy(ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
                ddns_cb->address.len = 16;
+
+               if (lease6->static_lease) {
+                       /* We add a reference to keep ia && iasubopt alive
+                       * since static v6s are retained anywhere */
+                       ia_reference(&ddns_cb->fixed6_ia, lease6->ia, MDL);
+                       ddns_cb->flags |= DDNS_STATIC_LEASE;
+               }
        }
 
        memset (&d1, 0, sizeof(d1));
@@ -754,7 +780,7 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
         * Final cleanup.
         */
        if (ddns_cb != NULL) {
-               ddns_cb_free(ddns_cb, MDL);
+               destroy_ddns_cb(ddns_cb, MDL);
        }
 
        data_string_forget(&d1, MDL);
@@ -1296,7 +1322,7 @@ ddns_ptr_add(dhcp_ddns_cb_t *ddns_cb,
        }
 
        ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
-       ddns_cb_free(ddns_cb, MDL);
+       destroy_ddns_cb(ddns_cb, MDL);
        /*
         * A single DDNS operation may require several calls depending on
         * the current state as the prerequisites for the first message
@@ -1362,7 +1388,7 @@ ddns_ptr_remove(dhcp_ddns_cb_t *ddns_cb,
 
        ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
        ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, result);
-       ddns_cb_free(ddns_cb, MDL);
+       destroy_ddns_cb(ddns_cb, MDL);
        return;
 }
 
@@ -1476,7 +1502,7 @@ ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb,
        }
 
        ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
-       ddns_cb_free(ddns_cb, MDL);
+       destroy_ddns_cb(ddns_cb, MDL);
        /*
         * A single DDNS operation may require several calls depending on
         * the current state as the prerequisites for the first message
@@ -1551,7 +1577,7 @@ ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb,
        }
 
        ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
-       ddns_cb_free(ddns_cb, MDL);
+       destroy_ddns_cb(ddns_cb, MDL);
        /*
         * A single DDNS operation may require several calls depending on
         * the current state as the prerequisites for the first message
@@ -1636,7 +1662,7 @@ ddns_fwd_srv_add3(dhcp_ddns_cb_t *ddns_cb,
        }
 
        ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
-       ddns_cb_free(ddns_cb, MDL);
+       destroy_ddns_cb(ddns_cb, MDL);
        /*
         * A single DDNS operation may require several calls depending on
         * the current state as the prerequisites for the first message
@@ -1694,7 +1720,7 @@ ddns_fwd_srv_connector(struct lease          *lease,
        if (result == ISC_R_SUCCESS) {
                ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL);
        } else {
-               ddns_cb_free(ddns_cb, MDL);
+               destroy_ddns_cb(ddns_cb, MDL);
        }
 
        return;
@@ -1762,7 +1788,7 @@ ddns_fwd_srv_rem2(dhcp_ddns_cb_t *ddns_cb,
 
        ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
        ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
-       ddns_cb_free(ddns_cb, MDL);
+       destroy_ddns_cb(ddns_cb, MDL);
        return;
 }
 
@@ -1866,7 +1892,7 @@ ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb,
 
        ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
        ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
-       ddns_cb_free(ddns_cb, MDL);
+       destroy_ddns_cb(ddns_cb, MDL);
 }
 
 /*%<
@@ -1952,7 +1978,7 @@ ddns_removals(struct lease    *lease,
                        } else {
                                /* Remvoval, check and remove updates */
                                if (ddns_cb->next_op != NULL) {
-                                       ddns_cb_free(ddns_cb->next_op, MDL);
+                                       destroy_ddns_cb(ddns_cb->next_op, MDL);
                                        ddns_cb->next_op = NULL;
                                }
 #if defined (DEBUG_DNS_UPDATES)
@@ -1980,7 +2006,7 @@ ddns_removals(struct lease    *lease,
                        } else {
                                /* Remvoval, check and remove updates */
                                if (ddns_cb->next_op != NULL) {
-                                       ddns_cb_free(ddns_cb->next_op, MDL);
+                                       destroy_ddns_cb(ddns_cb->next_op, MDL);
                                        ddns_cb->next_op = NULL;
                                }
 #if defined (DEBUG_DNS_UPDATES)
@@ -2191,7 +2217,7 @@ ddns_removals(struct lease    *lease,
         */
        ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add);
        if (ddns_cb != NULL)
-               ddns_cb_free(ddns_cb, MDL);
+               destroy_ddns_cb(ddns_cb, MDL);
 
        return (result);
 }
index cfedf63eb8678f35e0a4b228218aefd707d89964..0973f3095426b5f0a9255c59dd2eae6669f749cf 100644 (file)
@@ -3379,13 +3379,15 @@ statement
 .PP
 The \fIupdate-static-leases\fR flag, if enabled, causes the DHCP
 server to do DNS updates for clients even if those clients are being
-assigned their IP address using a \fIfixed-address\fR statement - that
-is, the client is being given a static assignment.  It is not
-recommended because the DHCP server has no way to tell that the update
-has been done, and therefore will not delete the record when it is not
-in use.  Also, the server must attempt the update each time the
-client renews its lease, which could have a significant performance
-impact in environments that place heavy demands on the DHCP server.
+assigned their IP address using a \fIfixed-address\fR or
+\fIfixed-address6\fR statement - that is, the client is being given a
+static assignment.  It is not recommended because the DHCP server has
+no way to tell that the update has been done, and therefore will not
+delete the record when it is not in use.  Also, the server must attempt
+the update each time the client renews its lease, which could have a
+significant performance impact in environments that place heavy demands
+on the DHCP server.  This feature is supported for both DHCPv4 and DHCPv6,
+and update modes standard or interim. It is disabled by default.
 .RE
 .PP
 The
index 339766eae4cc7753c18f25acf2d35cd2de39ffd7..6cdee72cf46bb98135f06da44b3d3a77cedc7c4d 100644 (file)
@@ -185,6 +185,10 @@ static void shorten_lifetimes(struct reply_state *reply, struct iasubopt *lease,
 static void write_to_packet(struct reply_state *reply, unsigned ia_cursor);
 static const char *iasubopt_plen_str(struct iasubopt *lease);
 
+#ifdef NSUPDATE
+static void ddns_update_static6(struct reply_state* reply);
+#endif
+
 #ifdef DHCP4o6
 /*
  * \brief Omapi I/O handler
@@ -2227,7 +2231,10 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
 
                /* Write the lease out in wire-format to the outbound buffer */
                write_to_packet(reply, ia_cursor);
-
+#ifdef NSUPDATE
+               /* Performs DDNS updates if we're configured to do them */
+               ddns_update_static6(reply);
+#endif
                if ((reply->buf.reply.msg_type == DHCPV6_REPLY) &&
                    (reply->on_star.on_commit != NULL)) {
                        execute_statements(NULL, reply->packet, NULL, NULL,
@@ -8448,4 +8455,66 @@ const char *iasubopt_plen_str(struct iasubopt *lease) {
        return (prefix_buf);
 }
 
+#ifdef NSUPDATE
+/*
+ * Initiates DDNS updates for static v6 leases if configured to do so.
+ *
+ * The function, which must be called after the IA has been written to the
+ * packet, adds an iasubopt to the IA for static lease.  This is done so we
+ * have an iasubopt to pass into ddns_updates().  A reference to the IA is
+ * added to the DDNS control block to ensure it and it's iasubopt remain in
+ * scope until the update is complete.
+ *
+ */
+void ddns_update_static6(struct reply_state* reply) {
+       struct iasubopt *iasub = NULL;
+       struct binding_scope *scope = NULL;
+       struct option_cache *oc = NULL;
+
+       oc = lookup_option(&server_universe, reply->opt_state, SV_DDNS_UPDATES);
+       if ((oc != NULL) &&
+               (evaluate_boolean_option_cache(NULL, reply->packet, NULL, NULL,
+                                              reply->packet->options,
+                                               reply->opt_state, NULL,
+                                               oc, MDL) == 0)) {
+               return;
+       }
+
+       oc = lookup_option(&server_universe, reply->opt_state,
+                          SV_UPDATE_STATIC_LEASES);
+       if ((oc == NULL) ||
+               (evaluate_boolean_option_cache(NULL, reply->packet,
+                                                    NULL, NULL,
+                                                    reply->packet->options,
+                                                    reply->opt_state, NULL,
+                                                    oc, MDL) == 0)) {
+               return;
+       }
+
+       if (iasubopt_allocate(&iasub, MDL) != ISC_R_SUCCESS) {
+               log_fatal("No memory for iasubopt.");
+       }
+
+       if (ia_add_iasubopt(reply->ia, iasub, MDL) != ISC_R_SUCCESS) {
+               log_fatal("Could not add iasubopt.");
+       }
+
+       ia_reference(&iasub->ia, reply->ia, MDL);
+
+       memcpy(iasub->addr.s6_addr, reply->fixed.data, 16);
+       iasub->plen = 0;
+       iasub->prefer =  MAX_TIME;
+       iasub->valid =  MAX_TIME;
+       iasub->static_lease = 1;
+
+       if (!binding_scope_allocate(&scope, MDL)) {
+               log_fatal("Out of memory for binding scope.");
+       }
+
+       binding_scope_reference(&iasub->scope, scope, MDL);
+
+       ddns_updates(reply->packet, NULL, NULL, iasub, NULL, reply->opt_state);
+}
+#endif /* NSUPDATE */
+
 #endif /* DHCPv6 */