summaryrefslogtreecommitdiff
path: root/lib/dns/adb.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/adb.c')
-rw-r--r--lib/dns/adb.c320
1 files changed, 308 insertions, 12 deletions
diff --git a/lib/dns/adb.c b/lib/dns/adb.c
index da77bb6c92a5..ec4af599da93 100644
--- a/lib/dns/adb.c
+++ b/lib/dns/adb.c
@@ -30,6 +30,7 @@
#include <isc/mutexblock.h>
#include <isc/netaddr.h>
+#include <isc/print.h>
#include <isc/random.h>
#include <isc/stats.h>
#include <isc/string.h> /* Required for HP/UX (and others?) */
@@ -162,6 +163,14 @@ struct dns_adb {
isc_boolean_t growentries_sent;
isc_event_t grownames;
isc_boolean_t grownames_sent;
+
+#ifdef ENABLE_FETCHLIMIT
+ isc_uint32_t quota;
+ isc_uint32_t atr_freq;
+ double atr_low;
+ double atr_high;
+ double atr_discount;
+#endif /* ENABLE_FETCHLIMIT */
};
/*
@@ -238,9 +247,21 @@ struct dns_adbentry {
int lock_bucket;
unsigned int refcnt;
+ unsigned int nh;
unsigned int flags;
unsigned int srtt;
+
+ unsigned int timeouts;
+ unsigned int completed;
+
+#ifdef ENABLE_FETCHLIMIT
+ isc_uint8_t mode;
+ isc_uint32_t quota;
+ isc_uint32_t active;
+ double atr;
+#endif /* ENABLE_FETCHLIMIT */
+
isc_sockaddr_t sockaddr;
isc_stdtime_t expires;
@@ -285,6 +306,7 @@ static inline dns_adbentry_t *find_entry_and_lock(dns_adb_t *,
static void dump_adb(dns_adb_t *, FILE *, isc_boolean_t debug, isc_stdtime_t);
static void print_dns_name(FILE *, dns_name_t *);
static void print_namehook_list(FILE *, const char *legend,
+ dns_adb_t *adb,
dns_adbnamehooklist_t *list,
isc_boolean_t debug,
isc_stdtime_t now);
@@ -320,10 +342,15 @@ static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);
static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);
static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t);
static void water(void *, int);
-static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t);
+static void dump_entry(FILE *, dns_adb_t *, dns_adbentry_t *,
+ isc_boolean_t, isc_stdtime_t);
static void adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt,
unsigned int factor, isc_stdtime_t now);
static void shutdown_task(isc_task_t *task, isc_event_t *ev);
+#ifdef ENABLE_FETCHLIMIT
+static void log_quota(dns_adbentry_t *entry, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(2, 3);
+#endif /* ENABLE_FETCHLIMIT */
/*
* MUST NOT overlap DNS_ADBFIND_* flags!
@@ -887,6 +914,7 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
entry->sockaddr = sockaddr;
entry->refcnt = 1;
+ entry->nh = 1;
nh->entry = entry;
@@ -899,6 +927,7 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
break;
if (anh == NULL) {
foundentry->refcnt++;
+ foundentry->nh++;
nh->entry = foundentry;
} else
free_adbnamehook(adb, &nh);
@@ -1293,6 +1322,7 @@ clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
LOCK(&adb->entrylocks[addr_bucket]);
}
+ entry->nh--;
result = dec_entry_refcnt(adb, overmem, entry,
ISC_FALSE);
}
@@ -1479,6 +1509,7 @@ clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype,
ev, task, find);
isc_task_sendanddetach(&task, (isc_event_t **)&ev);
+ find->flags |= FIND_EVENT_SENT;
} else {
DP(DEF_LEVEL, "cfan: skipping find %p", find);
}
@@ -1759,11 +1790,20 @@ new_adbentry(dns_adb_t *adb) {
e->magic = DNS_ADBENTRY_MAGIC;
e->lock_bucket = DNS_ADB_INVALIDBUCKET;
e->refcnt = 0;
+ e->nh = 0;
e->flags = 0;
+ e->completed = 0;
+ e->timeouts = 0;
isc_random_get(&r);
e->srtt = (r & 0x1f) + 1;
e->lastage = 0;
e->expires = 0;
+#ifdef ENABLE_FETCHLIMIT
+ e->active = 0;
+ e->mode = 0;
+ e->quota = adb->quota;
+ e->atr = 0.0;
+#endif /* ENABLE_FETCHLIMIT */
ISC_LIST_INIT(e->lameinfo);
ISC_LINK_INIT(e, plink);
LOCK(&adb->entriescntlock);
@@ -2072,6 +2112,27 @@ entry_is_lame(dns_adb_t *adb, dns_adbentry_t *entry, dns_name_t *qname,
return (is_bad);
}
+#ifdef ENABLE_FETCHLIMIT
+static void
+log_quota(dns_adbentry_t *entry, const char *fmt, ...) {
+ va_list ap;
+ char msgbuf[2048];
+ char addrbuf[ISC_NETADDR_FORMATSIZE];
+ isc_netaddr_t netaddr;
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ va_end(ap);
+
+ isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr);
+ isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
+ ISC_LOG_INFO, "adb: quota %s (%d/%d): %s",
+ addrbuf, entry->active, entry->quota, msgbuf);
+}
+#endif /* ENABLE_FETCHLIMIT */
+
static void
copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname,
dns_rdatatype_t qtype, dns_adbname_t *name,
@@ -2092,6 +2153,17 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname,
INSIST(bucket != DNS_ADB_INVALIDBUCKET);
LOCK(&adb->entrylocks[bucket]);
+#ifdef ENABLE_FETCHLIMIT
+ if (entry->quota != 0 &&
+ entry->active >= entry->quota)
+ {
+ find->options |=
+ (DNS_ADBFIND_LAMEPRUNED|
+ DNS_ADBFIND_OVERQUOTA);
+ goto nextv4;
+ }
+#endif /* ENABLE_FETCHLIMIT */
+
if (!FIND_RETURNLAME(find)
&& entry_is_lame(adb, entry, qname, qtype, now)) {
find->options |= DNS_ADBFIND_LAMEPRUNED;
@@ -2123,6 +2195,17 @@ copy_namehook_lists(dns_adb_t *adb, dns_adbfind_t *find, dns_name_t *qname,
INSIST(bucket != DNS_ADB_INVALIDBUCKET);
LOCK(&adb->entrylocks[bucket]);
+#ifdef ENABLE_FETCHLIMIT
+ if (entry->quota != 0 &&
+ entry->active >= entry->quota)
+ {
+ find->options |=
+ (DNS_ADBFIND_LAMEPRUNED|
+ DNS_ADBFIND_OVERQUOTA);
+ goto nextv6;
+ }
+#endif /* ENABLE_FETCHLIMIT */
+
if (!FIND_RETURNLAME(find)
&& entry_is_lame(adb, entry, qname, qtype, now)) {
find->options |= DNS_ADBFIND_LAMEPRUNED;
@@ -2455,6 +2538,14 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
adb, NULL, NULL);
adb->growentries_sent = ISC_FALSE;
+#ifdef ENABLE_FETCHLIMIT
+ adb->quota = 0;
+ adb->atr_freq = 0;
+ adb->atr_low = 0.0;
+ adb->atr_high = 0.0;
+ adb->atr_discount = 0.0;
+#endif /* ENABLE_FETCHLIMIT */
+
adb->nnames = nbuckets[0];
adb->namescnt = 0;
adb->names = NULL;
@@ -2474,7 +2565,6 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
nbuckets[11]);
adb->nentries = nbuckets[11];
adb->nnames = nbuckets[11];
-
}
isc_mem_attach(mem, &adb->mctx);
@@ -2667,6 +2757,8 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
fail0c:
DESTROYLOCK(&adb->lock);
fail0b:
+ if (adb->excl != NULL)
+ isc_task_detach(&adb->excl);
isc_mem_putanddetach(&adb->mctx, adb, sizeof(dns_adb_t));
return (result);
@@ -3349,14 +3441,15 @@ dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
fprintf(f, "\n");
- print_namehook_list(f, "v4", &name->v4, debug, now);
- print_namehook_list(f, "v6", &name->v6, debug, now);
+ print_namehook_list(f, "v4", adb,
+ &name->v4, debug, now);
+ print_namehook_list(f, "v6", adb,
+ &name->v6, debug, now);
if (debug)
print_fetch_list(f, name);
if (debug)
print_find_list(f, name);
-
}
}
@@ -3365,8 +3458,8 @@ dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
for (i = 0; i < adb->nentries; i++) {
entry = ISC_LIST_HEAD(adb->entries[i]);
while (entry != NULL) {
- if (entry->refcnt == 0)
- dump_entry(f, entry, debug, now);
+ if (entry->nh == 0)
+ dump_entry(f, adb, entry, debug, now);
entry = ISC_LIST_NEXT(entry, plink);
}
}
@@ -3381,14 +3474,18 @@ dump_adb(dns_adb_t *adb, FILE *f, isc_boolean_t debug, isc_stdtime_t now) {
}
static void
-dump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug,
- isc_stdtime_t now)
+dump_entry(FILE *f, dns_adb_t *adb, dns_adbentry_t *entry,
+ isc_boolean_t debug, isc_stdtime_t now)
{
char addrbuf[ISC_NETADDR_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
isc_netaddr_t netaddr;
dns_adblameinfo_t *li;
+#ifndef ENABLE_FETCHLIMIT
+ UNUSED(adb);
+#endif /* !ENABLE_FETCHLIMIT */
+
isc_netaddr_fromsockaddr(&netaddr, &entry->sockaddr);
isc_netaddr_format(&netaddr, addrbuf, sizeof(addrbuf));
@@ -3399,10 +3496,19 @@ dump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug,
addrbuf, entry->srtt, entry->flags);
if (entry->expires != 0)
fprintf(f, " [ttl %d]", entry->expires - now);
+
+#ifdef ENABLE_FETCHLIMIT
+ if (adb != NULL && adb->quota != 0 && adb->atr_freq != 0) {
+ fprintf(f, " [atr %0.2f] [quota %d]",
+ entry->atr, entry->quota);
+ }
+#endif /* ENABLE_FETCHLIMIT */
+
fprintf(f, "\n");
for (li = ISC_LIST_HEAD(entry->lameinfo);
li != NULL;
- li = ISC_LIST_NEXT(li, plink)) {
+ li = ISC_LIST_NEXT(li, plink))
+ {
fprintf(f, ";\t\t");
print_dns_name(f, &li->qname);
dns_rdatatype_format(li->qtype, typebuf, sizeof(typebuf));
@@ -3474,7 +3580,8 @@ print_dns_name(FILE *f, dns_name_t *name) {
}
static void
-print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list,
+print_namehook_list(FILE *f, const char *legend,
+ dns_adb_t *adb, dns_adbnamehooklist_t *list,
isc_boolean_t debug, isc_stdtime_t now)
{
dns_adbnamehook_t *nh;
@@ -3485,7 +3592,7 @@ print_namehook_list(FILE *f, const char *legend, dns_adbnamehooklist_t *list,
{
if (debug)
fprintf(f, ";\tHook(%s) %p\n", legend, nh);
- dump_entry(f, nh->entry, debug, now);
+ dump_entry(f, adb, nh->entry, debug, now);
}
}
@@ -4051,6 +4158,114 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
UNLOCK(&adb->entrylocks[bucket]);
}
+#ifdef ENABLE_FETCHLIMIT
+/*
+ * (10000 / ((10 + n) / 10)^(3/2)) for n in 0..99.
+ * These will be used to make quota adjustments.
+ */
+static int quota_adj[] = {
+ 10000, 8668, 7607, 6747, 6037, 5443, 4941, 4512, 4141,
+ 3818, 3536, 3286, 3065, 2867, 2690, 2530, 2385, 2254,
+ 2134, 2025, 1925, 1832, 1747, 1668, 1595, 1527, 1464,
+ 1405, 1350, 1298, 1250, 1205, 1162, 1121, 1083, 1048,
+ 1014, 981, 922, 894, 868, 843, 820, 797, 775, 755,
+ 735, 716, 698, 680, 664, 648, 632, 618, 603, 590, 577,
+ 564, 552, 540, 529, 518, 507, 497, 487, 477, 468, 459,
+ 450, 442, 434, 426, 418, 411, 404, 397, 390, 383, 377,
+ 370, 364, 358, 353, 347, 342, 336, 331, 326, 321, 316,
+ 312, 307, 303, 298, 294, 290, 286, 282, 278
+};
+
+/*
+ * Caller must hold adbentry lock
+ */
+static void
+maybe_adjust_quota(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
+ isc_boolean_t timeout)
+{
+ double tr;
+
+ UNUSED(adb);
+
+ if (adb->quota == 0 || adb->atr_freq == 0)
+ return;
+
+ if (timeout)
+ addr->entry->timeouts++;
+
+ if (addr->entry->completed++ <= adb->atr_freq)
+ return;
+
+ /*
+ * Calculate an exponential rolling average of the timeout ratio
+ *
+ * XXX: Integer arithmetic might be better than floating point
+ */
+ tr = (double) addr->entry->timeouts / addr->entry->completed;
+ addr->entry->timeouts = addr->entry->completed = 0;
+ INSIST(addr->entry->atr >= 0.0);
+ INSIST(addr->entry->atr <= 1.0);
+ INSIST(adb->atr_discount >= 0.0);
+ INSIST(adb->atr_discount <= 1.0);
+ addr->entry->atr *= 1.0 - adb->atr_discount;
+ addr->entry->atr += tr * adb->atr_discount;
+ addr->entry->atr = ISC_CLAMP(addr->entry->atr, 0.0, 1.0);
+
+ if (addr->entry->atr < adb->atr_low && addr->entry->mode > 0) {
+ addr->entry->quota = adb->quota *
+ quota_adj[--addr->entry->mode] / 10000;
+ log_quota(addr->entry, "atr %0.2f, quota increased to %d",
+ addr->entry->atr, addr->entry->quota);
+ } else if (addr->entry->atr > adb->atr_high && addr->entry->mode < 99) {
+ addr->entry->quota = adb->quota *
+ quota_adj[++addr->entry->mode] / 10000;
+ log_quota(addr->entry, "atr %0.2f, quota decreased to %d",
+ addr->entry->atr, addr->entry->quota);
+ }
+
+ /* Ensure we don't drop to zero */
+ if (addr->entry->quota == 0)
+ addr->entry->quota = 1;
+}
+#endif /* ENABLE_FETCHLIMIT */
+
+void
+dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+#ifdef ENABLE_FETCHLIMIT
+ maybe_adjust_quota(adb, addr, ISC_FALSE);
+#endif /* ENABLE_FETCHLIMIT */
+
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+void
+dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+#ifdef ENABLE_FETCHLIMIT
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+ maybe_adjust_quota(adb, addr, ISC_TRUE);
+ UNLOCK(&adb->entrylocks[bucket]);
+#else
+ UNUSED(adb);
+ UNUSED(addr);
+
+ return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
isc_result_t
dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa,
dns_adbaddrinfo_t **addrp, isc_stdtime_t now)
@@ -4230,3 +4445,84 @@ dns_adb_setadbsize(dns_adb_t *adb, size_t size) {
else
isc_mem_setwater(adb->mctx, water, adb, hiwater, lowater);
}
+
+void
+dns_adb_setquota(dns_adb_t *adb, isc_uint32_t quota, isc_uint32_t freq,
+ double low, double high, double discount)
+{
+#ifdef ENABLE_FETCHLIMIT
+ REQUIRE(DNS_ADB_VALID(adb));
+
+ adb->quota = quota;
+ adb->atr_freq = freq;
+ adb->atr_low = low;
+ adb->atr_high = high;
+ adb->atr_discount = discount;
+#else
+ UNUSED(adb);
+ UNUSED(quota);
+ UNUSED(freq);
+ UNUSED(low);
+ UNUSED(high);
+ UNUSED(discount);
+
+ return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
+isc_boolean_t
+dns_adbentry_overquota(dns_adbentry_t *entry) {
+#ifdef ENABLE_FETCHLIMIT
+ isc_boolean_t block;
+ REQUIRE(DNS_ADBENTRY_VALID(entry));
+ block = ISC_TF(entry->quota != 0 && entry->active >= entry->quota);
+ return (block);
+#else
+ UNUSED(entry);
+
+ return (ISC_FALSE);
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
+void
+dns_adb_beginudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+#ifdef ENABLE_FETCHLIMIT
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+
+ LOCK(&adb->entrylocks[bucket]);
+ addr->entry->active++;
+ UNLOCK(&adb->entrylocks[bucket]);
+#else
+ UNUSED(adb);
+ UNUSED(addr);
+
+ return;
+#endif /* !ENABLE_FETCHLIMIT */
+}
+
+void
+dns_adb_endudpfetch(dns_adb_t *adb, dns_adbaddrinfo_t *addr) {
+#ifdef ENABLE_FETCHLIMIT
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+
+ LOCK(&adb->entrylocks[bucket]);
+ if (addr->entry->active > 0)
+ addr->entry->active--;
+ UNLOCK(&adb->entrylocks[bucket]);
+#else
+ UNUSED(adb);
+ UNUSED(addr);
+
+ return;
+#endif /* !ENABLE_FETCHLIMIT */
+}