]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[master] Server(v6) releases pre-existing leases when client roams to new network
authorThomas Markwalder <tmark@isc.org>
Thu, 7 Dec 2017 12:02:06 +0000 (07:02 -0500)
committerThomas Markwalder <tmark@isc.org>
Thu, 7 Dec 2017 12:02:06 +0000 (07:02 -0500)
    Merges in rt44576.

RELNOTES
server/dhcpv6.c

index 06d398ffd723d64774458ef2af35b28fc958bef4..4067c253c9e297c0888ac5f576f4b270075c59f0 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -237,6 +237,20 @@ dhcp-users@lists.isc.org.
   doing load balancing within failover.
   [ISC-Bugs #45364]
 
+- If the server detects that a DHCPv6 client (IAID+DUID) has roamed to a new
+  network, it will now automatically release pre-existing leases on the old
+  network. When this occurs the server will emit a log statement:
+
+      "Client: <id> roamed to new network, releasing lease: <address>"
+
+  The server will carry out all of the same steps that would normally occur
+  when a client explicitly releases a lease.  Prior to this the server simply
+  dropped the pre-existing leases on the floor without doing the appropriate
+  clean-up.  Clients that need leases in multiple networks must supply a
+  unique IAID in each IA. Thanks to Fernando Soto from BlueCat Networks for
+  reporting the issue.
+  [ISC-Bugs #44576]
+
                        Changes since 4.3.0 (bug fixes)
 
 - Tidy up several small tickets.
index cc4b17d68d9ed6a1fe1ed65cadbecb85e7305aaf..a45e4025a11ecb4ac3b3a738bb6f154e2888b76b 100644 (file)
@@ -176,6 +176,9 @@ static isc_result_t get_first_ia_addr_val (struct packet* packet, int addr_type,
 static void
 set_reply_tee_times(struct reply_state* reply, unsigned ia_cursor);
 
+static const char *iasubopt_plen_str(struct iasubopt *lease);
+static int release_on_roam(struct reply_state *reply);
+
 #ifdef DHCP4o6
 /*
  * \brief Omapi I/O handler
@@ -2320,10 +2323,13 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
 
                /* Remove any old ia from the hash. */
                if (reply->old_ia != NULL) {
-                       ia_id = &reply->old_ia->iaid_duid;
-                       ia_hash_delete(ia_na_active,
-                                      (unsigned char *)ia_id->data,
-                                      ia_id->len, MDL);
+                       if (!release_on_roam(reply)) {
+                               ia_id = &reply->old_ia->iaid_duid;
+                               ia_hash_delete(ia_na_active,
+                                              (unsigned char *)ia_id->data,
+                                              ia_id->len, MDL);
+                       }
+
                        ia_dereference(&reply->old_ia, MDL);
                }
 
@@ -3067,10 +3073,13 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
 
                /* Remove any old ia from the hash. */
                if (reply->old_ia != NULL) {
-                       ia_id = &reply->old_ia->iaid_duid;
-                       ia_hash_delete(ia_ta_active,
-                                      (unsigned char *)ia_id->data,
-                                      ia_id->len, MDL);
+                       if (!release_on_roam(reply)) {
+                               ia_id = &reply->old_ia->iaid_duid;
+                               ia_hash_delete(ia_ta_active,
+                                              (unsigned char *)ia_id->data,
+                                              ia_id->len, MDL);
+                       }
+
                        ia_dereference(&reply->old_ia, MDL);
                }
 
@@ -4113,10 +4122,13 @@ reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia) {
 
                /* Remove any old ia from the hash. */
                if (reply->old_ia != NULL) {
-                       ia_id = &reply->old_ia->iaid_duid;
-                       ia_hash_delete(ia_pd_active,
-                                      (unsigned char *)ia_id->data,
-                                      ia_id->len, MDL);
+                       if (!release_on_roam(reply)) {
+                               ia_id = &reply->old_ia->iaid_duid;
+                               ia_hash_delete(ia_pd_active,
+                                              (unsigned char *)ia_id->data,
+                                              ia_id->len, MDL);
+                       }
+
                        ia_dereference(&reply->old_ia, MDL);
                }
 
@@ -8147,5 +8159,63 @@ set_reply_tee_times(struct reply_state* reply, unsigned ia_cursor)
        putULong(reply->buf.data + ia_cursor + 12, reply->rebind);
 }
 
+/*
+ * Releases the iasubopts in the pre-existing IA, if they are not in
+ * the same shared-network as the new IA.
+ *
+ * returns 1 if the release was done, 0 otherwise
+ */
+int
+release_on_roam(struct reply_state* reply) {
+       struct ia_xx* old_ia = reply->old_ia;
+       struct iasubopt *lease = NULL;
+       int i;
+
+       if (old_ia == NULL || old_ia->num_iasubopt <= 0) {
+               return(0);
+       }
+
+       /* If the old shared-network and new are the same, client hasn't
+       * roamed, nothing to do. We only check the first one because you
+       * cannot have iasubopts on different shared-networks within a
+       * single ia. */
+       lease = old_ia->iasubopt[0];
+       if (lease->ipv6_pool->shared_network == reply->shared) {
+               return (0);
+       }
+
+       /* Old and new are on different shared networks so the client must
+       * roamed. Release the old leases. */
+       for (i = 0;  i < old_ia->num_iasubopt; i++) {
+               lease = old_ia->iasubopt[i];
+
+               log_info("Client: %s roamed to new network,"
+                        " releasing lease: %s%s",
+                        print_hex_1(reply->client_id.len,
+                                    reply->client_id.data, 60),
+                        pin6_addr(&lease->addr), iasubopt_plen_str(lease));
+
+                release_lease6(lease->ipv6_pool, lease);
+                lease->ia->cltt = cur_time;
+                write_ia(lease->ia);
+        }
+
+       return (1);
+}
+
+/*
+ * Convenience function which returns a string (static buffer)
+ * containing either a "/" followed by the prefix length or an
+ * empty string depending on the lease type
+ */
+const char *iasubopt_plen_str(struct iasubopt *lease) {
+       static char prefix_buf[16];
+       *prefix_buf = 0;
+       if ((lease->ia) && (lease->ia->ia_type == D6O_IA_PD)) {
+               sprintf(prefix_buf, "/%-d", lease->plen);
+       }
+
+       return (prefix_buf);
+}
 
 #endif /* DHCPv6 */