]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[master] Log v6 shared network lease counts, when none available for a DUID
authorThomas Markwalder <tmark@isc.org>
Thu, 8 Jan 2015 14:44:51 +0000 (09:44 -0500)
committerThomas Markwalder <tmark@isc.org>
Thu, 8 Jan 2015 14:44:51 +0000 (09:44 -0500)
    Merges in rt26376

RELNOTES
includes/dhcpd.h
server/confpars.c
server/dhcpd.c
server/dhcpd.conf.5
server/dhcpv6.c
server/mdb6.c

index b60b2d05b5ee365ce740c30009a0f6281472c909..2935c99ec570db87f7679c797c386b1b14252bae 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -225,6 +225,16 @@ by Eric Young (eay@cryptsoft.com).
   [ISC-Bugs #36668]
   [ISC-Bugs #36652]
 
+- Log content has been changed to more directly suggest that admins should
+  check for multiple IPv6 clients attempting to use the same DUID when only
+  abandoned addresses are available.  Debug level logging will now emit counts
+  of the total number of, in-use, and abandoned addresses in a shared subnet
+  when the server finds no addresses available for a given DUID.  Lastly,
+  threshold logging is now automatically disabled for shared subnets whose
+  total number of possible addresses exceeds (2^64)-1.
+  [ISC-Bugs #26376]
+  [ISC-Bugs #38131]
+
                        Changes since 4.3.1b1
 
 - Modify the linux and openwrt dhclient scripts to process information
index b44682444c5b88ff495189426a0405663c80f5fb..f9fba731ec8a9be363820545dab0ff37c7eab1da 100644 (file)
@@ -3,7 +3,7 @@
    Definitions for dhcpd... */
 
 /*
- * Copyright (c) 2004-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2015 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
@@ -1583,7 +1583,8 @@ struct ipv6_pool {
        int bits;                               /* number of bits, CIDR style */
        int units;                              /* allocation unit in bits */
        iasubopt_hash_t *leases;                /* non-free leases */
-       int num_active;                         /* count of active leases */
+       isc_uint64_t num_active;                /* count of active leases */
+       isc_uint64_t num_abandoned;             /* count of abandoned leases */
        isc_heap_t *active_timeouts;            /* timeouts for active leases */
        int num_inactive;                       /* count of inactive leases */
        isc_heap_t *inactive_timeouts;          /* timeouts for expired or
@@ -1619,12 +1620,20 @@ struct ipv6_pond {
        struct ipv6_pool **ipv6_pools;  /* NULL-terminated array */
        int last_ipv6_pool;             /* offset of last IPv6 pool
                                           used to issue a lease */
-       int num_total;                  /* Total number of elements in the pond */
-       int num_active;                 /* Number of elements in the pond in use */
+       isc_uint64_t num_total;     /* Total number of elements in the pond */
+       isc_uint64_t num_active;    /* Number of elements in the pond in use */
+       isc_uint64_t num_abandoned;     /* count of abandoned leases */
        int logged;                     /* already logged a message */
-       int low_threshold;              /* low threshold to restart logging */
+       isc_uint64_t low_threshold;     /* low threshold to restart logging */
+       int jumbo_range;
 };
 
+/*
+ * Max addresses in a pond that can be supported by log threshold
+ * Currently based on max value supported by isc_uint64_t.
+*/
+#define POND_TRACK_MAX ULLONG_MAX
+
 /* Flags and state for dhcp_ddns_cb_t */
 #define DDNS_UPDATE_ADDR        0x01
 #define DDNS_UPDATE_PTR         0x02
@@ -3646,6 +3655,7 @@ void schedule_all_ipv6_lease_timeouts();
 void mark_hosts_unavailable(void);
 void mark_phosts_unavailable(void);
 void mark_interfaces_unavailable(void);
+void report_jumbo_ranges();
 
 #define MAX_ADDRESS_STRING_LEN \
    (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
@@ -3657,4 +3667,7 @@ void mark_interfaces_unavailable(void);
        ((count) > (INT_MAX / 100) ?    \
         ((count) / 100) * (percent) : ((count) * (percent)) / 100)
 
-       
+#define FIND_POND6_PERCENT(count, percent)     \
+       ((count) > (POND_TRACK_MAX / 100) ?     \
+        ((count) / 100) * (percent) : ((count) * (percent)) / 100)
+
index 006528af189bab36159766e9622786a4ef96c3b5..c9c149f5b3c279f2667e623bd9fc56705ebe43d7 100644 (file)
@@ -3,7 +3,7 @@
    Parser for dhcpd config file... */
 
 /*
- * Copyright (c) 2004-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2015 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
@@ -3842,12 +3842,40 @@ add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type,
         */
        ipv6_pool_reference(&pond->ipv6_pools[num_pools], pool, MDL);
        pond->ipv6_pools[num_pools+1] = NULL;
+
        /* Update the number of elements in the pond.  Conveniently
         * we have the total size of the block in bits and the amount
         * we would allocate per element in units.  For an address units
         * will always be 128, for a prefix it will be something else.
-        */
-       pond->num_total += 1 << (units - bits);
+        *
+        * We need to make sure the number of elements isn't too large
+        * to track.  If so, we flag it to avoid wasting time with log
+        * threshold logic.  We also emit a log stating that log-threshold
+        * will be disabled for the shared-network but that's done
+        * elsewhere via report_log_threshold().
+        *
+       */
+
+       /* Only bother if we aren't already flagged as jumbo */
+       if (pond->jumbo_range == 0) {
+               if ((units - bits) > (sizeof(isc_uint64_t) * 8)) {
+                       pond->jumbo_range = 1;
+                       pond->num_total = POND_TRACK_MAX;
+               }
+               else {
+                       isc_uint64_t space_left
+                               = POND_TRACK_MAX - pond->num_total;
+                       isc_uint64_t addon
+                               = (isc_uint64_t)(1) << (units - bits);
+
+                       if (addon > space_left) {
+                               pond->jumbo_range = 1;
+                               pond->num_total = POND_TRACK_MAX;
+                       } else {
+                               pond->num_total += addon;
+                       }
+               }
+       }
 }
 
 /*!
index 1d00507fd444de52c26c124ef8e21d21ef97be9d..04c4bbef420eb8b83f2e1a980b8d60626233c375 100644 (file)
@@ -3,7 +3,7 @@
    DHCP Server Daemon. */
 
 /*
- * Copyright (c) 2004-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2015 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
@@ -617,6 +617,11 @@ main(int argc, char **argv) {
        if (set_chroot) setup_chroot (set_chroot);
 #endif /* PARANOIA && !EARLY_CHROOT */
 
+#ifdef DHCPv6
+       /* log info about ipv6_ponds with large address ranges */
+       report_jumbo_ranges();
+#endif
+
         /* test option should cause an early exit */
        if (cftest && !lftest) 
                exit(0);
@@ -677,7 +682,6 @@ main(int argc, char **argv) {
        }
 #endif /* DHCPv6 */
 
-
        /* Make up a seed for the random number generator from current
           time plus the sum of the last four bytes of each
           interface's hardware address interpreted as an integer.
index e437ce3cfe7c5c2e46778931c051dc4d42138ae3..d7d62dc4b055c9d5aa25ec875b2c7ccf90197ae9 100644 (file)
@@ -1716,12 +1716,43 @@ lease the server has offered is not valid.  When the server receives
 a DHCPDECLINE for a particular address, it normally abandons that
 address, assuming that some unauthorized system is using it.
 Unfortunately, a malicious or buggy client can, using DHCPDECLINE
-messages, completely exhaust the DHCP server's allocation pool.  The
-server will reclaim these leases, but while the client is running
-through the pool, it may cause serious thrashing in the DNS, and it
-will also cause the DHCP server to forget old DHCP client address
+messages, to completely exhaust the DHCP server's allocation pool.  The
+server will eventually reclaim these leases, but not while the client
+is running through the pool. This may cause serious thrashing in the DNS,
+and it will also cause the DHCP server to forget old DHCP client address
 allocations.
 .PP
+Currently, abandoned IPv6 addresses are reclaimed in one of two ways:
+    a) Client renews a specific address:
+    If a client using a given DUID submits a DHCP REQUEST containing the
+    last address abandoned by that DUID, the address will be reassigned to
+    that client.
+
+    b) Upon the second restart following an address abandonment.  When an
+    address is abandoned it is both recorded as such in the lease file and
+    retained as abandoned in server memory until the server is restarted. Upon
+    restart, the server will process the lease file and all addresses whose
+    last known state is abandoned will be retained as such in memory but not
+    rewritten to the lease file.  This means that a subsequent restart of the
+    server will not see the abandoned addresses in the lease file and 
+    therefore have no record of them as abandoned in memory and as such
+    perceive them as free for assignment.
+.PP
+The total number addresses in a pool, available for a given DUID value,
+is internally limited by the server's address generation mechanism.  If
+through mistaken configuration, multiple clients are using the same
+DUID they will competing for the same addresses causing the server to reach
+this internal limit rather quickly.  The internal limit isolates this type
+of activity such that address range is not exhausted for other DUID values.
+The appearance of the following error log, can be an indication of this
+condition:
+
+    "Best match for DUID <XX> is an abandoned address, This may be a result of
+     multiple clients attempting to use this DUID"
+
+    where <XX> is an actual DUID value depicted as colon separated string of
+    bytes in hexadecimal values.
+.PP
 The \fBdeclines\fR flag tells the DHCP server whether or not to honor
 DHCPDECLINE messages.  If it is set to \fBdeny\fR or \fBignore\fR in
 a particular scope, the DHCP server will not respond to DHCPDECLINE
@@ -2543,6 +2574,16 @@ threshold is not given, it default to a value of zero.
 A special case occurs when the low threshold is set to be higer than
 the high threshold.  In this case, a message will be generated each time
 a lease is acknowledged when the pool usage is above the high threshold.
+.PP
+Note that threshold logging will be automatically disabled for shared
+subnets whose total number of addresses is larger than (2^64)-1.  The server
+will emit a log statement at startup when threshold logging is disabled as
+shown below:
+
+    "Threshold logging disabled for shared subnet of ranges: <addresses>"
+
+This is likely to have no practical runtime effect as CPUs are unlikely
+to support a server actually reaching such a large number of leases.
 .RE
 .PP
 The
index 138101de0226c24b0bd72ebd13b3b191cbd649ed..62d35c47f0dcd5264c238c6165762ed5d5d344ca 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2014 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2006-2015 by Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -823,7 +823,8 @@ void check_pool6_threshold(struct reply_state *reply,
                           struct iasubopt *lease)
 {
        struct ipv6_pond *pond;
-       int used, count, high_threshold, poolhigh = 0, poollow = 0;
+       isc_uint64_t used, count, high_threshold;
+       int poolhigh = 0, poollow = 0;
        char *shared_name = "no name";
        char tmp_addr[INET6_ADDRSTRLEN];
 
@@ -831,9 +832,20 @@ void check_pool6_threshold(struct reply_state *reply,
                return;
        pond = lease->ipv6_pool->ipv6_pond;
 
+       /* If the address range is too large to track, just skip all this. */
+       if (pond->jumbo_range == 1) {
+               return;
+       }
+
        count = pond->num_total;
        used = pond->num_active;
 
+       /* get network name for logging */
+       if ((pond->shared_network != NULL) &&
+           (pond->shared_network->name != NULL)) {
+               shared_name = pond->shared_network->name;
+       }
+
        /* The logged flag indicates if we have already crossed the high
         * threshold and emitted a log message.  If it is set we check to
         * see if we have re-crossed the low threshold and need to reset
@@ -849,7 +861,7 @@ void check_pool6_threshold(struct reply_state *reply,
                        pond->low_threshold = 0;
                        pond->logged = 0;
                        log_error("Pool threshold reset - shared subnet: %s; "
-                                 "address: %s; low threshold %d/%d.",
+                                 "address: %s; low threshold %llu/%llu.",
                                  shared_name,
                                  inet_ntop(AF_INET6, &lease->addr,
                                            tmp_addr, sizeof(tmp_addr)),
@@ -874,19 +886,15 @@ void check_pool6_threshold(struct reply_state *reply,
        }
 
        /* we have a valid value, have we exceeded it */
-       high_threshold = FIND_PERCENT(count, poolhigh);
+       high_threshold = FIND_POND6_PERCENT(count, poolhigh);
        if (used < high_threshold) {
                /* nope, no more to do */
                return;
        }
 
        /* we've exceeded it, output a message */
-       if ((pond->shared_network != NULL) &&
-           (pond->shared_network->name != NULL)) {
-               shared_name = pond->shared_network->name;
-       }
        log_error("Pool threshold exceeded - shared subnet: %s; "
-                 "address: %s; high threshold %d%% %d/%d.",
+                 "address: %s; high threshold %d%% %llu/%llu.",
                  shared_name,
                  inet_ntop(AF_INET6, &lease->addr, tmp_addr, sizeof(tmp_addr)),
                  poolhigh, used, count);
@@ -908,7 +916,7 @@ void check_pool6_threshold(struct reply_state *reply,
         */
        if (poollow < poolhigh) {
                pond->logged = 1;
-               pond->low_threshold = FIND_PERCENT(count, poollow);
+               pond->low_threshold = FIND_POND6_PERCENT(count, poollow);
        }
 }
 
@@ -1134,6 +1142,12 @@ pick_v6_address(struct reply_state *reply)
        unsigned int attempts;
        char tmp_buf[INET6_ADDRSTRLEN];
        struct iasubopt **addr = &reply->lease;
+        isc_uint64_t total = 0;
+        isc_uint64_t active = 0;
+        isc_uint64_t abandoned = 0;
+       int jumbo_range = 0;
+       char *shared_name = (reply->shared->name ?
+                            reply->shared->name : "(no name)");
 
        /*
         * Do a quick walk through of the ponds and pools
@@ -1170,6 +1184,8 @@ pick_v6_address(struct reply_state *reply)
         */
 
        for (pond = reply->shared->ipv6_pond; pond != NULL; pond = pond->next) {
+               isc_result_t result;
+
                if (((pond->prohibit_list != NULL) &&
                     (permitted(reply->packet, pond->prohibit_list))) ||
                    ((pond->permit_list != NULL) &&
@@ -1180,26 +1196,31 @@ pick_v6_address(struct reply_state *reply)
                i = start_pool;
                do {
                        p = pond->ipv6_pools[i];
-                       if ((p->pool_type == D6O_IA_NA) &&
-                           (create_lease6(p, addr, &attempts,
-                                          &reply->ia->iaid_duid,
-                                          cur_time + 120) == ISC_R_SUCCESS)) {
-                               /*
-                                * Record the pool used (or next one if there 
-                                * was a collision).
-                                */
-                               if (attempts > 1) {
-                                       i++;
-                                       if (pond->ipv6_pools[i] == NULL) {
-                                               i = 0;
+                       if (p->pool_type == D6O_IA_NA) {
+                               result = create_lease6(p, addr, &attempts,
+                                                      &reply->ia->iaid_duid,
+                                                      cur_time + 120);
+                               if (result == ISC_R_SUCCESS) {
+                                       /*
+                                        * Record the pool used (or next one if
+                                        * there was a collision).
+                                        */
+                                       if (attempts > 1) {
+                                               i++;
+                                               if (pond->ipv6_pools[i]
+                                                   == NULL) {
+                                                       i = 0;
+                                               }
                                        }
-                               }
-                               pond->last_ipv6_pool = i;
 
-                               log_debug("Picking pool address %s",
-                                         inet_ntop(AF_INET6, &((*addr)->addr),
-                                                   tmp_buf, sizeof(tmp_buf)));
-                               return (ISC_R_SUCCESS);
+                                       pond->last_ipv6_pool = i;
+
+                                       log_debug("Picking pool address %s",
+                                                 inet_ntop(AF_INET6,
+                                                 &((*addr)->addr),
+                                                 tmp_buf, sizeof(tmp_buf)));
+                                       return (ISC_R_SUCCESS);
+                               }
                        }
 
                        i++;
@@ -1207,13 +1228,31 @@ pick_v6_address(struct reply_state *reply)
                                i = 0;
                        }
                } while (i != start_pool);
+
+               if (result == ISC_R_NORESOURCES) {
+                       jumbo_range += pond->jumbo_range;
+                       total += pond->num_total;
+                       active += pond->num_active;
+                       abandoned += pond->num_abandoned;
+               }
        }
 
        /*
         * If we failed to pick an IPv6 address from any of the subnets.
         * Presumably that means we have no addresses for the client.
         */
-       log_debug("Unable to pick client address: no addresses available");
+       if (jumbo_range != 0) {
+               log_debug("Unable to pick client address: "
+                         "no addresses available  - shared network %s: "
+                         " 2^64-1 < total, %llu active,  %llu abandoned",
+                         shared_name, active - abandoned, abandoned);
+       } else {
+               log_debug("Unable to pick client address: "
+                         "no addresses available  - shared network %s: "
+                         "%llu total, %llu active,  %llu abandoned",
+                         shared_name, total, active - abandoned, abandoned);
+       }
+
        return ISC_R_NORESOURCES;
 }
 
@@ -3140,9 +3179,11 @@ find_client_address(struct reply_state *reply) {
        /* Pick the abandoned lease as a last resort. */
        if ((status == ISC_R_NORESOURCES) && (best_lease != NULL)) {
                /* I don't see how this is supposed to be done right now. */
-               log_error("Reclaiming abandoned addresses is not yet "
-                         "supported.  Treating this as an out of space "
-                         "condition.");
+               log_error("Best match for DUID %s is an abandoned address,"
+                         " This may be a result of multiple clients attempting"
+                         " to use this DUID",
+                        print_hex_1(reply->client_id.len,
+                                    reply->client_id.data, 60));
                /* iasubopt_reference(&reply->lease, best_lease, MDL); */
        }
 
index 6eac7fca3f2e253fde1b862f8e9a56ec6f17a900..3633cd58a5a651951cb34a79c5807024610014d9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2013 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2007-2015 by Internet Systems Consortium, Inc. ("ISC")
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -1191,6 +1191,12 @@ cleanup_lease6(ia_hash_t *ia_table,
        if (pool->ipv6_pond)
                pool->ipv6_pond->num_active--;
 
+       if (lease->state == FTS_ABANDONED) {
+               pool->num_abandoned--;
+               if (pool->ipv6_pond)
+                       pool->ipv6_pond->num_abandoned--;
+       }
+
        iasubopt_hash_delete(pool->leases, &test_iasubopt->addr,
                             sizeof(test_iasubopt->addr), MDL);
        ia_remove_iasubopt(old_ia, test_iasubopt, MDL);
@@ -1257,6 +1263,12 @@ add_lease6(struct ipv6_pool *pool, struct iasubopt *lease,
                        pool->num_active--;
                        if (pool->ipv6_pond)
                                pool->ipv6_pond->num_active--;
+
+                       if (test_iasubopt->state == FTS_ABANDONED) {
+                               pool->num_abandoned--;
+                               if (pool->ipv6_pond)
+                                       pool->ipv6_pond->num_abandoned--;
+                       }
                } else {
                        isc_heap_delete(pool->inactive_timeouts,
                                        test_iasubopt->heap_index);
@@ -1295,6 +1307,12 @@ add_lease6(struct ipv6_pool *pool, struct iasubopt *lease,
                        pool->num_active++;
                        if (pool->ipv6_pond)
                                pool->ipv6_pond->num_active++;
+
+                       if (tmp_iasubopt->state == FTS_ABANDONED) {
+                               pool->num_abandoned++;
+                               if (pool->ipv6_pond)
+                                       pool->ipv6_pond->num_abandoned++;
+                       }
                }
 
        } else {
@@ -1387,6 +1405,7 @@ move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) {
                lease->state = FTS_ACTIVE;
                if (pool->ipv6_pond)
                        pool->ipv6_pond->num_active++;
+
        }
        return insert_result;
 }
@@ -1443,6 +1462,11 @@ renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
                log_info("Reclaiming previously abandoned address %s",
                         inet_ntop(AF_INET6, &(lease->addr), tmp_addr,
                                   sizeof(tmp_addr)));
+
+               pool->num_abandoned--;
+               if (pool->ipv6_pond)
+                       pool->ipv6_pond->num_abandoned--;
+
                 return ISC_R_SUCCESS;
        } else {
                return move_lease_to_active(pool, lease);
@@ -1515,6 +1539,12 @@ move_lease_to_inactive(struct ipv6_pool *pool, struct iasubopt *lease,
                pool->num_inactive++;
                if (pool->ipv6_pond)
                        pool->ipv6_pond->num_active--;
+
+               if (lease->state == FTS_ABANDONED) {
+                       pool->num_abandoned--;
+                       if (pool->ipv6_pond)
+                               pool->ipv6_pond->num_abandoned--;
+               }
        }
        return insert_result;
 }
@@ -1575,6 +1605,11 @@ decline_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
                }
        }
        lease->state = FTS_ABANDONED;
+
+       pool->num_abandoned++;
+       if (pool->ipv6_pond)
+               pool->ipv6_pond->num_abandoned++;
+
        lease->hard_lifetime_end_time = MAX_TIME;
        isc_heap_decreased(pool->active_timeouts, lease->heap_index);
        return ISC_R_SUCCESS;
@@ -2459,4 +2494,70 @@ ipv6_pond_dereference(struct ipv6_pond **pond, const char *file, int line) {
        return ISC_R_SUCCESS;
 }
 
+/*
+ * Emits a log for each pond that has been flagged as being a "jumbo range"
+ * A pond is considered a "jumbo range" when the total number of elements
+ * exceeds the maximum value of POND_TRACK_MAX (currently maximum value
+ * that can be stored by ipv6_pond.num_total).  Since we disable threshold
+ * logging for jumbo ranges, we need to report this to the user.  This
+ * function allows us to report jumbo ponds after config parsing, so the
+ * logs can be seen both on the console (-T) and the log facility (i.e syslog).
+ *
+ * Note, threshold logging is done at the pond level, so we need emit a list
+ * of the addresses ranges of the pools in the pond affected.
+ */
+void
+report_jumbo_ranges() {
+       struct shared_network* s;
+       char log_buf[1084];
+
+       /* Loop thru all the networks looking for jumbo range ponds */
+       for (s = shared_networks; s; s = s -> next) {
+               struct ipv6_pond* pond = s->ipv6_pond;
+               while (pond) {
+                       /* if its a jumbo and has pools(sanity check) */
+                       if (pond->jumbo_range == 1 && (pond->ipv6_pools)) {
+                               struct ipv6_pool* pool;
+                               char *bufptr = log_buf;
+                               size_t space_left = sizeof(log_buf) - 1;
+                               int i = 0;
+                               int used = 0;
+
+                               /* Build list containing the start-address/CIDR
+                                * of each pool */
+                               *bufptr = '\0';
+                               while ((pool = pond->ipv6_pools[i++]) &&
+                                       (space_left > (INET6_ADDRSTRLEN + 6))) {
+                                       /* more than one so add a comma */
+                                       if (i > 1) {
+                                               *bufptr++ = ',';
+                                               *bufptr++ = ' ';
+                                               *bufptr = '\0';
+                                               space_left -= 2;
+                                       }
+
+                                       /* add the address */
+                                       inet_ntop(AF_INET6, &pool->start_addr,
+                                                 bufptr, INET6_ADDRSTRLEN);
+
+                                       used = strlen(bufptr);
+                                       bufptr += used;
+                                       space_left -= used;
+
+                                       /* add the CIDR */
+                                       sprintf (bufptr, "/%d",pool->bits);
+                                       used = strlen(bufptr);
+                                       bufptr += used;
+                                       space_left -= used;
+                                       *bufptr = '\0';
+                               }
+
+                               log_info("Threshold logging disabled for shared"
+                                        " subnet of ranges: %s", log_buf);
+                       }
+                       pond = pond->next;
+               }
+       }
+}
+
 /* unittest moved to server/tests/mdb6_unittest.c */