add an update quota

limit the number of simultaneous DNS UPDATE events that can be
processed by adding a quota for update and update forwarding.
this quota currently, arbitrarily, defaults to 100.

also add a statistics counter to record when the update quota
has been exceeded.
This commit is contained in:
Evan Hunt 2022-09-01 16:05:04 -07:00 committed by Michał Kępień
parent 6ecc0a2693
commit 7c47254a14
7 changed files with 52 additions and 5 deletions

View file

@ -15,7 +15,7 @@
<xsl:output method="html" indent="yes" version="4.0"/>
<!-- the version number **below** must match version in bin/named/statschannel.c -->
<!-- don't forget to update "/xml/v<STATS_XML_VERSION_MAJOR>" in the HTTP endpoints listed below -->
<xsl:template match="statistics[@version=&quot;3.12&quot;]">
<xsl:template match="statistics[@version=&quot;3.13&quot;]">
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

View file

@ -56,11 +56,11 @@
#include "xsl_p.h"
#define STATS_XML_VERSION_MAJOR "3"
#define STATS_XML_VERSION_MINOR "12"
#define STATS_XML_VERSION_MINOR "13"
#define STATS_XML_VERSION STATS_XML_VERSION_MAJOR "." STATS_XML_VERSION_MINOR
#define STATS_JSON_VERSION_MAJOR "1"
#define STATS_JSON_VERSION_MINOR "6"
#define STATS_JSON_VERSION_MINOR "7"
#define STATS_JSON_VERSION STATS_JSON_VERSION_MAJOR "." STATS_JSON_VERSION_MINOR
#define CHECK(m) \
@ -352,6 +352,7 @@ init_desc(void) {
SET_NSSTATDESC(reclimitdropped,
"queries dropped due to recursive client limit",
"RecLimitDropped");
SET_NSSTATDESC(updatequota, "Update quota exceeded", "UpdateQuota");
INSIST(i == ns_statscounter_max);

View file

@ -7838,6 +7838,11 @@ Name Server Statistics Counters
``UpdateBadPrereq``
This indicates the number of dynamic updates rejected due to a prerequisite failure.
``UpdateQuota``
This indicates the number of times a dynamic update or update
forwarding request was rejected because the number of pending
requests exceeded :any:`update-quota`.
``RateDropped``
This indicates the number of responses dropped due to rate limits.

View file

@ -84,6 +84,7 @@ struct ns_server {
isc_quota_t recursionquota;
isc_quota_t tcpquota;
isc_quota_t xfroutquota;
isc_quota_t updquota;
ISC_LIST(isc_quota_t) http_quotas;
isc_mutex_t http_quotas_lock;

View file

@ -107,7 +107,9 @@ enum {
ns_statscounter_reclimitdropped = 66,
ns_statscounter_max = 67,
ns_statscounter_updatequota = 67,
ns_statscounter_max = 68,
};
void

View file

@ -61,6 +61,7 @@ ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview,
isc_quota_init(&sctx->xfroutquota, 10);
isc_quota_init(&sctx->tcpquota, 10);
isc_quota_init(&sctx->recursionquota, 100);
isc_quota_init(&sctx->updquota, 100);
ISC_LIST_INIT(sctx->http_quotas);
isc_mutex_init(&sctx->http_quotas_lock);
@ -133,6 +134,7 @@ ns_server_detach(ns_server_t **sctxp) {
isc_mem_put(sctx->mctx, altsecret, sizeof(*altsecret));
}
isc_quota_destroy(&sctx->updquota);
isc_quota_destroy(&sctx->recursionquota);
isc_quota_destroy(&sctx->tcpquota);
isc_quota_destroy(&sctx->xfroutquota);

View file

@ -1644,6 +1644,19 @@ send_update_event(ns_client_t *client, dns_zone_t *zone) {
update_event_t *event = NULL;
isc_task_t *zonetask = NULL;
result = isc_quota_attach(&client->manager->sctx->updquota,
&(isc_quota_t *){ NULL });
if (result != ISC_R_SUCCESS) {
update_log(client, zone, LOGLEVEL_PROTOCOL,
"update failed: too many DNS UPDATEs queued (%s)",
isc_result_totext(result));
ns_stats_increment(client->manager->sctx->nsstats,
ns_statscounter_updatequota);
ns_client_drop(client, result);
isc_nmhandle_detach(&client->reqhandle);
return (DNS_R_DROP);
}
event = (update_event_t *)isc_event_allocate(
client->manager->mctx, client, DNS_EVENT_UPDATE, update_action,
NULL, sizeof(*event));
@ -1780,12 +1793,19 @@ failure:
dns_zone_gettype(zone) == dns_zone_mirror);
inc_stats(client, zone, ns_statscounter_updaterej);
}
/*
* We failed without having sent an update event to the zone.
* We are still in the client task context, so we can
* simply give an error response without switching tasks.
*/
respond(client, result);
if (result == DNS_R_DROP) {
ns_client_drop(client, result);
isc_nmhandle_detach(&client->reqhandle);
} else {
respond(client, result);
}
if (zone != NULL) {
dns_zone_detach(&zone);
}
@ -3582,6 +3602,7 @@ updatedone_action(isc_task_t *task, isc_event_t *event) {
respond(client, uev->result);
isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
isc_event_free(&event);
isc_nmhandle_detach(&client->updatehandle);
}
@ -3596,6 +3617,8 @@ forward_fail(isc_task_t *task, isc_event_t *event) {
UNUSED(task);
respond(client, DNS_R_SERVFAIL);
isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
isc_event_free(&event);
isc_nmhandle_detach(&client->updatehandle);
}
@ -3631,6 +3654,8 @@ forward_done(isc_task_t *task, isc_event_t *event) {
ns_client_sendraw(client, uev->answer);
dns_message_detach(&uev->answer);
isc_quota_detach(&(isc_quota_t *){ &client->manager->sctx->updquota });
isc_event_free(&event);
isc_nmhandle_detach(&client->reqhandle);
isc_nmhandle_detach(&client->updatehandle);
@ -3666,6 +3691,17 @@ send_forward_event(ns_client_t *client, dns_zone_t *zone) {
update_event_t *event = NULL;
isc_task_t *zonetask = NULL;
result = isc_quota_attach(&client->manager->sctx->updquota,
&(isc_quota_t *){ NULL });
if (result != ISC_R_SUCCESS) {
update_log(client, zone, LOGLEVEL_PROTOCOL,
"update failed: too many DNS UPDATEs queued (%s)",
isc_result_totext(result));
ns_stats_increment(client->manager->sctx->nsstats,
ns_statscounter_updatequota);
return (DNS_R_DROP);
}
event = (update_event_t *)isc_event_allocate(
client->manager->mctx, client, DNS_EVENT_UPDATE, forward_action,
NULL, sizeof(*event));