From 250f7134bbe06f5014ab12dfd8a459c9b39e17cb Mon Sep 17 00:00:00 2001 From: Shawn Routhier Date: Thu, 5 Jun 2014 14:55:01 -0700 Subject: [PATCH] [master] Add support for pool thresholds Add support to set high and low thresholds for pools for v4 and v6. A message will be emitted when the usage of the pool first exceeds the high threshold. More messages will be skipped until the usage has gone below the low threshold and then back above the high threshold. --- RELNOTES | 6 ++ common/options.c | 51 +++++++++++ common/tests/Makefile.am | 8 +- common/tests/Makefile.in | 31 ++++++- common/tests/misc_unittest.c | 166 +++++++++++++++++++++++++++++++++++ includes/dhcpd.h | 22 +++++ server/confpars.c | 6 ++ server/dhcp.c | 102 ++++++++++++++++++++- server/dhcpd.conf.5 | 25 ++++++ server/dhcpv6.c | 100 +++++++++++++++++++++ server/mdb6.c | 14 ++- server/stables.c | 2 + 12 files changed, 526 insertions(+), 7 deletions(-) create mode 100644 common/tests/misc_unittest.c diff --git a/RELNOTES b/RELNOTES index d4a163179..7a35d346b 100644 --- a/RELNOTES +++ b/RELNOTES @@ -164,6 +164,12 @@ by Eric Young (eay@cryptsoft.com). process to start up even if the resolv.conf file has problems. [ISC-Bugs #35989] +- Add theshold logging functionality. Two new options, + log-threshold-low and log-threshold-high, indicate to the + server if and when it should log an error message as addresses + in a pool are used. + [ISC-Bugs #34487] + Changes since 4.3.0rc1 - None diff --git a/common/options.c b/common/options.c index a15c51fa6..374cb5739 100644 --- a/common/options.c +++ b/common/options.c @@ -2126,6 +2126,57 @@ int get_option (result, universe, packet, lease, client_state, return 1; } +/* + * Look for the option and dig out the value assoicated with it. + * Currently this is used for 1 byte integers, it maybe expanded + * in the future to handle other integers at which point it will + * need a size argument. + */ +int get_option_int (result, universe, packet, lease, client_state, + in_options, cfg_options, options, scope, code, file, line) + int *result; + struct universe *universe; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct option_state *options; + struct binding_scope **scope; + unsigned code; + const char *file; + int line; +{ + struct option_cache *oc; + struct data_string d1; + int rcode = 0; + + /* basic sanity checks */ + if ((options == NULL) || (universe->lookup_func == NULL)) + return (0); + + /* find the option cache */ + oc = ((*universe->lookup_func)(universe, options, code)); + if (!oc) + return (0); + + /* if there is a value get it into the string */ + memset(&d1, 0, sizeof(d1)); + if (!evaluate_option_cache(&d1, packet, lease, client_state, + in_options, cfg_options, scope, oc, + file, line)) + return (0); + + /* If the length matches extract the value for the return */ + if (d1.len == 1) { + *result = d1.data[0]; + rcode = 1; + } + data_string_forget(&d1, MDL); + + return (rcode); +} + void set_option (universe, options, option, op) struct universe *universe; struct option_state *options; diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am index 18456be85..08d76fc37 100644 --- a/common/tests/Makefile.am +++ b/common/tests/Makefile.am @@ -8,7 +8,7 @@ ATF_TESTS = if HAVE_ATF -ATF_TESTS += alloc_unittest dns_unittest +ATF_TESTS += alloc_unittest dns_unittest misc_unittest alloc_unittest_SOURCES = test_alloc.c $(top_srcdir)/tests/t_api_dhcp.c alloc_unittest_LDADD = $(ATF_LDFLAGS) @@ -22,6 +22,12 @@ dns_unittest_LDADD += ../libdhcp.a \ ../../omapip/libomapi.a ../../bind/lib/libirs.a \ ../../bind/lib/libdns.a ../../bind/lib/libisccfg.a ../../bind/lib/libisc.a +misc_unittest_SOURCES = misc_unittest.c $(top_srcdir)/tests/t_api_dhcp.c +misc_unittest_LDADD = $(ATF_LDFLAGS) +misc_unittest_LDADD += ../libdhcp.a \ + ../../omapip/libomapi.a ../../bind/lib/libirs.a \ + ../../bind/lib/libdns.a ../../bind/lib/libisccfg.a ../../bind/lib/libisc.a + check: $(ATF_TESTS) atf-run | atf-report diff --git a/common/tests/Makefile.in b/common/tests/Makefile.in index 76b0ac8af..8a5d3f1ea 100644 --- a/common/tests/Makefile.in +++ b/common/tests/Makefile.in @@ -77,7 +77,7 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -@HAVE_ATF_TRUE@am__append_1 = alloc_unittest dns_unittest +@HAVE_ATF_TRUE@am__append_1 = alloc_unittest dns_unittest misc_unittest check_PROGRAMS = $(am__EXEEXT_2) subdir = common/tests DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ @@ -91,7 +91,7 @@ CONFIG_HEADER = $(top_builddir)/includes/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = @HAVE_ATF_TRUE@am__EXEEXT_1 = alloc_unittest$(EXEEXT) \ -@HAVE_ATF_TRUE@ dns_unittest$(EXEEXT) +@HAVE_ATF_TRUE@ dns_unittest$(EXEEXT) misc_unittest$(EXEEXT) am__EXEEXT_2 = $(am__EXEEXT_1) am__alloc_unittest_SOURCES_DIST = test_alloc.c \ $(top_srcdir)/tests/t_api_dhcp.c @@ -114,6 +114,16 @@ dns_unittest_OBJECTS = $(am_dns_unittest_OBJECTS) @HAVE_ATF_TRUE@ ../../bind/lib/libirs.a ../../bind/lib/libdns.a \ @HAVE_ATF_TRUE@ ../../bind/lib/libisccfg.a \ @HAVE_ATF_TRUE@ ../../bind/lib/libisc.a +am__misc_unittest_SOURCES_DIST = misc_unittest.c \ + $(top_srcdir)/tests/t_api_dhcp.c +@HAVE_ATF_TRUE@am_misc_unittest_OBJECTS = misc_unittest.$(OBJEXT) \ +@HAVE_ATF_TRUE@ t_api_dhcp.$(OBJEXT) +misc_unittest_OBJECTS = $(am_misc_unittest_OBJECTS) +@HAVE_ATF_TRUE@misc_unittest_DEPENDENCIES = $(am__DEPENDENCIES_1) \ +@HAVE_ATF_TRUE@ ../libdhcp.a ../../omapip/libomapi.a \ +@HAVE_ATF_TRUE@ ../../bind/lib/libirs.a ../../bind/lib/libdns.a \ +@HAVE_ATF_TRUE@ ../../bind/lib/libisccfg.a \ +@HAVE_ATF_TRUE@ ../../bind/lib/libisc.a AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false @@ -146,9 +156,11 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = -SOURCES = $(alloc_unittest_SOURCES) $(dns_unittest_SOURCES) +SOURCES = $(alloc_unittest_SOURCES) $(dns_unittest_SOURCES) \ + $(misc_unittest_SOURCES) DIST_SOURCES = $(am__alloc_unittest_SOURCES_DIST) \ - $(am__dns_unittest_SOURCES_DIST) + $(am__dns_unittest_SOURCES_DIST) \ + $(am__misc_unittest_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ @@ -334,6 +346,12 @@ ATF_TESTS = $(am__append_1) @HAVE_ATF_TRUE@ ../../bind/lib/libdns.a \ @HAVE_ATF_TRUE@ ../../bind/lib/libisccfg.a \ @HAVE_ATF_TRUE@ ../../bind/lib/libisc.a +@HAVE_ATF_TRUE@misc_unittest_SOURCES = misc_unittest.c $(top_srcdir)/tests/t_api_dhcp.c +@HAVE_ATF_TRUE@misc_unittest_LDADD = $(ATF_LDFLAGS) ../libdhcp.a \ +@HAVE_ATF_TRUE@ ../../omapip/libomapi.a ../../bind/lib/libirs.a \ +@HAVE_ATF_TRUE@ ../../bind/lib/libdns.a \ +@HAVE_ATF_TRUE@ ../../bind/lib/libisccfg.a \ +@HAVE_ATF_TRUE@ ../../bind/lib/libisc.a all: all-recursive .SUFFIXES: @@ -380,6 +398,10 @@ dns_unittest$(EXEEXT): $(dns_unittest_OBJECTS) $(dns_unittest_DEPENDENCIES) $(EX @rm -f dns_unittest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(dns_unittest_OBJECTS) $(dns_unittest_LDADD) $(LIBS) +misc_unittest$(EXEEXT): $(misc_unittest_OBJECTS) $(misc_unittest_DEPENDENCIES) $(EXTRA_misc_unittest_DEPENDENCIES) + @rm -f misc_unittest$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(misc_unittest_OBJECTS) $(misc_unittest_LDADD) $(LIBS) + mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -387,6 +409,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns_unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api_dhcp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_alloc.Po@am__quote@ diff --git a/common/tests/misc_unittest.c b/common/tests/misc_unittest.c new file mode 100644 index 000000000..6cefa6e77 --- /dev/null +++ b/common/tests/misc_unittest.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "dhcpd.h" + +struct basic_test { + int count; + int used_0; + int used_25; + int used_50; + int used_75; + int used_100; +}; + +struct basic_test basic_values[] = + {{ 0, 0, 0, 0, 0, 0}, + { 10, 0, 2, 5, 7, 10}, + { 20, 0, 5, 10, 15, 20}, + { 50, 0, 12, 25, 37, 50}, + { 100, 0, 25, 50, 75, 100}, + { 1000, 0, 250, 500, 750, 1000}, + { 10000, 0, 2500, 5000, 7500, 10000}, + { 100000, 0, 25000, 50000, 75000, 100000}, + { 1000000, 0, 250000, 500000, 750000, 1000000}, + { 10000000, 0, 2500000, 5000000, 7500000, 10000000}, + { 100000000, 0, 25000000, 50000000, 75000000, 100000000}, + {1000000000, 0, 250000000, 500000000, 750000000, 1000000000}, + {2000000000, 0, 500000000, 1000000000, 1500000000, 2000000000}, + {-1, 0, 0, 0, 0, 0}}; + +ATF_TC(find_percent_basic); + +ATF_TC_HEAD(find_percent_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "Verify basic percent calculation."); +} + +/* This test does a simple check to see if we get the right value + * given a count and a percnetage of that count + */ +ATF_TC_BODY(find_percent_basic, tc) +{ + struct basic_test *basic_value; + int used; + + for (basic_value = &basic_values[0]; + basic_value->count != -1; + basic_value++) { + used = FIND_PERCENT(basic_value->count, 0); + if (used != basic_value->used_0) { + atf_tc_fail("Wrong value for 0 - count: %d, used %d", + basic_value->count, used); + } + + used = FIND_PERCENT(basic_value->count, 25); + if (used != basic_value->used_25) { + atf_tc_fail("Wrong value for 25 - count: %d, used %d", + basic_value->count, used); + } + + used = FIND_PERCENT(basic_value->count, 50); + if (used != basic_value->used_50) { + atf_tc_fail("Wrong value for 50 - count: %d, used %d", + basic_value->count, used); + } + + used = FIND_PERCENT(basic_value->count, 75); + if (used != basic_value->used_75) { + atf_tc_fail("Wrong value for 75 - count: %d, used %d", + basic_value->count, used); + } + + used = FIND_PERCENT(basic_value->count, 100); + if (used != basic_value->used_100) { + atf_tc_fail("Wrong value for 100 - count: %d, used %d", + basic_value->count, used); + } + } + return; +} + +ATF_TC(find_percent_adv); + +ATF_TC_HEAD(find_percent_adv, tc) +{ + atf_tc_set_md_var(tc, "descr", "Verify advanced percent calculation."); +} + +/* This test tries some more complicated items, such as using the macro + * in a function or passing expressions into macro + */ +ATF_TC_BODY(find_percent_adv, tc) +{ + if (FIND_PERCENT(10*10, 10) != 10) { + atf_tc_fail("Wrong value for adv 1"); + } + + if (FIND_PERCENT(20*20, 80) != 320) { + atf_tc_fail("Wrong value for adv 2"); + } + + if (FIND_PERCENT(1000000*1000, 50) != 500000000) { + atf_tc_fail("Wrong value for adv 3"); + } + + if (FIND_PERCENT(100+100, 10) != 20) { + atf_tc_fail("Wrong value for adv 4"); + } + + if (FIND_PERCENT(1000+2000, 90) != 2700) { + atf_tc_fail("Wrong value for adv 5"); + } + + if (FIND_PERCENT(10000 - 8000, 70) != 1400) { + atf_tc_fail("Wrong value for adv 6"); + } + + if (FIND_PERCENT(2000000000 / 1000, 25) != 500000) { + atf_tc_fail("Wrong value for adv 7"); + } + + if (FIND_PERCENT(10*10, 10)/2 != 5) { + atf_tc_fail("Wrong value for adv 8"); + } + + if (FIND_PERCENT(100*10, 50) * 2 != 1000) { + atf_tc_fail("Wrong value for adv 9"); + } + + if (FIND_PERCENT(100*10, 50) * 2 > 1000) { + atf_tc_fail("Wrong value for adv 10"); + } + + if (FIND_PERCENT(100+100, 20) * 2 < 60) { + atf_tc_fail("Wrong value for adv 11"); + } + + return; +} + + +/* This macro defines main() method that will call specified + test cases. tp and simple_test_case names can be whatever you want + as long as it is a valid variable identifier. */ +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, find_percent_basic); + ATF_TP_ADD_TC(tp, find_percent_adv); + + return (atf_no_error()); +} diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 45bdd6ab8..6692026c8 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -730,6 +730,8 @@ struct lease_state { #define SV_DDNS_LOCAL_ADDRESS4 80 #define SV_DDNS_LOCAL_ADDRESS6 81 #define SV_IGNORE_CLIENT_UIDS 82 +#define SV_LOG_THRESHOLD_LOW 83 +#define SV_LOG_THRESHOLD_HIGH 84 #if !defined (DEFAULT_PING_TIMEOUT) # define DEFAULT_PING_TIMEOUT 1 @@ -925,6 +927,8 @@ struct pool { #if defined (FAILOVER_PROTOCOL) dhcp_failover_state_t *failover_peer; #endif + int logged; /* already logged a message */ + int low_threshold; /* low threshold to restart logging */ }; struct shared_network { @@ -1606,6 +1610,10 @@ 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 */ + int logged; /* already logged a message */ + int low_threshold; /* low threshold to restart logging */ }; /* Flags and state for dhcp_ddns_cb_t */ @@ -1774,6 +1782,11 @@ int get_option (struct data_string *, struct universe *, struct option_state *, struct option_state *, struct option_state *, struct binding_scope **, unsigned, const char *, int); +int get_option_int (int *, struct universe *, + struct packet *, struct lease *, struct client_state *, + struct option_state *, struct option_state *, + struct option_state *, struct binding_scope **, unsigned, + const char *, int); void set_option (struct universe *, struct option_state *, struct option_cache *, enum statement_op); struct option_cache *lookup_option (struct universe *, @@ -3617,3 +3630,12 @@ void mark_interfaces_unavailable(void); #define MAX_ADDRESS_STRING_LEN \ (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")) + +/* Find the percentage of count. We need to try two different + * ways to avoid rounding mistakes. + */ +#define FIND_PERCENT(count, percent) \ + ((count) > (INT_MAX / 100) ? \ + ((count) / 100) * (percent) : ((count) * (percent)) / 100) + + diff --git a/server/confpars.c b/server/confpars.c index a8fe1fb4e..8a5a4b33d 100644 --- a/server/confpars.c +++ b/server/confpars.c @@ -3837,6 +3837,12 @@ 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); } /*! diff --git a/server/dhcp.c b/server/dhcp.c index 80398176b..dbc2d5666 100644 --- a/server/dhcp.c +++ b/server/dhcp.c @@ -1745,6 +1745,98 @@ void nak_lease (packet, cip) } +void check_pool_threshold (packet, lease, state) + struct packet *packet; + struct lease *lease; + struct lease_state *state; + +{ + + struct pool *pool = lease->pool; + int used, count, high_threshold, poolhigh = 0, poollow = 0; + char *shared_name = "no name"; + + if (pool == NULL) + return; + + /* get a pointer to the name if we have one */ + if ((pool->shared_network != NULL) && + (pool->shared_network->name != NULL)) { + shared_name = pool->shared_network->name; + } + + count = pool->lease_count; + used = count - (pool->free_leases + pool->backup_leases); + + /* 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 + * things. When we cross the high threshold we determine what + * the low threshold is and save it into the low_threshold value. + * When we cross that threshold we reset the logged flag and + * the low_threshold to 0 which allows the high threshold message + * to be emitted once again. + * if we haven't recrossed the boundry we don't need to do anything. + */ + if (pool->logged !=0) { + if (used <= pool->low_threshold) { + pool->low_threshold = 0; + pool->logged = 0; + log_error("Pool threshold reset - shared subnet: %s; " + "address: %s; low threshold %d/%d.", + shared_name, piaddr(lease->ip_addr), + used, count); + } + return; + } + + /* find the high threshold */ + if (get_option_int(&poolhigh, &server_universe, packet, lease, NULL, + packet->options, state->options, state->options, + &lease->scope, SV_LOG_THRESHOLD_HIGH, MDL) == 0) { + /* no threshold bail out */ + return; + } + + /* We do have a threshold for this pool, see if its valid */ + if ((poolhigh <= 0) || (poolhigh > 100)) { + /* not valid */ + return; + } + + /* we have a valid value, have we exceeded it */ + high_threshold = FIND_PERCENT(count, poolhigh); + if (used < high_threshold) { + /* nope, no more to do */ + return; + } + + /* we've exceeded it, output a message */ + log_error("Pool threshold exceeded - shared subnet: %s; " + "address: %s; high threshold %d%% %d/%d.", + shared_name, piaddr(lease->ip_addr), + poolhigh, used, count); + + /* handle the low threshold now, if we don't + * have a valid one we default to 0. */ + if ((get_option_int(&poollow, &server_universe, packet, lease, NULL, + packet->options, state->options, state->options, + &lease->scope, SV_LOG_THRESHOLD_LOW, MDL) == 0) || + (poollow > 100)) { + poollow = 0; + } + + /* + * If the low theshold is higher than the high threshold we continue to log + * If it isn't then we set the flag saying we already logged and determine + * what the reset threshold is. + */ + if (poollow < poolhigh) { + pool->logged = 1; + pool->low_threshold = FIND_PERCENT(count, poollow); + } +} + void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp) struct packet *packet; struct lease *lease; @@ -2390,6 +2482,14 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp) data_string_forget(&d1, MDL); } + + /* + * If this is an ack check to see if we have used enough of + * the pool to want to log a message + */ + if (offer == DHCPACK) + check_pool_threshold(packet, lease, state); + /* a client requests an address which is not yet active*/ if (lease->pool && lease->pool->valid_from && cur_time < lease->pool->valid_from) { @@ -2410,7 +2510,7 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp) b) extend lease only up to the expiration date, but not below min-lease-time Setting min-lease-time is essential for this to work! - The value of min-lease-time determines the lenght + The value of min-lease-time determines the length of the transition window: A client renewing a second before the deadline will get a min-lease-time lease. Since the current ip might not diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5 index ea34edc2d..be624792d 100644 --- a/server/dhcpd.conf.5 +++ b/server/dhcpd.conf.5 @@ -2476,6 +2476,31 @@ possible. .RE .PP The +.I log-threshold-high +and +.I log-threshold-low +statements +.RS 0.25i +.PP +.B log-threshold-high \fIpercentage\fB;\fR +.PP +.B log-threshold-low \fIpercentage\fB;\fR +.PP +The \fIlog-threshold-low\fR and \fIlog-threshold-high\fR statements +are used to control when a message is output about pool usage. The +value for both of them is the percentage of the pool in use. If the +high threshold is 0 or has not been specified, no messages will be +produced. If a high threshold is given, a message is output once the +pool usage passes that level. After that, no more messages will be +output until the pool usage falls below the low threshold. If the low +threshold is not given, it default to a value of zero. +.PP +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. +.RE +.PP +The .I max-lease-time statement .RS 0.25i diff --git a/server/dhcpv6.c b/server/dhcpv6.c index d1a425530..112d1e1c6 100644 --- a/server/dhcpv6.c +++ b/server/dhcpv6.c @@ -796,6 +796,99 @@ set_status_code(u_int16_t status_code, const char *status_message, return ret_val; } +void check_pool6_threshold(struct reply_state *reply, + struct iasubopt *lease) +{ + struct ipv6_pond *pond; + int used, count, high_threshold, poolhigh = 0, poollow = 0; + char *shared_name = "no name"; + char tmp_addr[INET6_ADDRSTRLEN]; + + if ((lease->ipv6_pool == NULL) || (lease->ipv6_pool->ipv6_pond == NULL)) + return; + pond = lease->ipv6_pool->ipv6_pond; + + count = pond->num_total; + used = pond->num_active; + + /* 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 + * things. When we cross the high threshold we determine what + * the low threshold is and save it into the low_threshold value. + * When we cross that threshold we reset the logged flag and + * the low_threshold to 0 which allows the high threshold message + * to be emitted once again. + * if we haven't recrossed the boundry we don't need to do anything. + */ + if (pond->logged !=0) { + if (used <= pond->low_threshold) { + pond->low_threshold = 0; + pond->logged = 0; + log_error("Pool threshold reset - shared subnet: %s; " + "address: %s; low threshold %d/%d.", + shared_name, + inet_ntop(AF_INET6, &lease->addr, + tmp_addr, sizeof(tmp_addr)), + used, count); + } + return; + } + + /* find the high threshold */ + if (get_option_int(&poolhigh, &server_universe, reply->packet, NULL, + NULL, reply->packet->options, reply->opt_state, + reply->opt_state, &lease->scope, + SV_LOG_THRESHOLD_HIGH, MDL) == 0) { + /* no threshold bail out */ + return; + } + + /* We do have a threshold for this pool, see if its valid */ + if ((poolhigh <= 0) || (poolhigh > 100)) { + /* not valid */ + return; + } + + /* we have a valid value, have we exceeded it */ + high_threshold = FIND_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.", + shared_name, + inet_ntop(AF_INET6, &lease->addr, tmp_addr, sizeof(tmp_addr)), + poolhigh, used, count); + + /* handle the low threshold now, if we don't + * have one we default to 0. */ + if ((get_option_int(&poollow, &server_universe, reply->packet, NULL, + NULL, reply->packet->options, reply->opt_state, + reply->opt_state, &lease->scope, + SV_LOG_THRESHOLD_LOW, MDL) == 0) || + (poollow > 100)) { + poollow = 0; + } + + /* + * If the low theshold is higher than the high threshold we continue to log + * If it isn't then we set the flag saying we already logged and determine + * what the reset threshold is. + */ + if (poollow < poolhigh) { + pond->logged = 1; + pond->low_threshold = FIND_PERCENT(count, poollow); + } +} + /* * We have a set of operations we do to set up the reply packet, which * is the same for many message types. @@ -1939,6 +2032,8 @@ reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) { tmp, NULL, reply->opt_state); } #endif + /* Do our threshold check. */ + check_pool6_threshold(reply, tmp); } /* Remove any old ia from the hash. */ @@ -2655,6 +2750,8 @@ reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) { tmp, NULL, reply->opt_state); } #endif + /* Do our threshold check. */ + check_pool6_threshold(reply, tmp); } /* Remove any old ia from the hash. */ @@ -3713,6 +3810,9 @@ reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia) { executable_statement_dereference (&tmp->on_star.on_commit, MDL); } + + /* Do our threshold check. */ + check_pool6_threshold(reply, tmp); } /* Remove any old ia from the hash. */ diff --git a/server/mdb6.c b/server/mdb6.c index 58fec01ca..6eac7fca3 100644 --- a/server/mdb6.c +++ b/server/mdb6.c @@ -1188,6 +1188,8 @@ cleanup_lease6(ia_hash_t *ia_table, */ isc_heap_delete(pool->active_timeouts, test_iasubopt->heap_index); pool->num_active--; + if (pool->ipv6_pond) + pool->ipv6_pond->num_active--; iasubopt_hash_delete(pool->leases, &test_iasubopt->addr, sizeof(test_iasubopt->addr), MDL); @@ -1253,6 +1255,8 @@ add_lease6(struct ipv6_pool *pool, struct iasubopt *lease, isc_heap_delete(pool->active_timeouts, test_iasubopt->heap_index); pool->num_active--; + if (pool->ipv6_pond) + pool->ipv6_pond->num_active--; } else { isc_heap_delete(pool->inactive_timeouts, test_iasubopt->heap_index); @@ -1287,8 +1291,12 @@ add_lease6(struct ipv6_pool *pool, struct iasubopt *lease, sizeof(tmp_iasubopt->addr), lease, MDL); insert_result = isc_heap_insert(pool->active_timeouts, tmp_iasubopt); - if (insert_result == ISC_R_SUCCESS) + if (insert_result == ISC_R_SUCCESS) { pool->num_active++; + if (pool->ipv6_pond) + pool->ipv6_pond->num_active++; + } + } else { tmp_iasubopt->soft_lifetime_end_time = valid_lifetime_end_time; insert_result = isc_heap_insert(pool->inactive_timeouts, @@ -1377,6 +1385,8 @@ move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) { pool->num_active++; pool->num_inactive--; lease->state = FTS_ACTIVE; + if (pool->ipv6_pond) + pool->ipv6_pond->num_active++; } return insert_result; } @@ -1503,6 +1513,8 @@ move_lease_to_inactive(struct ipv6_pool *pool, struct iasubopt *lease, lease->state = state; pool->num_active--; pool->num_inactive++; + if (pool->ipv6_pond) + pool->ipv6_pond->num_active--; } return insert_result; } diff --git a/server/stables.c b/server/stables.c index 4a5a13b1a..f9b3a511c 100644 --- a/server/stables.c +++ b/server/stables.c @@ -265,6 +265,8 @@ static struct option server_options[] = { { "ddns-local-address4", "I", &server_universe, 80, 1 }, { "ddns-local-address6", "6", &server_universe, 81, 1 }, { "ignore-client-uids", "f", &server_universe, 82, 1 }, + { "log-threshold-low", "B", &server_universe, 83, 1 }, + { "log-threshold-high", "B", &server_universe, 84, 1 }, { NULL, NULL, NULL, 0, 0 } }; -- 2.47.2