From: Evan Hunt Date: Tue, 31 Mar 2020 22:04:20 +0000 (-0700) Subject: incrementally clean up old RPZ records during updates X-Git-Tag: v9.17.1~16^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=32da119ed84ad93aa5d754ccc88a3257e2cba10a;p=thirdparty%2Fbind9.git incrementally clean up old RPZ records during updates After an RPZ zone is updated via zone transfer, the RPZ summary database is updated, inserting the newly added names in the policy zone and deleting the newly removed ones. The first part of this was quantized so it would not run too long and starve other tasks during large updates, but the second part was not quantized, so that an update in which a large number of records were deleted could cause named to become briefly unresponsive. --- diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c index a16b760ee44..d970fa75ab5 100644 --- a/lib/dns/rpz.c +++ b/lib/dns/rpz.c @@ -1781,58 +1781,18 @@ 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; - - /* - * 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) { - char domain[DNS_NAME_FORMATSIZE]; - - 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; - result = isc_ht_iter_delcurrent_next(iter)) - { - isc_region_t region; - unsigned char *key = NULL; - size_t keysize; - - isc_ht_iter_currentkey(iter, &key, &keysize); - region.base = key; - region.length = (unsigned int)keysize; - dns_name_fromregion(name, ®ion); - dns_rpz_delete(rpz->rpzs, rpz->num, name); - } - - tmpht = rpz->nodes; - rpz->nodes = rpz->newnodes; - rpz->newnodes = tmpht; - LOCK(&rpz->rpzs->maint_lock); rpz->updaterunning = false; + /* - * If there's an update pending schedule it + * 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; + char dname[DNS_NAME_FORMATSIZE]; isc_interval_t interval; + dns_name_format(&rpz->origin, dname, DNS_NAME_FORMATSIZE); isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, @@ -1845,7 +1805,7 @@ finish_update(dns_rpz_zone_t *rpz) { isc_timer_reset(rpz->updatetimer, isc_timertype_once, NULL, &interval, true); } else { - isc_event_t *event; + isc_event_t *event = NULL; INSIST(!ISC_LINK_LINKED(&rpz->updateevent, ev_link)); ISC_EVENT_INIT(&rpz->updateevent, sizeof(rpz->updateevent), 0, NULL, @@ -1857,21 +1817,118 @@ finish_update(dns_rpz_zone_t *rpz) { } } 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 && count++ < DNS_RPZ_QUANTUM; + result = isc_ht_iter_delcurrent_next(iter)) + { + isc_region_t region; + unsigned char *key = NULL; + size_t keysize; + + isc_ht_iter_currentkey(iter, &key, &keysize); + region.base = key; + region.length = (unsigned int)keysize; + dns_name_fromregion(name, ®ion); + dns_rpz_delete(rpz->rpzs, rpz->num, name); + } + + if (result == ISC_R_SUCCESS) { + isc_event_t *nevent = NULL; + + /* + * 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); + } + + /* + * 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);