* 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
* 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
| 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 |
| 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
~# /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>
**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>
#!/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
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"
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"
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"
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
;;
*)
eval "append=\"\${${option}}\""
- eval "${option}=\"${append}${value} \""
+ if [ -n "${append}" ]; then
+ eval "${option}=\"${append} ${value}\""
+ else
+ eval "${option}=\"${value}\""
+ fi
;;
esac
}
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"}"
;;
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 ."}"
;;
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 ."}"
;;
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 #"}"
;;
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"}"
;;
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
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
"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
# set external config options
#
f_extconf() {
- local config section zone port fwcfg
+ local config section
case "${adb_dns}" in
"dnsmasq")
;;
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
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
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
;;
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
#
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)"
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
[ -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
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}"
# 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
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")
f_main
;;
"restart")
- f_jsnup "running"
+ f_jsnup "processing"
+ f_nftremove
f_rmdns
f_env
f_main