adblock: release 4.5.0-1
authorDirk Brenken <dev@brenken.org>
Sun, 18 Jan 2026 22:34:19 +0000 (23:34 +0100)
committerDirk Brenken <dev@brenken.org>
Sun, 18 Jan 2026 22:36:26 +0000 (23:36 +0100)
* added firewall rules based on nftables in a separate isolated nftables table (inet adblock)
  and chains (prerouting), with MAC addresses stored in an nftables set.
  Implemented the following firewall‑integrated features:
  * external DNS Routing (unfiltered): routes DNS queries from selected devices or interfaces
    to an external unfiltered DNS resolver
  * external DNS Routing (filtered): routes DNS queries from selected devices or interfaces
    to an external filtered DNS resolver
  * force DNS: blocks or redirects all external DNS traffic from selected interfaces
    to ensure that clients use the local resolver
* removed the optional generation of an additional jail list (only supported bydnsmasq),
  use the new, resolver independent ext. DNS routing instead
* removed the pz-client-ip feature (only supported by bind),
  use the new, resolver independent ext. DNS routing instead
* removed the obsolete, hardcoded fw4 rules for DNS enforcement
  existing rules will be removed via uci-defaults script after adblock update
* changed the Jail mode to a simple allowlist-only mode
* fixed minor issues in the mail template
* readme update
* LuCI: added a new config tab "Firewall Settings"
* LuCI: fixed minor usability issues

Signed-off-by: Dirk Brenken <dev@brenken.org>
net/adblock/Makefile
net/adblock/files/95-adblock-housekeeping
net/adblock/files/README.md
net/adblock/files/adblock.init
net/adblock/files/adblock.mail
net/adblock/files/adblock.sh

index 60163f89149ed5fd3d6aea28e30cb40eec6d7f76..57565ebca436aab0f81ea6c901fe65477b4015b4 100644 (file)
@@ -1,13 +1,13 @@
 # dns based ad/abuse domain blocking
-# Copyright (c) 2015-2025 Dirk Brenken (dev@brenken.org)
+# Copyright (c) 2015-2026 Dirk Brenken (dev@brenken.org)
 # This is free software, licensed under the GNU General Public License v3.
 #
 
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=adblock
-PKG_VERSION:=4.4.5
-PKG_RELEASE:=2
+PKG_VERSION:=4.5.0
+PKG_RELEASE:=1
 PKG_LICENSE:=GPL-3.0-or-later
 PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
 
@@ -17,7 +17,7 @@ define Package/adblock
        SECTION:=net
        CATEGORY:=Network
        TITLE:=adblock blocks ad/abuse domains by using DNS
-       DEPENDS:=+jshn +jsonfilter +coreutils +coreutils-sort +gawk +ca-bundle +rpcd +rpcd-mod-rpcsys
+       DEPENDS:=+jshn +jsonfilter +firewall4 +coreutils +coreutils-sort +gawk +ca-bundle +rpcd +rpcd-mod-rpcsys
        PKGARCH:=all
 endef
 
index bf62bf90c40735af37f7b3dd53ac64e638afbe29..bb7c62070710ef7646b97e808f22242531913538 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# Copyright (c) 2015-2025 Dirk Brenken (dev@brenken.org)
+# Copyright (c) 2015-2026 Dirk Brenken (dev@brenken.org)
 # This is free software, licensed under the GNU General Public License v3.
 
 # (s)hellcheck exceptions
@@ -11,7 +11,7 @@ export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
 config="adblock"
 old_options="adb_sources adb_forcedns adb_fetchutil adb_hag_sources adb_hst_sources adb_stb_sources adb_utc_sources \
        adb_maxqueue adb_backup adb_dnsfilereset adb_tmpbase adb_mailcnt adb_safesearchmod adb_srcfile adb_srcarc adb_nice \
-       adb_hag_feed"
+       adb_hag_feed adb_jaildir adb_dnsdenyip adb_dnsallowip adb_zonelist adb_portlist adb_dnsforce"
 
 for option in ${old_options}; do
        inplace="0"
@@ -63,10 +63,30 @@ for option in ${old_options}; do
                                                uci -q add_list ${config}.global.adb_hag_feed="wildcard/${value}"
                                        fi
                                ;;
+                               "adb_dnsforce")
+                                       uci -q set ${config}.global.adb_nftforce="${value}"
+                               ;;
+                               "adb_zonelist")
+                                       uci -q set ${config}.global.adb_nftdevforce="${value}"
+                               ;;
+                               "adb_portlist")
+                                       uci -q set ${config}.global.adb_nftportforce="${value}"
+                               ;;
                        esac
                done
                [ "${inplace}" = "0" ] && uci -q delete ${config}.global.${option}
        fi
 done
 [ -n "$(uci -q changes ${config})" ] && uci -q commit ${config}
+
+# remove former adblock-related firewall zones
+#
+fwcfg="$(uci -qNX show "firewall" | awk 'BEGIN{FS="[.=]"};/adblock_/{if(zone==$2){next}else{ORS=" ";zone=$2;print zone}}')"
+for section in ${fwcfg}; do
+       uci -q delete firewall."${section}"
+done
+if [ -n "$(uci -q changes firewall)" ]; then
+       uci -q commit firewall
+       /etc/init.d/firewall reload
+fi
 exit 0
index 7cc43841ba2e5bce9387d5c694693c84df00b8c1..27b52fcd05c13068263f301f4b4d67fcdc9f32a4 100644 (file)
@@ -56,7 +56,6 @@ A lot of people already use adblocker plugins within their desktop browsers, but
 * Supports six different DNS backend formats: dnsmasq, unbound, named (bind), kresd, smartdns or raw (e.g. used by dnscrypt-proxy)
 * Supports three different SSL-enabled download utilities: uclient-fetch, full wget or curl
 * Supports SafeSearch for google, bing, brave, duckduckgo, yandex, youtube and pixabay
-* Supports RPZ-trigger 'RPZ-CLIENT-IP' to always allow/deny certain DNS clients based on their IP address (currently only supported by bind dns backend)
 * Fast downloads & list processing as they are handled in parallel running background jobs with multicore support
 * The download engine supports ETAG headers to download only updated feeds
 * Supports a wide range of router modes, even AP modes are supported
@@ -75,7 +74,6 @@ A lot of people already use adblocker plugins within their desktop browsers, but
 * Provides comprehensive runtime information
 * Provides a detailed DNS Query Report with DNS related information about client requests, top (blocked) domains and more
 * Provides a powerful query function to quickly find blocked (sub-)domains, e.g. to allow certain domains
-* Includes an option to generate an additional, restrictive 'adb_list.jail' to block access to all domains except those listed in the allowlist file. You can use this restrictive blocklist manually e.g. for guest wifi or kidsafe configurations
 * Contains an option to route DNS queries to the local resolver via corresponding firewall rules
 * Automatic blocklist backup & restore, these backups will be used in case of download errors and during startup
 * Send notification E-Mails, see example configuration below
@@ -148,13 +146,11 @@ Available commands:
 | adb_debug          | 0, disabled                        | set to 1 to enable the debug output                                                            |
 | adb_nicelimit      | 0, standard prio.                  | valid nice level range 0-19 of the adblock processes                                           |
 | adb_dnsshift       | 0, disabled                        | shift the blocklist to the backup directory and only set a soft link to this file in memory    |
-| adb_dnsforce       | 0, disabled                        | set to 1 to force DNS requests to the local resolver                                           |
 | adb_dnsdir         | -, auto-detected                   | path for the generated blocklist file 'adb_list.overall'                                       |
 | adb_dnstimeout     | 20                                 | timeout in seconds to wait for a successful DNS backend restart                                |
-| adb_dnsinstance    | 0, first instance                  | set to the relevant dns backend instance used by adblock (dnsmasq only)                        |
+| adb_dnsinstance    | 0, first instance                  | set the relevant dnsmasq backend instance used by adblock                                      |
 | adb_dnsflush       | 0, disabled                        | set to 1 to flush the DNS Cache before & after adblock processing                              |
 | adb_lookupdomain   | localhost                          | domain to check for a successful DNS backend restart                                           |
-| adb_portlist       | 53 853 5353                        | space separated list of firewall ports which should be redirected locally                      |
 | adb_report         | 0, disabled                        | set to 1 to enable the background tcpdump gathering process for reporting                      |
 | adb_map            | 0, disabled                        | enable a GeoIP Map with blocked domains                                                        |
 | adb_reportdir      | /tmp/adblock-report                | path for DNS related report files                                                              |
@@ -167,14 +163,26 @@ Available commands:
 | adb_basedir        | /tmp                               | path for all adblock related runtime operations, e.g. downloading, sorting, merging etc.       |
 | adb_backupdir      | /tmp/adblock-backup                | path for adblock backups                                                                       |
 | adb_safesearch     | 0, disabled                        | enforce SafeSearch for google, bing, brave, duckduckgo, yandex, youtube and pixabay            |
-| adb_safesearchlist | -, not set                         | Limit SafeSearch to certain provider (see above)                                               |
+| adb_safesearchlist | -, not set                         | limit SafeSearch to certain provider (see above)                                               |
 | adb_mail           | 0, disabled                        | set to 1 to enable notification E-Mails in case of a processing errors                         |
 | adb_mailreceiver   | -, not set                         | receiver address for adblock notification E-Mails                                              |
 | adb_mailsender     | no-reply@adblock                   | sender address for adblock notification E-Mails                                                |
 | adb_mailtopic      | adblock notification               | topic for adblock notification E-Mails                                                         |
 | adb_mailprofile    | adb_notify                         | mail profile used in 'msmtp' for adblock notification E-Mails                                  |
-| adb_jail           | 0                                  | create the additional restrictive 'adb_list.jail'                                              |
-| adb_jaildir        | /tmp                               | path for the generated jail list                                                               |
+| adb_jail           | 0                                  | jail mode - only domains on the allowlist are permitted, all other DNS requests are rejected   |
+| adb_nftforce       | 0, disabled                        | redirect all local DNS queries from specified LAN zones to the local DNS resolver              |
+| adb_nftdevforce    | -, not set                         | firewall LAN Devices/VLANs that should be forced locally                                       |
+| adb_nftportforce   | -, not set                         | firewall ports that should be forced locally                                                   |
+| adb_nftallow       | 0, disabled                        | routes MACs or interfaces to an unfiltered external DNS resolver, bypassing local adblock      |
+| adb_nftmacallow    | -, not set                         | listed MAC addresses will always use the configured unfiltered DNS server                      |
+| adb_nftdevallow    | -, not set                         | entire interfaces or VLANs will be routed to the unfiltered DNS server                         |
+| adb_allowdnsv4     | -, not set                         | IPv4 DNS resolver applied to MACs and interfaces using the unfiltered DNS policy               |
+| adb_allowdnsv6     | -, not set                         | IPv6 DNS resolver applied to MACs and interfaces using the unfiltered DNS policy               |
+| adb_nftblock       | 0, disabled                        | routes MACs or interfaces to an filtered external DNS resolver, bypassing local adblock        |
+| adb_nftmacblock    | -, not set                         | listed MAC addresses will always use the configured filtered DNS server                        |
+| adb_nftdevblock    | -, not set                         | entire interfaces or VLANs will be routed to the filtered DNS server                           |
+| adb_blockdnsv4     | -, not set                         | IPv4 DNS resolver applied to MACs and interfaces using the filtered DNS policy                 |
+| adb_blockdnsv6     | -, not set                         | IPv6 DNS resolver applied to MACs and interfaces using the filtered DNS policy                 |
 
 <a id="examples"></a>
 ## Examples
@@ -215,16 +223,16 @@ To get the status in the CLI, just call _/etc/init.d/adblock status_ or _/etc/in
 ~# /etc/init.d/adblock status
 ::: adblock runtime information
   + adblock_status  : enabled
-  + frontend_ver    : 4.4.5-r1
-  + backend_ver     : 4.4.5-r1
-  + blocked_domains : 575 335
+  + frontend_ver    : 4.5.0-r1
+  + backend_ver     : 4.5.0-r1
+  + blocked_domains : 582 457
   + active_feeds    : 1hosts, adguard, adguard_tracking, bitcoin, certpl, doh_blocklist, hagezi, phishing_army, smarttv_tracking, stevenblack, winspy
-  + dns_backend     : unbound (1.24.2-r1), /mnt/data/adblock/backup, 232.20 MB
-  + run_ifaces      : trigger: wan , report: br-lan
-  + run_directories : base: /mnt/data/adblock, dns: /var/lib/unbound, backup: /mnt/data/adblock/backup, report: /mnt/data/adblock/report, jail: /tmp
-  + run_flags       : shift: ✔, custom feed: ✘, force: ✔, flush: ✘, tld: ✔, search: ✔, report: ✔, mail: ✔, jail: ✘
-  + last_run        : mode: reload, 2025-12-13T15:55:59+01:00, duration: 0m 46s, 1411.57 MB available
-  + system_info     : cores: 4, fetch: curl, Bananapi BPI-R3, mediatek/filogic, OpenWrt SNAPSHOT (r32305-52fa3728e5)
+  + dns_backend     : unbound (1.24.2-r1), /mnt/data/adblock/backup, 234.93 MB
+  + run_ifaces      : trigger: wan, report: br-lan
+  + run_directories : base: /mnt/data/adblock, dns: /var/lib/unbound, backup: /mnt/data/adblock/backup, report: /mnt/data/adblock/report
+  + run_flags       : shift: ✔, custom feed: ✘, ext. DNS (std/prot): ✘/✘, force: ✔, flush: ✘, tld: ✔, search: ✘, report: ✔, mail: ✔, jail: ✘
+  + last_run        : mode: restart, 2026-01-18T16:45:23+01:00, duration: 0m 19s, 1403.59 MB available
+  + system_info     : cores: 4, fetch: curl, Bananapi BPI-R3, mediatek/filogic, OpenWrt SNAPSHOT (r32670-66b6791abe)
 ```
 
 <a id="best-practise-and-tweaks"></a>
@@ -259,12 +267,32 @@ To make this work, adblock uses the following external components:
 **External adblock test**  
 In addition to the built‑in DNS reporting and GeoIP map, adblock users can verify the effectiveness of their configuration with an external test page. The [Adblock Test](https://adblock.turtlecute.org/) provides a simple way to check whether your current adblock setup is working as expected. It loads a series of test elements (ads, trackers, and other resources) and reports whether they are successfully blocked by your configuration.  
 
-The test runs entirely in the browser and does not require additional configuration. For best results, open the page in the same environment where adblock is active and review the results displayed.
+The test runs entirely in the browser and does not require additional configuration. For best results, open the page in the same environment where adblock is active and review the results displayed.  
 
-**Use the jail mode, a restrictive DNS blocklist:**
-You can enable a restrictive 'adb_list.jail' to block access to all domains except those listed in the allowlist file. Usually this list will be generated as an additional list for guest or kidsafe configurations (for a separate dns server instance). If the jail directory points to your primary dns directory, the jail blocklist replaces your default blocklist.
+**Firewall‑Based DNS Control**  
+adblock provides several advanced firewall‑integrated features that allow you to enforce DNS policies directly at the network layer. These mechanisms operate independently of the local DNS resolver and ensure that DNS traffic follows your filtering rules, even when clients attempt to bypass them.  
+* unfiltered external DNS Routing: routes DNS queries from selected devices or interfaces to an external unfiltered DNS resolver
+* filtered external DNS Routing: routes DNS queries from selected devices or interfaces to an external filtered DNS resolver
+* force DNS: blocks or redirects all external DNS traffic to ensure that clients use the local resolver
 
-**Enable E-Mail notification via 'msmtp':**
+The DNS routing allows you to apply external DNS (unfiltered and/or filtered) to specific devices or entire network segments. DNS queries from these targets are transparently redirected to a chosen external resolver (IPv4 and/or IPv6):  
+* MAC‑based targeting for individual devices
+* Interface/VLAN targeting for entire segments
+* separate IPv4/IPv6 resolver selection
+* transparent DNS redirection without client‑side configuration
+This mode is ideal for guest networks, IoT devices, or environments where certain clients require stricter/lesser DNS filtering.  
+
+force DNS ensures that all DNS traffic on your network by specific devices or entire network segments is processed by the local resolver. Any attempt to use external DNS servers is blocked or redirected.
+* blocks external DNS on port 53 and redirects DNS queries to the local resolver when appropriate
+* also prevents DNS bypassing by clients with hardcoded DNS settings on other ports, e.g. on port 853
+This mode guarantees that adblock’s filtering pipeline is always applied.  
+
+adblock's firewall rules are based on nftables in a separate isolated nftables table (inet adblock) and chains (prerouting), with MAC addresses stored in an nftables set. The configuration is carried out centrally in LuCI on the ‘Firewall Settings’ tab in adblock.  
+
+**Jail mode (allowlist-only):**  
+Enforces a strict allowlist‑only DNS policy in which only domains listed in the allowlist file are resolved, while every other query is rejected. This mode is intended for highly restrictive environments and depends on a carefully maintained allowlist, typically managed manually.  
+
+**Enable E-Mail notification via 'msmtp':**  
 To use the email notification you have to install & configure the package 'msmtp'.
 Modify the file '/etc/msmtprc':
 <pre><code>
index ed02d69ff4c4a9a69101db7961691a7c8af187d6..798a8bda9508309f95b611e21e7a798e5b7d24e1 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh /etc/rc.common
-# Copyright (c) 2015-2025 Dirk Brenken (dev@brenken.org)
+# Copyright (c) 2015-2026 Dirk Brenken (dev@brenken.org)
 # This is free software, licensed under the GNU General Public License v3.
 
 # (s)hellcheck exceptions
@@ -93,7 +93,7 @@ status_service() {
                        json_get_var value "${key}" >/dev/null 2>&1
                        if [ "${key}" = "active_feeds" ]; then
                                json_get_values values "${key}" >/dev/null 2>&1
-                               value="${values// /, }"
+                               value="${values}"
                        fi
                        printf "  + %-15s : %s\n" "${key}" "${value:-"-"}"
                done
index 45568cf313648cf9dff5ed9e55f7c6fed102b03f..608d4f203f6321142e7e270a6675c66be010722a 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # send mail script for adblock notifications
-# Copyright (c) 2015-2025 Dirk Brenken (dev@brenken.org)
+# Copyright (c) 2015-2026 Dirk Brenken (dev@brenken.org)
 # This is free software, licensed under the GNU General Public License v3.
 
 # Please note: you have to manually install and configure the package 'msmtp' before using this script
@@ -16,7 +16,7 @@ adb_mailreceiver="$(uci_get adblock global adb_mailreceiver)"
 adb_mailtopic="$(uci_get adblock global adb_mailtopic "adblock notification")"
 adb_mailprofile="$(uci_get adblock global adb_mailprofile "adb_notify")"
 
-[ -z "${adb_mailreceiver}" ] && f_log "err" "please set the mail receiver with the 'adb_mailreceiver' option"
+[ -z "${adb_mailreceiver}" ] && f_log "info" "please set the mail receiver with the 'adb_mailreceiver' option"
 [ "${adb_debug}" = "1" ] && debug="--debug"
 
 adb_mailhead="From: ${adb_mailsender}\nTo: ${adb_mailreceiver}\nSubject: ${adb_mailtopic}\nReply-to: ${adb_mailsender}\nMime-Version: 1.0\nContent-Type: text/html;charset=utf-8\nContent-Disposition: inline\n\n"
@@ -48,8 +48,9 @@ if [ -x "${adb_mailcmd}" ]; then
        if [ "${?}" = "0" ]; then
                f_log "info" "mail successfully sent to '${adb_mailreceiver}'"
        else
-               f_log "err" "failed to send mail to '${adb_mailreceiver}' with rc '${?}'"
+               f_log "info" "failed to send mail to '${adb_mailreceiver}' with rc '${?}'"
        fi
 else
-       f_log "err" "msmtp mail daemon not found"
+       f_log "info" "msmtp mail daemon not found"
 fi
+exit 0
\ No newline at end of file
index c1b4cb1237ba58335eba01a09e9b91ae5e10c668..b4504202c603c9e83a55fe571be77d092f46e4c1 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 # dns based ad/abuse domain blocking
-# Copyright (c) 2015-2025 Dirk Brenken (dev@brenken.org)
+# Copyright (c) 2015-2026 Dirk Brenken (dev@brenken.org)
 # This is free software, licensed under the GNU General Public License v3.
 
 # (s)hellcheck exceptions
@@ -13,7 +13,19 @@ export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
 
 adb_enabled="0"
 adb_debug="0"
-adb_dnsforce="0"
+adb_nftforce="0"
+adb_nftdevforce=""
+adb_nftportforce=""
+adb_nftallow="0"
+adb_nftmacallow=""
+adb_nftdevallow=""
+adb_nftblock="0"
+adb_nftmacblock=""
+adb_nftdevblock=""
+adb_allowdnsv4=""
+adb_allowdnsv6=""
+adb_blockdnsv4=""
+adb_blockdnsv6=""
 adb_dnsshift="0"
 adb_dnsflush="0"
 adb_dnstimeout="20"
@@ -27,18 +39,16 @@ adb_map="0"
 adb_tld="1"
 adb_dns=""
 adb_dnspid=""
-adb_locallist="allowlist blocklist iplist"
+adb_locallist="allowlist blocklist"
 adb_basedir="/tmp"
 adb_finaldir=""
 adb_backupdir="/tmp/adblock-backup"
 adb_reportdir="/tmp/adblock-report"
-adb_jaildir="/tmp"
 adb_pidfile="/var/run/adblock.pid"
 adb_allowlist="/etc/adblock/adblock.allowlist"
 adb_blocklist="/etc/adblock/adblock.blocklist"
 adb_mailservice="/etc/adblock/adblock.mail"
 adb_dnsfile="adb_list.overall"
-adb_dnsjail="adb_list.jail"
 adb_feedfile="/etc/adblock/adblock.feeds"
 adb_customfeedfile="/etc/adblock/adblock.custom.feeds"
 adb_rtfile="/var/run/adb_runtime.json"
@@ -104,6 +114,7 @@ f_load() {
        if [ "${adb_enabled}" = "0" ]; then
                f_extconf
                f_temp
+               f_nftremove
                f_rmdns
                f_jsnup "disabled"
                f_log "info" "adblock is currently disabled, please set the config option 'adb_enabled' to '1' to use this service"
@@ -161,9 +172,10 @@ f_load() {
 f_env() {
        adb_starttime="$(date "+%s")"
        f_log "info" "adblock instance started ::: action: ${adb_action}, priority: ${adb_nicelimit:-"0"}, pid: ${$}"
-       f_jsnup "running"
+       f_jsnup "processing"
        f_extconf
        f_temp
+       f_nftadd
        json_init
        if [ -s "${adb_customfeedfile}" ]; then
                if json_load_file "${adb_customfeedfile}" >/dev/null 2>&1; then
@@ -202,7 +214,11 @@ f_conf() {
                                        ;;
                                *)
                                        eval "append=\"\${${option}}\""
-                                       eval "${option}=\"${append}${value} \""
+                                       if [ -n "${append}" ]; then
+                                               eval "${option}=\"${append} ${value}\""
+                                       else
+                                               eval "${option}=\"${value}\""
+                                       fi
                                        ;;
                        esac
                }
@@ -361,8 +377,6 @@ f_dns() {
                        adb_dnsheader="${adb_dnsheader:-""}"
                        adb_dnsdeny="${adb_dnsdeny:-"${adb_awkcmd} '{print \"local-zone: \\042\"\$0\"\\042 always_nxdomain\"}'"}"
                        adb_dnsallow="${adb_dnsallow:-"${adb_awkcmd} '{print \"local-zone: \\042\"\$0\"\\042 always_transparent\"}'"}"
-                       adb_dnsdenyip="${adb_dnsdenyip:-"0"}"
-                       adb_dnsallowip="${adb_dnsallowip:-"0"}"
                        adb_dnssafesearch="${adb_dnssafesearch:-"${adb_awkcmd} -v item=\"\$item\" '{type=\"AAAA\";if(match(item,/^([0-9]{1,3}\.){3}[0-9]{1,3}$/)){type=\"A\"}}{print \"local-data: \\042\"\$0\" \"type\" \"item\"\\042\"}'"}"
                        adb_dnsstop="${adb_dnsstop:-"local-zone: \".\" always_nxdomain"}"
                        ;;
@@ -374,8 +388,6 @@ f_dns() {
                        adb_dnsheader="${adb_dnsheader:-"\$TTL 2h\n@ IN SOA localhost. root.localhost. (1 6h 1h 1w 2h)\n  IN NS  localhost.\n"}"
                        adb_dnsdeny="${adb_dnsdeny:-"${adb_awkcmd} '{print \"\"\$0\" CNAME .\\n*.\"\$0\" CNAME .\"}'"}"
                        adb_dnsallow="${adb_dnsallow:-"${adb_awkcmd} '{print \"\"\$0\" CNAME rpz-passthru.\\n*.\"\$0\" CNAME rpz-passthru.\"}'"}"
-                       adb_dnsdenyip="${adb_dnsdenyip:-"${adb_awkcmd} '{print \"\"\$0\".rpz-client-ip CNAME .\"}'"}"
-                       adb_dnsallowip="${adb_dnsallowip:-"${adb_awkcmd} '{print \"\"\$0\".rpz-client-ip CNAME rpz-passthru.\"}'"}"
                        adb_dnssafesearch="${adb_dnssafesearch:-"${adb_awkcmd} -v item=\"\$item\" '{print \"\"\$0\" CNAME \"item\".\\n*.\"\$0\" CNAME \"item\".\"}'"}"
                        adb_dnsstop="${adb_dnsstop:-"* CNAME ."}"
                        ;;
@@ -387,8 +399,6 @@ f_dns() {
                        adb_dnsheader="${adb_dnsheader:-"\$TTL 2h\n@ IN SOA localhost. root.localhost. (1 6h 1h 1w 2h)\n"}"
                        adb_dnsdeny="${adb_dnsdeny:-"${adb_awkcmd} '{print \"\"\$0\" CNAME .\\n*.\"\$0\" CNAME .\"}'"}"
                        adb_dnsallow="${adb_dnsallow:-"${adb_awkcmd} '{print \"\"\$0\" CNAME rpz-passthru.\\n*.\"\$0\" CNAME rpz-passthru.\"}'"}"
-                       adb_dnsdenyip="${adb_dnsdenyip:-"0"}"
-                       adb_dnsallowip="${adb_dnsallowip:-"0"}"
                        adb_dnssafesearch="${adb_dnssafesearch:-"${adb_awkcmd} -v item=\"\$item\" '{print \"\"\$0\" CNAME \"item\".\\n*.\"\$0\" CNAME \"item\".\"}'"}"
                        adb_dnsstop="${adb_dnsstop:-"* CNAME ."}"
                        ;;
@@ -400,8 +410,6 @@ f_dns() {
                        adb_dnsheader="${adb_dnsheader:-""}"
                        adb_dnsdeny="${adb_dnsdeny:-"${adb_awkcmd} '{print \"address /\"\$0\"/#\"}'"}"
                        adb_dnsallow="${adb_dnsallow:-"${adb_awkcmd} '{print \"address /\"\$0\"/-\"}'"}"
-                       adb_dnsdenyip="${adb_dnsdenyip:-"0"}"
-                       adb_dnsallowip="${adb_dnsallowip:-"0"}"
                        adb_dnssafesearch="${adb_dnssafesearch:-"${adb_awkcmd} -v item=\"\$item\" '{print \"cname /\"\$0\"/\"item\"\"}'"}"
                        adb_dnsstop="${adb_dnsstop:-"address #"}"
                        ;;
@@ -413,8 +421,6 @@ f_dns() {
                        adb_dnsheader="${adb_dnsheader:-""}"
                        adb_dnsdeny="${adb_dnsdeny:-"0"}"
                        adb_dnsallow="${adb_dnsallow:-"0"}"
-                       adb_dnsdenyip="${adb_dnsdenyip:-"0"}"
-                       adb_dnsallowip="${adb_dnsallowip:-"0"}"
                        adb_dnssafesearch="${adb_dnssafesearch:-"0"}"
                        adb_dnsstop="${adb_dnsstop:-"0"}"
                        ;;
@@ -426,7 +432,7 @@ f_dns() {
                adb_finaldir="${adb_backupdir}"
        fi
        if [ "${adb_action}" != "stop" ]; then
-               for dir in "${adb_dnsdir:-"/tmp"}" "${adb_backupdir:-"/tmp"}" "${adb_jaildir:-"/tmp"}"; do
+               for dir in "${adb_dnsdir:-"/tmp"}" "${adb_backupdir:-"/tmp"}"; do
                        [ ! -d "${dir}" ] && mkdir -p "${dir}"
                done
                if [ "${adb_dnsflush}" = "1" ] || [ "${free_mem}" -lt "64" ]; then
@@ -437,7 +443,7 @@ f_dns() {
                fi
        fi
 
-       f_log "debug" "f_dns    ::: dns: ${adb_dns}, dns_instance: ${adb_dnsinstance}, dns_user: ${adb_dnsuser}, dns_dir: ${adb_dnsdir}, backup_dir: ${adb_backupdir}, final_dir: ${adb_finaldir}, jail_dir: ${adb_jaildir}"
+       f_log "debug" "f_dns    ::: dns: ${adb_dns}, dns_instance: ${adb_dnsinstance}, dns_user: ${adb_dnsuser}, dns_dir: ${adb_dnsdir}, backup_dir: ${adb_backupdir}, final_dir: ${adb_finaldir}"
 }
 
 # load fetch utility
@@ -540,7 +546,7 @@ f_uci() {
                        "resolver")
                                printf "%b" "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
                                adb_cnt="0"
-                               f_jsnup "running"
+                               f_jsnup "processing"
                                "/etc/init.d/${adb_dns}" reload >/dev/null 2>&1
                                ;;
                esac
@@ -576,7 +582,7 @@ f_count() {
 # set external config options
 #
 f_extconf() {
-       local config section zone port fwcfg
+       local config section
 
        case "${adb_dns}" in
                "dnsmasq")
@@ -611,50 +617,6 @@ f_extconf() {
                        ;;
        esac
        f_uci "${config}"
-
-       fwcfg="$(uci -qNX show "firewall" | "${adb_awkcmd}" 'BEGIN{FS="[.=]"};/adblock_/{if(zone==$2){next}else{ORS=" ";zone=$2;print zone}}')"
-       if [ "${adb_enabled}" = "1" ] && [ "${adb_dnsforce}" = "1" ] &&
-               /etc/init.d/firewall enabled; then
-               for zone in ${adb_zonelist}; do
-                       for port in ${adb_portlist}; do
-                               if ! printf "%s" "${fwcfg}" | "${adb_grepcmd}" -q "adblock_${zone}${port}"; then
-                                       config="firewall"
-                                       if "${adb_lookupcmd}" "localhost." "127.0.0.1:${port}" >/dev/null 2>&1; then
-                                               uci -q batch <<-EOC
-                                                       set firewall."adblock_${zone}${port}"="redirect"
-                                                       set firewall."adblock_${zone}${port}".name="Adblock DNS (${zone}, ${port})"
-                                                       set firewall."adblock_${zone}${port}".src="${zone}"
-                                                       set firewall."adblock_${zone}${port}".proto="tcp udp"
-                                                       set firewall."adblock_${zone}${port}".src_dport="53"
-                                                       set firewall."adblock_${zone}${port}".dest_port="${port}"
-                                                       set firewall."adblock_${zone}${port}".target="DNAT"
-                                                       set firewall."adblock_${zone}${port}".family="any"
-                                               EOC
-                                       else
-                                               uci -q batch <<-EOC
-                                                       set firewall."adblock_${zone}${port}"="rule"
-                                                       set firewall."adblock_${zone}${port}".name="Adblock DNS (${zone}, ${port})"
-                                                       set firewall."adblock_${zone}${port}".src="${zone}"
-                                                       set firewall."adblock_${zone}${port}".proto="tcp udp"
-                                                       set firewall."adblock_${zone}${port}".dest_port="${port}"
-                                                       set firewall."adblock_${zone}${port}".target="REJECT"
-                                                       set firewall."adblock_${zone}${port}".dest="*"
-                                               EOC
-                                       fi
-                               fi
-                               fwcfg="${fwcfg/adblock_${zone}${port}[ |\$]/}"
-                       done
-               done
-               fwcfg="${fwcfg#"${fwcfg%%[![:space:]]*}"}"
-               fwcfg="${fwcfg%"${fwcfg##*[![:space:]]}"}"
-       fi
-       if [ "${adb_enabled}" = "0" ] || [ "${adb_dnsforce}" = "0" ] || [ -n "${fwcfg}" ]; then
-               config="firewall"
-               for section in ${fwcfg}; do
-                       uci_remove firewall "${section}"
-               done
-       fi
-       f_uci "${config}"
 }
 
 # restart dns backend
@@ -748,36 +710,132 @@ f_etag() {
        return "${out_rc}"
 }
 
+# add adblock-related nft rules
+#
+f_nftadd() {
+       local devices device port file="${adb_tmpdir}/adb_nft.add"
+
+       # only proceed if at least one feature is enabled
+       #
+       if [ "${adb_nftallow}" = "0" ] && [ "${adb_nftblock}" = "0" ] && [ "${adb_nftforce}" = "0" ]; then
+               return
+       fi
+
+       {
+               # nft header (tables, sets, base and regular chains)
+               #
+               printf "%s\n\n" "#!${adb_nftcmd} -f"
+               if "${adb_nftcmd}" -t list table inet adblock >/dev/null 2>&1; then
+                       printf "%s\n" "delete table inet adblock"
+               fi
+               printf "%s\n" "add table inet adblock"
+               if [ "${adb_nftallow}" = "1" ] && [ -n "${adb_nftmacallow}" ]; then
+                       printf "%s\n" "add set inet adblock mac_allow { type ether_addr; flags interval; auto-merge; elements = { ${adb_nftmacallow// /, } }; }"
+               fi
+               if [ "${adb_nftblock}" = "1" ] && [ -n "${adb_nftmacblock}" ]; then
+                       printf "%s\n" "add set inet adblock mac_block { type ether_addr; flags interval; auto-merge; elements = { ${adb_nftmacblock// /, } }; }"
+               fi
+               printf "%s\n" "add chain inet adblock pre-routing { type nat hook prerouting priority -150; policy accept; }"
+               printf "%s\n" "add chain inet adblock _reject"
+
+               # reject chain rules
+               #
+               printf "%s\n" "add rule inet adblock _reject meta l4proto tcp counter reject with tcp reset"
+               printf "%s\n" "add rule inet adblock _reject counter reject with icmpx host-unreachable"
+
+               # external allow rules
+               #
+               if [ "${adb_nftallow}" = "1" ]; then
+                       if [ -n "${adb_nftmacallow}" ]; then
+                               [ -n "${adb_allowdnsv4}" ] && printf "%s\n" "add rule inet adblock pre-routing meta nfproto ipv4 ether saddr @mac_allow meta l4proto { udp, tcp } th dport 53 counter dnat to ${adb_allowdnsv4}:53"
+                               [ -n "${adb_allowdnsv6}" ] && printf "%s\n" "add rule inet adblock pre-routing meta nfproto ipv6 ether saddr @mac_allow meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_allowdnsv6}]:53"
+                       fi
+                       for device in ${adb_nftdevallow}; do
+                               [ -n "${adb_allowdnsv4}" ] && printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto ipv4 meta l4proto { udp, tcp } th dport 53 counter dnat to ${adb_allowdnsv4}:53"
+                               [ -n "${adb_allowdnsv6}" ] && printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto ipv6 meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_allowdnsv6}]:53"
+                       done
+               fi
+
+               # external block rules
+               #
+               if [ "${adb_nftblock}" = "1" ]; then
+                       if [ -n "${adb_nftmacblock}" ]; then
+                               [ -n "${adb_blockdnsv4}" ] && printf "%s\n" "add rule inet adblock pre-routing meta nfproto ipv4 ether saddr @mac_block meta l4proto { udp, tcp } th dport 53 counter dnat to ${adb_blockdnsv4}:53"
+                               [ -n "${adb_blockdnsv6}" ] && printf "%s\n" "add rule inet adblock pre-routing meta nfproto ipv6 ether saddr @mac_block meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_blockdnsv6}]:53"
+                       fi
+                       for device in ${adb_nftdevblock}; do
+                               [ -n "${adb_blockdnsv4}" ] && printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto ipv4 meta l4proto { udp, tcp } th dport 53 counter dnat to ${adb_blockdnsv4}:53"
+                               [ -n "${adb_blockdnsv6}" ] && printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto ipv6 meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_blockdnsv6}]:53"
+                       done
+               fi
+
+               # local dns enforcement
+               #
+               if [ "${adb_nftforce}" = "1" ]; then
+                       # device/vlan exceptions
+                       #
+                       for device in ${adb_nftdevallow} ${adb_nftdevblock}; do
+                               case " ${devices} " in
+                                       *" ${device} "*)
+                                               ;;
+                                       *)      devices="${devices} ${device}"
+                                               printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" return"
+                                               ;;
+                               esac
+                       done
+                       # mac exceptions
+                       #
+                       for device in ${adb_nftdevforce}; do
+                               if [ "${adb_nftallow}" = "1" ] && [ -n "${adb_nftmacallow}" ]; then
+                                       printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" ether saddr @mac_allow return"
+                               fi
+                               if [ "${adb_nftblock}" = "1" ] && [ -n "${adb_nftmacblock}" ]; then
+                                       printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" ether saddr @mac_block return"
+                               fi
+                               # dns enforce rules
+                               #
+                               for port in ${adb_nftportforce}; do
+                                       if [ "${port}" = "53" ]; then
+                                               printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto { ipv4, ipv6 } meta l4proto { udp, tcp } th dport ${port} counter redirect to :${port}"
+                                       else
+                                               printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto { ipv4, ipv6 } meta l4proto { udp, tcp } th dport ${port} counter goto _reject"
+                                       fi
+                               done
+                       done
+               fi
+       } >"${file}"
+       if "${adb_nftcmd}" -f "${file}" >/dev/null 2>&1; then
+               f_log "info" "adblock-related nft rules added"
+       else
+               f_log "err" "failed to add adblock-related nft rules"
+       fi
+}
+
+# remove adblock-related nft rules
+#
+f_nftremove() {
+       local file="${adb_tmpdir}/adb_nft.remove"
+
+       if "${adb_nftcmd}" -t list table inet adblock >/dev/null 2>&1; then
+               {
+                       printf "%s\n" "#!${adb_nftcmd} -f"
+                       printf "%s\n" "delete table inet adblock"
+               } >"${file}"
+
+               if "${adb_nftcmd}" -f "${file}" >/dev/null 2>&1; then
+                       f_log "info" "adblock-related nft rules removed"
+               else
+                       f_log "err" "failed to remove adblock-related nft rules"
+               fi
+       fi
+}
+
 # backup/restore/remove blocklists
 #
 f_list() {
        local file rset item array safe_url safe_ips safe_cname safe_domains ip out_rc file_name mode="${1}" src_name="${2:-"${src_name}"}" in_rc="${src_rc:-0}" use_cname="0" ffiles="-maxdepth 1 -name adb_list.*.gz"
 
        case "${mode}" in
-               "iplist")
-                       src_name="${mode}"
-                       file_name="${adb_tmpdir}/tmp.add.${src_name}"
-                       if [ "${adb_dnsallowip}" != "0" ] && [ "${adb_dnsdenyip}" != "0" ]; then
-                               rset="BEGIN{FS=\"[.:]\";pfx=\"32\"}{if(match(\$0,/:/))pfx=\"128\"}{printf \"%s.\",pfx;for(seg=NF;seg>=1;seg--)if(seg==1)printf \"%s\n\",\$seg;else if(\$seg>=0)printf \"%s.\",\$seg; else printf \"%s.\",\"zz\"}"
-                               if [ -n "${adb_allowip}" ]; then
-                                       : >"${adb_tmpdir}/tmp.raw.${src_name}"
-                                       for ip in ${adb_allowip}; do
-                                               printf "%s" "${ip}" | "${adb_awkcmd}" "${rset}" >>"${adb_tmpdir}/tmp.raw.${src_name}"
-                                       done
-                                       eval "${adb_dnsallowip}" "${adb_tmpdir}/tmp.raw.${src_name}" >"${file_name}"
-                                       out_rc="${?}"
-                               fi
-                               if [ -n "${adb_denyip}" ] && { [ -z "${out_rc}" ] || [ "${out_rc}" = "0" ]; }; then
-                                       : >"${adb_tmpdir}/tmp.raw.${src_name}"
-                                       for ip in ${adb_denyip}; do
-                                               printf "%s" "${ip}" | "${adb_awkcmd}" "${rset}" >>"${adb_tmpdir}/tmp.raw.${src_name}"
-                                       done
-                                       eval "${adb_dnsdenyip}" "${adb_tmpdir}/tmp.raw.${src_name}" >>"${file_name}"
-                                       out_rc="${?}"
-                               fi
-                               : >"${adb_tmpdir}/tmp.raw.${src_name}"
-                       fi
-                       ;;
                "blocklist" | "allowlist")
                        src_name="${mode}"
                        case "${src_name}" in
@@ -809,9 +867,9 @@ f_list() {
                                                eval "${adb_dnsallow}" "${file_name}" >"${adb_tmpdir}/tmp.add.${src_name}"
                                                out_rc="${?}"
                                                if [ "${adb_jail}" = "1" ] && [ "${adb_dnsstop}" != "0" ]; then
-                                                       printf "%b" "${adb_dnsheader}" >"${adb_tmpdir}/${adb_dnsjail}"
-                                                       "${adb_catcmd}" "${adb_tmpdir}/tmp.add.${src_name}" >>"${adb_tmpdir}/${adb_dnsjail}"
-                                                       printf "%b\n" "${adb_dnsstop}" >>"${adb_tmpdir}/${adb_dnsjail}"
+                                                       printf "%b" "${adb_dnsheader}" >"${adb_tmpdir}/${adb_dnsfile}"
+                                                       "${adb_catcmd}" "${adb_tmpdir}/tmp.add.${src_name}" >>"${adb_tmpdir}/${adb_dnsfile}"
+                                                       printf "%b\n" "${adb_dnsstop}" >>"${adb_tmpdir}/${adb_dnsfile}"
                                                fi
                                        fi
                                        ;;
@@ -962,7 +1020,6 @@ f_list() {
                        file_name="${adb_finaldir}/${adb_dnsfile}"
                        rm -f "${file_name}"
                        [ -n "${adb_dnsheader}" ] && printf "%b" "${adb_dnsheader}" >>"${file_name}"
-                       [ -s "${adb_tmpdir}/tmp.add.iplist" ] && "${adb_sortcmd}" ${adb_srtopts} -u "${adb_tmpdir}/tmp.add.iplist" >>"${file_name}"
                        [ -s "${adb_tmpdir}/tmp.add.allowlist" ] && "${adb_sortcmd}" ${adb_srtopts} -u "${adb_tmpdir}/tmp.add.allowlist" >>"${file_name}"
                        [ "${adb_safesearch}" = "1" ] && "${adb_catcmd}" "${adb_tmpdir}/tmp.safesearch."* 2>/dev/null >>"${file_name}"
                        if [ "${adb_dnsdeny}" != "0" ]; then
@@ -1135,6 +1192,7 @@ f_query() {
 #
 f_jsnup() {
        local pids object feeds end_time runtime dns dns_ver dns_mem free_mem custom_feed="0" status="${1:-"enabled"}"
+       local duration jail="0" nft_unfiltered="0" nft_filtered="0" nft_force="0"
 
        if [ -n "${adb_dnspid}" ]; then
                pids="$("${adb_pgrepcmd}" -P "${adb_dnspid}" 2>/dev/null)"
@@ -1161,7 +1219,19 @@ f_jsnup() {
        free_mem="$("${adb_awkcmd}" '/^MemAvailable/{printf "%.2f", $2/1024}' "/proc/meminfo" 2>/dev/null)"
        adb_cnt="$("${adb_awkcmd}" -v cnt="${adb_cnt}" 'BEGIN{res="";pos=0;for(i=length(cnt);i>0;i--){res=substr(cnt,i,1)res;pos++;if(pos==3&&i>1){res=" "res;pos=0;}}; printf"%s",res}')"
        [ -s "${adb_customfeedfile}" ] && custom_feed="1"
-
+       if [ "${adb_nftforce}" = "1" ] && [ -n "${adb_nftdevforce}" ] && [ -n "${adb_nftportforce}" ]; then
+               nft_force="1"
+       fi
+       if [ "${adb_nftallow}" = "1" ] \
+               && { [ -n "${adb_nftmacallow}" ] || [ -n "${adb_nftdevallow}" ]; } \
+               && { [ -n "${adb_allowdnsv4}" ] || [ -n "${adb_allowdnsv6}" ]; }; then
+               nft_unfiltered="1"
+       fi
+       if [ "${adb_nftblock}" = "1" ] \
+               && { [ -n "${adb_nftmacblock}" ] || [ -n "${adb_nftdevblock}" ]; } \
+               && { [ -n "${adb_blockdnsv4}" ] || [ -n "${adb_blockdnsv6}" ]; }; then
+               nft_filtered="1"
+       fi
        case "${status}" in
                "enabled")
                        if [ -n "${adb_starttime}" ] && [ "${adb_action}" != "boot" ]; then
@@ -1187,11 +1257,12 @@ f_jsnup() {
                [ -z "${adb_cnt}" ] && json_get_var adb_cnt "blocked_domains"
                [ -z "${runtime}" ] && json_get_var runtime "last_run"
                if [ "${status}" = "enabled" ]; then
-                       if [ "${adb_jail}" = "1" ] && [ "${adb_jaildir}" = "${adb_dnsdir}" ]; then
+                       if [ "${adb_jail}" = "1" ] && [ "${adb_dnsstop}" != "0" ]; then
+                               jail="1"
                                adb_cnt="0"
-                               feeds="restrictive_jail"
+                               feeds="restrictive jail (allowlist-only)"
                        else
-                               feeds="$(printf "%s\n" ${adb_feed} | "${adb_sortcmd}" | "${adb_awkcmd}" '{ORS=" ";print $0}')"
+                               feeds="$(printf "%s\n" ${adb_feed// /, } | ${adb_sortcmd} | xargs)"
                        fi
                fi
        fi
@@ -1209,8 +1280,8 @@ f_jsnup() {
        json_close_array
        json_add_string "dns_backend" "${adb_dns:-"-"} (${dns_ver:-"-"}), ${adb_finaldir:-"-"}, ${dns_mem:-"0"} MB"
        json_add_string "run_ifaces" "trigger: ${adb_trigger:-"-"}, report: ${adb_repiface:-"-"}"
-       json_add_string "run_directories" "base: ${adb_basedir}, dns: ${adb_dnsdir}, backup: ${adb_backupdir}, report: ${adb_reportdir}, jail: ${adb_jaildir:-"-"}"
-       json_add_string "run_flags" "shift: $(f_char ${adb_dnsshift}), custom feed: $(f_char ${custom_feed}), force: $(f_char ${adb_dnsforce}), flush: $(f_char ${adb_dnsflush}), tld: $(f_char ${adb_tld}), search: $(f_char ${adb_safesearch}), report: $(f_char ${adb_report}), mail: $(f_char ${adb_mail}), jail: $(f_char ${adb_jail})"
+       json_add_string "run_directories" "base: ${adb_basedir}, dns: ${adb_dnsdir}, backup: ${adb_backupdir}, report: ${adb_reportdir}"
+       json_add_string "run_flags" "shift: $(f_char ${adb_dnsshift}), custom feed: $(f_char ${custom_feed}), ext. DNS (std/prot): $(f_char ${nft_unfiltered})/$(f_char ${nft_filtered}), force: $(f_char ${nft_force}), flush: $(f_char ${adb_dnsflush}), tld: $(f_char ${adb_tld}), search: $(f_char ${adb_safesearch}), report: $(f_char ${adb_report}), mail: $(f_char ${adb_mail}), jail: $(f_char ${jail})"
        json_add_string "last_run" "${runtime:-"-"}"
        json_add_string "system_info" "cores: ${adb_cores}, fetch: ${adb_fetchcmd##*/}, ${adb_sysver}"
        json_dump >"${adb_rtfile}"
@@ -1256,29 +1327,23 @@ f_main() {
        # jail mode preparation
        #
        if [ "${adb_jail}" = "1" ] && [ "${adb_dnsstop}" != "0" ]; then
-               if [ "${adb_jaildir}" = "${adb_dnsdir}" ]; then
-                       "${adb_mvcmd}" -f "${adb_tmpdir}/${adb_dnsjail}" "${adb_finaldir}/${adb_dnsfile}"
-                       chown "${adb_dnsuser}" "${adb_finaldir}/${adb_dnsfile}" 2>/dev/null
-                       if [ "${adb_dnsshift}" = "1" ] && [ ! -L "${adb_dnsdir}/${adb_dnsfile}" ]; then
-                               ln -fs "${adb_finaldir}/${adb_dnsfile}" "${adb_dnsdir}/${adb_dnsfile}"
-                       elif [ "${adb_dnsshift}" = "0" ] && [ -s "${adb_backupdir}/${adb_dnsfile}" ]; then
-                               rm -f "${adb_backupdir}/${adb_dnsfile}"
-                       fi
-                       if f_dnsup; then
-                               if [ "${adb_action}" != "resume" ]; then
-                                       f_jsnup "enabled"
-                               fi
-                               f_log "info" "restrictive jail mode enabled successfully (${adb_sysver})"
-                       else
-                               f_log "err" "dns backend restart in jail mode failed"
+               "${adb_mvcmd}" -f "${adb_tmpdir}/${adb_dnsfile}" "${adb_finaldir}/${adb_dnsfile}"
+               chown "${adb_dnsuser}" "${adb_finaldir}/${adb_dnsfile}" 2>/dev/null
+               if [ "${adb_dnsshift}" = "1" ] && [ ! -L "${adb_dnsdir}/${adb_dnsfile}" ]; then
+                       ln -fs "${adb_finaldir}/${adb_dnsfile}" "${adb_dnsdir}/${adb_dnsfile}"
+               elif [ "${adb_dnsshift}" = "0" ] && [ -s "${adb_backupdir}/${adb_dnsfile}" ]; then
+                       rm -f "${adb_backupdir}/${adb_dnsfile}"
+               fi
+               if f_dnsup; then
+                       if [ "${adb_action}" != "resume" ]; then
+                               f_jsnup "enabled"
                        fi
-                       f_rmtemp
-                       return
+                       f_log "info" "restrictive jail mode enabled successfully (${adb_sysver})"
                else
-                       "${adb_mvcmd}" -f "${adb_tmpdir}/${adb_dnsjail}" "${adb_jaildir}/${adb_dnsjail}"
-                       chown "${adb_dnsuser}" "${adb_jaildir}/${adb_dnsjail}" 2>/dev/null
-                       f_log "info" "additional restrictive jail blocklist placed in ${adb_jaildir}"
+                       f_log "err" "dns backend restart in jail mode failed"
                fi
+               f_rmtemp
+               return
        fi
 
        # safe search preparation
@@ -1689,13 +1754,16 @@ adb_lookupcmd="$(f_cmd nslookup)"
 adb_dumpcmd="$(f_cmd tcpdump optional)"
 adb_mailcmd="$(f_cmd msmtp optional)"
 adb_logreadcmd="$(f_cmd logread optional)"
+adb_nftcmd="$(f_cmd nft)"
 
 # handle different adblock actions
 #
 f_load
 case "${adb_action}" in
        "stop")
+               f_temp
                f_jsnup "stopped"
+               f_nftremove
                f_rmdns
                ;;
        "suspend")
@@ -1715,7 +1783,8 @@ case "${adb_action}" in
                f_main
                ;;
        "restart")
-               f_jsnup "running"
+               f_jsnup "processing"
+               f_nftremove
                f_rmdns
                f_env
                f_main