]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
incrementally clean up old RPZ records during updates
authorEvan Hunt <each@isc.org>
Tue, 31 Mar 2020 22:04:20 +0000 (15:04 -0700)
committerEvan Hunt <each@isc.org>
Wed, 1 Apr 2020 02:41:41 +0000 (19:41 -0700)
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.

lib/dns/rpz.c

index a16b760ee4427fdb409665477e2166ab3bc8d54f..d970fa75ab5ee250e852caf6e54b8ca55418503a 100644 (file)
@@ -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, &region);
-               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, &region);
+               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);