2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 14:35:26 +00:00

Merge branch '1447-incremental-rpz-update' into 'master'

incrementally clean up old RPZ records during updates

Closes #1447

See merge request isc-projects/bind9!3318
This commit is contained in:
Evan Hunt
2020-04-01 03:38:31 +00:00
3 changed files with 138 additions and 66 deletions

View File

@@ -1,3 +1,8 @@
5371. [bug] Improve incremental updates of the RPZ summary
database to reduce delays that could occur when
a policy zone update included a large number of
record deletions. [GL #1447]
5370. [bug] Deactivation of a netmgr handle associated with a
socket could be skipped in some circumstances.
Fixed by deactivating the netmgr handle before

View File

@@ -55,7 +55,12 @@
<itemizedlist>
<listitem>
<para>
None.
When an RPZ policy zone was updated via zone transfer
and a large number of records were deleted, <command>named</command>
could become nonresponsive for a short period while deleted
names were removed from the RPZ summary database. This database
cleanup is now done incrementally over a longer period of time,
reducing such delays. [GL #1447]
</para>
</listitem>
</itemizedlist>

View File

@@ -1781,32 +1781,85 @@ cleanup:
static void
finish_update(dns_rpz_zone_t *rpz) {
isc_result_t result;
isc_ht_t *tmpht = NULL;
isc_ht_iter_t *iter = NULL;
dns_fixedname_t fname;
char dname[DNS_NAME_FORMATSIZE];
dns_name_t *name;
LOCK(&rpz->rpzs->maint_lock);
rpz->updaterunning = false;
/*
* Iterate over old ht with existing nodes deleted to delete
* deleted nodes from RPZ
* If there's an update pending, schedule it.
*/
result = isc_ht_iter_create(rpz->nodes, &iter);
if (result != ISC_R_SUCCESS) {
char domain[DNS_NAME_FORMATSIZE];
if (rpz->updatepending == true) {
if (rpz->min_update_interval > 0) {
uint64_t defer = rpz->min_update_interval;
char dname[DNS_NAME_FORMATSIZE];
isc_interval_t interval;
dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
"rpz: %s: failed to create HT iterator - %s",
domain, isc_result_totext(result));
goto cleanup;
dns_name_format(&rpz->origin, dname,
DNS_NAME_FORMATSIZE);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
"rpz: %s: new zone version came "
"too soon, deferring update for "
"%" PRIu64 " seconds",
dname, defer);
isc_interval_set(&interval, (unsigned int)defer, 0);
isc_timer_reset(rpz->updatetimer, isc_timertype_once,
NULL, &interval, true);
} else {
isc_event_t *event = NULL;
INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link));
ISC_EVENT_INIT(&rpz->updateevent,
sizeof(rpz->updateevent), 0, NULL,
DNS_EVENT_RPZUPDATED,
dns_rpz_update_taskaction, rpz, rpz,
NULL, NULL);
event = &rpz->updateevent;
isc_task_send(rpz->rpzs->updater, &event);
}
}
UNLOCK(&rpz->rpzs->maint_lock);
}
static void
cleanup_quantum(isc_task_t *task, isc_event_t *event) {
isc_result_t result = ISC_R_SUCCESS;
char domain[DNS_NAME_FORMATSIZE];
dns_rpz_zone_t *rpz = NULL;
isc_ht_iter_t *iter = NULL;
dns_fixedname_t fname;
dns_name_t *name = NULL;
int count = 0;
UNUSED(task);
REQUIRE(event != NULL);
REQUIRE(event->ev_sender != NULL);
rpz = (dns_rpz_zone_t *)event->ev_sender;
iter = (isc_ht_iter_t *)event->ev_arg;
isc_event_free(&event);
if (iter == NULL) {
/*
* Iterate over old ht with existing nodes deleted to
* delete deleted nodes from RPZ
*/
result = isc_ht_iter_create(rpz->nodes, &iter);
if (result != ISC_R_SUCCESS) {
dns_name_format(&rpz->origin, domain,
DNS_NAME_FORMATSIZE);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTER, ISC_LOG_ERROR,
"rpz: %s: failed to create HT "
"iterator - %s",
domain, isc_result_totext(result));
goto cleanup;
}
}
name = dns_fixedname_initname(&fname);
for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;
for (result = isc_ht_iter_first(iter);
result == ISC_R_SUCCESS && count++ < DNS_RPZ_QUANTUM;
result = isc_ht_iter_delcurrent_next(iter))
{
isc_region_t region;
@@ -1820,58 +1873,62 @@ finish_update(dns_rpz_zone_t *rpz) {
dns_rpz_delete(rpz->rpzs, rpz->num, name);
}
tmpht = rpz->nodes;
rpz->nodes = rpz->newnodes;
rpz->newnodes = tmpht;
if (result == ISC_R_SUCCESS) {
isc_event_t *nevent = NULL;
LOCK(&rpz->rpzs->maint_lock);
rpz->updaterunning = false;
/*
* If there's an update pending schedule it
*/
if (rpz->updatepending == true) {
if (rpz->min_update_interval > 0) {
uint64_t defer = rpz->min_update_interval;
isc_interval_t interval;
dns_name_format(&rpz->origin, dname,
DNS_NAME_FORMATSIZE);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
"rpz: %s: new zone version came "
"too soon, deferring update for "
"%" PRIu64 " seconds",
dname, defer);
isc_interval_set(&interval, (unsigned int)defer, 0);
isc_timer_reset(rpz->updatetimer, isc_timertype_once,
NULL, &interval, true);
} else {
isc_event_t *event;
INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link));
ISC_EVENT_INIT(&rpz->updateevent,
sizeof(rpz->updateevent), 0, NULL,
DNS_EVENT_RPZUPDATED,
dns_rpz_update_taskaction, rpz, rpz,
NULL, NULL);
event = &rpz->updateevent;
isc_task_send(rpz->rpzs->updater, &event);
}
/*
* We finished a quantum; trigger the next one and return.
*/
INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link));
ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), 0,
NULL, DNS_EVENT_RPZUPDATED, cleanup_quantum,
iter, rpz, NULL, NULL);
nevent = &rpz->updateevent;
isc_task_send(rpz->rpzs->updater, &nevent);
return;
} else if (result == ISC_R_NOMORE) {
isc_ht_t *tmpht = NULL;
/*
* Done with cleanup of deleted nodes; finalize
* the update.
*/
tmpht = rpz->nodes;
rpz->nodes = rpz->newnodes;
rpz->newnodes = tmpht;
finish_update(rpz);
dns_name_format(&rpz->origin, domain, DNS_NAME_FORMATSIZE);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
"rpz: %s: reload done", domain);
}
UNLOCK(&rpz->rpzs->maint_lock);
/*
* If we're here, we're finished or something went wrong.
*/
cleanup:
if (iter != NULL) {
isc_ht_iter_destroy(&iter);
}
if (rpz->newnodes != NULL) {
isc_ht_destroy(&rpz->newnodes);
}
dns_db_closeversion(rpz->updb, &rpz->updbversion, false);
dns_db_detach(&rpz->updb);
rpz_detach(&rpz);
}
static void
update_quantum(isc_task_t *task, isc_event_t *event) {
isc_result_t result = ISC_R_SUCCESS;
dns_dbnode_t *node = NULL;
dns_rpz_zone_t *rpz;
dns_rpz_zone_t *rpz = NULL;
char domain[DNS_NAME_FORMATSIZE];
dns_fixedname_t fixname;
dns_name_t *name;
dns_name_t *name = NULL;
isc_event_t *nevent = NULL;
int count = 0;
UNUSED(task);
@@ -1975,13 +2032,13 @@ update_quantum(isc_task_t *task, isc_event_t *event) {
}
if (result == ISC_R_SUCCESS) {
isc_event_t *nevent;
/*
* Pause the iterator so that the DB is not locked
* Pause the iterator so that the DB is not locked.
*/
dns_dbiterator_pause(rpz->updbit);
/*
* We finished a quantum; trigger the next one and return
* We finished a quantum; trigger the next one and return.
*/
INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link));
ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), 0,
@@ -1992,17 +2049,22 @@ update_quantum(isc_task_t *task, isc_event_t *event) {
return;
} else if (result == ISC_R_NOMORE) {
/*
* All done.
* Done with the new database; now we just need to
* clean up the old.
*/
finish_update(rpz);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTER, ISC_LOG_INFO,
"rpz: %s: reload done", domain);
dns_dbiterator_destroy(&rpz->updbit);
INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link));
ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), 0,
NULL, DNS_EVENT_RPZUPDATED, cleanup_quantum,
NULL, rpz, NULL, NULL);
nevent = &rpz->updateevent;
isc_task_send(rpz->rpzs->updater, &nevent);
return;
}
/*
* If we're here, we've either finished or something went wrong,
* so clean up.
* If we're here, something went wrong, so clean up.
*/
if (rpz->updbit != NULL) {
dns_dbiterator_destroy(&rpz->updbit);