2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 22:45:39 +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 5370. [bug] Deactivation of a netmgr handle associated with a
socket could be skipped in some circumstances. socket could be skipped in some circumstances.
Fixed by deactivating the netmgr handle before Fixed by deactivating the netmgr handle before

View File

@@ -55,7 +55,12 @@
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<para> <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> </para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>

View File

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