banip: release 0.8.0 (nft rewrite) 20491/head
authorDirk Brenken <dev@brenken.org>
Mon, 13 Feb 2023 16:56:57 +0000 (17:56 +0100)
committerDirk Brenken <dev@brenken.org>
Sat, 18 Feb 2023 20:06:26 +0000 (21:06 +0100)
- complete rewrite of banIP to support nftables
- all sets are handled in a separate nft table/namespace 'banIP'
- for incoming blocking it uses the inet input hook, for outgoing blocking it uses the inet forward hook
- full IPv4 and IPv6 support
- supports nft atomic set loading
- supports blocking by ASN numbers and by iso country codes
- 42 preconfigured external feeds are available, plus local allow- and blocklist
- supports local allow- and blocklist (IPv4, IPv6, CIDR notation or domain names)
- auto-add the uplink subnet to the local allowlist
- provides a small background log monitor to ban unsuccessful login attempts in real-time
- the logterms for the log monitor service can be freely defined via regex
- auto-add unsuccessful LuCI, nginx, Asterisk or ssh login attempts to the local blocklist
- fast feed processing as they are handled in parallel as background jobs
- per feed it can be defined whether the input chain or the forward chain should be blocked (default: both chains)
- automatic blocklist backup & restore, the backups will be used in case of download errors or during startup
- automatically selects one of the following download utilities with ssl support: aria2c, curl, uclient-fetch or wget
- supports a 'allowlist only' mode, this option restricts internet access from/to a small number of secure websites/IPs
- provides comprehensive runtime information
- provides a detailed set report
- provides a set search engine for certain IPs
- feed parsing by fast & flexible regex rulesets
- minimal status & error logging to syslog, enable debug logging to receive more output
- procd based init system support (start/stop/restart/reload/status/report/search)
- procd network interface trigger support
- ability to add new banIP feeds on your own
- add a readme with all available options/feeds to customize your installation to your needs
- a new LuCI frontend will be available in due course

Signed-off-by: Dirk Brenken <dev@brenken.org>
19 files changed:
net/banip/Makefile
net/banip/files/README.md
net/banip/files/banip-functions.sh [new file with mode: 0644]
net/banip/files/banip-service.sh [new file with mode: 0755]
net/banip/files/banip.allowlist [new file with mode: 0644]
net/banip/files/banip.blacklist [deleted file]
net/banip/files/banip.blocklist [new file with mode: 0644]
net/banip/files/banip.conf
net/banip/files/banip.dns [deleted file]
net/banip/files/banip.feeds [new file with mode: 0644]
net/banip/files/banip.hotplug [deleted file]
net/banip/files/banip.init
net/banip/files/banip.maclist [deleted file]
net/banip/files/banip.mail [deleted file]
net/banip/files/banip.service [deleted file]
net/banip/files/banip.sh [deleted file]
net/banip/files/banip.sources [deleted file]
net/banip/files/banip.tpl [new file with mode: 0644]
net/banip/files/banip.whitelist [deleted file]

index 7da810862418c0aa6a12a133f8634d5fbbf67fb0..a816ec858d1339a73049e2cb0885710774fe4b5d 100644 (file)
@@ -1,13 +1,14 @@
 #
-# Copyright (c) 2018-2021 Dirk Brenken (dev@brenken.org)
+# banIP - ban incoming and outgoing ip adresses/subnets via sets in nftables
+# Copyright (c) 2018-2023 Dirk Brenken (dev@brenken.org)
 # This is free software, licensed under the GNU General Public License v3.
 #
 
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=banip
-PKG_VERSION:=0.7.10
-PKG_RELEASE:=6
+PKG_VERSION:=0.8.0
+PKG_RELEASE:=1
 PKG_LICENSE:=GPL-3.0-or-later
 PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
 
@@ -16,23 +17,22 @@ include $(INCLUDE_DIR)/package.mk
 define Package/banip
        SECTION:=net
        CATEGORY:=Network
-       TITLE:=Ban incoming and outgoing ip adresses via ipsets
-       DEPENDS:=+jshn +jsonfilter +ip +ipset +iptables +ca-bundle @BROKEN
+       TITLE:=banIP blocks IP addresses via named nftables sets
+       DEPENDS:=+jshn +jsonfilter +firewall4 +ca-bundle +logd +rpcd +rpcd-mod-rpcsys
        PKGARCH:=all
 endef
 
 define Package/banip/description
-Powerful banIP script to block ip addresses via ipsets.
-The script supports many ip blacklist sites plus manual black- and whitelist overrides.
+banIP blocks IP addresses via named nftables sets.
+banIP supports many IP blocklist feeds and provides a log service to block suspicious IPs in realtime.
 Please see https://github.com/openwrt/packages/blob/master/net/banip/files/README.md for further information.
 
 endef
 
 define Package/banip/conffiles
 /etc/config/banip
-/etc/banip/banip.maclist
-/etc/banip/banip.blacklist
-/etc/banip/banip.whitelist
+/etc/banip/banip.allowlist
+/etc/banip/banip.blocklist
 endef
 
 define Build/Prepare
@@ -46,27 +46,23 @@ endef
 
 define Package/banip/install
        $(INSTALL_DIR) $(1)/usr/bin
-       $(INSTALL_BIN) ./files/banip.sh $(1)/usr/bin
+       $(INSTALL_BIN) ./files/banip-service.sh $(1)/usr/bin
 
        $(INSTALL_DIR) $(1)/etc/init.d
        $(INSTALL_BIN) ./files/banip.init $(1)/etc/init.d/banip
 
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_CONF) ./files/banip-functions.sh $(1)/usr/lib
+
        $(INSTALL_DIR) $(1)/etc/config
        $(INSTALL_CONF) ./files/banip.conf $(1)/etc/config/banip
 
        $(INSTALL_DIR) $(1)/etc/banip
-       $(INSTALL_BIN) ./files/banip.dns $(1)/etc/banip
-       $(INSTALL_BIN) ./files/banip.mail $(1)/etc/banip
-       $(INSTALL_BIN) ./files/banip.service $(1)/etc/banip
-       $(INSTALL_CONF) ./files/banip.maclist $(1)/etc/banip
-       $(INSTALL_CONF) ./files/banip.blacklist $(1)/etc/banip
-       $(INSTALL_CONF) ./files/banip.whitelist $(1)/etc/banip
-       $(INSTALL_CONF) ./files/banip.countries $(1)/etc/banip
-       $(INSTALL_CONF) ./files/banip.sources $(1)/etc/banip
-       gzip -9n $(1)/etc/banip/banip.sources
-
-       $(INSTALL_DIR) $(1)/etc/hotplug.d/firewall
-       $(INSTALL_DATA) ./files/banip.hotplug $(1)/etc/hotplug.d/firewall/30-banip
+       $(INSTALL_CONF) ./files/banip.tpl $(1)/etc/banip
+       $(INSTALL_CONF) ./files/banip.allowlist $(1)/etc/banip
+       $(INSTALL_CONF) ./files/banip.blocklist $(1)/etc/banip
+       $(INSTALL_CONF) ./files/banip.feeds $(1)/etc/banip
+       gzip -9n $(1)/etc/banip/banip.feeds
 endef
 
 $(eval $(call BuildPackage,banip))
index 5f7a37c721ef814ab3aa913b3c867755ea29735f..e0bf951f9ade798c3082c75838c41b8185bafad5 100644 (file)
 <!-- markdownlint-disable -->
 
-# banIP - ban incoming and/or outgoing ip adresses via ipsets
+# banIP - ban incoming and outgoing IP addresses/subnets via sets in nftables
 
 ## Description
-IP address blocking is commonly used to protect against brute force attacks, prevent disruptive or unauthorized address(es) from access or it can be used to restrict access to or from a particular geographic area — for example.  
+IP address blocking is commonly used to protect against brute force attacks, prevent disruptive or unauthorized address(es) from access or it can be used to restrict access to or from a particular geographic area — for example. Further more banIP scans the log file via logread and bans IP addresses that make too many password failures, e.g. via ssh.  
 
 ## Main Features
-* Support of the following fully pre-configured domain blocklist sources (free for private usage, for commercial use please check their individual licenses)
+* banIP supports the following fully pre-configured domain blocklist feeds (free for private usage, for commercial use please check their individual licenses).  
+  **Please note:** the columns "INP" and "FWD" show for which chains the feeds are suitable in common scenarios, e.g. the first entry should be limited to forward chain - see the config options 'ban\_blockforward' and 'ban\_blockinput' below.
 
-| Source              | Focus                          | Information                                                                       |
-| :------------------ | :----------------------------: | :-------------------------------------------------------------------------------- |
-| asn                 | ASN block                      | [Link](https://asn.ipinfo.app)                                                    |
-| bogon               | Bogon prefixes                 | [Link](https://team-cymru.com)                                                    |
-| country             | Country blocks                 | [Link](https://www.ipdeny.com/ipblocks)                                           |
-| darklist            | blocks suspicious attacker IPs | [Link](https://darklist.de)                                                       |
-| debl                | Fail2ban IP blacklist          | [Link](https://www.blocklist.de)                                                  |
-| doh                 | Public DoH-Provider            | [Link](https://github.com/dibdot/DoH-IP-blocklists)                               |
-| drop                | Spamhaus drop compilation      | [Link](https://www.spamhaus.org)                                                  |
-| dshield             | Dshield IP blocklist           | [Link](https://www.dshield.org)                                                   |
-| edrop               | Spamhaus edrop compilation     | [Link](https://www.spamhaus.org)                                                  |
-| feodo               | Feodo Tracker                  | [Link](https://feodotracker.abuse.ch)                                             |
-| firehol1            | Firehol Level 1 compilation    | [Link](https://iplists.firehol.org/?ipset=firehol_level1)                         |
-| firehol2            | Firehol Level 2 compilation    | [Link](https://iplists.firehol.org/?ipset=firehol_level2)                         |
-| firehol3            | Firehol Level 3 compilation    | [Link](https://iplists.firehol.org/?ipset=firehol_level3)                         |
-| firehol4            | Firehol Level 4 compilation    | [Link](https://iplists.firehol.org/?ipset=firehol_level4)                         |
-| greensnow           | blocks suspicious server IPs   | [Link](https://greensnow.co)                                                      |
-| iblockads           | Advertising blocklist          | [Link](https://www.iblocklist.com)                                                |
-| iblockspy           | Malicious spyware blocklist    | [Link](https://www.iblocklist.com)                                                |
-| myip                | Myip Live IP blacklist         | [Link](https://myip.ms)                                                           |
-| nixspam             | iX spam protection             | [Link](http://www.nixspam.org)                                                    |
-| proxy               | Firehol list of open proxies   | [Link](https://iplists.firehol.org/?ipset=proxylists)                             |
-| ssbl                | SSL botnet IP blacklist        | [Link](https://sslbl.abuse.ch)                                                    |
-| talos               | Cisco Talos IP Blacklist       | [Link](https://talosintelligence.com/reputation_center)                           |
-| threat              | Emerging Threats               | [Link](https://rules.emergingthreats.net)                                         |
-| tor                 | Tor exit nodes                 | [Link](https://fissionrelays.net/lists)                                           |
-| uceprotect1         | Spam protection level 1        | [Link](http://www.uceprotect.net/en/index.php)                                    |
-| uceprotect2         | Spam protection level 2        | [Link](http://www.uceprotect.net/en/index.php)                                    |
-| voip                | VoIP fraud blocklist           | [Link](http://www.voipbl.org)                                                     |
-| yoyo                | Ad protection blacklist        | [Link](https://pgl.yoyo.org/adservers/)                                           |
+| Feed                | Focus                          | INP | FWD | Information                                                           |
+| :------------------ | :----------------------------: | :-: | :-: | :-------------------------------------------------------------------- |
+| adaway              | adaway IPs                     |     |  x  | [Link](https://github.com/dibdot/banIP-IP-blocklists)                 |
+| adguard             | adguard IPs                    |     |  x  | [Link](https://github.com/dibdot/banIP-IP-blocklists)                 |
+| adguardtrackers     | adguardtracker IPs             |     |  x  | [Link](https://github.com/dibdot/banIP-IP-blocklists)                 |
+| antipopads          | antipopads IPs                 |     |  x  | [Link](https://github.com/dibdot/banIP-IP-blocklists)                 |
+| asn                 | ASN IPs                        |     |  x  | [Link](https://asn.ipinfo.app)                                        |
+| backscatterer       | backscatterer IPs              |  x  |  x  | [Link](https://www.uceprotect.net/en/index.php)                       |
+| bogon               | bogon prefixes                 |  x  |  x  | [Link](https://team-cymru.com)                                        |
+| country             | country blocks                 |  x  |     | [Link](https://www.ipdeny.com/ipblocks)                               |
+| cinsscore           | suspicious attacker IPs        |  x  |  x  | [Link](https://cinsscore.com/#list)                                   |
+| darklist            | blocks suspicious attacker IPs |  x  |  x  | [Link](https://darklist.de)                                           |
+| debl                | fail2ban IP blacklist          |  x  |  x  | [Link](https://www.blocklist.de)                                      |
+| doh                 | public DoH-Provider            |     |  x  | [Link](https://github.com/dibdot/DoH-IP-blocklists)                   |
+| drop                | spamhaus drop compilation      |  x  |  x  | [Link](https://www.spamhaus.org)                                      |
+| dshield             | dshield IP blocklist           |  x  |  x  | [Link](https://www.dshield.org)                                       |
+| edrop               | spamhaus edrop compilation     |  x  |  x  | [Link](https://www.spamhaus.org)                                      |
+| feodo               | feodo tracker                  |  x  |  x  | [Link](https://feodotracker.abuse.ch)                                 |
+| firehol1            | firehol level 1 compilation    |  x  |  x  | [Link](https://iplists.firehol.org/?ipset=firehol_level1)             |
+| firehol2            | firehol level 2 compilation    |  x  |  x  | [Link](https://iplists.firehol.org/?ipset=firehol_level2)             |
+| firehol3            | firehol level 3 compilation    |  x  |  x  | [Link](https://iplists.firehol.org/?ipset=firehol_level3)             |
+| firehol4            | firehol level 4 compilation    |  x  |  x  | [Link](https://iplists.firehol.org/?ipset=firehol_level4)             |
+| greensnow           | suspicious server IPs          |  x  |  x  | [Link](https://greensnow.co)                                          |
+| iblockads           | Advertising IPs                |     |  x  | [Link](https://www.iblocklist.com)                                    |
+| iblockspy           | Malicious spyware IPs          |  x  |  x  | [Link](https://www.iblocklist.com)                                    |
+| myip                | real-time IP blocklist         |  x  |  x  | [Link](https://myip.ms)                                               |
+| nixspam             | iX spam protection             |  x  |  x  | [Link](http://www.nixspam.org)                                        |
+| oisdnsfw            | OISD-nsfw IPs                  |     |  x  | [Link](https://github.com/dibdot/banIP-IP-blocklists)                 |
+| oisdsmall           | OISD-small IPs                 |     |  x  | [Link](https://github.com/dibdot/banIP-IP-blocklists)                 |
+| proxy               | open proxies                   |  x  |     | [Link](https://iplists.firehol.org/?ipset=proxylists)                 |
+| ssbl                | SSL botnet IPs                 |  x  |  x  | [Link](https://sslbl.abuse.ch)                                        |
+| stevenblack         | stevenblack IPs                |     |  x  | [Link](https://github.com/dibdot/banIP-IP-blocklists)                 |
+| talos               | talos IPs                      |  x  |  x  | [Link](https://talosintelligence.com/reputation_center)               |
+| threat              | emerging threats               |  x  |  x  | [Link](https://rules.emergingthreats.net)                             |
+| threatview          | malicious IPs                  |  x  |  x  | [Link](https://threatview.io)                                         |
+| tor                 | tor exit nodes                 |  x  |     | [Link](https://github.com/SecOps-Institute/Tor-IP-Addresses)          |
+| uceprotect1         | spam protection level 1        |  x  |  x  | [Link](http://www.uceprotect.net/en/index.php)                        |
+| uceprotect2         | spam protection level 2        |  x  |  x  | [Link](http://www.uceprotect.net/en/index.php)                        |
+| uceprotect3         | spam protection level 3        |  x  |  x  | [Link](http://www.uceprotect.net/en/index.php)                        |
+| urlhaus             | urlhaus IDS IPs                |  x  |  x  | [Link](https://urlhaus.abuse.ch)                                      |
+| urlvir              | malware related IPs            |  x  |  x  | [Link](https://iplists.firehol.org/?ipset=urlvir)                     |
+| webclient           | malware related IPs            |  x  |  x  | [Link](https://iplists.firehol.org/?ipset=firehol_webclient)          |
+| voip                | VoIP fraud blocklist           |  x  |  x  | [Link](https://voipbl.org)                                            |
+| yoyo                | yoyo IPs                       |     |  x  | [Link](https://github.com/dibdot/banIP-IP-blocklists)                 |
 
 * zero-conf like automatic installation & setup, usually no manual changes needed
-* automatically selects one of the following supported download utilities: aria2c, curl, uclient-fetch, wget
-* fast downloads & list processing as they are handled in parallel as background jobs in a configurable 'Download Queue'
+* all sets are handled in a separate nft table/namespace 'banIP'
 * full IPv4 and IPv6 support
-* ipsets (one per source) are used to ban a large number of IP addresses
-* supports blocking by ASN numbers
-* supports blocking by iso country codes
-* supports local black- & whitelist (IPv4, IPv6, CIDR notation or domain names)
-* auto-add unsuccessful LuCI, nginx or ssh login attempts via 'dropbear'/'sshd' to local blacklist
-* auto-add the uplink subnet to local whitelist
-* black- and whitelist also accept domain names as input to allow IP filtering based on these names
-* supports a 'whitelist only' mode, this option allows to restrict Internet access from/to a small number of secure websites/IPs
+* supports nft atomic set loading
+* supports blocking by ASN numbers and by iso country codes
+* supports local allow- and blocklist (IPv4, IPv6, CIDR notation or domain names)
+* auto-add the uplink subnet to the local allowlist
 * provides a small background log monitor to ban unsuccessful login attempts in real-time
-* per source configuration of SRC (incoming) and DST (outgoing)
-* integrated IPSet-Lookup
-* integrated bgpview-Lookup
-* blocklist source parsing by fast & flexible regex rulesets
+* auto-add unsuccessful LuCI, nginx, Asterisk or ssh login attempts to the local blocklist
+* fast feed processing as they are handled in parallel as background jobs
+* per feed it can be defined whether the input chain or the forward chain should be blocked (default: both chains)
+* automatic blocklist backup & restore, the backups will be used in case of download errors or during startup
+* automatically selects one of the following download utilities with ssl support: aria2c, curl, uclient-fetch or wget
+* supports a 'allowlist only' mode, this option restricts internet access from/to a small number of secure websites/IPs
+* provides comprehensive runtime information
+* provides a detailed set report
+* provides a set search engine for certain IPs
+* feed parsing by fast & flexible regex rulesets
 * minimal status & error logging to syslog, enable debug logging to receive more output
-* procd based init system support (start/stop/restart/reload/refresh/status)
+* procd based init system support (start/stop/restart/reload/status/report/search)
 * procd network interface trigger support
-* automatic blocklist backup & restore, they will be used in case of download errors or during startup
-* provides comprehensive runtime information
-* provides a detailed IPSet Report
-* provides a powerful query function to quickly find blocked IPs/CIDR in banIP related IPSets
-* provides an easily configurable blocklist update scheduler called 'Refresh Timer'
-* strong LuCI support
-* optional: add new banIP sources on your own
+* ability to add new banIP feeds on your own
 
 ## Prerequisites
-* [OpenWrt](https://openwrt.org), tested with the stable release series (21.02.x) and with the latest rolling snapshot releases. On turris devices it has been successfully tested with TurrisOS 5.2.x  
-  <b>Please note:</b> Ancient OpenWrt releases like 18.06.x or 17.01.x are _not_ supported!  
-  <b>Please note:</b> Devices with less than 128 MByte RAM are _not_ supported!  
-  <b>Please note:</b> If you're updating from former banIP 0.3x please manually remove your config (/etc/config/banip) before you start!  
-* A download utility with SSL support: 'wget', 'uclient-fetch' with one of the 'libustream-*' ssl libraries, 'aria2c' or 'curl' is required
-* A certificate store like 'ca-bundle', as banIP checks the validity of the SSL certificates of all download sites by default
-* Optional E-Mail notification support: for E-Mail notifications you need to install and setup the additional 'msmtp' package
+* **[OpenWrt](https://openwrt.org)**, latest stable release or a snapshot with nft/firewall 4 support  
+* a download utility with SSL support: 'wget', 'uclient-fetch' with one of the 'libustream-*' SSL libraries, 'aria2c' or 'curl' is required
+* a certificate store like 'ca-bundle', as banIP checks the validity of the SSL certificates of all download sites by default
+* for E-Mail notifications you need to install and setup the additional 'msmtp' package
+
+**Please note the following:**
+* Devices with less than 256Mb of RAM are **_not_** supported
+* Any previous installation of banIP must be uninstalled, and the /etc/banip folder and the /etc/config/banip configuration file must be deleted (they are recreated when this version is installed)
+* There is no LuCI frontend at this time
 
 ## Installation & Usage
-* Update your local opkg repository (_opkg update_)
-* Install 'banip' (_opkg install banip_). The banIP service is disabled by default
-* Install the LuCI companion package 'luci-app-banip' (_opkg install luci-app-banip_)
-* It's strongly recommended to use the LuCI frontend to easily configure all aspects of banIP, the application is located in LuCI under the 'Services' menu
+* update your local opkg repository (_opkg update_)
+* install banIP (_opkg install banip_) - the banIP service is disabled by default
+* edit the config file '/etc/config/banip' and enable the service (set ban\_enabled to '1'), then add pre-configured feeds via 'ban\_feed' (see the config options below)
+* start the service with '/etc/init.d/banip start' and check check everything is working by running '/etc/init.d/banip status'
 
-## banIP CLI
-* All important banIP functions are accessible via CLI as well.  
-<pre><code>
-~# /etc/init.d/banip 
+## banIP CLI interface
+* All important banIP functions are accessible via CLI. A LuCI frontend will be available in due course.
+```
+~# /etc/init.d/banip
 Syntax: /etc/init.d/banip [command]
 
 Available commands:
@@ -96,259 +108,151 @@ Available commands:
        enable          Enable service autostart
        disable         Disable service autostart
        enabled         Check if service is started on boot
-       refresh         Refresh ipsets without new list downloads
-       suspend         Suspend banIP processing
-       resume          Resume banIP processing
-       query           &lt;IP&gt; Query active banIP IPSets for a specific IP address
-       report          [&lt;cli&gt;|&lt;mail&gt;|&lt;gen&gt;|&lt;json&gt;] Print banIP related IPset statistics
-       list            [&lt;add&gt;|&lt;add_asn&gt;|&lt;add_country&gt;|&lt;remove>|&lt;remove_asn&gt;|&lt;remove_country&gt;] &lt;source(s)&gt; List/Edit available sources
-       timer           [&lt;add&gt; &lt;tasks&gt; &lt;hour&gt; [&lt;minute&gt;] [&lt;weekday&gt;]]|[&lt;remove&gt; &lt;line no.&gt;] List/Edit cron update intervals
-       version         Print version information
+       report          [text|json|mail] Print banIP related set statistics
+       search          [<IPv4 address>|<IPv6 address>] Check if an element exists in the banIP sets
        running         Check if service is running
        status          Service status
        trace           Start with syscall trace
-</code></pre>
+       info            Dump procd service info
+```
 
 ## banIP config options
-* Usually the auto pre-configured banIP setup works quite well and no manual overrides are needed
 
 | Option                  | Type   | Default                       | Description                                                                           |
 | :---------------------- | :----- | :---------------------------- | :------------------------------------------------------------------------------------ |
 | ban_enabled             | option | 0                             | enable the banIP service                                                              |
+| ban_nicelimit           | option | 0                             | ulimit nice level of the banIP service (range 0-19)                                   |
+| ban_filelimit           | option | 1024                          | ulimit max open/number of files (range 1024-4096)                                     |
+| ban_loglimit            | option | 100                           | the logread monitor scans only the last n lines of the logfile                        |
+| ban_logcount            | option | 1                             | how many times the IP must appear in the log to be considered as suspicious           |
+| ban_logterm             | list   | regex                         | various regex for logfile parsing (default: dropbear, sshd, luci, nginx, asterisk)    |
 | ban_autodetect          | option | 1                             | auto-detect wan interfaces, devices and subnets                                       |
 | ban_debug               | option | 0                             | enable banIP related debug logging                                                    |
-| ban_mail_enabled        | option | 0                             | enable the mail service                                                               |
-| ban_monitor_enabled     | option | 0                             | enable the log monitor, e.g. to catch failed ssh/luci logins                          |
-| ban_logsrc_enabled      | option | 0                             | enable the src-related logchain                                                       |
-| ban_logdst_enabled      | option | 0                             | enable the dst-related logchain                                                       |
-| ban_autoblacklist       | option | 1                             | add suspicious IPs automatically to the local blacklist                               |
-| ban_autowhitelist       | option | 1                             | add wan IPs/subnets automatically to the local whitelist                              |
-| ban_whitelistonly       | option | 0                             | allow to restrict Internet access from/to a small number of secure websites/IPs       |
-| ban_maxqueue            | option | 4                             | size of the download queue to handle downloads and processing in parallel             |
-| ban_reportdir           | option | /tmp/banIP-Report             | directory where banIP stores the report files                                         |
-| ban_backupdir           | option | /tmp/banIP-Backup             | directory where banIP stores the compressed backup files                              |
-| ban_ifaces              | list   | -                             | list option to add logical wan interfaces manually                                    |
-| ban_sources             | list   | -                             | list option to add banIP sources                                                      |
-| ban_countries           | list   | -                             | list option to add certain countries as an alpha-2 ISO code, e.g. 'de' for germany    |
-| ban_asns                | list   | -                             | list option to add certain ASNs (autonomous system number), e.g. '32934' for facebook |
-| ban_chain               | option | banIP                         | name of the root chain used by banIP                                                  |
-| ban_global_settype      | option | src+dst                       | global settype as default for all sources                                             |
-| ban_settype_src         | list   | -                             | special SRC settype for a certain sources                                             |
-| ban_settype_dst         | list   | -                             | special DST settype for a certain sources                                             |
-| ban_settype_all         | list   | -                             | special SRC+DST settype for a certain sources                                         |
-| ban_target_src          | option | DROP                          | default src action (used by log chains as well)                                       |
-| ban_target_dst          | option | REJECT                        | default dst action (used by log chains as well)                                       |
-| ban_lan_inputchains_4   | list   | input_lan_rule                | list option to add IPv4 lan input chains                                              |
-| ban_lan_inputchains_6   | list   | input_lan_rule                | list option to add IPv6 lan input chains                                              |
-| ban_lan_forwardchains_4 | list   | forwarding_lan_rule           | list option to add IPv4 lan forward chains                                            |
-| ban_lan_forwardchains_6 | list   | forwarding_lan_rule           | list option to add IPv6 lan forward chains                                            |
-| ban_wan_inputchains_4   | list   | input_wan_rule                | list option to add IPv4 wan input chains                                              |
-| ban_wan_inputchains_6   | list   | input_wan_rule                | list option to add IPv6 wan input chains                                              |
-| ban_wan_forwardchains_4 | list   | forwarding_wan_rule           | list option to add IPv4 wan forward chains                                            |
-| ban_wan_forwardchains_6 | list   | forwarding_wan_rule           | list option to add IPv6 wan forward chains                                            |
-| ban_fetchutil           | option | -, auto-detected              | 'uclient-fetch', 'wget', 'curl' or 'aria2c'                                           |
-| ban_fetchparm           | option | -, auto-detected              | manually override the config options for the selected download utility                |
-| ban_fetchinsecure       | option | 0, disabled                   | don't check SSL server certificates during download                                   |
+| ban_loginput            | option | 1                             | log drops in the input chain                                                          |
+| ban_logforward          | option | 0                             | log rejects in the forward chain                                                      |
+| ban_autoallowlist       | option | 1                             | add wan IPs/subnets automatically to the local allowlist                              |
+| ban_autoblocklist       | option | 1                             | add suspicious attacker IPs automatically to the local blocklist                      |
+| ban_allowlistonly       | option | 0                             | restrict the internet access from/to a small number of secure websites/IPs            |
+| ban_reportdir           | option | /tmp/banIP-report             | directory where banIP stores the report files                                         |
+| ban_backupdir           | option | /tmp/banIP-backup             | directory where banIP stores the compressed backup files                              |
+| ban_protov4             | option | - / autodetect                | enable IPv4 support                                                                   |
+| ban_protov6             | option | - / autodetect                | enable IPv4 support                                                                   |
+| ban_ifv4                | list   | - / autodetect                | logical wan IPv4 interfaces, e.g. 'wan'                                               |
+| ban_ifv6                | list   | - / autodetect                | logical wan IPv6 interfaces, e.g. 'wan6'                                              |
+| ban_dev                 | list   | - / autodetect                | wan device(s), e.g. 'eth2'                                                            |
+| ban_trigger             | list   | -                             | logical startup trigger interface(s), e.g. 'wan'                                      |
+| ban_triggerdelay        | option | 10                            | trigger timeout before banIP processing begins                                        |
+| ban_deduplicate         | option | 1                             | deduplicate IP addresses across all active sets                                       |
+| ban_splitsize           | option | 0                             | split ext. sets after every n lines/members (saves RAM)                               |
+| ban_cores               | option | - / autodetect                | limit the cpu cores used by banIP (saves RAM)                                         |
+| ban_nftexpiry           | option | -                             | expiry time for auto added blocklist members, e.g. '5m', '2h' or '1d'                 |
+| ban_nftpriority         | option | -200                          | nft banIP table priority (default is the prerouting table priority)                   |
+| ban_feed                | list   | -                             | external download feeds, e.g. 'yoyo', 'doh', 'country' or 'talos' (see feed table)    |
+| ban_asn                 | list   | -                             | ASNs for the 'asn' feed, e.g.'32934'                                                  |
+| ban_country             | list   | -                             | country iso codes for the 'country' feed, e.g. 'ru'                                   |
+| ban_blockinput          | list   | -                             | limit a feed to the input chain, e.g. 'country'                                       |
+| ban_blockforward        | list   | -                             | limit a feed to the forward chain, e.g. 'doh'                                         |
+| ban_fetchcmd            | option | - / autodetect                | 'uclient-fetch', 'wget', 'curl' or 'aria2c'                                           |
+| ban_fetchparm           | option | - / autodetect                | set the config options for the selected download utility                              |
+| ban_fetchinsecure       | option | 0                             | don't check SSL server certificates during download                                   |
 | ban_mailreceiver        | option | -                             | receiver address for banIP related notification E-Mails                               |
 | ban_mailsender          | option | no-reply@banIP                | sender address for banIP related notification E-Mails                                 |
 | ban_mailtopic           | option | banIP notification            | topic for banIP related notification E-Mails                                          |
 | ban_mailprofile         | option | ban_notify                    | mail profile used in 'msmtp' for banIP related notification E-Mails                   |
-| ban_srcarc              | option | /etc/banip/banip.sources.gz   | full path to the compressed source archive file used by banIP                         |
-| ban_localsources        | list   | maclist, whitelist, blacklist | limit the selection to certain local sources                                          |
-| ban_extrasources        | list   | -                             | add additional, non-banIP related IPSets e.g. for reporting or queries                |
-| ban_maclist_timeout     | option | -                             | individual maclist IPSet timeout                                                      |
-| ban_whitelist_timeout   | option | -                             | individual whitelist IPSet timeout                                                    |
-| ban_blacklist_timeout   | option | -                             | individual blacklist IPSet timeout                                                    |
-| ban_logterms            | list   | dropbear, sshd, luci, nginx   | limit the log monitor to certain log terms                                            |
-| ban_loglimit            | option | 100                           | parse only the last stated number of log entries for suspicious events                |
-| ban_ssh_logcount        | option | 3                             | number of the failed ssh login repetitions of the same ip in the log before banning   |
-| ban_luci_logcount       | option | 3                             | number of the failed luci login repetitions of the same ip in the log before banning  |
-| ban_nginx_logcount      | option | 5                             | number of the failed nginx requests of the same ip in the log before banning          |
-  
-## Examples
-**list/edit banIP sources:**  
-<pre><code>
-~# /etc/init.d/banip list
-::: Available banIP sources
-:::
-    Name                 Enabled   Focus                               Info URL
-    ---------------------------------------------------------------------------
-  + asn                            ASN blocks                          https://asn.ipinfo.app
-  + bogon                          Bogon prefixes                      https://team-cymru.com
-  + country              x         Country blocks                      https://www.ipdeny.com/ipblocks
-  + darklist             x         Blocks suspicious attacker IPs      https://darklist.de
-  + debl                 x         Fail2ban IP blacklist               https://www.blocklist.de
-  + doh                  x         Public DoH-Provider                 https://github.com/dibdot/DoH-IP-blocklists
-  + drop                 x         Spamhaus drop compilation           https://www.spamhaus.org
-  + dshield              x         Dshield IP blocklist                https://www.dshield.org
-  + edrop                          Spamhaus edrop compilation          https://www.spamhaus.org
-  + feodo                x         Feodo Tracker                       https://feodotracker.abuse.ch
-  + firehol1             x         Firehol Level 1 compilation         https://iplists.firehol.org/?ipset=firehol_level1
-  + firehol2                       Firehol Level 2 compilation         https://iplists.firehol.org/?ipset=firehol_level2
-  + firehol3                       Firehol Level 3 compilation         https://iplists.firehol.org/?ipset=firehol_level3
-  + firehol4                       Firehol Level 4 compilation         https://iplists.firehol.org/?ipset=firehol_level4
-  + greensnow            x         Blocks suspicious server IPs        https://greensnow.co
-  + iblockads                      Advertising blocklist               https://www.iblocklist.com
-  + iblockspy            x         Malicious spyware blocklist         https://www.iblocklist.com
-  + myip                           Myip Live IP blacklist              https://myip.ms
-  + nixspam              x         iX spam protection                  http://www.nixspam.org
-  + proxy                          Firehol list of open proxies        https://iplists.firehol.org/?ipset=proxylists
-  + sslbl                x         SSL botnet IP blacklist             https://sslbl.abuse.ch
-  + talos                x         Cisco Talos IP Blacklist            https://talosintelligence.com/reputation_center
-  + threat               x         Emerging Threats                    https://rules.emergingthreats.net
-  + tor                  x         Tor exit nodes                      https://fissionrelays.net/lists
-  + uceprotect1          x         Spam protection level 1             http://www.uceprotect.net/en/index.php
-  + uceprotect2                    Spam protection level 2             http://www.uceprotect.net/en/index.php
-  + voip                 x         VoIP fraud blocklist                http://www.voipbl.org
-  + yoyo                 x         Ad protection blacklist             https://pgl.yoyo.org/adservers/
-    ---------------------------------------------------------------------------
-  * Configured ASNs: -
-  * Configured Countries: af, bd, br, cn, hk, hu, id, il, in, iq, ir, kp, kr, no, pk, pl, ro, ru, sa, th, tr, ua, gb
-</code></pre>
-  
-**receive banIP runtime information:**  
-<pre><code>
-~# /etc/init.d/banip status
-::: banIP runtime information
-  + status          : enabled
-  + version         : 0.7.7
-  + ipset_info      : 2 IPSets with 30 IPs/Prefixes
-  + active_sources  : whitelist
-  + active_devs     : wlan0
-  + active_ifaces   : trm_wwan, trm_wwan6
-  + active_logterms : dropbear, sshd, luci, nginx
-  + active_subnets  : xxx.xxx.xxx.xxx/24, xxxx:xxxx:xxxx:xx::xxx/128
-  + run_infos       : settype: src+dst, backup_dir: /tmp/banIP-Backup, report_dir: /tmp/banIP-Report
-  + run_flags       : protocols (4/6): ✔/✔, log (src/dst): ✔/✘, monitor: ✔, mail: ✘, whitelist only: ✔
-  + last_run        : restart, 0m 3s, 122/30/14, 21.04.2021 20:14:36
-  + system          : TP-Link RE650 v1, OpenWrt SNAPSHOT r16574-f7e00d81bc
-</code></pre>
-  
-**black-/whitelist handling:**  
-banIP supports a local black & whitelist (IPv4, IPv6, CIDR notation or domain names), located by default in /etc/banip/banip.whitelist and /etc/banip/banip.blacklist.  
-Unsuccessful LuCI logins, suspicious nginx request or ssh login attempts via 'dropbear'/'sshd' could be tracked and automatically added to the local blacklist (see the 'ban_autoblacklist' option). Furthermore the uplink subnet could be automatically added to local whitelist (see 'ban_autowhitelist' option). The list behaviour could be further tweaked with different timeout and counter options (see the config options section above).  
-Last but not least, both lists also accept domain names as input to allow IP filtering based on these names. The corresponding IPs (IPv4 & IPv6) will be resolved in a detached background process and added to the IPsets. The detached name lookup takes place only during 'restart' or 'reload' action, 'start' and 'refresh' actions are using an auto-generated backup instead.
-  
-**whitelist-only mode:**  
-banIP supports a "whitelist only" mode. This option allows to restrict the internet access from/to a small number of secure websites/IPs, and block access from/to the rest of the internet. All IPs and Domains which are _not_ listed in the whitelist are blocked. Please note: suspend/resume does not work in this mode.
-  
-**Manually override the download options:**  
-By default banIP uses the following pre-configured download options:  
-* aria2c: <code>--timeout=20 --allow-overwrite=true --auto-file-renaming=false --log-level=warn --dir=/ -o</code>
-* curl: <code>--connect-timeout 20 --silent --show-error --location -o</code>
-* uclient-fetch: <code>--timeout=20 -O</code>
-* wget: <code>--no-cache --no-cookies --max-redirect=0 --timeout=20 -O</code>
+| ban_resolver            | option | -                             | external resolver used for DNS lookups                                                |
+| ban_feedarchive         | option | /etc/banip/banip.feeds.gz     | full path to the compressed feed archive file used by banIP                           |
 
-To override the default set 'ban_fetchparm' manually to your needs.
-  
-**generate an IPSet report:**  
-<pre><code>
+## Examples
+**banIP report information**  
+```
 ~# /etc/init.d/banip report
 :::
-::: report on all banIP related IPSets
+::: banIP Set Statistics
 :::
-  + Report timestamp           ::: 04.02.2021 06:24:41
-  + Number of all IPSets       ::: 24
-  + Number of all entries      ::: 302448
-  + Number of IP entries       ::: 224748
-  + Number of CIDR entries     ::: 77700
-  + Number of MAC entries      ::: 0
-  + Number of accessed entries ::: 36
+    Timestamp: 2023-02-08 22:12:40
+    ------------------------------
+    auto-added to allowlist: 1
+    auto-added to blocklist: 0
+
+    Set                  | Set Elements  | Chain Input   | Chain Forward | Input Packets | Forward Packets
+    ---------------------+---------------+---------------+---------------+---------------+----------------
+    allowlistvMAC        | 0             | n/a           | OK            | n/a           | 0             
+    allowlistv4          | 1             | OK            | OK            | 0             | 0             
+    allowlistv6          | 0             | OK            | OK            | 0             | 0             
+    blocklistvMAC        | 0             | n/a           | OK            | n/a           | 0             
+    blocklistv4          | 0             | OK            | OK            | 0             | 0             
+    blocklistv6          | 0             | OK            | OK            | 0             | 0             
+    dohv4                | 542           | n/a           | OK            | n/a           | 22            
+    adguardv4            | 23007         | n/a           | OK            | n/a           | 18            
+    yoyov4               | 1936          | n/a           | OK            | n/a           | 1             
+    oisdbasicv4          | 26000         | n/a           | OK            | n/a           | 325           
+    ---------------------+---------------+---------------+---------------+---------------+----------------
+    10                   | 51486         | 4             | 10            | 0             | 366
+```
+
+**banIP runtime information**  
+```
+~# etc/init.d/banip status
+::: banIP runtime information
+  + status            : active
+  + version           : 0.8.0
+  + element_count     : 51486
+  + active_feeds      : allowlistvMAC, allowlistv4, allowlistv6, blocklistvMAC, blocklistv4, blocklistv6, dohv4, adguardv4
+                        , yoyov4, oisdbasicv4
+  + active_devices    : eth2
+  + active_interfaces : wan
+  + active_subnets    : 192.168.98.107/24
+  + run_info          : base_dir: /tmp, backup_dir: /tmp/banIP-backup, report_dir: /tmp/banIP-report, feed_archive: /etc/b
+                        anip/banip.feeds.gz
+  + run_flags         : protocol (4/6): ✔/✘, log (inp/fwd): ✔/✘, deduplicate: ✔, split: ✘, allowed only: ✘
+  + last_run          : action: start, duration: 0m 15s, date: 2023-02-08 22:12:46
+  + system_info       : cores: 2, memory: 3614, device: PC Engines apu1, OpenWrt SNAPSHOT r21997-b5193291bd
+```
+
+**banIP search information**  
+```
+~# /etc/init.d/banip search 221.228.105.173
 :::
-::: IPSet details
+::: banIP Search
 :::
-    Name                 Type        Count      Cnt_IP    Cnt_CIDR  Cnt_MAC   Cnt_ACC   Entry details (Entry/Count)
-    --------------------------------------------------------------------------------------------------------------------
-    whitelist_4          src+dst     1          0         1         0         1
-                                                                                        xxx.xxxx.xxx.xxxx/24     85
-    --------------------------------------------------------------------------------------------------------------------
-    whitelist_6          src+dst     2          0         2         0         1
-                                                                                        xxxx:xxxx:xxxx::/64      29
-    --------------------------------------------------------------------------------------------------------------------
-    blacklist_4          src+dst     513        513       0         0         2
-                                                                                        192.35.168.16            3
-                                                                                        80.82.65.74              1
-    --------------------------------------------------------------------------------------------------------------------
-    blacklist_6          src+dst     1          1         0         0         0
-    --------------------------------------------------------------------------------------------------------------------
-    country_4            src         52150      0         52150     0         23
-                                                                                        124.5.0.0/16             1
-                                                                                        95.188.0.0/14            1
-                                                                                        121.16.0.0/12            1
-                                                                                        46.161.0.0/18            1
-                                                                                        42.56.0.0/14             1
-                                                                                        113.64.0.0/10            1
-                                                                                        113.252.0.0/14           1
-                                                                                        5.201.128.0/17           1
-                                                                                        125.64.0.0/11            1
-                                                                                        90.188.0.0/15            1
-                                                                                        60.0.0.0/11              1
-                                                                                        78.160.0.0/11            1
-                                                                                        1.80.0.0/12              1
-                                                                                        183.184.0.0/13           1
-                                                                                        175.24.0.0/14            1
-                                                                                        119.176.0.0/12           1
-                                                                                        59.88.0.0/13             1
-                                                                                        103.78.12.0/22           1
-                                                                                        123.128.0.0/13           1
-                                                                                        116.224.0.0/12           1
-                                                                                        42.224.0.0/12            1
-                                                                                        82.80.0.0/15             1
-                                                                                        14.32.0.0/11             1
-    --------------------------------------------------------------------------------------------------------------------
-    country_6            src         20099      0         20099     0         0
-    --------------------------------------------------------------------------------------------------------------------
-    debl_4               src+dst     29389      29389     0         0         1
-                                                                                        5.182.210.16             4
-    --------------------------------------------------------------------------------------------------------------------
-    debl_6               src+dst     64         64        0         0         0
-    --------------------------------------------------------------------------------------------------------------------
-    doh_4                src+dst     168        168       0         0         0
-    --------------------------------------------------------------------------------------------------------------------
-    doh_6                src+dst     122        122       0         0         0
-    --------------------------------------------------------------------------------------------------------------------
-    drop_4               src+dst     965        0         965       0         0
-    --------------------------------------------------------------------------------------------------------------------
-    drop_6               src+dst     36         0         36        0         0
-    --------------------------------------------------------------------------------------------------------------------
-    dshield_4            src+dst     20         0         20        0         1
-                                                                                        89.248.165.0/24          1
-    --------------------------------------------------------------------------------------------------------------------
-    feodo_4              src+dst     325        325       0         0         0
-    --------------------------------------------------------------------------------------------------------------------
-    firehol1_4           src+dst     2763       403       2360      0         0
-    --------------------------------------------------------------------------------------------------------------------
-    iblockspy_4          src+dst     3650       2832      818       0         0
-    --------------------------------------------------------------------------------------------------------------------
-    nixspam_4            src+dst     9577       9577      0         0         0
-    --------------------------------------------------------------------------------------------------------------------
-    sslbl_4              src+dst     104        104       0         0         0
-    --------------------------------------------------------------------------------------------------------------------
-    threat_4             src+dst     1300       315       985       0         0
-    --------------------------------------------------------------------------------------------------------------------
-    tor_4                src+dst     1437       1437      0         0         0
-    --------------------------------------------------------------------------------------------------------------------
-    tor_6                src+dst     478        478       0         0         0
-    --------------------------------------------------------------------------------------------------------------------
-    uceprotect1_4        src+dst     156249     156249    0         0         6
-                                                                                        192.241.220.137          1
-                                                                                        128.14.137.178           1
-                                                                                        61.219.11.153            1
-                                                                                        138.34.32.33             1
-                                                                                        107.174.133.130          2
-                                                                                        180.232.99.46            1
-    --------------------------------------------------------------------------------------------------------------------
-    voip_4               src+dst     12563      12299     264       0         0
-    --------------------------------------------------------------------------------------------------------------------
-    yoyo_4               src+dst     10472      10472     0         0         1
-                                                                                        204.79.197.200           2
-    --------------------------------------------------------------------------------------------------------------------
-</code></pre>
-  
-**Enable E-Mail notification via 'msmtp':**  
-To use the email notification you have to install & configure the package 'msmtp'.  
+    Looking for IP 221.228.105.173 on 2023-02-08 22:12:48
+    ---
+    IP found in set oisdbasicv4
+```
+
+**allow-/blocklist handling**  
+banIP supports local allow and block lists (IPv4, IPv6, CIDR notation or domain names), located in /etc/banip/banip.allowlist and /etc/banip/banip.blocklist.  
+Unsuccessful login attempts or suspicious requests will be tracked and added to the local blocklist (see the 'ban\_autoblocklist' option). The blocklist behaviour can be further tweaked with the 'ban\_nftexpiry' option.  
+Furthermore the uplink subnet will be added to local allowlist (see 'ban\_autowallowlist' option).  
+Both lists also accept domain names as input to allow IP filtering based on these names. The corresponding IPs (IPv4 & IPv6) will be extracted in a detached background process and added to the sets.
+
+**allowlist-only mode**  
+banIP supports an "allowlist only" mode. This option restricts the internet access from/to a small number of secure websites/IPs, and block access from/to the rest of the internet. All IPs and Domains which are _not_ listed in the allowlist are blocked.
+
+**redirect Asterisk security logs to lodg/logread**   
+banIP only supports logfile scanning via logread, so to monitor attacks on Asterisk, its security log must be available via logread. To do this, edit '/etc/asterisk/logger.conf' and add the line 'syslog.local0 = security', then run 'asterisk -rx reload logger' to update the running Asterisk configuration.
+
+**tweaks for low memory systems**  
+nftables supports the atomic loading of rules/sets/members, which is cool but unfortunately is also very memory intensive. To reduce the memory pressure on low memory systems (i.e. those with 256-512Mb RAM), you should optimize your configuration with the following options:  
+
+    * point 'ban_reportdir' and 'ban_backupdir' to an external usb drive
+    * set 'ban_cores' to '1' (only useful on a multicore system) to force sequential feed processing
+    * set 'ban_splitsize' e.g. to '1000' to split the load of an external set after every 1000 lines/members
+
+**tweak the download options**  
+By default banIP uses the following pre-configured download options:
+```
+    * aria2c: --timeout=20 --allow-overwrite=true --auto-file-renaming=false --log-level=warn --dir=/ -o
+    * curl: --connect-timeout 20 --silent --show-error --location -o
+    * uclient-fetch: --timeout=20 -O
+    * wget: --no-cache --no-cookies --max-redirect=0 --timeout=20 -O
+```
+To override the default set 'ban_fetchparm' manually to your needs.
+
+**send E-Mail notifications via 'msmtp'**  
+To use the email notification you must install & configure the package 'msmtp'.  
 Modify the file '/etc/msmtprc', e.g.:
-<pre><code>
+```
 [...]
 defaults
 auth            on
@@ -360,39 +264,37 @@ syslog          LOG_MAIL
 account         ban_notify
 host            smtp.gmail.com
 port            587
-from            &lt;address&gt;@gmail.com
-user            &lt;gmail-user&gt;
-password        &lt;password&gt;
-</code></pre>
-Finally enable E-Mail support and add a valid E-Mail receiver address in LuCI.
-  
-**Edit, add new banIP sources:**  
-The banIP blocklist sources are stored in an external, compressed JSON file '/etc/banip/banip.sources.gz'. 
-This file is directly parsed in LuCI and accessible via CLI, just call _/etc/init.d/banip list_.
+from            <address>@gmail.com
+user            <gmail-user>
+password        <password>
+```
+Finally add a valid E-Mail receiver address.
 
-To add new or edit existing sources extract the compressed JSON file _gunzip /etc/banip/banip.sources.gz_.  
+**add new banIP feeds**  
+The banIP blocklist feeds are stored in an external, compressed JSON file '/etc/banip/banip.feeds.gz'.  
+To add a new or edit an existing feed extract the compressed JSON file _gunzip /etc/banip/banip.feeds.gz_.
 A valid JSON source object contains the following required information, e.g.:
-<pre><code>
+```
        [...]
        "tor": {
-               "url_4": "https://lists.fissionrelays.net/tor/exits-ipv4.txt",
-               "url_6": "https://lists.fissionrelays.net/tor/exits-ipv6.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add tor_4 \"$1}",
-               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add tor_6 \"$1}",
-               "focus": "Tor exit nodes",
-               "descurl": "https://fissionrelays.net/lists"
+               "url_4": "https://raw.githubusercontent.com/SecOps-Institute/Tor-IP-Addresses/master/tor-exit-nodes.lst",
+               "url_6": "https://raw.githubusercontent.com/SecOps-Institute/Tor-IP-Addresses/master/tor-exit-nodes.lst",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "tor exit nodes",
+               "descurl": "https://github.com/SecOps-Institute/Tor-IP-Addresses"
        },
        [...]
-</code></pre>
-Add an unique object name, make the required changes to 'url_4', 'rule_4' (and/or 'url_6', 'rule_6'), 'focus' and 'descurl' and finally compress the changed JSON file _gzip /etc/banip/banip.sources.gz_ to use the new source object in banIP.  
-<b>Please note:</b> if you're going to add new sources on your own, please make a copy of the default file and work with that copy further on, cause the default will be overwritten with every banIP update. To reference your copy set the option 'ban\_srcarc' which points by default to '/etc/banip/banip.sources.gz'  
-  
+```
+Add an unique object name, make the required changes and compress the changed JSON file finally with _gzip /etc/banip/banip.feeds_ to use the new feed file in banIP.  
+**Please note:** if you're going to add new feeds, **always** work with a copy of the default file; this file is always overwritten with every banIP update. To reference your own file set the option 'ban\_feedarchive' accordingly
+
 ## Support
-Please join the banIP discussion in this [forum thread](https://forum.openwrt.org/t/banip-support-thread/16985) or contact me by mail <dev@brenken.org>  
+Please join the banIP discussion in this [forum thread](https://forum.openwrt.org/t/banip-support-thread/16985) or contact me by mail <dev@brenken.org>
 
 ## Removal
 * stop all banIP related services with _/etc/init.d/banip stop_
 * optional: remove the banip package (_opkg remove banip_)
 
 Have fun!  
-Dirk  
+Dirk
diff --git a/net/banip/files/banip-functions.sh b/net/banip/files/banip-functions.sh
new file mode 100644 (file)
index 0000000..532b3b1
--- /dev/null
@@ -0,0 +1,1159 @@
+# banIP shared function library/include
+# Copyright (c) 2018-2023 Dirk Brenken (dev@brenken.org)
+# This is free software, licensed under the GNU General Public License v3.
+
+# (s)hellcheck exceptions
+# shellcheck disable=all
+
+# set initial defaults
+#
+export LC_ALL=C
+export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
+
+ban_basedir="/tmp"
+ban_backupdir="${ban_basedir}/banIP-backup"
+ban_reportdir="${ban_basedir}/banIP-report"
+ban_feedarchive="/etc/banip/banip.feeds.gz"
+ban_pidfile="/var/run/banip.pid"
+ban_lock="/var/run/banip.lock"
+ban_blocklist="/etc/banip/banip.blocklist"
+ban_allowlist="/etc/banip/banip.allowlist"
+ban_fetchcmd=""
+ban_logreadcmd="$(command -v logread)"
+ban_logcmd="$(command -v logger)"
+ban_ubuscmd="$(command -v ubus)"
+ban_nftcmd="$(command -v nft)"
+ban_fw4cmd="$(command -v fw4)"
+ban_awkcmd="$(command -v awk)"
+ban_grepcmd="$(command -v grep)"
+ban_lookupcmd="$(command -v nslookup)"
+ban_mailcmd="$(command -v msmtp)"
+ban_mailsender="no-reply@banIP"
+ban_mailreceiver=""
+ban_mailtopic="banIP notification"
+ban_mailprofile="ban_notify"
+ban_mailtemplate="/etc/banip/banip.tpl"
+ban_nftpriority="-200"
+ban_nftexpiry=""
+ban_loglevel="warn"
+ban_loglimit="100"
+ban_logcount="1"
+ban_logterm=""
+ban_country=""
+ban_asn=""
+ban_loginput="0"
+ban_logforward="0"
+ban_allowlistonly="0"
+ban_autoallowlist="1"
+ban_autoblocklist="1"
+ban_deduplicate="1"
+ban_splitsize="0"
+ban_autodetect=""
+ban_feed=""
+ban_blockinput=""
+ban_blockforward=""
+ban_protov4="0"
+ban_protov6="0"
+ban_ifv4=""
+ban_ifv6=""
+ban_dev=""
+ban_sub=""
+ban_fetchinsecure=""
+ban_cores=""
+ban_memory=""
+ban_trigger=""
+ban_triggerdelay="10"
+ban_resolver=""
+ban_enabled="0"
+ban_debug="0"
+
+# gather system information
+#
+f_system() {
+       local cpu core
+
+       ban_memory="$("${ban_awkcmd}" '/^MemAvailable/{printf "%s",int($2/1000)}' "/proc/meminfo" 2>/dev/null)"
+       ban_ver="$(${ban_ubuscmd} -S call rpc-sys packagelist 2>/dev/null | jsonfilter -ql1 -e '@.packages.banip')"
+       ban_sysver="$(${ban_ubuscmd} -S call system board 2>/dev/null | jsonfilter -ql1 -e '@.model' -e '@.release.description' |
+               "${ban_awkcmd}" 'BEGIN{RS="";FS="\n"}{printf "%s, %s",$1,$2}')"
+       if [ -z "${ban_cores}" ]; then
+               cpu="$("${ban_grepcmd}" -c '^processor' /proc/cpuinfo 2>/dev/null)"
+               core="$("${ban_grepcmd}" -cm1 '^core id' /proc/cpuinfo 2>/dev/null)"
+               [ "${cpu}" = "0" ] && cpu="1"
+               [ "${core}" = "0" ] && core="1"
+               ban_cores="$((cpu * core))"
+       fi
+
+       f_log "debug" "f_system  ::: system: ${ban_sysver:-"n/a"}, version: ${ban_ver:-"n/a"}, memory: ${ban_memory:-"0"}, cpu_cores: ${ban_cores}"
+}
+
+# create directories
+#
+f_mkdir() {
+       local dir="${1}"
+
+       if [ ! -d "${dir}" ]; then
+               rm -f "${dir}"
+               mkdir -p "${dir}"
+               f_log "debug" "f_mkdir   ::: created directory: ${dir}"
+       fi
+}
+
+# create files
+#
+f_mkfile() {
+       local file="${1}"
+
+       if [ ! -f "${file}" ]; then
+               : >"${file}"
+               f_log "debug" "f_mkfile  ::: created file: ${file}"
+       fi
+}
+
+# create temporary files and directories
+#
+f_tmp() {
+       f_mkdir "${ban_basedir}"
+       ban_tmpdir="$(mktemp -p "${ban_basedir}" -d)"
+       ban_tmpfile="$(mktemp -p "${ban_tmpdir}" -tu)"
+
+       f_log "debug" "f_tmp     ::: base_dir: ${ban_basedir:-"-"}, tmp_dir: ${ban_tmpdir:-"-"}"
+}
+
+# remove directories
+#
+f_rmdir() {
+       local dir="${1}"
+
+       if [ -d "${dir}" ]; then
+               rm -rf "${dir}"
+               f_log "debug" "f_rmdir   ::: deleted directory: ${dir}"
+       fi
+}
+
+# convert chars
+#
+f_char() {
+       local char="${1}"
+
+       [ "${char}" = "1" ] && printf "%s" "✔" || printf "%s" "✘"
+}
+
+# trim strings
+#
+f_trim() {
+       local string="${1}"
+
+       string="${string#"${string%%[![:space:]]*}"}"
+       string="${string%"${string##*[![:space:]]}"}"
+       printf "%s" "${string}"
+}
+
+# write log messages
+#
+f_log() {
+       local class="${1}" log_msg="${2}"
+
+       if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${ban_debug}" = "1" ]; }; then
+               if [ -x "${ban_logcmd}" ]; then
+                       "${ban_logcmd}" -p "${class}" -t "banIP-${ban_ver}[${$}]" "${log_msg}"
+               else
+                       printf "%s %s %s\n" "${class}" "banIP-${ban_ver}[${$}]" "${log_msg}"
+               fi
+       fi
+       if [ "${class}" = "err" ]; then
+               f_genstatus "error"
+               f_rmdir "${ban_tmpdir}"
+               rm -rf "${ban_lock}"
+               exit 1
+       fi
+}
+
+# load config
+#
+f_conf() {
+       unset ban_dev ban_ifv4 ban_ifv6 ban_feed ban_blockinput ban_blockforward ban_logterm ban_country ban_asn
+       config_cb() {
+               option_cb() {
+                       local option="${1}"
+                       local value="${2}"
+                       eval "${option}=\"${value}\""
+               }
+               list_cb() {
+                       local option="${1}"
+                       local value="${2}"
+                       case "${option}" in
+                               "ban_dev")
+                                       eval "${option}=\"$(printf "%s" "${ban_dev}")${value} \""
+                                       ;;
+                               "ban_ifv4")
+                                       eval "${option}=\"$(printf "%s" "${ban_ifv4}")${value} \""
+                                       ;;
+                               "ban_ifv6")
+                                       eval "${option}=\"$(printf "%s" "${ban_ifv6}")${value} \""
+                                       ;;
+                               "ban_feed")
+                                       eval "${option}=\"$(printf "%s" "${ban_feed}")${value} \""
+                                       ;;
+                               "ban_blockinput")
+                                       eval "${option}=\"$(printf "%s" "${ban_blockinput}")${value} \""
+                                       ;;
+                               "ban_blockforward")
+                                       eval "${option}=\"$(printf "%s" "${ban_blockforward}")${value} \""
+                                       ;;
+                               "ban_logterm")
+                                       eval "${option}=\"$(printf "%s" "${ban_logterm}")${value}\\|\""
+                                       ;;
+                               "ban_country")
+                                       eval "${option}=\"$(printf "%s" "${ban_country}")${value} \""
+                                       ;;
+                               "ban_asn")
+                                       eval "${option}=\"$(printf "%s" "${ban_asn}")${value} \""
+                                       ;;
+                       esac
+               }
+       }
+       config_load banip
+
+       [ "${ban_action}" = "boot" ] && [ -z "${ban_trigger}" ] && sleep ${ban_triggerdelay}
+}
+
+# prepare fetch utility
+#
+f_fetch() {
+       local ut utils packages insecure
+
+       if [ -z "${ban_fetchcmd}" ] || [ ! -x "${ban_fetchcmd}" ]; then
+               packages="$(${ban_ubuscmd} -S call rpc-sys packagelist 2>/dev/null)"
+               [ -z "${packages}" ] && f_log "err" "local opkg package repository is not available, please set the download utility 'ban_fetchcmd' manually"
+               utils="aria2c curl wget uclient-fetch"
+               for ut in ${utils}; do
+                       if { [ "${ut}" = "uclient-fetch" ] && printf "%s" "${packages}" | "${ban_grepcmd}" -q '"libustream-'; } ||
+                               { [ "${ut}" = "wget" ] && printf "%s" "${packages}" | "${ban_grepcmd}" -q '"wget-ssl'; } ||
+                               [ "${ut}" = "curl" ] || [ "${ut}" = "aria2c" ]; then
+                               ban_fetchcmd="$(command -v "${ut}")"
+                               if [ -x "${ban_fetchcmd}" ]; then
+                                       uci_set banip global ban_fetchcmd "${ban_fetchcmd##*/}"
+                                       uci_commit "banip"
+                                       break
+                               fi
+                       fi
+               done
+       fi
+       [ ! -x "${ban_fetchcmd}" ] && f_log "err" "download utility with SSL support not found"
+       case "${ban_fetchcmd##*/}" in
+               "aria2c")
+                       [ "${ban_fetchinsecure}" = "1" ] && insecure="--check-certificate=false"
+                       ban_fetchparm="${ban_fetchparm:-"${insecure} --timeout=20 --allow-overwrite=true --auto-file-renaming=false --log-level=warn --dir=/ -o"}"
+                       ;;
+               "curl")
+                       [ "${ban_fetchinsecure}" = "1" ] && insecure="--insecure"
+                       ban_fetchparm="${ban_fetchparm:-"${insecure} --connect-timeout 20 --fail --silent --show-error --location -o"}"
+                       ;;
+               "uclient-fetch")
+                       [ "${ban_fetchinsecure}" = "1" ] && insecure="--no-check-certificate"
+                       ban_fetchparm="${ban_fetchparm:-"${insecure} --timeout=20 -O"}"
+                       ;;
+               "wget")
+                       [ "${ban_fetchinsecure}" = "1" ] && insecure="--no-check-certificate"
+                       ban_fetchparm="${ban_fetchparm:-"${insecure} --no-cache --no-cookies --max-redirect=0 --timeout=20 -O"}"
+                       ;;
+       esac
+
+       f_log "debug" "f_fetch   ::: fetch_cmd: ${ban_fetchcmd:-"-"}, fetch_parm: ${ban_fetchparm:-"-"}"
+}
+
+# remove logservice
+#
+f_rmpid() {
+       local ppid pid pids
+
+       ppid="$(cat "${ban_pidfile}" 2>/dev/null)"
+       [ -n "${ppid}" ] && pids="$(pgrep -P "${ppid}" 2>/dev/null)" || return 0
+       for pid in ${pids}; do
+               kill -INT "${pid}" >/dev/null 2>&1
+       done
+       : >"${ban_pidfile}"
+}
+
+# get wan interfaces
+#
+f_getif() {
+       local iface
+
+       "${ban_ubuscmd}" -t 5 wait_for network.device network.interface 2>/dev/null
+       if [ "${ban_autodetect}" = "1" ]; then
+               if [ -z "${ban_ifv4}" ]; then
+                       network_find_wan iface
+                       if [ -n "${iface}" ] && ! printf "%s" "${ban_ifv4}" | "${ban_grepcmd}" -q "${iface}"; then
+                               ban_protov4="1"
+                               ban_ifv4="${ban_ifv4}${iface} "
+                               uci_set banip global ban_protov4 "1"
+                               uci_add_list banip global ban_ifv4 "${iface}"
+                       fi
+               fi
+               if [ -z "${ban_ifv6}" ]; then
+                       network_find_wan6 iface
+                       if [ -n "${iface}" ] && ! printf "%s" "${ban_ifv6}" | "${ban_grepcmd}" -q "${iface}"; then
+                               ban_protov6="1"
+                               ban_ifv6="${ban_ifv6}${iface} "
+                               uci_set banip global ban_protov6 "1"
+                               uci_add_list banip global ban_ifv6 "${iface}"
+                       fi
+               fi
+               ban_ifv4="${ban_ifv4%%?}"
+               ban_ifv6="${ban_ifv6%%?}"
+               [ -n "$(uci -q changes "banip")" ] && uci_commit "banip"
+       fi
+       [ -z "${ban_ifv4}" ] && [ -z "${ban_ifv6}" ] && f_log "err" "wan interfaces not found, please check your configuration"
+
+       f_log "debug" "f_getif   ::: auto_detect: ${ban_autodetect}, interfaces (4/6): ${ban_ifv4}/${ban_ifv6}, protocols (4/6): ${ban_protov4}/${ban_protov6}"
+}
+
+# get wan devices
+#
+f_getdev() {
+       local dev iface
+
+       if [ "${ban_autodetect}" = "1" ] && [ -z "${ban_dev}" ]; then
+               for iface in ${ban_ifv4} ${ban_ifv6}; do
+                       network_get_device dev "${iface}"
+                       if [ -n "${dev}" ] && ! printf "%s" "${ban_dev}" | "${ban_grepcmd}" -q "${dev}"; then
+                               ban_dev="${ban_dev}${dev} "
+                               uci_add_list banip global ban_dev "${dev}"
+                       else
+                               network_get_physdev dev "${iface}"
+                               if [ -n "${dev}" ] && ! printf "%s" "${ban_dev}" | "${ban_grepcmd}" -q "${dev}"; then
+                                       ban_dev="${ban_dev}${dev} "
+                                       uci_add_list banip global ban_dev "${dev}"
+                               fi
+                       fi
+               done
+               ban_dev="${ban_dev%%?}"
+               [ -n "$(uci -q changes "banip")" ] && uci_commit "banip"
+       fi
+       [ -z "${ban_dev}" ] && f_log "err" "wan devices not found, please check your configuration"
+
+       f_log "debug" "f_getdev  ::: auto_detect: ${ban_autodetect}, devices: ${ban_dev}"
+}
+
+# get local subnets
+#
+f_getsub() {
+       local sub iface ip
+
+       for iface in ${ban_ifv4} ${ban_ifv6}; do
+               network_get_subnet sub "${iface}"
+               if [ -n "${sub}" ] && ! printf "%s" "${ban_sub}" | "${ban_grepcmd}" -q "${sub}"; then
+                       ban_sub="${ban_sub} ${sub}"
+               fi
+               network_get_subnet6 sub "${iface}"
+               if [ -n "${sub}" ] && ! printf "%s" "${ban_sub}" | "${ban_grepcmd}" -q "${sub}"; then
+                       ban_sub="${ban_sub} ${sub}"
+               fi
+       done
+       if [ "${ban_autoallowlist}" = "1" ]; then
+               for ip in ${ban_sub}; do
+                       if ! "${ban_grepcmd}" -q "${ip}" "${ban_allowlist}"; then
+                               printf "%-42s%s\n" "${ip}" "added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_allowlist}"
+                               f_log "info" "add subnet '${ip}' to local allowlist"
+                       fi
+               done
+       fi
+       [ -z "${ban_sub}" ] && f_log "err" "wan subnet(s) not found, please check your configuration"
+
+       f_log "debug" "f_getsub  ::: auto_allowlist: ${ban_autoallowlist}, subnet(s): ${ban_sub:-"-"}"
+}
+
+# get set elements
+#
+f_getelements() {
+       local file="${1}"
+
+       [ -s "${file}" ] && printf "%s" "elements={ $(cat "${file}") };"
+}
+
+# build initial nft file with base table, chains and rules
+#
+f_nftinit() {
+       local feed_log feed_rc file="${1}"
+
+       {
+               # nft header (tables and chains)
+               #
+               printf "%s\n\n" "#!/usr/sbin/nft -f"
+               if "${ban_nftcmd}" -t list table inet banIP >/dev/null 2>&1; then
+                       printf "%s\n" "delete table inet banIP"
+               fi
+               printf "%s\n" "add table inet banIP"
+               printf "%s\n" "add chain inet banIP wan-input { type filter hook input priority ${ban_nftpriority}; policy accept; }"
+               printf "%s\n" "add chain inet banIP lan-forward { type filter hook forward priority ${ban_nftpriority}; policy accept; }"
+
+               # default input rules
+               #
+               printf "%s\n" "add rule inet banIP wan-input ct state established,related counter accept"
+               printf "%s\n" "add rule inet banIP wan-input iifname != { ${ban_dev// /, } } counter accept"
+               printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv4 icmp type { echo-request } limit rate 1000/second counter accept"
+               printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv6 icmpv6 type { echo-request } limit rate 1000/second counter accept"
+               printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv6 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} limit rate 1000/second ip6 hoplimit 1 counter accept"
+               printf "%s\n" "add rule inet banIP wan-input meta nfproto ipv6 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} limit rate 1000/second ip6 hoplimit 255 counter accept"
+
+               # default forward rules
+               #
+               printf "%s\n" "add rule inet banIP lan-forward ct state established,related counter accept"
+               printf "%s\n" "add rule inet banIP lan-forward oifname != { ${ban_dev// /, } } counter accept"
+       } >"${file}"
+
+       # load initial banIP table within nft (atomic load)
+       #
+       feed_log="$("${ban_nftcmd}" -f "${file}" 2>&1)"
+       feed_rc="${?}"
+
+       f_log "debug" "f_nftinit ::: devices: ${ban_dev}, priority: ${ban_nftpriority}, rc: ${feed_rc:-"-"}, log: ${feed_log:-"-"}"
+       return ${feed_rc}
+}
+
+f_down() {
+       local nft_loginput nft_logforward start_ts end_ts tmp_raw tmp_load tmp_file split_file input_handles forward_handles handle
+       local cnt_set cnt_dl restore_rc feed_direction feed_rc feed_log feed="${1}" proto="${2}" feed_url="${3}" feed_rule="${4}" feed_flag="${5}"
+
+       start_ts="$(date +%s)"
+       feed="${feed}v${proto}"
+       tmp_load="${ban_tmpfile}.${feed}.load"
+       tmp_raw="${ban_tmpfile}.${feed}.raw"
+       tmp_split="${ban_tmpfile}.${feed}.split"
+       tmp_file="${ban_tmpfile}.${feed}.file"
+       tmp_flush="${ban_tmpfile}.${feed}.flush"
+       tmp_nft="${ban_tmpfile}.${feed}.nft"
+
+       [ "${ban_loginput}" = "1" ] && nft_loginput="limit rate 2/second log level ${ban_loglevel} prefix \"banIP_drp/${feed}: \""
+       [ "${ban_logforward}" = "1" ] && nft_logforward="limit rate 2/second log level ${ban_loglevel} prefix \"banIP_rej/${feed}: \""
+
+       # set source block direction
+       #
+       if printf "%s" "${ban_blockinput}" | "${ban_grepcmd}" -q "${feed%v*}"; then
+               feed_direction="input"
+       elif printf "%s" "${ban_blockforward}" | "${ban_grepcmd}" -q "${feed%v*}"; then
+               feed_direction="forward"
+       fi
+
+       # chain/rule maintenance
+       #
+       if [ "${ban_action}" = "reload" ] && "${ban_nftcmd}" -t list set inet banIP "${feed}" >/dev/null 2>&1; then
+               input_handles="$("${ban_nftcmd}" -t --handle --numeric list chain inet banIP wan-input 2>/dev/null)"
+               forward_handles="$("${ban_nftcmd}" -t --handle --numeric list chain inet banIP lan-forward 2>/dev/null)"
+               {
+                       printf "%s\n" "flush set inet banIP ${feed}"
+                       handle="$(printf "%s\n" "${input_handles}" | "${ban_awkcmd}" "/@${feed} /{print \$NF}")"
+                       [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-input handle ${handle}"
+                       handle="$(printf "%s\n" "${forward_handles}" | "${ban_awkcmd}" "/@${feed} /{print \$NF}")"
+                       [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP lan-forward handle ${handle}"
+               } >"${tmp_flush}"
+       fi
+
+       # restore local backups during init
+       #
+       if { [ "${ban_action}" != "reload" ] || [ "${feed_url}" = "local" ]; } && [ "${feed%v*}" != "allowlist" ] && [ "${feed%v*}" != "blocklist" ]; then
+               f_restore "${feed}" "${feed_url}" "${tmp_load}"
+               restore_rc="${?}"
+               feed_rc="${restore_rc}"
+       fi
+
+       # handle local lists
+       #
+       if [ "${feed%v*}" = "allowlist" ]; then
+               {
+                       printf "%s\n\n" "#!/usr/sbin/nft -f"
+                       [ -s "${tmp_flush}" ] && cat "${tmp_flush}"
+                       if [ "${proto}" = "MAC" ]; then
+                               "${ban_awkcmd}" '/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s, ",tolower($1)}' "${ban_allowlist}" >"${tmp_file}"
+                               printf "%s\n" "add set inet banIP ${feed} { type ether_addr; policy memory; $(f_getelements "${tmp_file}") }"
+                               if [ "${feed_direction}" != "input" ]; then
+                                       printf "%s\n" "add rule inet banIP lan-forward ether saddr @${feed} counter accept"
+                               fi
+                       elif [ "${proto}" = "4" ]; then
+                               "${ban_awkcmd}" '/^(([0-9]{1,3}\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{printf "%s, ",$1}' "${ban_allowlist}" >"${tmp_file}"
+                               printf "%s\n" "add set inet banIP ${feed} { type ipv4_addr; flags interval; auto-merge; policy memory; $(f_getelements "${tmp_file}") }"
+                               if [ "${feed_direction}" != "forward" ]; then
+                                       if [ "${ban_allowlistonly}" = "1" ]; then
+                                               printf "%s\n" "add rule inet banIP wan-input ip saddr != @${feed} ${nft_loginput} counter drop"
+                                       else
+                                               printf "%s\n" "add rule inet banIP wan-input ip saddr @${feed} counter accept"
+                                       fi
+                               fi
+                               if [ "${feed_direction}" != "input" ]; then
+                                       if [ "${ban_allowlistonly}" = "1" ]; then
+                                               printf "%s\n" "add rule inet banIP lan-forward ip daddr != @${feed} ${nft_logforward} counter reject with icmp type admin-prohibited"
+                                       else
+                                               printf "%s\n" "add rule inet banIP lan-forward ip daddr @${feed} counter accept"
+                                       fi
+                               fi
+                       elif [ "${proto}" = "6" ]; then
+                               "${ban_awkcmd}" '!/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s\n",$1}' "${ban_allowlist}" |
+                                       "${ban_awkcmd}" '/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{printf "%s, ",tolower($1)}' >"${tmp_file}"
+                               printf "%s\n" "add set inet banIP ${feed} { type ipv6_addr; flags interval; auto-merge; policy memory; $(f_getelements "${tmp_file}") }"
+                               if [ "${feed_direction}" != "forward" ]; then
+                                       if [ "${ban_allowlistonly}" = "1" ]; then
+                                               printf "%s\n" "add rule inet banIP wan-input ip6 saddr != @${feed} ${nft_loginput} counter drop"
+                                       else
+                                               printf "%s\n" "add rule inet banIP wan-input ip6 saddr @${feed} counter accept"
+                                       fi
+                               fi
+                               if [ "${feed_direction}" != "input" ]; then
+                                       if [ "${ban_allowlistonly}" = "1" ]; then
+                                               printf "%s\n" "add rule inet banIP lan-forward ip6 daddr != @${feed} ${nft_logforward} counter reject with icmpv6 type admin-prohibited"
+                                       else
+                                               printf "%s\n" "add rule inet banIP lan-forward ip6 daddr @${feed} counter accept"
+                                       fi
+                               fi
+                       fi
+               } >"${tmp_nft}"
+               feed_rc="${?}"
+       elif [ "${feed%v*}" = "blocklist" ]; then
+               {
+                       printf "%s\n\n" "#!/usr/sbin/nft -f"
+                       [ -s "${tmp_flush}" ] && cat "${tmp_flush}"
+                       if [ "${proto}" = "MAC" ]; then
+                               "${ban_awkcmd}" '/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s, ",tolower($1)}' "${ban_blocklist}" >"${tmp_file}"
+                               printf "%s\n" "add set inet banIP ${feed} { type ether_addr; policy memory; $(f_getelements "${tmp_file}") }"
+                               if [ "${feed_direction}" != "input" ]; then
+                                       printf "%s\n" "add rule inet banIP lan-forward ether saddr @${feed} ${nft_logforward} counter reject"
+                               fi
+                       elif [ "${proto}" = "4" ]; then
+                               if [ "${ban_deduplicate}" = "1" ]; then
+                                       "${ban_awkcmd}" '/^(([0-9]{1,3}\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{printf "%s,\n",$1}' "${ban_blocklist}" >"${tmp_raw}"
+                                       "${ban_awkcmd}" 'NR==FNR{member[$0];next}!($0 in member)' "${ban_tmpfile}.deduplicate" "${tmp_raw}" 2>/dev/null >"${tmp_split}"
+                                       "${ban_awkcmd}" 'BEGIN{FS="[ ,]"}NR==FNR{member[$1];next}!($1 in member)' "${ban_tmpfile}.deduplicate" "${ban_blocklist}" 2>/dev/null >"${tmp_raw}"
+                                       cat "${tmp_raw}" 2>/dev/null >"${ban_blocklist}"
+                               else
+                                       "${ban_awkcmd}" '/^(([0-9]{1,3}\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{printf "%s,\n",$1}' "${ban_blocklist}" >"${tmp_split}"
+                               fi
+                               "${ban_awkcmd}" '{ORS=" ";print}' "${tmp_split}" 2>/dev/null >"${tmp_file}"
+                               printf "%s\n" "add set inet banIP ${feed} { type ipv4_addr; flags interval, timeout; auto-merge; policy memory; $(f_getelements "${tmp_file}") }"
+                               if [ "${feed_direction}" != "forward" ]; then
+                                       printf "%s\n" "add rule inet banIP wan-input ip saddr @${feed} ${nft_loginput} counter drop"
+                               fi
+                               if [ "${feed_direction}" != "input" ]; then
+                                       printf "%s\n" "add rule inet banIP lan-forward ip daddr @${feed} ${nft_logforward} counter reject with icmp type admin-prohibited"
+                               fi
+                       elif [ "${proto}" = "6" ]; then
+                               if [ "${ban_deduplicate}" = "1" ]; then
+                                       "${ban_awkcmd}" '!/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s\n",$1}' "${ban_blocklist}" |
+                                               "${ban_awkcmd}" '/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{printf "%s,\n",tolower($1)}' >"${tmp_raw}"
+                                       "${ban_awkcmd}" 'NR==FNR{member[$0];next}!($0 in member)' "${ban_tmpfile}.deduplicate" "${tmp_raw}" 2>/dev/null >"${tmp_split}"
+                                       "${ban_awkcmd}" 'BEGIN{FS="[ ,]"}NR==FNR{member[$1];next}!($1 in member)' "${ban_tmpfile}.deduplicate" "${ban_blocklist}" 2>/dev/null >"${tmp_raw}"
+                                       cat "${tmp_raw}" 2>/dev/null >"${ban_blocklist}"
+                               else
+                                       "${ban_awkcmd}" '!/^([0-9A-f]{2}:){5}[0-9A-f]{2}([[:space:]]|$)/{printf "%s\n",$1}' "${ban_blocklist}" |
+                                               "${ban_awkcmd}" '/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{printf "%s,\n",tolower($1)}' >"${tmp_split}"
+                               fi
+                               "${ban_awkcmd}" '{ORS=" ";print}' "${tmp_split}" 2>/dev/null >"${tmp_file}"
+                               printf "%s\n" "add set inet banIP ${feed} { type ipv6_addr; flags interval, timeout; auto-merge; policy memory; $(f_getelements "${tmp_file}") }"
+                               if [ "${feed_direction}" != "forward" ]; then
+                                       printf "%s\n" "add rule inet banIP wan-input ip6 saddr @${feed} ${nft_loginput} counter drop"
+                               fi
+                               if [ "${feed_direction}" != "input" ]; then
+                                       printf "%s\n" "add rule inet banIP lan-forward ip6 daddr @${feed} ${nft_logforward} counter reject with icmpv6 type admin-prohibited"
+                               fi
+                       fi
+               } >"${tmp_nft}"
+               feed_rc="${?}"
+       # handle external downloads
+       #
+       elif [ "${restore_rc}" != "0" ] && [ "${feed_url}" != "local" ]; then
+               # handle country downloads
+               #
+               if [ "${feed%v*}" = "country" ]; then
+                       for country in ${ban_country}; do
+                               feed_log="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_raw}" "${feed_url}${country}-aggregated.zone" 2>&1)"
+                               feed_rc="${?}"
+                               [ "${feed_rc}" = "0" ] && cat "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
+                       done
+                       rm -f "${tmp_raw}"
+
+               # handle asn downloads
+               #
+               elif [ "${feed%v*}" = "asn" ]; then
+                       for asn in ${ban_asn}; do
+                               feed_log="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_raw}" "${feed_url}AS${asn}" 2>&1)"
+                               feed_rc="${?}"
+                               [ "${feed_rc}" = "0" ] && cat "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
+                       done
+                       rm -f "${tmp_raw}"
+
+               # handle compressed downloads
+               #
+               elif [ -n "${feed_flag}" ]; then
+                       case "${feed_flag}" in
+                               "gz")
+                                       feed_log="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_raw}" "${feed_url}" 2>&1)"
+                                       feed_rc="${?}"
+                                       if [ "${feed_rc}" = "0" ]; then
+                                               zcat "${tmp_raw}" 2>/dev/null >"${tmp_load}"
+                                               feed_rc="${?}"
+                                       fi
+                                       rm -f "${tmp_raw}"
+                                       ;;
+                       esac
+
+               # handle normal downloads
+               #
+               else
+                       feed_log="$("${ban_fetchcmd}" ${ban_fetchparm} "${tmp_load}" "${feed_url}" 2>&1)"
+                       feed_rc="${?}"
+               fi
+       fi
+
+       # backup/restore
+       #
+       if [ "${restore_rc}" != "0" ] && [ "${feed_rc}" = "0" ] && [ "${feed_url}" != "local" ] && [ ! -s "${tmp_nft}" ]; then
+               f_backup "${feed}" "${tmp_load}"
+               feed_rc="${?}"
+       elif [ -z "${restore_rc}" ] && [ "${feed_rc}" != "0" ] && [ "${feed_url}" != "local" ] && [ ! -s "${tmp_nft}" ]; then
+               f_restore "${feed}" "${feed_url}" "${tmp_load}" "${feed_rc}"
+               feed_rc="${?}"
+       fi
+
+       # build nft file with set and rules for regular downloads
+       #
+       if [ "${feed_rc}" = "0" ] && [ ! -s "${tmp_nft}" ]; then
+               # deduplicate sets
+               #
+               if [ "${ban_deduplicate}" = "1" ] && [ "${feed_url}" != "local" ]; then
+                       "${ban_awkcmd}" "${feed_rule}" "${tmp_load}" 2>/dev/null >"${tmp_raw}"
+                       "${ban_awkcmd}" 'NR==FNR{member[$0];next}!($0 in member)' "${ban_tmpfile}.deduplicate" "${tmp_raw}" 2>/dev/null | tee -a "${ban_tmpfile}.deduplicate" >"${tmp_split}"
+               else
+                       "${ban_awkcmd}" "${feed_rule}" "${tmp_load}" 2>/dev/null >"${tmp_split}"
+               fi
+               feed_rc="${?}"
+               # split sets
+               #
+               if [ "${feed_rc}" = "0" ]; then
+                       if [ -n "${ban_splitsize//[![:digit]]/}" ] && [ "${ban_splitsize//[![:digit]]/}" -gt "0" ]; then
+                               if ! "${ban_awkcmd}" "NR%${ban_splitsize//[![:digit]]/}==1{file=\"${tmp_file}.\"++i;}{ORS=\" \";print > file}" "${tmp_split}" 2>/dev/null; then
+                                       rm -f "${tmp_file}".*
+                                       f_log "info" "failed to split ${feed} set to size '${ban_splitsize//[![:digit]]/}'"
+                               fi
+                       else
+                               "${ban_awkcmd}" '{ORS=" ";print}' "${tmp_split}" 2>/dev/null >"${tmp_file}.1"
+                       fi
+                       feed_rc="${?}"
+               fi
+               rm -f "${tmp_raw}" "${tmp_load}"
+               if [ "${feed_rc}" = "0" ] && [ "${proto}" = "4" ]; then
+                       {
+                               # nft header (IPv4 set)
+                               #
+                               printf "%s\n\n" "#!/usr/sbin/nft -f"
+                               [ -s "${tmp_flush}" ] && cat "${tmp_flush}"
+                               printf "%s\n" "add set inet banIP ${feed} { type ipv4_addr; flags interval; auto-merge; policy memory; $(f_getelements "${tmp_file}.1") }"
+
+                               # input and forward rules
+                               #
+                               if [ "${feed_direction}" != "forward" ]; then
+                                       printf "%s\n" "add rule inet banIP wan-input ip saddr @${feed} ${nft_loginput} counter drop"
+                               fi
+                               if [ "${feed_direction}" != "input" ]; then
+                                       printf "%s\n" "add rule inet banIP lan-forward ip daddr @${feed} ${nft_logforward} counter reject with icmp type admin-prohibited"
+                               fi
+                       } >"${tmp_nft}"
+               elif [ "${feed_rc}" = "0" ] && [ "${proto}" = "6" ]; then
+                       {
+                               # nft header (IPv6 set)
+                               #
+                               printf "%s\n\n" "#!/usr/sbin/nft -f"
+                               [ -s "${tmp_flush}" ] && cat "${tmp_flush}"
+                               printf "%s\n" "add set inet banIP ${feed} { type ipv6_addr; flags interval; auto-merge; policy memory; $(f_getelements "${tmp_file}.1") }"
+
+                               # input and forward rules
+                               #
+                               if [ "${feed_direction}" != "forward" ]; then
+                                       printf "%s\n" "add rule inet banIP wan-input ip6 saddr @${feed} ${nft_loginput} counter drop"
+                               fi
+                               if [ "${feed_direction}" != "input" ]; then
+                                       printf "%s\n" "add rule inet banIP lan-forward ip6 daddr @${feed} ${nft_logforward} counter reject with icmpv6 type admin-prohibited"
+                               fi
+                       } >"${tmp_nft}"
+               fi
+       fi
+
+       # load generated nft file in banIP table
+       #
+       if [ "${feed_rc}" = "0" ]; then
+               cnt_dl="$("${ban_awkcmd}" 'END{printf "%d",NR}' "${tmp_split}" 2>/dev/null)"
+               if [ "${cnt_dl:-"0"}" -gt "0" ] || [ "${feed_url}" = "local" ] || [ "${feed%v*}" = "allowlist" ] || [ "${feed%v*}" = "blocklist" ]; then
+                       feed_log="$("${ban_nftcmd}" -f "${tmp_nft}" 2>&1)"
+                       feed_rc="${?}"
+                       # load additional split files
+                       #
+                       if [ "${feed_rc}" = "0" ]; then
+                               for split_file in "${tmp_file}".*; do
+                                       [ ! -f "${split_file}" ] && break
+                                       if [ "${split_file##*.}" = "1" ]; then
+                                               rm -f "${split_file}"
+                                               continue
+                                       fi
+                                       if ! "${ban_nftcmd}" add element inet banIP "${feed}" "{ $(cat "${split_file}") }" >/dev/null 2>&1; then
+                                               f_log "info" "failed to add split file '${split_file##*.}' to ${feed} set"
+                                       fi
+                                       rm -f "${split_file}"
+                               done
+                               cnt_set="$("${ban_nftcmd}" -j list set inet banIP "${feed}" 2>/dev/null | jsonfilter -qe '@.nftables[*].set.elem[*]' | wc -l 2>/dev/null)"
+                       fi
+               else
+                       f_log "info" "empty feed ${feed} will be skipped"
+               fi
+       fi
+       rm -f "${tmp_split}" "${tmp_nft}"
+       end_ts="$(date +%s)"
+
+       f_log "debug" "f_down    ::: name: ${feed}, cnt_dl: ${cnt_dl:-"-"}, cnt_set: ${cnt_set:-"-"}, split_size: ${ban_splitsize:-"-"}, time: $((end_ts - start_ts)), rc: ${feed_rc:-"-"}, log: ${feed_log:-"-"}"
+}
+
+# backup feeds
+#
+f_backup() {
+       local backup_rc feed="${1}" feed_file="${2}"
+
+       gzip -cf "${feed_file}" >"${ban_backupdir}/banIP.${feed}.gz"
+       backup_rc="${?}"
+
+       f_log "debug" "f_backup  ::: name: ${feed}, source: ${feed_file##*/}, target: banIP.${feed}.gz, rc: ${backup_rc}"
+       return ${backup_rc}
+}
+
+# restore feeds
+#
+f_restore() {
+       local tmp_feed restore_rc="1" feed="${1}" feed_url="${2}" feed_file="${3}" feed_rc="${4:-"0"}"
+
+       [ "${feed_rc}" != "0" ] && restore_rc="${feed_rc}"
+       [ "${feed_url}" = "local" ] && tmp_feed="${feed%v*}v4" || tmp_feed="${feed}"
+       if [ -f "${ban_backupdir}/banIP.${tmp_feed}.gz" ]; then
+               zcat "${ban_backupdir}/banIP.${tmp_feed}.gz" 2>/dev/null >"${feed_file}"
+               restore_rc="${?}"
+       fi
+
+       f_log "debug" "f_restore ::: name: ${feed}, source: banIP.${tmp_feed}.gz, target: ${feed_file##*/}, in_rc: ${feed_rc}, rc: ${restore_rc}"
+       return ${restore_rc}
+}
+
+# remove disabled feeds
+#
+f_rmset() {
+       local tmp_del table_sets input_handles forward_handles handle sets feed feed_log feed_rc
+
+       tmp_del="${ban_tmpfile}.final.delete"
+       table_sets="$("${ban_nftcmd}" -t list table inet banIP 2>/dev/null | "${ban_awkcmd}" '/^[[:space:]]+set [[:alnum:]]+ /{printf "%s ",$2}' 2>/dev/null)"
+       input_handles="$("${ban_nftcmd}" -t --handle --numeric list chain inet banIP wan-input 2>/dev/null)"
+       forward_handles="$("${ban_nftcmd}" -t --handle --numeric list chain inet banIP lan-forward 2>/dev/null)"
+       {
+               printf "%s\n\n" "#!/usr/sbin/nft -f"
+               for feed in ${table_sets}; do
+                       if ! printf "%s" "allowlist blocklist ${ban_feed}" | "${ban_grepcmd}" -q "${feed%v*}"; then
+                               sets="${sets}${feed}/"
+                               rm -f "${ban_backupdir}/banIP.${feed}.gz"
+                               printf "%s\n" "flush set inet banIP ${feed}"
+                               handle="$(printf "%s\n" "${input_handles}" | "${ban_awkcmd}" "/@${feed} /{print \$NF}" 2>/dev/null)"
+                               [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP wan-input handle ${handle}"
+                               handle="$(printf "%s\n" "${forward_handles}" | "${ban_awkcmd}" "/@${feed} /{print \$NF}" 2>/dev/null)"
+                               [ -n "${handle}" ] && printf "%s\n" "delete rule inet banIP lan-forward handle ${handle}"
+                               printf "%s\n\n" "delete set inet banIP ${feed}"
+                       fi
+               done
+       } >"${tmp_del}"
+
+       if [ -n "${sets}" ]; then
+               feed_log="$("${ban_nftcmd}" -f "${tmp_del}" 2>&1)"
+               feed_rc="${?}"
+       fi
+       rm -f "${tmp_del}"
+
+       f_log "debug" "f_rmset   ::: sets: ${sets:-"-"}, tmp: ${tmp_del}, rc: ${feed_rc:-"-"}, log: ${feed_log:-"-"}"
+}
+
+# generate status information
+#
+f_genstatus() {
+       local object duration nft_table nft_feeds cnt_elements="0" split="0" status="${1}"
+
+       [ -z "${ban_dev}" ] && f_conf
+       if [ "${status}" = "active" ]; then
+               if [ -n "${ban_starttime}" ]; then
+                       ban_endtime="$(date "+%s")"
+                       duration="$(((ban_endtime - ban_starttime) / 60))m $(((ban_endtime - ban_starttime) % 60))s"
+               fi
+               nft_table="$("${ban_nftcmd}" -t list table inet banIP 2>/dev/null)"
+               nft_feeds="$(f_trim "$(printf "%s\n" "${nft_table}" | "${ban_awkcmd}" '/^[[:space:]]+set [[:alnum:]]+ /{printf "%s ",$2}')")"
+               for object in ${nft_feeds}; do
+                       cnt_elements="$((cnt_elements + $("${ban_nftcmd}" -j list set inet banIP "${object}" 2>/dev/null | jsonfilter -qe '@.nftables[*].set.elem[*]' | wc -l 2>/dev/null)))"
+               done
+               runtime="action: ${ban_action:-"-"}, duration: ${duration:-"-"}, date: $(date "+%Y-%m-%d %H:%M:%S")"
+       fi
+       f_system
+       [ ${ban_splitsize:-"0"} -gt "0" ] && split="1"
+
+       : >"${ban_basedir}/ban_runtime.json"
+       json_init
+       json_load_file "${ban_basedir}/ban_runtime.json" >/dev/null 2>&1
+       json_add_string "status" "${status}"
+       json_add_string "version" "${ban_ver}"
+       json_add_string "element_count" "${cnt_elements}"
+       json_add_array "active_feeds"
+       if [ "${status}" != "active" ]; then
+               json_add_object
+               json_add_string "feed" "-"
+               json_close_object
+       else
+               for object in ${nft_feeds}; do
+                       json_add_object
+                       json_add_string "feed" "${object}"
+                       json_close_object
+               done
+       fi
+       json_close_array
+       json_add_array "active_devices"
+       if [ "${status}" != "active" ]; then
+               json_add_object
+               json_add_string "device" "-"
+               json_close_object
+       else
+               for object in ${ban_dev}; do
+                       json_add_object
+                       json_add_string "device" "${object}"
+                       json_close_object
+               done
+       fi
+       json_close_array
+       json_add_array "active_interfaces"
+       if [ "${status}" != "active" ]; then
+               json_add_object
+               json_add_string "interface" "-"
+               json_close_object
+       else
+               for object in ${ban_ifv4} ${ban_ifv6}; do
+                       json_add_object
+                       json_add_string "interface" "${object}"
+                       json_close_object
+               done
+       fi
+       json_close_array
+       json_add_array "active_subnets"
+       if [ "${status}" != "active" ]; then
+               json_add_object
+               json_add_string "subnet" "-"
+               json_close_object
+       else
+               for object in ${ban_sub}; do
+                       json_add_object
+                       json_add_string "subnet" "${object}"
+                       json_close_object
+               done
+       fi
+       json_close_array
+       json_add_string "run_info" "base_dir: ${ban_basedir}, backup_dir: ${ban_backupdir}, report_dir: ${ban_reportdir}, feed_archive: ${ban_feedarchive}"
+       json_add_string "run_flags" "protocol (4/6): $(f_char ${ban_protov4})/$(f_char ${ban_protov6}), log (inp/fwd): $(f_char ${ban_loginput})/$(f_char ${ban_logforward}), deduplicate: $(f_char ${ban_deduplicate}), split: $(f_char ${split}), allowed only: $(f_char ${ban_allowlistonly})"
+       json_add_string "last_run" "${runtime:-"-"}"
+       json_add_string "system_info" "cores: ${ban_cores}, memory: ${ban_memory}, device: ${ban_sysver}"
+       json_dump >"${ban_basedir}/ban_runtime.json"
+}
+
+# get status information
+#
+f_getstatus() {
+       local key keylist type value index_value
+
+       [ -z "${ban_dev}" ] && f_conf
+       json_load_file "${ban_basedir}/ban_runtime.json" >/dev/null 2>&1
+       if json_get_keys keylist; then
+               printf "%s\n" "::: banIP runtime information"
+               for key in ${keylist}; do
+                       json_get_var value "${key}" >/dev/null 2>&1
+                       if [ "${key%_*}" = "active" ]; then
+                               json_select "${key}" >/dev/null 2>&1
+                               index=1
+                               while json_get_type type "${index}" && [ "${type}" = "object" ]; do
+                                       json_get_values index_value "${index}" >/dev/null 2>&1
+                                       if [ "${index}" = "1" ]; then
+                                               value="${index_value}"
+                                       else
+                                               value="${value}, ${index_value}"
+                                       fi
+                                       index=$((index + 1))
+                               done
+                               json_select ".."
+                       fi
+                       value="$(
+                               printf "%s" "${value}" |
+                                       awk '{NR=1;max=98;if(length($0)>max+1)while($0){if(NR==1){print substr($0,1,max)}else{printf"%-24s%s\n","",substr($0,1,max)}{$0=substr($0,max+1);NR=NR+1}}else print}'
+                       )"
+                       printf "  + %-17s : %s\n" "${key}" "${value:-"-"}"
+               done
+       else
+               printf "%s\n" "::: no banIP runtime information available"
+       fi
+}
+
+# domain lookup
+#
+f_lookup() {
+       local cnt list domain lookup ip start_time end_time duration cnt_domain="0" cnt_ip="0" feed="${1}"
+
+       start_time="$(date "+%s")"
+       if [ "${feed}" = "allowlist" ]; then
+               list="$("${ban_awkcmd}" '/^([[:alnum:]_-]{1,63}\.)+[[:alpha:]]+([[:space:]]|$)/{printf "%s ",tolower($1)}' "${ban_allowlist}" 2>/dev/null)"
+       elif [ "${feed}" = "blocklist" ]; then
+               list="$("${ban_awkcmd}" '/^([[:alnum:]_-]{1,63}\.)+[[:alpha:]]+([[:space:]]|$)/{printf "%s ",tolower($1)}' "${ban_blocklist}" 2>/dev/null)"
+       fi
+
+       for domain in ${list}; do
+               lookup="$("${ban_lookupcmd}" "${domain}" ${ban_resolver} 2>/dev/null | "${ban_awkcmd}" '/^Address[ 0-9]*: /{if(!seen[$NF]++)printf "%s ",$NF}' 2>/dev/null)"
+               for ip in ${lookup}; do
+                       if [ "${ip%%.*}" = "0" ] || [ -z "${ip%%::*}" ]; then
+                               continue
+                       else
+                               if { [ "${feed}" = "allowlist" ] && ! "${ban_grepcmd}" -q "^${ip}" "${ban_allowlist}"; } ||
+                                       { [ "${feed}" = "blocklist" ] && ! "${ban_grepcmd}" -q "^${ip}" "${ban_blocklist}"; }; then
+                                       cnt_ip="$((cnt_ip + 1))"
+                                       if [ "${ip##*:}" = "${ip}" ]; then
+                                               if ! "${ban_nftcmd}" add element inet banIP "${feed}v4" "{ ${ip} }" >/dev/null 2>&1; then
+                                                       f_log "info" "failed to add IP '${ip}' (${domain}) to ${feed}v4 set"
+                                                       continue
+                                               fi
+                                       else
+                                               if ! "${ban_nftcmd}" add element inet banIP "${feed}v6" "{ ${ip} }" >/dev/null 2>&1; then
+                                                       f_log "info" "failed to add IP '${ip}' (${domain}) to ${feed}v6 set"
+                                                       continue
+                                               fi
+                                       fi
+                                       if [ "${feed}" = "allowlist" ] && [ "${ban_autoallowlist}" = "1" ]; then
+                                               printf "%-42s%s\n" "${ip}" "# ip of '${domain}' added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_allowlist}"
+                                       elif [ "${feed}" = "blocklist" ] && [ "${ban_autoblocklist}" = "1" ]; then
+                                               printf "%-42s%s\n" "${ip}" "# ip of '${domain}' added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_blocklist}"
+                                       fi
+                               fi
+                       fi
+               done
+               cnt_domain="$((cnt_domain + 1))"
+       done
+       end_time="$(date "+%s")"
+       duration="$(((end_time - start_time) / 60))m $(((end_time - start_time) % 60))s"
+
+       f_log "debug" "f_lookup  ::: name: ${feed}, cnt_domain: ${cnt_domain}, cnt_ip: ${cnt_ip}, duration: ${duration}"
+}
+
+# banIP table statistics
+#
+f_report() {
+       local report_jsn report_txt set nft_raw nft_sets set_cnt set_input set_forward set_cntinput set_cntforward output="${1}"
+       local detail set_details jsnval timestamp autoadd_allow autoadd_block sum_sets sum_setinput sum_setforward sum_setelements sum_cntinput sum_cntforward
+
+       [ -z "${ban_dev}" ] && f_conf
+       f_mkdir "${ban_reportdir}"
+       report_jsn="${ban_reportdir}/ban_report.jsn"
+       report_txt="${ban_reportdir}/ban_report.txt"
+
+       # json output preparation
+       #
+       nft_raw="$("${ban_nftcmd}" -tj list table inet banIP 2>/dev/null)"
+       nft_sets="$(printf "%s" "${nft_raw}" | jsonfilter -qe '@.nftables[*].set.name')"
+       sum_sets="0"
+       sum_setinput="0"
+       sum_setforward="0"
+       sum_setelements="0"
+       sum_cntinput="0"
+       sum_cntforward="0"
+       timestamp="$(date "+%Y-%m-%d %H:%M:%S")"
+       : >"${report_jsn}"
+       {
+               printf "%s\n" "{"
+               printf "\t%s\n" '"sets": {'
+               for set in ${nft_sets}; do
+                       set_cnt="$("${ban_nftcmd}" -j list set inet banIP "${set}" 2>/dev/null | jsonfilter -qe '@.nftables[*].set.elem[*]' | wc -l 2>/dev/null)"
+                       sum_setelements="$((sum_setelements + set_cnt))"
+                       set_cntinput="$(printf "%s" "${nft_raw}" | jsonfilter -qe "@.nftables[@.rule.chain=\"wan-input\"][@.expr[*].match.right=\"@${set}\"].expr[*].counter.packets")"
+                       set_cntforward="$(printf "%s" "${nft_raw}" | jsonfilter -qe "@.nftables[@.rule.chain=\"lan-forward\"][@.expr[*].match.right=\"@${set}\"].expr[*].counter.packets")"
+                       if [ -n "${set_cntinput}" ]; then
+                               set_input="OK"
+                               sum_setinput="$((sum_setinput + 1))"
+                               sum_cntinput="$((sum_cntinput + set_cntinput))"
+                       else
+                               set_input="n/a"
+                               set_cntinput="n/a"
+                       fi
+                       if [ -n "${set_cntforward}" ]; then
+                               set_forward="OK"
+                               sum_setforward="$((sum_setforward + 1))"
+                               sum_cntforward="$((sum_cntforward + set_cntforward))"
+                       else
+                               set_forward="n/a"
+                               set_cntforward="n/a"
+                       fi
+                       [ "${sum_sets}" -gt "0" ] && printf "%s\n" ","
+                       printf "\t\t%s\n" "\"${set}\": {"
+                       printf "\t\t\t%s\n" "\"cnt_elements\": \"${set_cnt}\","
+                       printf "\t\t\t%s\n" "\"input\": \"${set_input}\","
+                       printf "\t\t\t%s\n" "\"forward\": \"${set_forward}\","
+                       printf "\t\t\t%s\n" "\"cnt_input\": \"${set_cntinput}\","
+                       printf "\t\t\t%s\n" "\"cnt_forward\": \"${set_cntforward}\""
+                       printf "\t\t%s" "}"
+                       sum_sets="$((sum_sets + 1))"
+               done
+               printf "\n\t%s\n" "},"
+               printf "\t%s\n" "\"timestamp\": \"${timestamp}\","
+               printf "\t%s\n" "\"autoadd_allow\": \"$("${ban_grepcmd}" -c "added on ${timestamp% *}" "${ban_allowlist}")\","
+               printf "\t%s\n" "\"autoadd_block\": \"$("${ban_grepcmd}" -c "added on ${timestamp% *}" "${ban_blocklist}")\","
+               printf "\t%s\n" "\"sum_sets\": \"${sum_sets}\","
+               printf "\t%s\n" "\"sum_setinput\": \"${sum_setinput}\","
+               printf "\t%s\n" "\"sum_setforward\": \"${sum_setforward}\","
+               printf "\t%s\n" "\"sum_setelements\": \"${sum_setelements}\","
+               printf "\t%s\n" "\"sum_cntinput\": \"${sum_cntinput}\","
+               printf "\t%s\n" "\"sum_cntforward\": \"${sum_cntforward}\""
+               printf "%s\n" "}"
+       } >>"${report_jsn}"
+
+       # text output preparation
+       #
+       if [ "${output}" != "json" ] && [ -s "${report_jsn}" ]; then
+               : >"${report_txt}"
+               json_init
+               if json_load_file "${report_jsn}" >/dev/null 2>&1; then
+                       json_get_var timestamp "timestamp" >/dev/null 2>&1
+                       json_get_var autoadd_allow "autoadd_allow" >/dev/null 2>&1
+                       json_get_var autoadd_block "autoadd_block" >/dev/null 2>&1
+                       json_get_var sum_sets "sum_sets" >/dev/null 2>&1
+                       json_get_var sum_setinput "sum_setinput" >/dev/null 2>&1
+                       json_get_var sum_setforward "sum_setforward" >/dev/null 2>&1
+                       json_get_var sum_setelements "sum_setelements" >/dev/null 2>&1
+                       json_get_var sum_cntinput "sum_cntinput" >/dev/null 2>&1
+                       json_get_var sum_cntforward "sum_cntforward" >/dev/null 2>&1
+                       {
+                               printf "%s\n%s\n%s\n" ":::" "::: banIP Set Statistics" ":::"
+                               printf "%s\n" "    Timestamp: ${timestamp}"
+                               printf "%s\n" "    ------------------------------"
+                               printf "%s\n" "    auto-added to allowlist: ${autoadd_allow}"
+                               printf "%s\n\n" "    auto-added to blocklist: ${autoadd_block}"
+                               json_select "sets" >/dev/null 2>&1
+                               json_get_keys nft_sets >/dev/null 2>&1
+                               if [ -n "${nft_sets}" ]; then
+                                       printf "%-25s%-16s%-16s%-16s%-16s%s\n" "    Set" "| Set Elements" "| Chain Input" "| Chain Forward" "| Input Packets" "| Forward Packets"
+                                       printf "%s\n" "    ---------------------+---------------+---------------+---------------+---------------+----------------"
+                                       for set in ${nft_sets}; do
+                                               printf "    %-21s" "${set}"
+                                               json_select "${set}"
+                                               json_get_keys set_details
+                                               for detail in ${set_details}; do
+                                                       json_get_var jsnval "${detail}" >/dev/null 2>&1
+                                                       printf "%-16s" "| ${jsnval}"
+                                               done
+                                               printf "\n"
+                                               json_select ".."
+                                       done
+                                       printf "%s\n" "    ---------------------+---------------+---------------+---------------+---------------+----------------"
+                                       printf "%-25s%-16s%-16s%-16s%-16s%s\n" "    ${sum_sets}" "| ${sum_setelements}" "| ${sum_setinput}" "| ${sum_setforward}" "| ${sum_cntinput}" "| ${sum_cntforward}"
+                               fi
+                       } >>"${report_txt}"
+               fi
+       fi
+
+       # output channel (text|json|mail)
+       #
+       case "${output}" in
+               "text")
+                       [ -s "${report_txt}" ] && cat "${report_txt}"
+                       ;;
+               "json")
+                       [ -s "${report_jsn}" ] && cat "${report_jsn}"
+                       ;;
+               "mail")
+                       [ -x "${ban_mailcmd}" ] && f_mail
+                       ;;
+       esac
+}
+
+# banIP set search
+#
+f_search() {
+       local nft_sets ip proto run_search search="${1}"
+
+       f_system
+       run_search="/var/run/banIP.search"
+
+       if [ -n "${search}" ]; then
+               ip="$(printf "%s" "${search}" | "${ban_awkcmd}" 'BEGIN{RS="(([0-9]{1,3}\\.){3}[0-9]{1,3})+"}{printf "%s",RT}')"
+               [ -n "${ip}" ] && proto="v4"
+               if [ -z "${proto}" ]; then
+                       ip="$(printf "%s" "${search}" | "${ban_awkcmd}" 'BEGIN{RS="([A-Fa-f0-9]{1,4}::?){3,7}[A-Fa-f0-9]{1,4}"}{printf "%s",RT}')"
+                       [ -n "${ip}" ] && proto="v6"
+               fi
+               if [ -n "${proto}" ]; then
+                       nft_sets="$("${ban_nftcmd}" -tj list table inet banIP 2>/dev/null | jsonfilter -qe "@.nftables[@.set.type=\"ip${proto}_addr\"].set.name")"
+               else
+                       printf "%s\n%s\n%s\n" ":::" "::: no valid search input (single IPv4/IPv6 address)" ":::"
+                       return
+               fi
+       else
+               printf "%s\n%s\n%s\n" ":::" "::: no valid search input (single IPv4/IPv6 address)" ":::"
+               return
+       fi
+       printf "%s\n%s\n%s\n" ":::" "::: banIP Search" ":::"
+       printf "%s\n" "    Looking for IP ${ip} on $(date "+%Y-%m-%d %H:%M:%S")"
+       printf "%s\n" "    ---"
+       cnt=1
+       for set in ${nft_sets}; do
+               (
+                       if "${ban_nftcmd}" get element inet banIP "${set}" "{ ${ip} }" >/dev/null 2>&1; then
+                               printf "%s\n" "    IP found in set ${set}"
+                               : >"${run_search}"
+                       fi
+               ) &
+               hold="$((cnt % ban_cores))"
+               [ "${hold}" = "0" ] && wait
+               cnt="$((cnt + 1))"
+       done
+       wait
+       [ ! -f "${run_search}" ] && printf "%s\n" "    IP not found"
+       rm -f "${run_search}"
+}
+
+# send status mails
+#
+f_mail() {
+       local msmtp_debug
+
+       # load mail template
+       #
+       [ ! -r "${ban_mailtemplate}" ] && f_log "err" "the mail template is missing"
+       . "${ban_mailtemplate}"
+
+       [ -z "${ban_mailreceiver}" ] && f_log "err" "the option 'ban_mailreceiver' is missing"
+       [ -z "${mail_text}" ] && f_log "err" "the 'mail_text' is empty"
+       [ "${ban_debug}" = "1" ] && msmtp_debug="--debug"
+
+       # send mail
+       #
+       ban_mailhead="From: ${ban_mailsender}\nTo: ${ban_mailreceiver}\nSubject: ${ban_mailtopic}\nReply-to: ${ban_mailsender}\nMime-Version: 1.0\nContent-Type: text/html;charset=utf-8\nContent-Disposition: inline\n\n"
+       if printf "%b" "${ban_mailhead}${mail_text}" | "${ban_mailcmd}" --timeout=10 ${msmtp_debug} -a "${ban_mailprofile}" "${ban_mailreceiver}" >/dev/null 2>&1; then
+               f_log "info" "status mail was sent successfully"
+       else
+               f_log "info" "failed to send status mail (${?})"
+       fi
+
+       f_log "debug" "f_mail    ::: template: ${ban_mailtemplate}, profile: ${ban_mailprofile}, receiver: ${ban_mailreceiver}, rc: ${?}"
+}
+
+# check banIP availability and initial sourcing
+#
+if [ "${ban_action}" != "stop" ]; then
+       if [ -r "/lib/functions.sh" ] && [ -r "/lib/functions/network.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]; then
+               . "/lib/functions.sh"
+               . "/lib/functions/network.sh"
+               . "/usr/share/libubox/jshn.sh"
+       else
+               f_log "err" "system libraries not found"
+       fi
+       [ ! -d "/etc/banip" ] && f_log "err" "banIP config directory not found, please re-install the package"
+       [ ! -r "/etc/config/banip" ] && f_log "err" "banIP config not found, please re-install the package"
+       [ ! -r "/etc/banip/banip.feeds.gz" ] || ! zcat "$(uci_get banip global ban_feedarchive "/etc/banip/banip.feeds.gz")" >"$(uci_get banip global ban_basedir "/tmp")/ban_feeds.json" && f_log "err" "banIP feed archive not found, please re-install the package"
+       [ "$(uci_get banip global ban_enabled)" = "0" ] && f_log "err" "banIP is currently disabled, please set the config option 'ban_enabled' to '1' to use this service"
+fi
diff --git a/net/banip/files/banip-service.sh b/net/banip/files/banip-service.sh
new file mode 100755 (executable)
index 0000000..29a0436
--- /dev/null
@@ -0,0 +1,193 @@
+#!/bin/sh
+# banIP main service script - ban incoming and outgoing ip adresses/subnets via sets in nftables
+# Copyright (c) 2018-2023 Dirk Brenken (dev@brenken.org)
+# This is free software, licensed under the GNU General Public License v3.
+
+# (s)hellcheck exceptions
+# shellcheck disable=all
+
+ban_action="${1}"
+ban_starttime="$(date "+%s")"
+ban_funlib="/usr/lib/banip-functions.sh"
+[ -z "$(command -v "f_system")" ] && . "${ban_funlib}"
+
+# load config and set banIP environment
+#
+f_conf
+f_log "info" "start banIP processing (${ban_action})"
+f_genstatus "processing"
+f_tmp
+f_fetch
+f_getif
+f_getdev
+f_getsub
+f_mkdir "${ban_backupdir}"
+f_mkfile "${ban_blocklist}"
+f_mkfile "${ban_allowlist}"
+
+# firewall check
+#
+if [ "${ban_action}" != "reload" ]; then
+       if [ -x "${ban_fw4cmd}" ]; then
+               cnt=0
+               while [ "${cnt}" -lt "10" ] && ! /etc/init.d/firewall status | grep -q "^active"; do
+                       cnt="$((cnt + 1))"
+                       sleep 1
+               done
+               if ! /etc/init.d/firewall status | grep -q "^active"; then
+                       f_log "err" "nft based firewall/fw4 not functional"
+               fi
+       else
+               f_log "err" "nft based firewall/fw4 not found"
+       fi
+fi
+
+# init nft namespace
+#
+if [ "${ban_action}" != "reload" ] || ! "${ban_nftcmd}" -t list table inet banIP >/dev/null 2>&1; then
+       if f_nftinit "${ban_tmpfile}".init.nft; then
+               f_log "info" "nft namespace initialized"
+       else
+               f_log "err" "nft namespace can't be initialized"
+       fi
+fi
+
+# handle downloads
+#
+f_log "info" "start banIP download processes"
+if [ "${ban_allowlistonly}" = "1" ]; then
+       ban_feed=""
+else
+       json_init
+       if ! json_load_file "${ban_basedir}/ban_feeds.json" >/dev/null 2>&1; then
+               f_log "err" "banIP feed file can't be loaded"
+       fi
+       [ "${ban_deduplicate}" = "1" ] && printf "\n" >"${ban_tmpfile}.deduplicate"
+fi
+
+cnt="1"
+for feed in allowlist ${ban_feed} blocklist; do
+       # local feeds
+       #
+       if [ "${feed}" = "allowlist" ] || [ "${feed}" = "blocklist" ]; then
+               for proto in MAC 4 6; do
+                       [ "${feed}" = "blocklist" ] && wait
+                       (f_down "${feed}" "${proto}") &
+                       [ "${feed}" = "blocklist" ] || { [ "${feed}" = "allowlist" ] && [ "${proto}" = "MAC" ]; } && wait
+                       hold="$((cnt % ban_cores))"
+                       [ "${hold}" = "0" ] && wait
+                       cnt="$((cnt + 1))"
+               done
+               wait
+               continue
+       fi
+
+       # read external feed information
+       #
+       if ! json_select "${feed}" >/dev/null 2>&1; then
+               continue
+       fi
+       json_objects="url_4 rule_4 url_6 rule_6 flag"
+       for object in ${json_objects}; do
+               eval json_get_var feed_"${object}" '${object}' >/dev/null 2>&1
+       done
+       json_select ..
+       # handle IPv4/IPv6 feeds with the same/single download URL
+       #
+       if [ "${feed_url_4}" = "${feed_url_6}" ]; then
+               if [ "${ban_protov4}" = "1" ] && [ -n "${feed_url_4}" ] && [ -n "${feed_rule_4}" ]; then
+                       (f_down "${feed}" "4" "${feed_url_4}" "${feed_rule_4}" "${feed_flag}") &
+                       feed_url_6="local"
+                       wait
+               fi
+               if [ "${ban_protov6}" = "1" ] && [ -n "${feed_url_6}" ] && [ -n "${feed_rule_6}" ]; then
+                       (f_down "${feed}" "6" "${feed_url_6}" "${feed_rule_6}" "${feed_flag}") &
+                       hold="$((cnt % ban_cores))"
+                       [ "${hold}" = "0" ] && wait
+                       cnt="$((cnt + 1))"
+               fi
+               continue
+       fi
+       # handle IPv4/IPv6 feeds with separated download URLs
+       #
+       if [ "${ban_protov4}" = "1" ] && [ -n "${feed_url_4}" ] && [ -n "${feed_rule_4}" ]; then
+               (f_down "${feed}" "4" "${feed_url_4}" "${feed_rule_4}" "${feed_flag}") &
+               hold="$((cnt % ban_cores))"
+               [ "${hold}" = "0" ] && wait
+               cnt="$((cnt + 1))"
+       fi
+       if [ "${ban_protov6}" = "1" ] && [ -n "${feed_url_6}" ] && [ -n "${feed_rule_6}" ]; then
+               (f_down "${feed}" "6" "${feed_url_6}" "${feed_rule_6}" "${feed_flag}") &
+               hold="$((cnt % ban_cores))"
+               [ "${hold}" = "0" ] && wait
+               cnt="$((cnt + 1))"
+       fi
+done
+wait
+
+# start domain lookup
+#
+f_log "info" "start detached banIP domain lookup"
+(f_lookup "allowlist") &
+hold="$((cnt % ban_cores))"
+[ "${hold}" = "0" ] && wait
+(f_lookup "blocklist") &
+
+# tidy up
+#
+f_rmset
+f_rmdir "${ban_tmpdir}"
+f_genstatus "active"
+f_log "info" "finished banIP download processes"
+rm -rf "${ban_lock}"
+
+# start log service
+#
+if [ -x "${ban_logreadcmd}" ] && [ -n "${ban_logterm%%??}" ]; then
+       f_log "info" "start detached banIP log service"
+
+       nft_expiry="$(printf "%s" "${ban_nftexpiry}" | grep -oE "([0-9]+[h|m|s]$)")"
+       [ -n "${nft_expiry}" ] && nft_expiry="timeout ${nft_expiry}"
+
+       # read log continuously with given logterms
+       #
+       "${ban_logreadcmd}" -fe "${ban_logterm%%??}" 2>/dev/null |
+               while read -r line; do
+                       # IPv4 log parsing
+                       #
+                       ip="$(printf "%s" "${line}" | "${ban_awkcmd}" 'BEGIN{RS="(([0-9]{1,3}\\.){3}[0-9]{1,3})+"}{if(!seen[RT]++)printf "%s ",RT}')"
+                       ip="$(f_trim "${ip}")"
+                       ip="${ip##* }"
+                       [ -n "${ip}" ] && proto="v4"
+                       if [ -z "${proto}" ]; then
+                               # IPv6 log parsing
+                               #
+                               ip="$(printf "%s" "${line}" | "${ban_awkcmd}" 'BEGIN{RS="([A-Fa-f0-9]{1,4}::?){3,7}[A-Fa-f0-9]{1,4}"}{if(!seen[RT]++)printf "%s ",RT}')"
+                               ip="$(f_trim "${ip}")"
+                               ip="${ip##* }"
+                               [ -n "${ip}" ] && proto="v6"
+                       fi
+                       if [ -n "${proto}" ] && ! "${ban_nftcmd}" get element inet banIP blocklist"${proto}" "{ ${ip} }" >/dev/null 2>&1; then
+                               f_log "info" "suspicious IP found '${ip}'"
+                               log_raw="$("${ban_logreadcmd}" -l "${ban_loglimit}" 2>/dev/null)"
+                               log_count="$(printf "%s\n" "${log_raw}" | grep -c "found '${ip}'")"
+                               if [ "${log_count}" -ge "${ban_logcount}" ]; then
+                                       if "${ban_nftcmd}" add element inet banIP "blocklist${proto}" "{ ${ip} ${nft_expiry} }" >/dev/null 2>&1; then
+                                               f_log "info" "added IP '${ip}' (${nft_expiry:-"-"}) to blocklist${proto} set"
+                                               if [ "${ban_autoblocklist}" = "1" ] && ! grep -q "^${ip}" "${ban_blocklist}"; then
+                                                       printf "%-42s%s\n" "${ip}" "# added on $(date "+%Y-%m-%d %H:%M:%S")" >>"${ban_blocklist}"
+                                                       f_log "info" "added IP '${ip}' to local blocklist"
+                                               fi
+                                       fi
+                               fi
+                       fi
+               done
+
+# start no-op service loop
+#
+else
+       f_log "info" "start detached no-op banIP service (logterms are missing)"
+       while :; do
+               sleep 1
+       done
+fi
diff --git a/net/banip/files/banip.allowlist b/net/banip/files/banip.allowlist
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/net/banip/files/banip.blacklist b/net/banip/files/banip.blacklist
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/net/banip/files/banip.blocklist b/net/banip/files/banip.blocklist
new file mode 100644 (file)
index 0000000..e69de29
index faae505380f00277e9b7940eac2ead67f8084352..ce0a9cac1448b0dd8b91a6b14be43c6f32011790 100644 (file)
@@ -1,16 +1,9 @@
 config banip 'global'
        option ban_enabled '0'
        option ban_debug '0'
-       option ban_mail_enabled '0'
-       option ban_monitor_enabled '0'
-       option ban_logsrc_enabled '0'
-       option ban_logdst_enabled '0'
        option ban_autodetect '1'
-       option ban_autoblacklist '1'
-       option ban_autowhitelist '1'
-       option ban_nice '0'
-       option ban_maxqueue '4'
-       option ban_global_settype 'src+dst'
-       option ban_target_src 'DROP'
-       option ban_target_dst 'REJECT'
-       option ban_loglimit '100'
+       list ban_logterm 'Exit before auth from'
+       list ban_logterm 'luci: failed login'
+       list ban_logterm 'error: maximum authentication attempts exceeded'
+       list ban_logterm 'sshd.*Connection closed by.*\[preauth\]'
+       list ban_logterm 'SecurityEvent=\"ChallengeResponseFailed\".*RemoteAddress='
diff --git a/net/banip/files/banip.dns b/net/banip/files/banip.dns
deleted file mode 100755 (executable)
index ab33650..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/sh
-# helper script to resolve domains for adding to banIP-related IPSets
-# Copyright (c) 2020-2021 Dirk Brenken (dev@brenken.org)
-# This is free software, licensed under the GNU General Public License v3.
-
-# (s)hellcheck exceptions
-# shellcheck disable=1091,3040
-
-export LC_ALL=C
-export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
-set -o pipefail
-
-. "/lib/functions.sh"
-
-ban_src_name="${1}"
-ban_src_file="${2}"
-ban_tmpbase="$(uci_get banip global ban_tmpbase "/tmp")"
-ban_backupdir="$(uci_get banip global ban_backupdir "${ban_tmpbase}/banIP-Backup")"
-ban_proto4_enabled="$(uci_get banip global ban_proto4_enabled "0")"
-ban_proto6_enabled="$(uci_get banip global ban_proto6_enabled "0")"
-ban_ipset_cmd="$(command -v ipset)"
-ban_lookup_cmd="$(command -v nslookup)"
-ban_logger_cmd="$(command -v logger)"
-ban_cnt_err="0"
-ban_message=""
-
-rm -f "${ban_backupdir}/banIP.${ban_src_name}_addon_4" "${ban_backupdir}/banIP.${ban_src_name}_addon_6"
-while read -r domain; do
-       result="$(
-               "${ban_lookup_cmd}" "${domain}" 2>/dev/null
-               printf "%s" "${?}"
-       )"
-       if [ "$(printf "%s" "${result}" | tail -1)" = "0" ]; then
-               ips="$(printf "%s" "${result}" | awk '/^Address[ 0-9]*: /{ORS=" ";print $NF}')"
-               for ip in ${ips}; do
-                       for proto in "4" "6"; do
-                               if { [ "${proto}" = "4" ] && [ "${ban_proto4_enabled}" = "1" ] && [ -n "$("${ban_ipset_cmd}" -q -n list "${ban_src_name}_${proto}")" ] &&
-                                       [ -n "$(printf "%s" "${ip}" | awk '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print $1}')" ]; } ||
-                                       { [ "${proto}" = "6" ] && [ "${ban_proto6_enabled}" = "1" ] && [ -n "$("${ban_ipset_cmd}" -q -n list "${ban_src_name}_${proto}")" ] &&
-                                               [ -n "$(printf "%s" "${ip}" | awk '/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print $1}')" ]; }; then
-                                       printf "%s\n" "add ${ban_src_name}_${proto} ${ip}" >>"${ban_backupdir}/banIP.${ban_src_name}_addon_${proto}"
-                               fi
-                       done
-               done
-               [ -n "${ips}" ] && "${ban_logger_cmd}" -p "debug" -t "banIP-resolve [${$}]" "added IPs of '${domain}' to ${ban_src_name} (${ips})" 2>/dev/null
-       else
-               ban_cnt_err=$((ban_cnt_err + 1))
-       fi
-done <"${ban_src_file}"
-
-for proto in "4" "6"; do
-       if { { [ "${proto}" = "4" ] && [ "${ban_proto4_enabled}" = "1" ]; } || { [ "${proto}" = "6" ] && [ "${ban_proto6_enabled}" = "1" ]; }; } &&
-               [ ! -s "${ban_backupdir}/banIP.${ban_src_name}_addon_${proto}" ] && [ -s "${ban_backupdir}/banIP.${ban_src_name}_addon_${proto}.gz" ]; then
-               gzip -df "${ban_backupdir}/banIP.${ban_src_name}_addon_${proto}.gz" 2>/dev/null
-               "${ban_ipset_cmd}" -q -! restore <"${ban_backupdir}/banIP.${ban_src_name}_addon_${proto}"
-               ban_message="backup used"
-       elif [ -n "$("${ban_ipset_cmd}" -q -n list "${ban_src_name}_${proto}")" ] && [ -s "${ban_backupdir}/banIP.${ban_src_name}_addon_${proto}" ]; then
-               "${ban_ipset_cmd}" -q -! restore <"${ban_backupdir}/banIP.${ban_src_name}_addon_${proto}"
-               ban_message="${ban_cnt_err} lookup errors"
-       fi
-       gzip -f "${ban_backupdir}/banIP.${ban_src_name}_addon_${proto}" 2>/dev/null
-done
-"${ban_logger_cmd}" -p "info" -t "banIP-resolve [${$}]" "${ban_src_name} domain import has been finished (${ban_message:-"-"})" 2>/dev/null
-rm -f "${ban_src_file}"
diff --git a/net/banip/files/banip.feeds b/net/banip/files/banip.feeds
new file mode 100644 (file)
index 0000000..8add8eb
--- /dev/null
@@ -0,0 +1,295 @@
+{
+       "adaway": {
+               "url_4": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/adaway-ipv4.txt",
+               "url_6": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/adaway-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "adaway IPs",
+               "descurl": "https://github.com/dibdot/banIP-IP-blocklists"
+       },
+       "adguard": {
+               "url_4": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/adguard-ipv4.txt",
+               "url_6": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/adguard-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "adguard IPs",
+               "descurl": "https://github.com/dibdot/banIP-IP-blocklists"
+       },
+       "adguardtrackers": {
+               "url_4": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/adguardtrackers-ipv4.txt",
+               "url_6": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/adguardtrackers-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "adguardtracker IPs",
+               "descurl": "https://github.com/dibdot/banIP-IP-blocklists"
+       },
+       "antipopads": {
+               "url_4": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/antipopads-ipv4.txt",
+               "url_6": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/antipopads-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "antipopads IPs",
+               "descurl": "https://github.com/dibdot/banIP-IP-blocklists"
+       },
+       "asn": {
+               "url_4": "https://asn.ipinfo.app/api/text/list/",
+               "url_6": "https://asn.ipinfo.app/api/text/list/",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "ASN IPs",
+               "descurl": "https://asn.ipinfo.app"
+       },
+       "backscatterer": {
+               "url_4": "http://wget-mirrors.uceprotect.net/rbldnsd-all/ips.backscatterer.org.gz",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "backscatterer IPs",
+               "descurl": "https://www.uceprotect.net/en/index.php",
+               "flag": "gz"
+       },
+       "bogon": {
+               "url_4": "https://www.team-cymru.org/Services/Bogons/fullbogons-ipv4.txt",
+               "url_6": "https://www.team-cymru.org/Services/Bogons/fullbogons-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "bogon prefixes",
+               "descurl": "https://team-cymru.com"
+       },
+       "cinsscore": {
+               "url_4": "https://cinsscore.com/list/ci-badguys.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "suspicious attacker IPs",
+               "descurl": "https://cinsscore.com/#list"
+       },
+       "country": {
+               "url_4": "https://www.ipdeny.com/ipblocks/data/aggregated/",
+               "url_6": "https://www.ipdeny.com/ipv6/ipaddresses/aggregated/",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "country blocks",
+               "descurl": "http://www.ipdeny.com/ipblocks"
+       },
+       "darklist": {
+               "url_4": "https://darklist.de/raw.php",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "suspicious attacker IPs",
+               "descurl": "https://darklist.de"
+       },
+       "debl": {
+               "url_4": "https://www.blocklist.de/downloads/export-ips_all.txt",
+               "url_6": "https://www.blocklist.de/downloads/export-ips_all.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "fail2ban IP blacklist",
+               "descurl": "https://www.blocklist.de"
+       },
+       "doh": {
+               "url_4": "https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv4.txt",
+               "url_6": "https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "public DoH-Provider",
+               "descurl": "https://github.com/dibdot/DoH-IP-blocklists"
+       },
+       "drop": {
+               "url_4": "https://www.spamhaus.org/drop/drop.txt",
+               "url_6": "https://www.spamhaus.org/drop/dropv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "spamhaus drop compilation",
+               "descurl": "https://www.spamhaus.org"
+       },
+       "dshield": {
+               "url_4": "https://feeds.dshield.org/block.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s/%s,\\n\",$1,$3}",
+               "focus": "dshield IP blocklist",
+               "descurl": "https://www.dshield.org"
+       },
+       "edrop": {
+               "url_4": "https://www.spamhaus.org/drop/edrop.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "spamhaus edrop compilation",
+               "descurl": "https://www.spamhaus.org"
+       },
+       "feodo": {
+               "url_4": "https://feodotracker.abuse.ch/downloads/ipblocklist.txt",
+               "rule_4": "BEGIN{RS=\"\\r\\n\"}/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "feodo tracker",
+               "descurl": "https://feodotracker.abuse.ch"
+       },
+       "firehol1": {
+               "url_4": "https://iplists.firehol.org/files/firehol_level1.netset",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "firehol level 1 compilation",
+               "descurl": "https://iplists.firehol.org/?ipset=firehol_level1"
+       },
+       "firehol2": {
+               "url_4": "https://iplists.firehol.org/files/firehol_level2.netset",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "firehol level 2 compilation",
+               "descurl": "https://iplists.firehol.org/?ipset=firehol_level2"
+       },
+       "firehol3": {
+               "url_4": "https://iplists.firehol.org/files/firehol_level3.netset",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "firehol level 3 compilation",
+               "descurl": "https://iplists.firehol.org/?ipset=firehol_level3"
+       },
+       "firehol4": {
+               "url_4": "https://iplists.firehol.org/files/firehol_level4.netset",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{if(!seen[$1]++)printf \"%s,\\n\",$1}",
+               "focus": "firehol level 4 compilation",
+               "descurl": "https://iplists.firehol.org/?ipset=firehol_level4"
+       },
+       "greensnow": {
+               "url_4": "https://blocklist.greensnow.co/greensnow.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "suspicious server IPs",
+               "descurl": "https://greensnow.co"
+       },
+       "iblockads": {
+               "url_4": "https://list.iblocklist.com/?list=dgxtneitpuvgqqcpfulq&fileformat=cidr&archiveformat=gz",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "advertising IPs",
+               "descurl": "https://www.iblocklist.com",
+               "flag": "gz",
+               "login": true
+       },
+       "iblockspy": {
+               "url_4": "https://list.iblocklist.com/?list=llvtlsjyoyiczbkjsxpf&fileformat=cidr&archiveformat=gz",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "malicious spyware IPs",
+               "descurl": "https://www.iblocklist.com",
+               "flag": "gz",
+               "login": true
+       },
+       "myip": {
+               "url_4": "https://myip.ms/files/blacklist/general/latest_blacklist.txt",
+               "url_6": "https://myip.ms/files/blacklist/general/latest_blacklist.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "real-time IP blocklist",
+               "descurl": "https://myip.ms"
+       },
+       "nixspam": {
+               "url_4": "https://www.nixspam.net/download/nixspam-ip.dump.gz",
+               "rule_4": "/(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$2}",
+               "focus": "iX spam protection",
+               "descurl": "https://www.nixspam.net",
+               "flag": "gz"
+       },
+       "oisdnsfw": {
+               "url_4": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/oisdnsfw-ipv4.txt",
+               "url_6": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/oisdnsfw-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "OISD-nsfw IPs",
+               "descurl": "https://github.com/dibdot/banIP-IP-blocklists"
+       },
+       "oisdsmall": {
+               "url_4": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/oisdsmall-ipv4.txt",
+               "url_6": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/oisdsmall-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "OISD-small IPs",
+               "descurl": "https://github.com/dibdot/banIP-IP-blocklists"
+       },
+       "proxy": {
+               "url_4": "https://iplists.firehol.org/files/proxylists.ipset",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "open proxies",
+               "descurl": "https://iplists.firehol.org/?ipset=proxylists"
+       },
+       "sslbl": {
+               "url_4": "https://sslbl.abuse.ch/blacklist/sslipblacklist.csv",
+               "rule_4": "BEGIN{FS=\",\"}/(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)/{printf \"%s,\\n\",$2}",
+               "focus": "SSL botnet IPs",
+               "descurl": "https://sslbl.abuse.ch"
+       },
+       "stevenblack": {
+               "url_4": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/stevenblack-ipv4.txt",
+               "url_6": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/stevenblack-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "stevenblack IPs",
+               "descurl": "https://github.com/dibdot/banIP-IP-blocklists"
+       },
+       "talos": {
+               "url_4": "https://www.talosintelligence.com/documents/ip-blacklist",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "talos IPs",
+               "descurl": "https://talosintelligence.com/reputation_center"
+       },
+       "threat": {
+               "url_4": "https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "emerging threats",
+               "descurl": "https://rules.emergingthreats.net"
+       },
+       "threatview": {
+               "url_4": "https://threatview.io/Downloads/IP-High-Confidence-Feed.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "malicious IPs",
+               "descurl": "https://threatview.io"
+       },
+       "tor": {
+               "url_4": "https://raw.githubusercontent.com/SecOps-Institute/Tor-IP-Addresses/master/tor-exit-nodes.lst",
+               "url_6": "https://raw.githubusercontent.com/SecOps-Institute/Tor-IP-Addresses/master/tor-exit-nodes.lst",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "tor exit nodes",
+               "descurl": "https://github.com/SecOps-Institute/Tor-IP-Addresses"
+       },
+       "uceprotect1": {
+               "url_4": "http://wget-mirrors.uceprotect.net/rbldnsd-all/dnsbl-1.uceprotect.net.gz",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "spam protection level 1",
+               "descurl": "http://www.uceprotect.net/en/index.php",
+               "flag": "gz"
+       },
+       "uceprotect2": {
+               "url_4": "http://wget-mirrors.uceprotect.net/rbldnsd-all/dnsbl-2.uceprotect.net.gz",
+               "rule_4": "BEGIN{IGNORECASE=1}/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]NET)/{printf \"%s,\\n\",$1}",
+               "focus": "spam protection level 2",
+               "descurl": "http://www.uceprotect.net/en/index.php",
+               "flag": "gz"
+       },
+       "uceprotect3": {
+               "url_4": "http://wget-mirrors.uceprotect.net/rbldnsd-all/dnsbl-3.uceprotect.net.gz",
+               "rule_4": "BEGIN{IGNORECASE=1}/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]YOUR)/{printf \"%s,\\n\",$1}",
+               "focus": "spam protection level 3",
+               "descurl": "http://www.uceprotect.net/en/index.php",
+               "flag": "gz"
+       },
+       "urlhaus": {
+               "url_4": "https://urlhaus.abuse.ch/downloads/ids/",
+               "rule_4": "match($0,/(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5]))/){printf \"%s,\\n\",substr($0,RSTART,RLENGTH)}",
+               "focus": "urlhaus IDS IPs",
+               "descurl": "https://urlhaus.abuse.ch"
+       },
+       "urlvir": {
+               "url_4": "https://iplists.firehol.org/files/urlvir.ipset",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "malware related IPs",
+               "descurl": "https://iplists.firehol.org/?ipset=urlvir"
+       },
+       "voip": {
+               "url_4": "https://voipbl.org/update/",
+               "rule_4": "BEGIN{RS=\"(([0-9]{1,3}\\\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)\"}{if(RT)printf \"%s,\\n\",RT}",
+               "focus": "VoIP fraud blocklist",
+               "descurl": "https://voipbl.org"
+       },
+       "webclient": {
+               "url_4": "https://iplists.firehol.org/files/firehol_webclient.netset",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{printf \"%s,\\n\",$1}",
+               "focus": "malware related IPs",
+               "descurl": "https://iplists.firehol.org/?ipset=firehol_webclient"
+       },
+       "yoyo": {
+               "url_4": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/yoyo-ipv4.txt",
+               "url_6": "https://raw.githubusercontent.com/dibdot/banIP-IP-blocklists/main/yoyo-ipv6.txt",
+               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)[[:space:]]/{printf \"%s,\\n\",$1}",
+               "focus": "yoyo IPs",
+               "descurl": "https://github.com/dibdot/banIP-IP-blocklists"
+       }
+}
diff --git a/net/banip/files/banip.hotplug b/net/banip/files/banip.hotplug
deleted file mode 100644 (file)
index 436a3a6..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-# firewall hotplug script for banIP
-# Copyright (c) 2019-2021 Dirk Brenken (dev@brenken.org)
-# This is free software, licensed under the GNU General Public License v3.
-
-if /etc/init.d/banip enabled && [ "${ACTION}" = "add" ] && [ -n "${INTERFACE}" ]; then
-       if [ ! -s "/var/run/banip.pid" ] && uci_get banip global ban_ifaces | grep -q "${INTERFACE}"; then
-               /etc/init.d/banip refresh
-       fi
-fi
index 08405e608c4a6110ee30fab32f12f58ddf4351f6..ab126f230489ad5e3f69ca08e02c5327d880eda1 100755 (executable)
 #!/bin/sh /etc/rc.common
-# Copyright (c) 2018-2021 Dirk Brenken (dev@brenken.org)
+# banIP init script - ban incoming and outgoing ip adresses/subnets via sets in nftables
+# Copyright (c) 2018-2023 Dirk Brenken (dev@brenken.org)
 # This is free software, licensed under the GNU General Public License v3.
 
 # (s)hellcheck exceptions
-# shellcheck disable=1091,2034,3043,3057,3060
+# shellcheck disable=all
 
 START=30
 USE_PROCD=1
 
-if type extra_command >/dev/null 2>&1; then
-       extra_command "refresh" "Refresh ipsets without new list downloads"
-       extra_command "suspend" "Suspend banIP processing"
-       extra_command "resume" "Resume banIP processing"
-       extra_command "query" "<IP> Query active banIP IPSets for a specific IP address"
-       extra_command "report" "[<cli>|<mail>|<gen>|<json>] Print banIP related IPset statistics"
-       extra_command "list" "[<add>|<add_asn>|<add_country>|<remove>|<remove_asn>|<remove_country>] <source(s)> List/Edit available sources"
-       extra_command "timer" "[<add> <tasks> <hour> [<minute>] [<weekday>]]|[<remove> <line no.>] List/Edit cron update intervals"
-else
-       EXTRA_COMMANDS="status refresh suspend resume query report list timer version"
-       EXTRA_HELP="    status  Service status
-       refresh Refresh ipsets without new list downloads
-       suspend Suspend banIP processing
-       resume  Resume banIP processing
-       query   <IP> Query active banIP IPSets for a specific IP address
-       report  [<cli>|<mail>|<gen>|<json>] Print banIP related IPset statistics
-       list    [<add>|<add_asn>|<add_country>|<remove>|<remove_asn>|<remove_country>] <source(s)> List/Edit available sources
-       timer   [<add> <tasks> <hour> [<minute>] [<weekday>]]|[<remove> <line no.>] List/Edit cron update intervals"
-fi
+extra_command "report" "[text|json|mail] Print banIP related set statistics"
+extra_command "search" "[<IPv4 address>|<IPv6 address>] Check if an element exists in the banIP sets"
 
 ban_init="/etc/init.d/banip"
-ban_script="/usr/bin/banip.sh"
+ban_service="/usr/bin/banip-service.sh"
+ban_funlib="/usr/lib/banip-functions.sh"
 ban_pidfile="/var/run/banip.pid"
+ban_lock="/var/run/banip.lock"
 
-if [ -s "${ban_pidfile}" ] && { [ "${action}" = "start" ] || [ "${action}" = "stop" ] ||
-       [ "${action}" = "restart" ] || [ "${action}" = "reload" ] || [ "${action}" = "refresh" ] ||
-       [ "${action}" = "suspend" ] || [ "${action}" = "resume" ] || [ "${action}" = "query" ] ||
-       { [ "${action}" = "list" ] && [ -n "${1}" ]; } || { [ "${action}" = "report" ] && [ "${1}" != "json" ]; }; }; then
-       exit 0
-fi
+[ ! -r "${ban_funlib}" ] && exit 1
+[ "${action}" = "stop" ] && ! /etc/init.d/banip running && exit 0
+[ -d "${ban_lock}" ] && { [ "${action}" = "start" ] || [ "${action}" = "restart" ] || [ "${action}" = "reload" ]; } && exit 1
+[ ! -d "${ban_lock}" ] && { [ "${action}" = "start" ] || [ "${action}" = "restart" ] || [ "${action}" = "reload" ]; } && mkdir -p "${ban_lock}"
 
 boot() {
        : >"${ban_pidfile}"
-       rc_procd start_service
+       rc_procd start_service "boot"
 }
 
 start_service() {
        if "${ban_init}" enabled; then
-               if [ "${action}" = "boot" ]; then
-                       return 0
-               fi
-               procd_open_instance "banip"
-               procd_set_param command "${ban_script}" "${@}"
+               [ "${action}" = "boot" ] && [ -n "$(uci_get banip global ban_trigger)" ] && return 0
+               [ -z "$(command -v "f_system")" ] && . "${ban_funlib}"
+               f_rmpid
+               procd_open_instance "banip-service"
+               procd_set_param command "${ban_service}" "${@:-"${action}"}"
                procd_set_param pidfile "${ban_pidfile}"
-               procd_set_param nice "$(uci_get banip global ban_nice "0")"
+               procd_set_param nice "$(uci_get banip global ban_nicelimit "0")"
+               procd_set_param limits nofile="$(uci_get banip global ban_filelimit "1024")"
                procd_set_param stdout 1
                procd_set_param stderr 1
                procd_close_instance
+       else
+               [ -z "$(command -v "f_system")" ] && . "${ban_funlib}"
+               f_log "err" "banIP service autostart is currently disabled, please enable the service autostart with '/etc/init.d/banip enable'"
+               rm -rf "${ban_lock}"
        fi
 }
 
-version() {
-       rc_procd "${ban_script}" version
-}
-
-refresh() {
-       rc_procd start_service refresh
-}
-
 reload_service() {
-       rc_procd start_service reload
+       [ -z "$(command -v "f_system")" ] && . "${ban_funlib}"
+       f_rmpid
+       rc_procd start_service "reload"
 }
 
 stop_service() {
-       rc_procd "${ban_script}" stop
+       [ -z "$(command -v "f_system")" ] && . "${ban_funlib}"
+       "${ban_nftcmd}" delete table inet banIP >/dev/null 2>&1
+       f_genstatus "stopped"
+       f_rmpid
 }
 
 restart() {
-       rc_procd start_service restart
-}
-
-suspend() {
-       rc_procd start_service suspend
-}
-
-resume() {
-       rc_procd start_service resume
-}
-
-query() {
-       rc_procd "${ban_script}" query "${1}"
-}
-
-list() {
-       local src_archive src_file src_enabled key name enabled focus descurl url_4 rule_4 url_6 rule_6 action="${1}"
-
-       if [ "${action%_*}" = "add" ] || [ "${action%_*}" = "remove" ]; then
-               shift
-               for name in "${@}"; do
-                       case "${action}" in
-                               "add")
-                                       if ! uci_get banip global ban_sources | grep -q "${name}"; then
-                                               uci_add_list banip global ban_sources "${name}"
-                                               printf "%s\n" "::: banIP source '${name}' added to config"
-                                       fi
-                                       ;;
-                               "remove")
-                                       if uci_get banip global ban_sources | grep -q "${name}"; then
-                                               uci_remove_list banip global ban_sources "${name}"
-                                               printf "%s\n" "::: banIP source '${name}' removed from config"
-                                       fi
-                                       ;;
-                               "add_asn")
-                                       if ! uci_get banip global ban_asns | grep -q "${name}"; then
-                                               uci_add_list banip global ban_asns "${name}"
-                                               printf "%s\n" "::: banIP asn '${name}' added to config"
-                                       fi
-                                       ;;
-                               "remove_asn")
-                                       if uci_get banip global ban_asns | grep -q "${name}"; then
-                                               uci_remove_list banip global ban_asns "${name}"
-                                               printf "%s\n" "::: banIP asn '${name}' removed from config"
-                                       fi
-                                       ;;
-                               "add_country")
-                                       if ! uci_get banip global ban_countries | grep -q "${name}"; then
-                                               uci_add_list banip global ban_countries "${name}"
-                                               printf "%s\n" "::: banIP country '${name}' added to config"
-                                       fi
-                                       ;;
-                               "remove_country")
-                                       if uci_get banip global ban_countries | grep -q "${name}"; then
-                                               uci_remove_list banip global ban_countries "${name}"
-                                               printf "%s\n" "::: banIP country '${name}' removed from config"
-                                       fi
-                                       ;;
-                       esac
-               done
-               if [ -n "$(uci -q changes banip)" ]; then
-                       uci_commit banip
-                       "${ban_init}" start
-               fi
-       else
-               src_archive="$(uci_get banip global ban_srcarc "/etc/banip/banip.sources.gz")"
-               src_file="$(uci_get banip global ban_srcfile "/tmp/ban_sources.json")"
-               src_enabled="$(uci -q show banip.global.ban_sources)"
-               if [ -r "${src_archive}" ]; then
-                       zcat "${src_archive}" >"${src_file}"
-               else
-                       printf "%s\n" "::: banIP source archive '${src_archive}' not found"
-               fi
-               if [ -r "${src_file}" ]; then
-                       src_enabled="${src_enabled#*=}"
-                       src_enabled="${src_enabled//\'/}"
-                       printf "%s\n" "::: Available banIP sources"
-                       printf "%s\n" ":::"
-                       printf "%-25s%-10s%-36s%s\n" "    Name" "Enabled" "Focus" "Info URL"
-                       printf "%s\n" "    ---------------------------------------------------------------------------"
-                       json_load_file "${src_file}"
-                       json_get_keys keylist
-                       for key in ${keylist}; do
-                               json_select "${key}"
-                               json_get_var focus "focus"
-                               json_get_var descurl "descurl"
-                               json_get_var url_4 "url_4"
-                               json_get_var rule_4 "rule_4"
-                               json_get_var url_6 "url_6"
-                               json_get_var rule_6 "rule_6"
-                               if { [ -n "${url_4}" ] && [ -n "${rule_4}" ]; } || { [ -n "${url_6}" ] && [ -n "${rule_6}" ]; }; then
-                                       if printf "%s" "${src_enabled}" | grep -q "${key}"; then
-                                               enabled="x"
-                                       else
-                                               enabled=" "
-                                       fi
-                                       src_enabled="${src_enabled/${key}/}"
-                                       printf "  + %-21s%-10s%-36s%s\n" "${key:0:20}" "${enabled}" "${focus:0:35}" "${descurl:0:50}"
-                               else
-                                       src_enabled="${src_enabled} ${key}"
-                               fi
-                               json_select ..
-                       done
-                       asn_list="$(uci_get banip global ban_asns "-")"
-                       country_list="$(uci_get banip global ban_countries "-")"
-                       printf "%s\n" "    ---------------------------------------------------------------------------"
-                       printf "  * %s\n" "Configured ASNs: ${asn_list// /, }"
-                       printf "  * %s\n" "Configured Countries: ${country_list// /, }"
-
-                       if [ -n "${src_enabled// /}" ]; then
-                               printf "%s\n" "    ---------------------------------------------------------------------------"
-                               printf "%s\n" "    Sources without valid configuration"
-                               printf "%s\n" "    ---------------------------------------------------------------------------"
-                               for key in ${src_enabled}; do
-                                       printf "  - %s\n" "${key:0:20}"
-                               done
-                       fi
-               else
-                       printf "%s\n" "::: banIP source file '${src_file}' not found"
-               fi
-       fi
+       stop_service
+       rc_procd start_service "restart"
 }
 
 status() {
@@ -204,90 +71,29 @@ status() {
 }
 
 status_service() {
-       local key keylist type value index_value values rtfile
-
-       rtfile="$(uci_get banip global ban_rtfile "/tmp/ban_runtime.json")"
-
-       json_load_file "${rtfile}" >/dev/null 2>&1
-       json_get_keys keylist
-       if [ -n "${keylist}" ]; then
-               printf "%s\n" "::: banIP runtime information"
-               for key in ${keylist}; do
-                       json_get_var value "${key}" >/dev/null 2>&1
-                       if [ "${key%_*}" = "active" ]; then
-                               printf "  + %-15s : " "${key}"
-                               json_select "${key}" >/dev/null 2>&1
-                               values=""
-                               index=1
-                               while json_get_type type "${index}" && [ "${type}" = "object" ]; do
-                                       json_get_values index_value "${index}" >/dev/null 2>&1
-                                       if [ "${index}" = "1" ]; then
-                                               values="${index_value}"
-                                       else
-                                               values="${values}, ${index_value}"
-                                       fi
-                                       index=$((index + 1))
-                               done
-                               values="$(printf "%s" "${values}" | awk '{NR=1;max=98;if(length($0)>max+1)while($0){if(NR==1){print substr($0,1,max)}else{printf"%-22s%s\n","",substr($0,1,max)}{$0=substr($0,max+1);NR=NR+1}}else print}')"
-                               printf "%s\n" "${values:-"-"}"
-                               json_select ".."
-                       else
-                               printf "  + %-15s : %s\n" "${key}" "${value:-"-"}"
-                       fi
-               done
-       else
-               printf "%s\n" "::: no banIP runtime information available"
-       fi
+       [ -z "$(command -v "f_system")" ] && . "${ban_funlib}"
+       f_getstatus
 }
 
 report() {
-       rc_procd "${ban_script}" report "${1:-"cli"}"
+       [ -z "$(command -v "f_system")" ] && . "${ban_funlib}"
+       f_report "${1:-"text"}"
 }
 
-timer() {
-       local cron_file cron_content cron_lineno action="${1:-"list"}" cron_tasks="${2}" hour="${3}" minute="${4:-0}" weekday="${5:-"*"}"
-
-       cron_file="/etc/crontabs/root"
-
-       if [ -s "${cron_file}" ] && [ "${action}" = "list" ]; then
-               awk '{print NR ">  " $0}' "${cron_file}"
-       elif [ "${action}" = "add" ]; then
-               hour="${hour//[[:alpha:]]/}"
-               minute="${minute//[[:alpha:]]/}"
-               if [ -n "${cron_tasks}" ] && [ -n "${hour}" ] && [ -n "${minute}" ] && [ -n "${weekday}" ] &&
-                       [ "${hour}" -ge 0 ] && [ "${hour}" -le 23 ] &&
-                       [ "${minute}" -ge 0 ] && [ "${minute}" -le 59 ]; then
-                       printf "%02d %02d %s\n" "${minute}" "${hour}" "* * ${weekday} ${ban_init} ${cron_tasks}" >>"${cron_file}"
-                       /etc/init.d/cron restart
-               fi
-       elif [ -s "${cron_file}" ] && [ "${action}" = "remove" ]; then
-               cron_tasks="${cron_tasks//[[:alpha:]]/}"
-               cron_lineno="$(awk 'END{print NR}' "${cron_file}")"
-               cron_content="$(awk '{print $0}' "${cron_file}")"
-               if [ "${cron_tasks:-"0"}" -le "${cron_lineno:-"1"}" ] && [ -n "${cron_content}" ]; then
-                       printf "%s\n" "${cron_content}" | awk "NR!~/^${cron_tasks}$/" >"${cron_file}"
-                       /etc/init.d/cron restart
-               fi
-       fi
+search() {
+       [ -z "$(command -v "f_system")" ] && . "${ban_funlib}"
+       f_search "${1}"
 }
 
 service_triggers() {
-       local iface delay
+       local iface trigger delay
 
-       iface="$(uci_get banip global ban_trigger)"
+       trigger="$(uci_get banip global ban_trigger)"
        delay="$(uci_get banip global ban_triggerdelay "5")"
        PROCD_RELOAD_DELAY=$((delay * 1000))
 
-       if [ -z "${iface}" ]; then
-               . "/lib/functions/network.sh"
-               network_find_wan iface
-               if [ -n "${iface}" ]; then
-                       uci_set banip global ban_trigger "${iface}"
-                       uci_commit "banip"
-               fi
-       fi
-       if [ -n "${iface}" ]; then
+       for iface in ${trigger}; do
                procd_add_interface_trigger "interface.*.up" "${iface}" "${ban_init}" "start"
-       fi
+       done
        procd_add_reload_trigger "banip"
 }
diff --git a/net/banip/files/banip.maclist b/net/banip/files/banip.maclist
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/net/banip/files/banip.mail b/net/banip/files/banip.mail
deleted file mode 100755 (executable)
index a89b92d..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/sh
-# send mail script for banIP notifications
-# Copyright (c) 2020-2021 Dirk Brenken (dev@brenken.org)
-# This is free software, licensed under the GNU General Public License v3.
-
-# (s)hellcheck exceptions
-# shellcheck disable=1091,3040
-
-# Please note: you have to setup the package 'msmtp' before using this script
-
-export LC_ALL=C
-export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
-set -o pipefail
-
-. "/lib/functions.sh"
-ban_debug="$(uci_get banip global ban_debug "0")"
-ban_loglimit="$(uci_get banip global ban_loglimit "100")"
-ban_mailsender="$(uci_get banip global ban_mailsender "no-reply@banIP")"
-ban_mailreceiver="$(uci_get banip global ban_mailreceiver)"
-ban_mailtopic="$(uci_get banip global ban_mailtopic "banIP notification")"
-ban_mailprofile="$(uci_get banip global ban_mailprofile "ban_notify")"
-
-ban_mail="$(command -v msmtp)"
-ban_logger="$(command -v logger)"
-ban_logread="$(command -v logread)"
-
-if [ -z "${ban_mailreceiver}" ]; then
-       f_log "err" "please set the mail receiver with the 'ban_mailreceiver' option"
-       exit 1
-fi
-
-if [ "${ban_debug}" = "1" ]; then
-       msmtp_debug="--debug"
-fi
-
-ban_mailhead="From: ${ban_mailsender}\nTo: ${ban_mailreceiver}\nSubject: ${ban_mailtopic}\nReply-to: ${ban_mailsender}\nMime-Version: 1.0\nContent-Type: text/html;charset=utf-8\nContent-Disposition: inline\n\n"
-
-# info preparation
-#
-sys_info="$(
-       strings /etc/banner 2>/dev/null
-       ubus call system board | awk 'BEGIN{FS="[{}\"]"}{if($2=="kernel"||$2=="hostname"||$2=="system"||$2=="model"||$2=="description")printf "  + %-12s: %s\n",$2,$4}'
-)"
-ban_info="$(/etc/init.d/banip "status" 2>/dev/null)"
-rep_info="${1}"
-log_info="$("${ban_logread}" -l "${ban_loglimit}" -e "banIP-" 2>/dev/null | awk '{NR=1;max=120;if(length($0)>max+1)while($0){if(NR==1){print substr($0,1,max)}else{print substr($0,1,max)}{$0=substr($0,max+1);NR=NR+1}}else print}')"
-
-# mail body
-#
-ban_mailtext="<html><body><pre style='display:block;font-family:monospace;font-size:1rem;padding:20;background-color:#f3eee5;white-space:pre'>"
-ban_mailtext="${ban_mailtext}\n<strong>++\n++ System Information ++\n++</strong>\n${sys_info}"
-ban_mailtext="${ban_mailtext}\n\n<strong>++\n++ banIP Status ++\n++</strong>\n${ban_info}"
-if [ -n "${rep_info}" ]; then
-       ban_mailtext="${ban_mailtext}\n\n<strong>++\n++ banIP Report ++\n++</strong>\n${rep_info}"
-fi
-ban_mailtext="${ban_mailtext}\n\n<strong>++\n++ Logfile Information ++\n++</strong>\n${log_info}"
-ban_mailtext="${ban_mailtext}</pre></body></html>"
-
-# send mail
-#
-printf "%b" "${ban_mailhead}${ban_mailtext}" 2>/dev/null | "${ban_mail}" ${msmtp_debug} -a "${ban_mailprofile}" "${ban_mailreceiver}" >/dev/null 2>&1
-"${ban_logger}" -p "info" -t "banIP-mail [${$}]" "mail sent to '${ban_mailreceiver}' with rc '${?}'" 2>/dev/null
diff --git a/net/banip/files/banip.service b/net/banip/files/banip.service
deleted file mode 100755 (executable)
index 12e7bda..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/sh
-# log service to trace suspicious logins and conditionally refresh banIP
-# Copyright (c) 2019-2021 Dirk Brenken (dev@brenken.org)
-# This is free software, licensed under the GNU General Public License v3.
-
-# (s)hellcheck exceptions
-# shellcheck disable=3040
-
-export LC_ALL=C
-export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
-set -o pipefail
-
-ban_search="${1}"
-ban_logger_cmd="$(command -v logger)"
-ban_logread_cmd="$(command -v logread)"
-
-if [ -x "${ban_logread_cmd}" ]; then
-       "${ban_logger_cmd}" -p "info" -t "banIP-service [${$}]" "log/banIP service started" 2>/dev/null
-       "${ban_logread_cmd}" -f |
-               {
-                       grep -qE "${ban_search}" && {
-                               /etc/init.d/banip refresh
-                               exit 0
-                       }
-               }
-else
-       "${ban_logger_cmd}" -p "err" -t "banIP-service [${$}]" "can't start log/banIP service" 2>/dev/null
-fi
diff --git a/net/banip/files/banip.sh b/net/banip/files/banip.sh
deleted file mode 100755 (executable)
index 8672ead..0000000
+++ /dev/null
@@ -1,1595 +0,0 @@
-#!/bin/sh
-# banIP - ban incoming and outgoing ip adresses/subnets via ipset
-# Copyright (c) 2018-2021 Dirk Brenken (dev@brenken.org)
-# This is free software, licensed under the GNU General Public License v3.
-
-# (s)hellcheck exceptions
-# shellcheck disable=1091,2030,2031,2086,2183,3040,3043,3060
-
-# set initial defaults
-#
-export LC_ALL=C
-export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
-set -o pipefail
-ban_ver="0.7.10"
-ban_status=""
-ban_enabled="0"
-ban_mail_enabled="0"
-ban_proto4_enabled="0"
-ban_proto6_enabled="0"
-ban_logsrc_enabled="0"
-ban_logdst_enabled="0"
-ban_monitor_enabled="0"
-ban_autodetect="1"
-ban_autoblacklist="1"
-ban_autowhitelist="1"
-ban_whitelistonly="0"
-ban_logterms=""
-ban_loglimit="100"
-ban_ssh_logcount="3"
-ban_luci_logcount="3"
-ban_nginx_logcount="5"
-ban_mailactions=""
-ban_search=""
-ban_devs=""
-ban_ifaces=""
-ban_debug="0"
-ban_maxqueue="4"
-ban_fetchutil=""
-ban_fetchinsecure="0"
-ban_ip_cmd="$(command -v ip)"
-ban_ipt4_cmd="$(command -v iptables)"
-ban_ipt4_savecmd="$(command -v iptables-save)"
-ban_ipt4_restorecmd="$(command -v iptables-restore)"
-ban_ipt6_cmd="$(command -v ip6tables)"
-ban_ipt6_savecmd="$(command -v ip6tables-save)"
-ban_ipt6_restorecmd="$(command -v ip6tables-restore)"
-ban_ipset_cmd="$(command -v ipset)"
-ban_logger_cmd="$(command -v logger)"
-ban_logread_cmd="$(command -v logread)"
-ban_allsources=""
-ban_extrasources=""
-ban_sources=""
-ban_asns=""
-ban_countries=""
-ban_settype_src=""
-ban_settype_dst=""
-ban_settype_all=""
-ban_lan_inputchains_4=""
-ban_lan_inputchains_6=""
-ban_lan_forwardchains_4=""
-ban_lan_forwardchains_6=""
-ban_wan_inputchains_4=""
-ban_wan_inputchains_6=""
-ban_wan_forwardchains_4=""
-ban_wan_forwardchains_6=""
-ban_action="${1:-"start"}"
-ban_pidfile="/var/run/banip.pid"
-ban_bgpidfile="/var/run/banip_bg.pid"
-ban_tmpbase="/tmp"
-ban_rtfile="${ban_tmpbase}/ban_runtime.json"
-ban_srcfile="${ban_tmpbase}/ban_sources.json"
-ban_reportdir="${ban_tmpbase}/banIP-Report"
-ban_backupdir="${ban_tmpbase}/banIP-Backup"
-ban_srcarc="/etc/banip/banip.sources.gz"
-ban_dnsservice="/etc/banip/banip.dns"
-ban_mailservice="/etc/banip/banip.mail"
-ban_logservice="/etc/banip/banip.service"
-ban_maclist="/etc/banip/banip.maclist"
-ban_blacklist="/etc/banip/banip.blacklist"
-ban_whitelist="/etc/banip/banip.whitelist"
-ban_setcnt="0"
-ban_cnt="0"
-
-# load environment
-#
-f_load() {
-       ban_sysver="$(ubus -S call system board 2>/dev/null | jsonfilter -q -e '@.model' -e '@.release.description' |
-               awk 'BEGIN{RS="";FS="\n"}{printf "%s, %s",$1,$2}')"
-
-       f_conf
-       if [ "${ban_enabled}" = "0" ]; then
-               f_bgsrv "stop"
-               f_ipset "destroy"
-               f_jsnup "disabled"
-               f_rmbckp
-               f_rmtmp
-               f_log "info" "banIP is currently disabled, please set the config option 'ban_enabled' to '1' to use this service"
-               exit 0
-       fi
-       f_dir "${ban_backupdir}"
-       f_dir "${ban_reportdir}"
-}
-
-# check/create directories
-#
-f_dir() {
-       local dir="${1}"
-
-       if [ -d "${dir}" ]; then
-               f_log "debug" "directory '${dir}' is used"
-       else
-               rm -f "${dir}"
-               mkdir -p "${dir}"
-               f_log "debug" "directory '${dir}' created"              
-       fi
-}
-
-# load banIP config
-#
-f_conf() {
-       if [ ! -r "/etc/config/banip" ] || [ -z "$(uci -q show banip.global.ban_autodetect)" ]; then
-               f_log "err" "no valid banIP config found, please re-install the package via opkg with the '--force-reinstall --force-maintainer' options"
-       fi
-
-       config_cb() {
-               option_cb() {
-                       local option="${1}"
-                       local value="${2}"
-                       eval "${option}=\"${value}\""
-               }
-               list_cb() {
-                       local option="${1}"
-                       local value="${2}"
-                       if [ "${option}" = "ban_ifaces" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_ifaces}")${value} \""
-                       elif [ "${option}" = "ban_sources" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_sources}")${value} \""
-                       elif [ "${option}" = "ban_localsources" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_localsources}")${value} \""
-                       elif [ "${option}" = "ban_extrasources" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_extrasources}")${value} \""
-                       elif [ "${option}" = "ban_settype_src" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_settype_src}")${value} \""
-                       elif [ "${option}" = "ban_settype_dst" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_settype_dst}")${value} \""
-                       elif [ "${option}" = "ban_settype_all" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_settype_all}")${value} \""
-                       elif [ "${option}" = "ban_mailactions" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_mailactions}")${value} \""
-                       elif [ "${option}" = "ban_logterms" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_logterms}")${value} \""
-                       elif [ "${option}" = "ban_countries" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_countries}")${value} \""
-                       elif [ "${option}" = "ban_asns" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_asns}")${value} \""
-                       elif [ "${option}" = "ban_lan_inputchains_4" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_lan_inputchains_4}")${value} \""
-                       elif [ "${option}" = "ban_lan_inputchains_6" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_lan_inputchains_6}")${value} \""
-                       elif [ "${option}" = "ban_lan_forwardchains_4" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_lan_forwardchains_4}")${value} \""
-                       elif [ "${option}" = "ban_lan_forwardchains_6" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_lan_forwardchains_6}")${value} \""
-                       elif [ "${option}" = "ban_wan_inputchains_4" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_wan_inputchains_4}")${value} \""
-                       elif [ "${option}" = "ban_wan_inputchains_6" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_wan_inputchains_6}")${value} \""
-                       elif [ "${option}" = "ban_wan_forwardchains_4" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_wan_forwardchains_4}")${value} \""
-                       elif [ "${option}" = "ban_wan_forwardchains_6" ]; then
-                               eval "${option}=\"$(printf "%s" "${ban_wan_forwardchains_6}")${value} \""
-                       fi
-               }
-       }
-       config_load banip
-
-       ban_chain="${ban_chain:-"banIP"}"
-       ban_global_settype="${ban_global_settype:-"src+dst"}"
-       ban_target_src="${ban_target_src:-"DROP"}"
-       ban_target_dst="${ban_target_dst:-"REJECT"}"
-       ban_lan_inputchains_4="${ban_lan_inputchains_4:-"input_lan_rule"}"
-       ban_lan_inputchains_6="${ban_lan_inputchains_6:-"input_lan_rule"}"
-       ban_lan_forwardchains_4="${ban_lan_forwardchains_4:-"forwarding_lan_rule"}"
-       ban_lan_forwardchains_6="${ban_lan_forwardchains_6:-"forwarding_lan_rule"}"
-       ban_wan_inputchains_4="${ban_wan_inputchains_4:-"input_wan_rule"}"
-       ban_wan_inputchains_6="${ban_wan_inputchains_6:-"input_wan_rule"}"
-       ban_wan_forwardchains_4="${ban_wan_forwardchains_4:-"forwarding_wan_rule"}"
-       ban_wan_forwardchains_6="${ban_wan_forwardchains_6:-"forwarding_wan_rule"}"
-       ban_logchain_src="${ban_logchain_src:-"${ban_chain}_log_src"}"
-       ban_logchain_dst="${ban_logchain_dst:-"${ban_chain}_log_dst"}"
-       ban_logtarget_src="${ban_target_src}"
-       ban_logtarget_dst="${ban_target_dst}"
-       if [ "${ban_logsrc_enabled}" = "1" ]; then
-               ban_logprefix_src="${ban_logprefix_src:-"[banIP-${ban_ver%-*}, src/${ban_target_src}] "}"
-               ban_logopts_src="${ban_logopts_src:-"-m limit --limit 2/sec"}"
-               ban_target_src="${ban_logchain_src}"
-       fi
-       if [ "${ban_logdst_enabled}" = "1" ]; then
-               ban_logprefix_dst="${ban_logprefix_dst:-"[banIP-${ban_ver%-*}, dst/${ban_target_dst}] "}"
-               ban_logopts_dst="${ban_logopts_dst:-"-m limit --limit 2/sec"}"
-               ban_target_dst="${ban_logchain_dst}"
-       fi
-       ban_localsources="${ban_localsources:-"maclist whitelist blacklist"}"
-       ban_logterms="${ban_logterms:-"dropbear sshd luci nginx"}"
-       f_log "debug" "f_conf  ::: ifaces: ${ban_ifaces:-"-"}, chain: ${ban_chain}, set_type: ${ban_global_settype}, log_chains (src/dst): ${ban_logchain_src}/${ban_logchain_dst}, targets (src/dst): ${ban_target_src}/${ban_target_dst}, whitelist_only: ${ban_whitelistonly}"
-       f_log "debug" "f_conf  ::: lan_inputs (4/6): ${ban_lan_inputchains_4}/${ban_lan_inputchains_6}, lan_forwards (4/6): ${ban_lan_forwardchains_4}/${ban_lan_forwardchains_6}, wan_inputs (4/6): ${ban_wan_inputchains_4}/${ban_wan_inputchains_6}, wan_forwards (4/6): ${ban_wan_forwardchains_4}/${ban_wan_forwardchains_6}"
-       f_log "debug" "f_conf  ::: local_sources: ${ban_localsources:-"-"}, extra_sources: ${ban_extrasources:-"-"}, log_terms: ${ban_logterms:-"-"}, log_prefixes (src/dst): ${ban_logprefix_src}/${ban_logprefix_dst}, log_options (src/dst): ${ban_logopts_src}/${ban_logopts_dst}"
-}
-
-# check environment
-#
-f_env() {
-       local util utils packages iface insecure tmp cnt="0" cnt_max="10"
-
-       ban_starttime="$(date "+%s")"
-       f_jsnup "running"
-       f_log "info" "start banIP processing (${ban_action})"
-
-       f_tmp
-
-       if [ "${ban_autodetect}" = "1" ] && [ -z "${ban_ifaces}" ]; then
-               while [ "${cnt}" -le "${cnt_max}" ]; do
-                       network_find_wan iface
-                       if [ -n "${iface}" ] && ! printf "%s\n" "${ban_ifaces}" | grep -q "${iface}"; then
-                               ban_proto4_enabled="1"
-                               ban_ifaces="${ban_ifaces}${iface} "
-                               uci_set banip global ban_proto4_enabled "1"
-                               uci_add_list banip global ban_ifaces "${iface}"
-                       fi
-                       network_find_wan6 iface
-                       if [ -n "${iface}" ] && ! printf "%s\n" "${ban_ifaces}" | grep -q "${iface}"; then
-                               ban_proto6_enabled="1"
-                               ban_ifaces="${ban_ifaces}${iface} "
-                               uci_set banip global ban_proto6_enabled "1"
-                               uci_add_list banip global ban_ifaces "${iface}"
-                       fi
-                       if [ -z "${ban_ifaces}" ]; then
-                               if [ "${cnt}" -le "${cnt_max}" ]; then
-                                       network_flush_cache
-                                       cnt=$((cnt + 1))
-                                       sleep 1
-                               else
-                                       break
-                               fi
-                       else
-                               if [ -n "$(uci -q changes "banip")" ]; then
-                                       uci_commit "banip"
-                               fi
-                               break
-                       fi
-               done
-       fi
-
-       while [ "${cnt}" -le "${cnt_max}" ]; do
-               for iface in ${ban_ifaces}; do
-                       network_get_device tmp "${iface}"
-                       if [ -n "${tmp}" ] && ! printf "%s\n" "${ban_devs}" | grep -q "${tmp}"; then
-                               ban_devs="${ban_devs} ${tmp}"
-                       else
-                               network_get_physdev tmp "${iface}"
-                               if [ -n "${tmp}" ] && ! printf "%s\n" "${ban_devs}" | grep -q "${tmp}"; then
-                                       ban_devs="${ban_devs} ${tmp}"
-                               fi
-                       fi
-                       network_get_subnet tmp "${iface}"
-                       if [ -n "${tmp}" ] && ! printf "%s\n" "${ban_subnets}" | grep -q "${tmp}"; then
-                               ban_subnets="${ban_subnets} ${tmp}"
-                       fi
-                       network_get_subnet6 tmp "${iface}"
-                       if [ -n "${tmp}" ] && ! printf "%s\n" "${ban_subnets}" | grep -q "${tmp}"; then
-                               ban_subnets="${ban_subnets} ${tmp}"
-                       fi
-               done
-               if [ -z "${ban_devs}" ] || [ -z "${ban_subnets}" ]; then
-                       if [ "${cnt}" -le "${cnt_max}" ]; then
-                               network_flush_cache
-                               cnt=$((cnt + 1))
-                               sleep 1
-                       else
-                               break
-                       fi
-               else
-                       break
-               fi
-       done
-       ban_ipdevs="$("${ban_ip_cmd}" link show 2>/dev/null | awk 'BEGIN{FS="[@: ]"}/^[0-9:]/{if($3!="lo"){ORS=" ";print $3}}')"
-
-       if [ -z "${ban_ifaces}" ] || [ -z "${ban_devs}" ] || [ -z "${ban_ipdevs}" ]; then
-               f_log "err" "logical wan interface(s)/device(s) '${ban_ifaces:-"-"}/${ban_devs:-"-"}' not found, please check your configuration"
-       elif [ -z "${ban_ipdevs}" ]; then
-               f_log "err" "ip device(s) '${ban_ipdevs:-"-"}' not found, please check your configuration"
-       fi
-
-       if [ ! -x "${ban_ipset_cmd}" ]; then
-               f_log "err" "ipset utility '${ban_ipset_cmd:-"-"}' not executable, please install package 'ipset'"
-       fi
-       if { [ "${ban_proto4_enabled}" = "1" ] && { [ ! -x "${ban_ipt4_cmd}" ] || [ ! -x "${ban_ipt4_savecmd}" ] || [ ! -x "${ban_ipt4_restorecmd}" ]; }; } ||
-               { [ "${ban_proto6_enabled}" = "1" ] && { [ ! -x "${ban_ipt6_cmd}" ] || [ ! -x "${ban_ipt6_savecmd}" ] || [ ! -x "${ban_ipt6_restorecmd}" ]; }; }; then
-               f_log "err" "iptables utilities '${ban_ipt4_cmd:-"-"}, ${ban_ipt4_savecmd:-"-"}, ${ban_ipt4_restorecmd:-"-"}/${ban_ipt6_cmd:-"-"}', ${ban_ipt6_savecmd:-"-"}, ${ban_ipt6_restorecmd:-"-"} not executable, please install the relevant iptables packages"
-       fi
-
-       if [ -z "${ban_fetchutil}" ]; then
-               while [ -z "${packages}" ] && [ "${cnt}" -le "${cnt_max}" ]; do
-                       packages="$(opkg list-installed 2>/dev/null)"
-                       cnt=$((cnt + 1))
-                       sleep 1
-               done
-               if [ -z "${packages}" ]; then
-                       f_log "err" "local opkg package repository is not available, please set 'ban_fetchutil' manually"
-               fi
-
-               utils="aria2c curl wget uclient-fetch"
-               for util in ${utils}; do
-                       if { [ "${util}" = "uclient-fetch" ] && printf "%s" "${packages}" | grep -q "^libustream-"; } ||
-                               { [ "${util}" = "wget" ] && printf "%s" "${packages}" | grep -q "^wget -"; } ||
-                               [ "${util}" = "curl" ] || [ "${util}" = "aria2c" ]; then
-                               if [ -x "$(command -v "${util}")" ]; then
-                                       ban_fetchutil="${util}"
-                                       uci_set banip global ban_fetchutil "${util}"
-                                       uci_commit "banip"
-                                       break
-                               fi
-                       fi
-               done
-       elif [ ! -x "$(command -v "${ban_fetchutil}")" ]; then
-               unset ban_fetchutil
-       fi
-       case "${ban_fetchutil}" in
-               "aria2c")
-                       if [ "${ban_fetchinsecure}" = "1" ]; then
-                               insecure="--check-certificate=false"
-                       fi
-                       ban_fetchparm="${ban_fetchparm:-"${insecure} --timeout=20 --allow-overwrite=true --auto-file-renaming=false --log-level=warn --dir=/ -o"}"
-                       ;;
-               "curl")
-                       if [ "${ban_fetchinsecure}" = "1" ]; then
-                               insecure="--insecure"
-                       fi
-                       ban_fetchparm="${ban_fetchparm:-"${insecure} --connect-timeout 20 --silent --show-error --location -o"}"
-                       ;;
-               "uclient-fetch")
-                       if [ "${ban_fetchinsecure}" = "1" ]; then
-                               insecure="--no-check-certificate"
-                       fi
-                       ban_fetchparm="${ban_fetchparm:-"${insecure} --timeout=20 -O"}"
-                       ;;
-               "wget")
-                       if [ "${ban_fetchinsecure}" = "1" ]; then
-                               insecure="--no-check-certificate"
-                       fi
-                       ban_fetchparm="${ban_fetchparm:-"${insecure} --no-cache --no-cookies --max-redirect=0 --timeout=20 -O"}"
-                       ;;
-       esac
-       if [ -n "${ban_fetchutil}" ] && [ -n "${ban_fetchparm}" ]; then
-               ban_fetchutil="$(command -v "${ban_fetchutil}")"
-       else
-               f_log "err" "download utility with SSL support not found, please install 'uclient-fetch' with a 'libustream-*' variant or another download utility like 'wget', 'curl' or 'aria2'"
-       fi
-
-       if [ ! -r "${ban_srcfile}" ]; then
-               if [ -r "${ban_srcarc}" ]; then
-                       zcat "${ban_srcarc}" >"${ban_srcfile}"
-               else
-                       f_log "err" "banIP source archive not found"
-               fi
-       fi
-       if [ -r "${ban_srcfile}" ]; then
-               json_init
-               json_load_file "${ban_srcfile}"
-               json_get_keys ban_allsources
-               ban_allsources="${ban_allsources} maclist blacklist whitelist"
-       else
-               f_log "err" "banIP source file not found"
-       fi
-       f_log "debug" "f_env   ::: auto_detect: ${ban_autodetect}, fetch_util: ${ban_fetchutil:-"-"}, fetch_parm: ${ban_fetchparm:-"-"}, src_file: ${ban_srcfile:-"-"}, log_terms: ${ban_logterms}, interfaces: ${ban_ifaces:-"-"}, devices: ${ban_devs:-"-"}, subnets: ${ban_subnets:-"-"}, ip_devices: ${ban_ipdevs:-"-"}, protocols (4/6): ${ban_proto4_enabled}/${ban_proto6_enabled}"
-}
-
-# create temporary files and directories
-#
-f_tmp() {
-       f_dir "${ban_tmpbase}"
-
-       ban_tmpdir="$(mktemp -p "${ban_tmpbase}" -d)"
-       ban_tmpfile="$(mktemp -p "${ban_tmpdir}" -tu)"
-
-       if [ ! -f "${ban_pidfile}" ] || [ ! -s "${ban_pidfile}" ]; then
-               printf "%s" "${$}" >"${ban_pidfile}"
-       fi
-       f_log "debug" "f_tmp   ::: tmp_base: ${ban_tmpbase:-"-"}, tmp_dir: ${ban_tmpdir:-"-"}, pid_file: ${ban_pidfile:-"-"}"
-}
-
-# remove temporary files and directories
-#
-f_rmtmp() {
-       if [ -d "${ban_tmpdir}" ]; then
-               rm -rf "${ban_tmpdir}"
-       fi
-       rm -f "${ban_srcfile}"
-       : >"${ban_pidfile}"
-       f_log "debug" "f_rmtmp ::: tmp_base: ${ban_tmpbase:-"-"}, tmp_dir: ${ban_tmpdir:-"-"}, pid_file: ${ban_pidfile:-"-"}"
-}
-
-# remove backup files
-#
-f_rmbckp() {
-       if [ -d "${ban_backupdir}" ]; then
-               rm -f "${ban_backupdir}/banIP."*".gz"
-       fi
-}
-
-# status helper function
-#
-f_char() {
-       local result input="${1}"
-
-       if [ "${input}" = "1" ]; then
-               result="✔"
-       else
-               result="✘"
-       fi
-       printf "%s" "${result}"
-}
-
-# apply iptables rules
-#
-f_iptrule() {
-       local rc timeout="-w 5" action="${1}" chain="${2}" rule="${3}" pos="${4}"
-
-       if [ "${ban_proto4_enabled}" = "1" ] && { [ "${src_name}" = "maclist" ] || [ "${src_name##*_}" = "4" ]; }; then
-               rc="$(
-                       "${ban_ipt4_cmd}" "${timeout}" -C ${chain} ${rule} 2>/dev/null
-                       printf "%u" ${?}
-               )"
-               if { [ "${rc}" != "0" ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; }; } ||
-                       { [ "${rc}" = "0" ] && [ "${action}" = "-D" ]; }; then
-                       "${ban_ipt4_cmd}" "${timeout}" "${action}" ${chain} ${pos} ${rule} 2>/dev/null
-                       rc="${?}"
-               else
-                       rc=0
-               fi
-       fi
-       if [ "${ban_proto6_enabled}" = "1" ] && { [ "${src_name}" = "maclist" ] || [ "${src_name##*_}" = "6" ]; }; then
-               rc="$(
-                       "${ban_ipt6_cmd}" "${timeout}" -C ${chain} ${rule} 2>/dev/null
-                       printf "%u" ${?}
-               )"
-               if { [ "${rc}" != "0" ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; }; } ||
-                       { [ "${rc}" = "0" ] && [ "${action}" = "-D" ]; }; then
-                       "${ban_ipt6_cmd}" "${timeout}" "${action}" ${chain} ${pos} ${rule} 2>/dev/null
-                       rc="${?}"
-               else
-                       rc=0
-               fi
-       fi
-       if [ -n "${rc}" ] && [ "${rc}" != "0" ]; then
-               : >"${tmp_err}"
-               f_log "info" "${src_name}: iptables action '${action:-"-"}' failed with '${chain}, ${pos:-"-"}, ${rule:-"-"}'"
-       fi
-}
-
-# iptables controller
-#
-f_iptables() {
-       local ipt_cmd chain chainsets dev pos timeout="-w 5" destroy="${1}"
-
-       if [ "${ban_action}" != "refresh" ] && [ "${ban_action}" != "resume" ]; then
-               for dev in ${ban_ipdevs}; do
-                       if [ "${src_name}" = "maclist" ]; then
-                               f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} src -j RETURN"
-                       elif [ "${src_name%_*}" = "whitelist" ]; then
-                               f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set ! --match-set ${src_name} src -j ${ban_logtarget_src}"
-                               f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set ! --match-set ${src_name} dst -j ${ban_logtarget_dst}"
-                               f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set ! --match-set ${src_name} src -j ${ban_logchain_src}"
-                               f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set ! --match-set ${src_name} dst -j ${ban_logchain_dst}"
-                               f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j RETURN"
-                               f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j RETURN"
-                       else
-                               f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j ${ban_logtarget_src}"
-                               f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j ${ban_logtarget_dst}"
-                               f_iptrule "-D" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j ${ban_logchain_src}"
-                               f_iptrule "-D" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j ${ban_logchain_dst}"
-                       fi
-               done
-       fi
-       if [ -z "${destroy}" ] && { [ "${cnt}" -gt "0" ] || [ "${src_name%_*}" = "blacklist" ] || [ "${src_name%_*}" = "whitelist" ]; }; then
-               if [ "${src_name##*_}" = "4" ]; then
-                       ipt_cmd="${ban_ipt4_cmd}"
-                       if [ ! -f "${ban_tmpfile}.${src_name##*_}.chains" ]; then
-                               : >"${ban_tmpfile}.${src_name##*_}.chains"
-                               chainsets="${ban_lan_inputchains_4} ${ban_wan_inputchains_4} ${ban_lan_forwardchains_4} ${ban_wan_forwardchains_4}"
-                               for chain in ${chainsets}; do
-                                       f_iptrule "-I" "${chain}" "-j ${ban_chain}"
-                               done
-                               f_iptrule "-A" "${ban_chain}" "-p udp --dport 67:68 --sport 67:68 -j RETURN"
-                               f_iptrule "-A" "${ban_chain}" "-m conntrack ! --ctstate NEW -j RETURN"
-                       fi
-               elif [ "${src_name##*_}" = "6" ]; then
-                       ipt_cmd="${ban_ipt6_cmd}"
-                       if [ ! -f "${ban_tmpfile}.${src_name##*_}.chains" ]; then
-                               : >"${ban_tmpfile}.${src_name##*_}.chains"
-                               chainsets="${ban_lan_inputchains_6} ${ban_wan_inputchains_6} ${ban_lan_forwardchains_6} ${ban_wan_forwardchains_6}"
-                               for chain in ${chainsets}; do
-                                       f_iptrule "-I" "${chain}" "-j ${ban_chain}"
-                               done
-                               f_iptrule "-A" "${ban_chain}" "-p ipv6-icmp -s fe80::/10 -d fe80::/10 -j RETURN"
-                               f_iptrule "-A" "${ban_chain}" "-p udp -s fc00::/6 --sport 547 -d fc00::/6 --dport 546 -j RETURN"
-                               f_iptrule "-A" "${ban_chain}" "-m conntrack ! --ctstate NEW -j RETURN"
-                       fi
-               fi
-               if [ "${src_settype}" != "dst" ]; then
-                       for dev in ${ban_devs}; do
-                               if [ "${src_name}" = "maclist" ]; then
-                                       f_iptrule "-I" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} src -j RETURN" "1"
-                               elif [ "${src_name%_*}" = "whitelist" ]; then
-                                       pos="$(($("${ipt_cmd}" "${timeout}" -vnL "${ban_chain}" --line-numbers | grep -cF "RETURN") + 1))"
-                                       if [ "${ban_whitelistonly}" = "1" ]; then
-                                               f_iptrule "-I" "${ban_chain}" "-i ${dev} -m set ! --match-set ${src_name} src -j ${ban_target_src}" "${pos}"
-                                       else
-                                               f_iptrule "-I" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j RETURN" "${pos}"
-                                       fi
-                               else
-                                       f_iptrule "${action:-"-A"}" "${ban_chain}" "-i ${dev} -m set --match-set ${src_name} src -j ${ban_target_src}"
-                               fi
-                       done
-               fi
-               if [ "${src_settype}" != "src" ]; then
-                       for dev in ${ban_devs}; do
-                               if [ "${src_name%_*}" = "whitelist" ]; then
-                                       pos="$(($("${ipt_cmd}" "${timeout}" -vnL "${ban_chain}" --line-numbers | grep -cF "RETURN") + 1))"
-                                       if [ "${ban_whitelistonly}" = "1" ]; then
-                                               f_iptrule "-I" "${ban_chain}" "-o ${dev} -m set ! --match-set ${src_name} dst -j ${ban_target_dst}" "${pos}"
-                                       else
-                                               f_iptrule "-I" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j RETURN" "${pos}"
-                                       fi
-                               elif [ "${src_name}" != "maclist" ]; then
-                                       f_iptrule "${action:-"-A"}" "${ban_chain}" "-o ${dev} -m set --match-set ${src_name} dst -j ${ban_target_dst}"
-                               fi
-                       done
-               fi
-       else
-               "${ban_ipset_cmd}" -q destroy "${src_name}"
-       fi
-}
-
-# ipset controller
-#
-f_ipset() {
-       local src src_list action rule ipt_cmd out_rc max="0" cnt="0" cnt_ip="0" cnt_cidr="0" cnt_mac="0" timeout="-w 5" mode="${1}" in_rc="4"
-
-       case "${mode}" in
-               "backup")
-                       gzip -cf "${tmp_load}" 2>/dev/null >"${ban_backupdir}/banIP.${src_name}.gz"
-                       out_rc="${?}"
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
-                       return "${out_rc}"
-                       ;;
-               "restore")
-                       if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]; then
-                               zcat "${ban_backupdir}/banIP.${src_name}.gz" 2>/dev/null >"${tmp_load}"
-                               out_rc="${?}"
-                       else
-                               out_rc="${in_rc}"
-                       fi
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
-                       return "${out_rc}"
-                       ;;
-               "remove")
-                       if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]; then
-                               rm -f "${ban_backupdir}/banIP.${src_name}.gz"
-                               out_rc="${?}"
-                       else
-                               out_rc="${in_rc}"
-                       fi
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
-                       return "${out_rc}"
-                       ;;
-               "initial")
-                       for proto in "4" "6"; do
-                               if [ "${proto}" = "4" ] && [ "${ban_proto4_enabled}" = "1" ]; then
-                                       ipt_cmd="${ban_ipt4_cmd}"
-                                       chainsets="${ban_lan_inputchains_4} ${ban_lan_forwardchains_4} ${ban_wan_inputchains_4} ${ban_wan_forwardchains_4}"
-                               elif [ "${proto}" = "6" ] && [ "${ban_proto6_enabled}" = "1" ]; then
-                                       ipt_cmd="${ban_ipt6_cmd}"
-                                       chainsets="${ban_lan_inputchains_6} ${ban_lan_forwardchains_6} ${ban_wan_inputchains_6} ${ban_wan_forwardchains_6}"
-                               fi
-
-                               if { [ "${proto}" = "4" ] && [ "${ban_proto4_enabled}" = "1" ]; } ||
-                                       { [ "${proto}" = "6" ] && [ "${ban_proto6_enabled}" = "1" ]; }; then
-                                       if [ -z "$("${ipt_cmd}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]; then
-                                               "${ipt_cmd}" "${timeout}" -N "${ban_chain}" 2>/dev/null
-                                               out_rc="${?}"
-                                               f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, chain: ${ban_chain:-"-"}, out_rc: ${out_rc}"
-                                       else
-                                               out_rc=0
-                                               for chain in ${chainsets}; do
-                                                       f_iptrule "-D" "${chain}" "-j ${ban_chain}"
-                                               done
-                                       fi
-
-                                       if [ "${ban_logsrc_enabled}" = "1" ] && [ "${out_rc}" = "0" ] && [ -z "$("${ipt_cmd}" "${timeout}" -nL "${ban_logchain_src}" 2>/dev/null)" ]; then
-                                               "${ipt_cmd}" "${timeout}" -N "${ban_logchain_src}" 2>/dev/null
-                                               out_rc="${?}"
-                                               if [ "${out_rc}" = "0" ]; then
-                                                       "${ipt_cmd}" "${timeout}" -A "${ban_logchain_src}" -j LOG ${ban_logopts_src} --log-prefix "${ban_logprefix_src}"
-                                                       out_rc="${?}"
-                                                       if [ "${out_rc}" = "0" ]; then
-                                                               "${ipt_cmd}" "${timeout}" -A "${ban_logchain_src}" -j "${ban_logtarget_src}"
-                                                               out_rc="${?}"
-                                                       fi
-                                               fi
-                                               f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, logchain_src: ${ban_logchain_src:-"-"}, out_rc: ${out_rc}"
-                                       fi
-
-                                       if [ "${ban_logdst_enabled}" = "1" ] && [ "${out_rc}" = "0" ] && [ -z "$("${ipt_cmd}" "${timeout}" -nL "${ban_logchain_dst}" 2>/dev/null)" ]; then
-                                               "${ipt_cmd}" "${timeout}" -N "${ban_logchain_dst}" 2>/dev/null
-                                               out_rc="${?}"
-                                               if [ "${out_rc}" = "0" ]; then
-                                                       "${ipt_cmd}" "${timeout}" -A "${ban_logchain_dst}" -j LOG ${ban_logopts_dst} --log-prefix "${ban_logprefix_dst}"
-                                                       out_rc="${?}"
-                                                       if [ "${out_rc}" = "0" ]; then
-                                                               "${ipt_cmd}" "${timeout}" -A "${ban_logchain_dst}" -j "${ban_logtarget_dst}"
-                                                               out_rc="${?}"
-                                                       fi
-                                               fi
-                                               f_log "debug" "f_ipset ::: name: initial, mode: ${mode:-"-"}, logchain_dst: ${ban_logchain_dst:-"-"}, out_rc: ${out_rc}"
-                                       fi
-                               fi
-                       done
-                       out_rc="${out_rc:-"${in_rc}"}"
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
-                       return "${out_rc}"
-                       ;;
-               "create")
-                       if [ -z "$("${ban_ipset_cmd}" -q -n list "${src_name}")" ] &&
-                               { [ -s "${tmp_file}" ] || [ "${src_name%_*}" = "whitelist" ] || [ "${src_name%_*}" = "blacklist" ]; }; then
-                               max="$(awk 'END{print NR}' "${tmp_file}" 2>/dev/null)"
-                               max=$((max + 262144))
-                               if [ "${src_name}" = "maclist" ]; then
-                                       "${ban_ipset_cmd}" create "${src_name}" hash:mac hashsize 64 maxelem "${max}" counters timeout "${ban_maclist_timeout:-"0"}"
-                                       out_rc="${?}"
-                               elif [ "${src_name%_*}" = "whitelist" ]; then
-                                       "${ban_ipset_cmd}" create "${src_name}" hash:net hashsize 64 maxelem "${max}" family "${src_ipver}" counters timeout "${ban_whitelist_timeout:-"0"}"
-                                       out_rc="${?}"
-                               elif [ "${src_name%_*}" = "blacklist" ]; then
-                                       "${ban_ipset_cmd}" create "${src_name}" hash:net hashsize 64 maxelem "${max}" family "${src_ipver}" counters timeout "${ban_blacklist_timeout:-"0"}"
-                                       out_rc="${?}"
-                               else
-                                       "${ban_ipset_cmd}" create "${src_name}" hash:net hashsize 64 maxelem "${max}" family "${src_ipver}" counters
-                                       out_rc="${?}"
-                               fi
-                       elif [ -n "$("${ban_ipset_cmd}" -q -n list "${src_name}")" ]; then
-                               "${ban_ipset_cmd}" -q flush "${src_name}"
-                               out_rc="${?}"
-                       fi
-                       if [ -s "${tmp_file}" ] && [ "${out_rc}" = "0" ]; then
-                               "${ban_ipset_cmd}" -q -! restore <"${tmp_file}"
-                               out_rc="${?}"
-                               if [ "${out_rc}" = "0" ]; then
-                                       src_list="$("${ban_ipset_cmd}" -q list "${src_name}")"
-                                       cnt="$(printf "%s\n" "${src_list}" | awk '/^Number of entries:/{print $4}')"
-                                       cnt_mac="$(printf "%s\n" "${src_list}" | grep -cE "^(([0-9A-Z][0-9A-Z]:){5}[0-9A-Z]{2} )")"
-                                       cnt_cidr="$(printf "%s\n" "${src_list}" | grep -cE "(/[0-9]{1,3} )")"
-                                       cnt_ip=$((cnt - cnt_cidr - cnt_mac))
-                                       printf "%s\n" "${cnt}" >"${tmp_cnt}"
-                               fi
-                       fi
-                       f_iptables
-                       end_ts="$(date +%s)"
-                       out_rc="${out_rc:-"${in_rc}"}"
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, ipver: ${src_ipver:-"-"}, settype: ${src_settype:-"-"}, count(sum/ip/cidr/mac): ${cnt}/${cnt_ip}/${cnt_cidr}/${cnt_mac}, time: $((end_ts - start_ts)), out_rc: ${out_rc}"
-                       return "${out_rc}"
-                       ;;
-               "refresh")
-                       if [ -n "$("${ban_ipset_cmd}" -q -n list "${src_name}")" ]; then
-                               out_rc=0
-                               src_list="$("${ban_ipset_cmd}" -q list "${src_name}")"
-                               cnt="$(printf "%s\n" "${src_list}" | awk '/^Number of entries:/{print $4}')"
-                               cnt_mac="$(printf "%s\n" "${src_list}" | grep -cE "^(([0-9A-Z][0-9A-Z]:){5}[0-9A-Z]{2} )")"
-                               cnt_cidr="$(printf "%s\n" "${src_list}" | grep -cE "(/[0-9]{1,3} )")"
-                               cnt_ip=$((cnt - cnt_cidr - cnt_mac))
-                               printf "%s\n" "${cnt}" >"${tmp_cnt}"
-                               f_iptables
-                       fi
-                       end_ts="$(date +%s)"
-                       out_rc="${out_rc:-"${in_rc}"}"
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, count(sum/ip/cidr/mac): ${cnt}/${cnt_ip}/${cnt_cidr}/${cnt_mac}, time: $((end_ts - start_ts)), out_rc: ${out_rc}"
-                       return "${out_rc}"
-                       ;;
-               "suspend")
-                       for src in ${ban_sources} ${ban_localsources}; do
-                               if [ "${src}" = "maclist" ] && [ -n "$("${ban_ipset_cmd}" -q -n list "${src}")" ]; then
-                                       tmp_file="${ban_backupdir}/${src}.file"
-                                       "${ban_ipset_cmd}" -q save "${src}" | tail -n +2 >"${tmp_file}"
-                                       "${ban_ipset_cmd}" -q flush "${src}"
-                               else
-                                       for proto in "4" "6"; do
-                                               if [ -n "$("${ban_ipset_cmd}" -q -n list "${src}_${proto}")" ]; then
-                                                       tmp_file="${ban_backupdir}/${src}_${proto}.file"
-                                                       "${ban_ipset_cmd}" -q save "${src}_${proto}" | tail -n +2 >"${tmp_file}"
-                                                       "${ban_ipset_cmd}" -q flush "${src}_${proto}"
-                                               fi
-                                       done
-                               fi
-                       done
-                       f_log "debug" "f_ipset ::: name: ${src:-"-"}, mode: ${mode:-"-"}"
-                       ;;
-               "resume")
-                       if [ -f "${ban_backupdir}/${src_name}.file" ]; then
-                               "${ban_ipset_cmd}" -q -! restore <"${ban_backupdir}/${src_name}.file"
-                               out_rc="${?}"
-                               if [ "${out_rc}" = "0" ]; then
-                                       rm -f "${ban_backupdir}/${src_name}.file"
-                                       src_list="$("${ban_ipset_cmd}" -q list "${src_name}")"
-                                       cnt="$(printf "%s\n" "${src_list}" | awk '/^Number of entries:/{print $4}')"
-                                       cnt_mac="$(printf "%s\n" "${src_list}" | grep -cE "^(([0-9A-Z][0-9A-Z]:){5}[0-9A-Z]{2} )")"
-                                       cnt_cidr="$(printf "%s\n" "${src_list}" | grep -cE "(/[0-9]{1,3} )")"
-                                       cnt_ip=$((cnt - cnt_cidr - cnt_mac))
-                                       printf "%s\n" "${cnt}" >"${tmp_cnt}"
-                               fi
-                               f_iptables
-                       fi
-                       end_ts="$(date +%s)"
-                       out_rc="${out_rc:-"${in_rc}"}"
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, ipver: ${src_ipver:-"-"}, settype: ${src_settype:-"-"}, count(sum/ip/cidr/mac): ${cnt}/${cnt_ip}/${cnt_cidr}/${cnt_mac}, time: $((end_ts - start_ts)), out_rc: ${out_rc}"
-                       return "${out_rc}"
-                       ;;
-               "flush")
-                       if [ -n "$("${ban_ipset_cmd}" -q -n list "${src_name}")" ]; then
-                               f_iptables "destroy"
-                               out_rc=0
-                       fi
-                       out_rc="${out_rc:-"${in_rc}"}"
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
-                       return "${out_rc}"
-                       ;;
-               "destroy")
-                       for chain in ${ban_chain} ${ban_logchain_src} ${ban_logchain_dst}; do
-                               if [ -n "$("${ban_ipt4_cmd}" "${timeout}" -nL "${chain}" 2>/dev/null)" ]; then
-                                       "${ban_ipt4_savecmd}" | grep -v -- "-j ${chain}" | "${ban_ipt4_restorecmd}"
-                                       "${ban_ipt4_cmd}" "${timeout}" -F "${chain}" 2>/dev/null
-                                       "${ban_ipt4_cmd}" "${timeout}" -X "${chain}" 2>/dev/null
-                               fi
-                               if [ -n "$("${ban_ipt6_cmd}" "${timeout}" -nL "${chain}" 2>/dev/null)" ]; then
-                                       "${ban_ipt6_savecmd}" | grep -v -- "-j ${chain}" | "${ban_ipt6_restorecmd}"
-                                       "${ban_ipt6_cmd}" "${timeout}" -F "${chain}" 2>/dev/null
-                                       "${ban_ipt6_cmd}" "${timeout}" -X "${chain}" 2>/dev/null
-                               fi
-                       done
-                       for src in ${ban_sources} maclist blacklist whitelist; do
-                               if [ "${src}" = "maclist" ] && [ -n "$("${ban_ipset_cmd}" -q -n list "${src}")" ]; then
-                                       "${ban_ipset_cmd}" -q destroy "${src}"
-                               else
-                                       for proto in "4" "6"; do
-                                               if [ -n "$("${ban_ipset_cmd}" -q -n list "${src}_${proto}")" ]; then
-                                                       "${ban_ipset_cmd}" -q destroy "${src}_${proto}"
-                                               fi
-                                       done
-                               fi
-                       done
-                       f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}"
-                       ;;
-       esac
-}
-
-# write to syslog
-#
-f_log() {
-       local class="${1}" log_msg="${2}"
-
-       if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${ban_debug}" = "1" ]; }; then
-               if [ -x "${ban_logger_cmd}" ]; then
-                       "${ban_logger_cmd}" -p "${class}" -t "banIP-${ban_ver%-*}[${$}]" "${log_msg}"
-               else
-                       printf "%s %s %s\n" "${class}" "banIP-${ban_ver%-*}[${$}]" "${log_msg}"
-               fi
-               if [ "${class}" = "err" ]; then
-                       f_jsnup "error"
-                       f_ipset "destroy"
-                       f_rmbckp
-                       f_rmtmp
-                       exit 1
-               fi
-       fi
-}
-
-# kill all relevant background processes
-#
-f_pidx() {
-       local pids ppid="${1}"
-
-       pids="$(pgrep -P "${ppid}" 2>/dev/null | awk '{ORS=" ";print $0}')"
-       kill -HUP "${ppid}" "${pids}" 2>/dev/null
-       : >"${ban_bgpidfile}"
-}
-
-# start log service to trace failed ssh/luci logins
-#
-f_bgsrv() {
-       local bg_pid action="${1}"
-
-       bg_pid="$(cat "${ban_bgpidfile}" 2>/dev/null)"
-       if [ "${action}" = "start" ] && [ -x "${ban_logservice}" ] && [ "${ban_monitor_enabled}" = "1" ] && [ "${ban_whitelistonly}" = "0" ]; then
-               if [ -n "${bg_pid}" ]; then
-                       f_pidx "${bg_pid}"
-               fi
-               if printf "%s\n" "${ban_logterms}" | grep -q "dropbear"; then
-                       ban_search="Exit before auth from|"
-               fi
-               if printf "%s\n" "${ban_logterms}" | grep -q "sshd"; then
-                       ban_search="${ban_search}error: maximum authentication attempts exceeded|sshd.*Connection closed by.*\[preauth\]|"
-               fi
-               if printf "%s\n" "${ban_logterms}" | grep -q "luci"; then
-                       ban_search="${ban_search}luci: failed login|"
-               fi
-               if printf "%s\n" "${ban_logterms}" | grep -q "nginx"; then
-                       ban_search="${ban_search}nginx(\[[0-9]+\])?:.*\[error\].*open().*client: [[:alnum:].:]+|"
-               fi
-               (
-                       "${ban_logservice}" "${ban_search%?}" &
-                       printf "%s" "${!}" >"${ban_bgpidfile}"
-               )
-       elif { [ "${action}" = "stop" ] || [ "${ban_monitor_enabled}" = "0" ]; } && [ -n "${bg_pid}" ]; then
-               f_pidx "${bg_pid}"
-       fi
-       f_log "debug" "f_bgsrv ::: action: ${action:-"-"}, bg_pid (old/new): ${bg_pid}/$(cat "${ban_bgpidfile}" 2>/dev/null), monitor_enabled: ${ban_monitor_enabled:-"-"}, log_service: ${ban_logservice:-"-"}"
-}
-
-# download controller
-#
-f_down() {
-       local src_name="${1}" proto="${2}" src_ipver="${3}" src_url="${4}" src_rule="${5}" src_comp="${6}"
-       local ip start_ts end_ts src_settype src_log src_rc tmp_load tmp_file tmp_raw tmp_cnt tmp_err
-
-       start_ts="$(date +%s)"
-       if printf "%s\n" "${ban_settype_src}" | grep -q "${src_name}"; then
-               src_settype="src"
-       elif printf "%s\n" "${ban_settype_dst}" | grep -q "${src_name}"; then
-               src_settype="dst"
-       elif printf "%s\n" "${ban_settype_all}" | grep -q "${src_name}"; then
-               src_settype="src+dst"
-       else
-               src_settype="${ban_global_settype}"
-       fi
-       src_name="${src_name}_${proto}"
-       tmp_load="${ban_tmpfile}.${src_name}.load"
-       tmp_file="${ban_tmpfile}.${src_name}.file"
-       tmp_raw="${tmp_file}.raw"
-       tmp_cnt="${tmp_file}.cnt"
-       tmp_err="${tmp_file}.err"
-
-       # 'resume' mode
-       #
-       if [ "${ban_action}" = "resume" ]; then
-               if [ "${src_name%_*}" = "maclist" ]; then
-                       src_name="maclist"
-               fi
-               f_ipset "resume"
-               src_rc="${?}"
-               if [ "${src_rc}" = "0" ]; then
-                       return
-               fi
-       fi
-
-       # handle local downloads
-       #
-       case "${src_name%_*}" in
-               "blacklist" | "whitelist")
-                       printf "%s\n" "0" >"${tmp_cnt}"
-                       awk "${src_rule}" "${src_url}" >"${tmp_file}"
-                       src_rc="${?}"
-                       if [ "${src_rc}" = "0" ]; then
-                               f_ipset "create"
-                               if [ ! -f "${tmp_dns}" ] && { { [ "${proto}" = "4" ] && [ "${ban_proto4_enabled}" = "1" ]; } ||
-                                       { [ "${proto}" = "6" ] && [ "${ban_proto6_enabled}" = "1" ] && [ "${ban_proto4_enabled}" = "0" ]; }; }; then
-                                       tmp_dns="${ban_tmpbase}/${src_name%_*}.dns"
-                                       src_rule="/^([[:alnum:]_-]{1,63}\\.)+[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}"
-                                       awk "${src_rule}" "${src_url}" >"${tmp_dns}"
-                                       src_rc="${?}"
-                                       if [ "${src_rc}" = "0" ] && [ -s "${tmp_dns}" ]; then
-                                               ("${ban_dnsservice}" "${src_name%_*}" "${tmp_dns}" &)
-                                       else
-                                               rm -f "${tmp_dns}"
-                                       fi
-                               fi
-                       else
-                               f_log "debug" "f_down  ::: name: ${src_name}, url: ${src_url}, rule: ${src_rule}, rc: ${src_rc}"
-                       fi
-                       return
-                       ;;
-               "maclist")
-                       src_name="${src_name%_*}"
-                       tmp_file="${ban_tmpfile}.${src_name}.file"
-                       tmp_cnt="${tmp_file}.cnt"
-                       tmp_err="${tmp_file}.err"
-                       awk "${src_rule}" "${src_url}" >"${tmp_file}"
-                       src_rc="${?}"
-                       if [ "${src_rc}" = "0" ]; then
-                               f_ipset "create"
-                       else
-                               f_log "debug" "f_down  ::: name: ${src_name}, url: ${src_url}, rule: ${src_rule}, rc: ${src_rc}"
-                       fi
-                       return
-                       ;;
-       esac
-
-       # 'refresh' mode
-       #
-       if [ "${ban_action}" = "refresh" ]; then
-               f_ipset "refresh"
-               src_rc="${?}"
-               if [ "${src_rc}" = "0" ]; then
-                       return
-               fi
-       fi
-
-       # 'start' mode
-       #
-       if [ "${ban_action}" = "start" ]; then
-               f_ipset "restore"
-       fi
-       src_rc="${?}"
-       if [ "${src_rc}" = "0" ]; then
-               awk "${src_rule}" "${tmp_load}" 2>/dev/null >"${tmp_file}"
-               src_rc="${?}"
-               if [ "${src_rc}" = "0" ]; then
-                       f_ipset "create"
-                       src_rc="${?}"
-                       if [ "${src_rc}" = "0" ]; then
-                               return
-                       fi
-               fi
-       fi
-
-       # handle country related downloads
-       #
-       if [ "${src_name%_*}" = "country" ]; then
-               for country in ${ban_countries}; do
-                       src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}${country}-aggregated.zone" 2>&1)"
-                       src_rc="${?}"
-                       if [ "${src_rc}" = "0" ]; then
-                               cat "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
-                       else
-                               continue
-                       fi
-               done
-
-       # handle asn related downloads
-       #
-       elif [ "${src_name%_*}" = "asn" ]; then
-               for asn in ${ban_asns}; do
-                       src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}AS${asn}" 2>&1)"
-                       src_rc="${?}"
-                       if [ "${src_rc}" = "0" ]; then
-                               cat "${tmp_raw}" 2>/dev/null >>"${tmp_load}"
-                       else
-                               continue
-                       fi
-               done
-
-       # handle compressed downloads
-       #
-       elif [ -n "${src_comp}" ]; then
-               case "${src_comp}" in
-                       "gz")
-                               src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}" 2>&1)"
-                               src_rc="${?}"
-                               if [ "${src_rc}" -eq 0 ]; then
-                                       zcat "${tmp_raw}" 2>/dev/null >"${tmp_load}"
-                                       src_rc="${?}"
-                               fi
-                               ;;
-               esac
-
-       # handle normal downloads
-       #
-       else
-               src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_load}" "${src_url}" 2>&1)"
-               src_rc="${?}"
-       fi
-
-       # download post-processing
-       #
-       if [ "${src_rc}" = "0" ]; then
-               f_ipset "backup"
-               src_rc="${?}"
-       elif [ "${ban_action}" != "start" ] && [ "${ban_action}" != "refresh" ]; then
-               f_ipset "restore"
-               src_rc="${?}"
-       fi
-       if [ "${src_rc}" = "0" ]; then
-               awk "${src_rule}" "${tmp_load}" 2>/dev/null >"${tmp_file}"
-               src_rc="${?}"
-               if [ "${src_rc}" = "0" ]; then
-                       f_ipset "create"
-                       src_rc="${?}"
-               elif [ "${ban_action}" != "refresh" ]; then
-                       f_ipset "refresh"
-                       src_rc="${?}"
-               fi
-       else
-               src_log="$(printf "%s" "${src_log}" | awk '{ORS=" ";print $0}')"
-               if [ "${ban_action}" != "refresh" ]; then
-                       f_ipset "refresh"
-                       src_rc="${?}"
-               fi
-               f_log "debug" "f_down  ::: name: ${src_name}, url: ${src_url}, rule: ${src_rule}, rc: ${src_rc}, log: ${src_log:-"-"}"
-       fi
-}
-
-# main controller
-#
-f_main() {
-       local src_name src_url_4 src_rule_4 src_url_6 src_rule_6 src_comp src_rc src_ts log_raw log_merge log_ips log_count hold err_file cnt_file cnt=0
-
-       # prepare logfile excerpts
-       #
-       if [ "${ban_autoblacklist}" = "1" ] || [ "${ban_monitor_enabled}" = "1" ]; then
-               log_raw="$(${ban_logread_cmd} -l "${ban_loglimit}")"
-               if printf "%s\n" "${ban_logterms}" | grep -q "dropbear"; then
-                       log_ips="$(printf "%s\n" "${log_raw}" | grep -E "Exit before auth from" |
-                               awk 'match($0,/<[0-9A-f:\.]+:/){printf "%s\n",substr($0,RSTART+1,RLENGTH-2)}' | awk '!seen[$NF]++' | awk '{ORS=" ";print $NF}')"
-                       for ip in ${log_ips}; do
-                               log_count="$(printf "%s\n" "${log_raw}" | grep -cE "Exit before auth from <${ip}")"
-                               if [ "${log_count}" -ge "${ban_ssh_logcount}" ]; then
-                                       log_merge="${log_merge} ${ip}"
-                               fi
-                       done
-               fi
-               if printf "%s\n" "${ban_logterms}" | grep -q "sshd"; then
-                       log_ips="$(printf "%s\n" "${log_raw}" | grep -E "error: maximum authentication attempts exceeded|sshd.*Connection closed by.*\[preauth\]" |
-                               awk 'match($0,/[0-9A-f:\.]+ port/){printf "%s\n",substr($0,RSTART,RLENGTH-5)}' | awk '!seen[$NF]++' | awk '{ORS=" ";print $NF}')"
-                       for ip in ${log_ips}; do
-                               log_count="$(printf "%s\n" "${log_raw}" | grep -cE "error: maximum authentication attempts exceeded.*${ip}|sshd.*Connection closed by.*${ip}.*\[preauth\]")"
-                               if [ "${log_count}" -ge "${ban_ssh_logcount}" ]; then
-                                       log_merge="${log_merge} ${ip}"
-                               fi
-                       done
-               fi
-               if printf "%s\n" "${ban_logterms}" | grep -q "luci"; then
-                       log_ips="$(printf "%s\n" "${log_raw}" | grep -E "luci: failed login on " |
-                               awk 'match($0,/[0-9A-f:\.]+$/){printf "%s\n",substr($0,RSTART,RLENGTH)}' | awk '!seen[$NF]++' | awk '{ORS=" ";print $NF}')"
-                       for ip in ${log_ips}; do
-                               log_count="$(printf "%s\n" "${log_raw}" | grep -cE "luci: failed login on .*from ${ip}")"
-                               if [ "${log_count}" -ge "${ban_luci_logcount}" ]; then
-                                       log_merge="${log_merge} ${ip}"
-                               fi
-                       done
-               fi
-               if printf "%s\n" "${ban_logterms}" | grep -q "nginx"; then
-                       log_ips="$(printf "%s\n" "${log_raw}" | grep -oE "nginx(\[[0-9]+\])?:.*\[error\].*open\(\).*client: [[:alnum:].:]+" |
-                               awk '!seen[$NF]++' | awk '{ORS=" ";print $NF}')"
-                       for ip in ${log_ips}; do
-                               log_count="$(printf "%s\n" "${log_raw}" | grep -cE "nginx(\[[0-9]+\])?:.*\[error\].*open\(\).*client: ${ip}")"
-                               if [ "${log_count}" -ge "${ban_nginx_logcount}" ]; then
-                                       log_merge="${log_merge} ${ip}"
-                               fi
-                       done
-               fi
-       fi
-
-       # prepare new black- and whitelist entries
-       #
-       if [ "${ban_autowhitelist}" = "1" ] && [ -f "${ban_whitelist}" ]; then
-               for ip in ${ban_subnets}; do
-                       if ! grep -q "${ip}" "${ban_whitelist}"; then
-                               src_ts="# added on $(date "+%d.%m.%Y %H:%M:%S")"
-                               printf "%-42s%s\n" "${ip}" "${src_ts}" >>"${ban_whitelist}"
-                               f_log "info" "IP address '${ip}' added to whitelist"
-                       fi
-               done
-       fi
-       if [ "${ban_autoblacklist}" = "1" ] && [ -f "${ban_blacklist}" ]; then
-               for ip in ${log_merge}; do
-                       if ! grep -q "${ip}" "${ban_blacklist}"; then
-                               src_ts="# added on $(date "+%d.%m.%Y %H:%M:%S")"
-                               printf "%-42s%s\n" "${ip}" "${src_ts}" >>"${ban_blacklist}"
-                               f_log "info" "IP address '${ip}' added to blacklist"
-                       fi
-               done
-       fi
-
-       # initial ipset/iptables creation
-       #
-       if ! f_ipset "initial"; then
-               f_log "err" "banIP processing failed, fatal error during ipset/iptables creation (${ban_sysver})"
-       fi
-
-       # load local source files (maclist, blacklist, whitelist)
-       #
-       for src_name in ${ban_localsources}; do
-               if [ "${src_name}" = "maclist" ] && [ -s "${ban_maclist}" ]; then
-                       (
-                               src_rule_4="/^([0-9A-z][0-9A-z]:){5}[0-9A-z]{2}([[:space:]]|$)/{print \"add ${src_name} \"toupper(\$1)}"
-                               f_down "${src_name}" "mac" "mac" "${ban_maclist}" "${src_rule_4}"
-                       ) &
-               fi
-               if [ "${ban_proto4_enabled}" = "1" ]; then
-                       if [ "${src_name}" = "blacklist" ] && [ -s "${ban_blacklist}" ] && [ "${ban_whitelistonly}" = "0" ]; then
-                               (
-                                       src_rule_4="/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add ${src_name}_4 \"\$1}"
-                                       f_down "${src_name}" "4" "inet" "${ban_blacklist}" "${src_rule_4}"
-                               ) &
-                       elif [ "${src_name}" = "whitelist" ] && [ -s "${ban_whitelist}" ]; then
-                               (
-                                       src_rule_4="/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add ${src_name}_4 \"\$1}"
-                                       f_down "${src_name}" "4" "inet" "${ban_whitelist}" "${src_rule_4}"
-                               ) &
-                       fi
-               else
-                       (
-                               src_name="${src_name}_4"
-                               f_ipset "flush"
-                       ) &
-               fi
-               if [ "${ban_proto6_enabled}" = "1" ]; then
-                       if [ "${src_name}" = "blacklist" ] && [ -s "${ban_blacklist}" ] && [ "${ban_whitelistonly}" = "0" ]; then
-                               (
-                                       src_rule_6="/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add ${src_name}_6 \"\$1}"
-                                       f_down "${src_name}" "6" "inet6" "${ban_blacklist}" "${src_rule_6}"
-                               ) &
-                       elif [ "${src_name}" = "whitelist" ] && [ -s "${ban_whitelist}" ]; then
-                               (
-                                       src_rule_6="/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add ${src_name}_6 \"\$1}"
-                                       f_down "${src_name}" "6" "inet6" "${ban_whitelist}" "${src_rule_6}"
-                               ) &
-                       fi
-               else
-                       (
-                               src_name="${src_name}_6"
-                               f_ipset "flush"
-                       ) &
-               fi
-       done
-       wait
-
-       # loop over all external sources
-       #
-       if [ "${ban_whitelistonly}" = "0" ]; then
-               for src_name in ${ban_sources}; do
-                       # get source data from JSON file
-                       #
-                       if ! json_select "${src_name}" >/dev/null 2>&1; then
-                               continue
-                       fi
-                       json_objects="url_4 rule_4 url_6 rule_6 comp"
-                       for object in ${json_objects}; do
-                               eval json_get_var src_${object} '${object}' >/dev/null 2>&1
-                       done
-                       json_select ..
-
-                       # handle external IPv4 source downloads in a subshell
-                       #
-                       if [ "${ban_proto4_enabled}" = "1" ] && [ -n "${src_url_4}" ] && [ -n "${src_rule_4}" ]; then
-                               (
-                                       f_down "${src_name}" "4" "inet" "${src_url_4}" "${src_rule_4}" "${src_comp}"
-                               ) &
-                       fi
-
-                       # handle external IPv6 source downloads in a subshell
-                       #
-                       if [ "${ban_proto6_enabled}" = "1" ] && [ -n "${src_url_6}" ] && [ -n "${src_rule_6}" ]; then
-                               (
-                                       f_down "${src_name}" "6" "inet6" "${src_url_6}" "${src_rule_6}" "${src_comp}"
-                               ) &
-                       fi
-
-                       # control/limit download queues
-                       #
-                       hold=$((cnt % ban_maxqueue))
-                       if [ "${hold}" = "0" ]; then
-                               wait
-                       fi
-                       cnt=$((cnt + 1))
-               done
-               wait
-       fi
-
-       # error out
-       #
-       for err_file in "${ban_tmpfile}."*".err"; do
-               if [ -f "${err_file}" ]; then
-                       f_log "err" "banIP processing failed, fatal iptables errors during subshell processing (${ban_sysver})"
-               fi
-       done
-
-       # finish processing
-       #
-       ban_sources=""
-       for cnt_file in "${ban_tmpfile}."*".cnt"; do
-               if [ -f "${cnt_file}" ]; then
-                       read -r cnt <"${cnt_file}"
-                       ban_cnt=$((ban_cnt + cnt))
-                       ban_setcnt=$((ban_setcnt + 1))
-                       src_name="$(printf "%s" "${cnt_file}" | grep -Eo "[a-z0-9_]+.file.cnt")"
-                       src_name="${src_name%%.*}"
-                       if ! printf "%s" "${ban_sources}" | grep -q "${src_name%_*}"; then
-                               ban_sources="${ban_sources} ${src_name%_*}"
-                               ban_allsources="${ban_allsources//${src_name%_*}/}"
-                       fi
-               fi
-       done
-       for src_name in ${ban_allsources}; do
-               if [ "${src_name}" = "maclist" ]; then
-                       f_ipset "flush"
-               else
-                       for proto in "4" "6"; do
-                               src_name="${src_name%_*}_${proto}"
-                               f_ipset "flush"
-                               if [ "${src_name%_*}" != "blacklist" ] && [ "${src_name%_*}" != "whitelist" ]; then
-                                       f_ipset "remove"
-                               fi
-                       done
-               fi
-       done
-       f_log "info" "${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes loaded successfully (${ban_sysver})"
-       f_jsnup
-       f_rmtmp
-       f_bgsrv "start"
-}
-
-# query ipsets for certain IP
-#
-f_query() {
-       local src proto result query_start query_end query_timeout="30" match="0" search="${1}"
-
-       if [ -z "${search}" ]; then
-               printf "%s\n" "::: missing search term, please submit a single ip or mac address :::"
-       else
-               query_start="$(date "+%s")"
-               printf "%s\n%s\n%s\n" ":::" "::: search '${search}' in banIP related IPSets" ":::"
-
-               for src in ${ban_localsources} ${ban_sources} ${ban_extrasources}; do
-                       if [ "${src}" = "maclist" ] && [ -n "$("${ban_ipset_cmd}" -q -n list "${src}")" ]; then
-                               result="$(
-                                       ipset -q test ${src} ${search} >/dev/null 2>&1
-                                       printf "%u" "${?}"
-                               )"
-                               if [ "${result}" = "0" ]; then
-                                       match="1"
-                                       printf "%s\n" "  - found in IPSet '${src}'"
-                                       break
-                               fi
-                       else
-                               for proto in "4" "6"; do
-                                       if [ -n "$("${ban_ipset_cmd}" -q -n list "${src}_${proto}")" ]; then
-                                               result="$(
-                                                       ipset -q test ${src}_${proto} ${search} >/dev/null 2>&1
-                                                       printf "%u" "${?}"
-                                               )"
-                                               if [ "${result}" = "0" ]; then
-                                                       match="1"
-                                                       printf "%s\n" "  - found in IPSet '${src}_${proto}'"
-                                               fi
-                                       fi
-                               done
-                       fi
-                       query_end="$(date "+%s")"
-                       if [ "$((query_end - query_start))" -gt "${query_timeout}" ]; then
-                               printf "%s\n\n" "  - [...]"
-                               break
-                       fi
-               done
-               if [ "${match}" = "0" ]; then
-                       printf "%s\n\n" "  - no match"
-               fi
-       fi
-}
-
-# generate statistics
-#
-f_report() {
-       local report_json report_txt bg_pid content proto src src_list detaillist type jsnval jsnvals jsnval1 jsnval2 jsnval3 jsnval4 jsnval5 jsnval6 jsnval7
-       local cnt cnt_mac cnt_cidr cnt_ip cnt_acc cnt_sum="0" cnt_set_sum="1" cnt_acc_sum="0" cnt_mac_sum="0" cnt_ip_sum="0" cnt_cidr_sum="0" cnt_set_sum="0" action="${1}"
-
-       report_json="${ban_reportdir}/ban_report.json"
-       report_txt="${ban_reportdir}/ban_mailreport.txt"
-
-       # build json file
-       #
-       if [ "${action}" != "json" ] && { "${ban_ipt4_savecmd}" | grep -q " ${ban_chain} " || "${ban_ipt6_savecmd}" | grep -q " ${ban_chain} "; }; then
-               : >"${report_json}"
-               printf "%s\n" "{" >>"${report_json}"
-               printf "%s\n" '"ipsets": {' >>"${report_json}"
-               for src in ${ban_localsources} ${ban_sources} ${ban_extrasources}; do
-                       if printf "%s" "${ban_extrasources}" | grep -q "${src}"; then
-                               set_type="n/a"
-                       else
-                               if printf "%s\n" "${ban_settype_src}" | grep -q "${src}"; then
-                                       set_type="src"
-                               elif printf "%s\n" "${ban_settype_dst}" | grep -q "${src}"; then
-                                       set_type="dst"
-                               elif printf "%s\n" "${ban_settype_all}" | grep -q "${src}"; then
-                                       set_type="src+dst"
-                               else
-                                       set_type="${ban_global_settype}"
-                               fi
-                       fi
-                       if [ "${src}" = "maclist" ]; then
-                               src_list="$("${ban_ipset_cmd}" -q list "${src}")"
-                               if [ -n "${src_list}" ]; then
-                                       cnt="$(printf "%s" "${src_list}" | awk '/^Number of entries:/{print $4}')"
-                                       cnt_acc="$(printf "%s" "${src_list}" | grep -cE "packets [1-9]+")"
-                                       cnt_acc_sum=$((cnt_acc_sum + cnt_acc))
-                                       cnt_mac_sum="${cnt}"
-                                       cnt_sum=$((cnt_sum + cnt))
-                                       {
-                                               if [ "${cnt_set_sum}" != "0" ]; then
-                                                       printf "%s\n" ","
-                                               fi
-                                               printf "\t%s\n" "\"${src}\": {"
-                                               printf "\t\t%s\n" "\"type\": \"${set_type}\","
-                                               printf "\t\t%s\n" "\"count\": \"${cnt}\","
-                                               printf "\t\t%s\n" '"count_ip": "0",'
-                                               printf "\t\t%s\n" '"count_cidr": "0",'
-                                               printf "\t\t%s\n" "\"count_mac\": \"${cnt}\","
-                                               printf "\t\t%s" "\"count_acc\": \"${cnt_acc}\""
-                                               printf ",\n\t\t%s" '"member_acc": ['
-                                               printf "%s" "${src_list}" | awk 'match($0,/ packets [1-9]+/){printf "%s %s\n",$1,substr($0,RSTART+9,RLENGTH-9)}' |
-                                                       awk 'BEGIN{i=0};{i=i+1;if(i==1){printf "{\n\t\t\t\"member\": \"%s\",\n\t\t\t\"packets\": \"%s\"\n\t\t}",$1,$2}
-                                                               else{printf ",\n\t\t{\n\t\t\t\"member\": \"%s\",\n\t\t\t\"packets\": \"%s\"\n\t\t}",$1,$2}}'
-                                               printf "%s\n" "]"
-                                               printf "\t%s" "}"
-                                       } >>"${report_json}"
-                                       cnt_set_sum=$((cnt_set_sum + 1))
-                               fi
-                       else
-                               for proto in "4" "6"; do
-                                       src_list="$("${ban_ipset_cmd}" -q list "${src}_${proto}")"
-                                       if [ -n "${src_list}" ]; then
-                                               cnt="$(printf "%s\n" "${src_list}" | awk '/^Number of entries:/{print $4}')"
-                                               cnt_cidr="$(printf "%s\n" "${src_list}" | grep -cE "/[0-9]{1,3} ")"
-                                               cnt_ip=$((cnt - cnt_cidr - cnt_mac))
-                                               cnt_acc="$(printf "%s\n" "${src_list}" | grep -cE "packets [1-9]+")"
-                                               cnt_cidr_sum=$((cnt_cidr_sum + cnt_cidr))
-                                               cnt_ip_sum=$((cnt_ip_sum + cnt_ip))
-                                               cnt_acc_sum=$((cnt_acc_sum + cnt_acc))
-                                               cnt_sum=$((cnt_sum + cnt))
-                                               {
-                                                       if [ "${cnt_set_sum}" != "0" ]; then
-                                                               printf "%s\n" ","
-                                                       fi
-                                                       printf "\t%s\n" "\"${src}_${proto}\": {"
-                                                       printf "\t\t%s\n" "\"type\": \"${set_type}\","
-                                                       printf "\t\t%s\n" "\"count\": \"${cnt}\","
-                                                       printf "\t\t%s\n" "\"count_ip\": \"${cnt_ip}\","
-                                                       printf "\t\t%s\n" "\"count_cidr\": \"${cnt_cidr}\","
-                                                       printf "\t\t%s\n" '"count_mac": "0",'
-                                                       printf "\t\t%s" "\"count_acc\": \"${cnt_acc}\""
-                                                       printf ",\n\t\t%s" '"member_acc": ['
-                                                       printf "%s" "${src_list}" | awk 'match($0,/ packets [1-9]+/){printf "%s %s\n",$1,substr($0,RSTART+9,RLENGTH-9)}' |
-                                                               awk 'BEGIN{i=0};{i=i+1;if(i==1){printf "{\n\t\t\t\"member\": \"%s\",\n\t\t\t\"packets\": \"%s\"\n\t\t}",$1,$2}
-                                                                       else{printf ",\n\t\t{\n\t\t\t\"member\": \"%s\",\n\t\t\t\"packets\": \"%s\"\n\t\t}",$1,$2}}'
-                                                       printf "%s\n" "]"
-                                                       printf "\t%s" "}"
-                                               } >>"${report_json}"
-                                               cnt_set_sum=$((cnt_set_sum + 1))
-                                       fi
-                               done
-                       fi
-               done
-               {
-                       printf "\n%s" "}"
-                       printf ",\n%s\n" "\"timestamp\": \"$(date "+%d.%m.%Y %H:%M:%S")\","
-                       printf "%s\n" "\"cnt_set_sum\": \"${cnt_set_sum}\","
-                       printf "%s\n" "\"cnt_ip_sum\": \"${cnt_ip_sum}\","
-                       printf "%s\n" "\"cnt_cidr_sum\": \"${cnt_cidr_sum}\","
-                       printf "%s\n" "\"cnt_mac_sum\": \"${cnt_mac_sum}\","
-                       printf "%s\n" "\"cnt_sum\": \"${cnt_sum}\","
-                       printf "%s\n" "\"cnt_acc_sum\": \"${cnt_acc_sum}\""
-                       printf "%s\n" "}"
-               } >>"${report_json}"
-       fi
-
-       # output preparation
-       #
-       if [ -s "${report_json}" ] && { [ "${action}" = "cli" ] || [ "${action}" = "mail" ]; }; then
-               : >"${report_txt}"
-               json_init
-               if json_load_file "${report_json}" >/dev/null 2>&1; then
-                       json_get_var jsnval1 "timestamp" >/dev/null 2>&1
-                       json_get_var jsnval2 "cnt_set_sum" >/dev/null 2>&1
-                       json_get_var jsnval3 "cnt_sum" >/dev/null 2>&1
-                       json_get_var jsnval4 "cnt_ip_sum" >/dev/null 2>&1
-                       json_get_var jsnval5 "cnt_cidr_sum" >/dev/null 2>&1
-                       json_get_var jsnval6 "cnt_mac_sum" >/dev/null 2>&1
-                       json_get_var jsnval7 "cnt_acc_sum" >/dev/null 2>&1
-                       {
-                               printf "%s\n%s\n%s\n" ":::" "::: report on all banIP related IPSets" ":::"
-                               printf "  + %s\n" "Report timestamp           ::: ${jsnval1:-"-"}"
-                               printf "  + %s\n" "Number of all IPSets       ::: ${jsnval2:-"0"}"
-                               printf "  + %s\n" "Number of all entries      ::: ${jsnval3:-"0"}"
-                               printf "  + %s\n" "Number of IP entries       ::: ${jsnval4:-"0"}"
-                               printf "  + %s\n" "Number of CIDR entries     ::: ${jsnval5:-"0"}"
-                               printf "  + %s\n" "Number of MAC entries      ::: ${jsnval6:-"0"}"
-                               printf "  + %s\n" "Number of accessed entries ::: ${jsnval7:-"0"}"
-                       } >>"${report_txt}"
-
-                       json_select "ipsets"
-                       json_get_keys ipsetlist
-                       if [ -n "${ipsetlist}" ]; then
-                               {
-                                       printf "%s\n%s\n%s\n" ":::" "::: IPSet details" ":::"
-                                       printf "%-25s%-12s%-11s%-10s%-10s%-10s%-10s%s\n" "    Name" "Type" "Count" "Cnt_IP" "Cnt_CIDR" "Cnt_MAC" "Cnt_ACC" "Entry details (Entry/Count)"
-                                       printf "%s\n" "    --------------------------------------------------------------------------------------------------------------------"
-                               } >>"${report_txt}"
-                       fi
-                       for ipset in ${ipsetlist}; do
-                               set_info="${ipset}"
-                               acc_info=""
-                               json_select "${ipset}"
-                               json_get_keys detaillist
-                               for detail in ${detaillist}; do
-                                       if [ "${detail}" != "member_acc" ]; then
-                                               json_get_var jsnval "${detail}" >/dev/null 2>&1
-                                               set_info="${set_info} ${jsnval}"
-                                       elif [ "${detail}" = "member_acc" ]; then
-                                               index=1
-                                               json_select "${detail}"
-                                               while json_get_type type "${index}" && [ "${type}" = "object" ]; do
-                                                       json_get_values jsnvals "${index}" >/dev/null 2>&1
-                                                       acc_info="${acc_info} ${jsnvals}"
-                                                       index=$((index + 1))
-                                               done
-                                               json_select ".."
-                                       fi
-                               done
-                               {
-                                       printf "    %-21s%-12s%-11s%-10s%-10s%-10s%s\n" ${set_info}
-                                       if [ -n "${acc_info}" ]; then
-                                               printf "                                                                                        %-25s%s\n" ${acc_info}
-                                       fi
-                                       printf "%s\n" "    --------------------------------------------------------------------------------------------------------------------"
-                               } >>"${report_txt}"
-                               json_select ".."
-                       done
-                       content="$(cat "${report_txt}" 2>/dev/null)"
-                       rm -f "${report_txt}"
-               fi
-       fi
-
-       # report output
-       #
-       if [ "${action}" = "cli" ]; then
-               printf "%s\n" "${content}"
-       elif [ "${action}" = "json" ]; then
-               cat "${ban_reportdir}/ban_report.json"
-       elif [ "${action}" = "mail" ] && [ "${ban_mail_enabled}" = "1" ] && [ -x "${ban_mailservice}" ]; then
-               ("${ban_mailservice}" "${content}" >/dev/null 2>&1) &
-               bg_pid="${!}"
-       fi
-       f_log "debug" "f_report ::: action: ${action}, report_json: ${report_json}, report_txt: ${report_txt}, bg_pid: ${bg_pid:-"-"}"
-}
-
-# update runtime information
-#
-f_jsnup() {
-       local memory entry runtime cnt_info status="${1:-"enabled"}"
-
-       if [ "${status}" = "enabled" ] || [ "${status}" = "error" ]; then
-               ban_endtime="$(date "+%s")"
-               cnt_info="${ban_setcnt} IPSets with ${ban_cnt} IPs/Prefixes"
-               memory="$(awk '/^MemTotal|^MemFree|^MemAvailable/{ORS="/"; print int($2/1000)}' "/proc/meminfo" 2>/dev/null | awk '{print substr($0,1,length($0)-1)}')"
-               if [ "$(((ban_endtime - ban_starttime) / 60))" -lt "60" ]; then
-                       runtime="${ban_action}, $(((ban_endtime - ban_starttime) / 60))m $(((ban_endtime - ban_starttime) % 60))s, ${memory:-0}, $(date "+%d.%m.%Y %H:%M:%S")"
-               else
-                       runtime="${ban_action}, n/a, ${memory:-0}, $(date "+%d.%m.%Y %H:%M:%S")"
-               fi
-       fi
-
-       : >"${ban_rtfile}"
-       json_init
-       json_load_file "${ban_rtfile}" >/dev/null 2>&1
-       json_add_string "status" "${status}"
-       json_add_string "version" "${ban_ver}"
-       json_add_string "ipset_info" "${cnt_info:-"-"}"
-       json_add_array "active_sources"
-       if [ "${status}" = "running" ] || [ "${status}" = "error" ]; then
-               json_add_object
-               json_add_string "source" "-"
-               json_close_object
-       else
-               for entry in ${ban_sources}; do
-                       json_add_object
-                       json_add_string "source" "${entry}"
-                       json_close_object
-               done
-       fi
-       json_close_array
-       json_add_array "active_devs"
-       for entry in ${ban_devs}; do
-               json_add_object
-               json_add_string "dev" "${entry}"
-               json_close_object
-       done
-       json_close_array
-       json_add_array "active_ifaces"
-       for entry in ${ban_ifaces}; do
-               json_add_object
-               json_add_string "iface" "${entry}"
-               json_close_object
-       done
-       json_close_array
-       json_add_array "active_logterms"
-       for entry in ${ban_logterms}; do
-               json_add_object
-               json_add_string "term" "${entry}"
-               json_close_object
-       done
-       json_close_array
-       json_add_array "active_subnets"
-       for entry in ${ban_subnets}; do
-               json_add_object
-               json_add_string "subnet" "${entry}"
-               json_close_object
-       done
-       json_close_array
-       json_add_string "run_infos" "settype: ${ban_global_settype}, backup_dir: ${ban_backupdir}, report_dir: ${ban_reportdir}"
-       json_add_string "run_flags" "protocols (4/6): $(f_char ${ban_proto4_enabled})/$(f_char ${ban_proto6_enabled}), log (src/dst): $(f_char ${ban_logsrc_enabled})/$(f_char ${ban_logdst_enabled}), monitor: $(f_char ${ban_monitor_enabled}), mail: $(f_char ${ban_mail_enabled}), whitelist only: $(f_char ${ban_whitelistonly})"
-       json_add_string "last_run" "${runtime:-"-"}"
-       json_add_string "system" "${ban_sysver}"
-       json_dump >"${ban_rtfile}"
-
-       if [ "${ban_mail_enabled}" = "1" ] && [ -x "${ban_mailservice}" ] && { [ "${status}" = "error" ] ||
-               { [ "${status}" = "enabled" ] && { [ -z "${ban_mailactions}" ] || printf "%s\n" "${ban_mailactions}" | grep -q "${ban_action}"; }; }; }; then
-               ("${ban_mailservice}" "${ban_ver}" >/dev/null 2>&1) &
-               bg_pid="${!}"
-       fi
-       f_log "debug" "f_jsnup ::: status: ${status:-"-"}, action: ${ban_action}, mail_enabled: ${ban_mail_enabled}, mail_actions: ${ban_mailactions}, mail_service: ${ban_mailservice}, mail_pid: ${bg_pid:-"-"}"
-}
-
-# source required system libraries
-#
-if [ -r "/lib/functions.sh" ] && [ -r "/lib/functions/network.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]; then
-       . "/lib/functions.sh"
-       . "/lib/functions/network.sh"
-       . "/usr/share/libubox/jshn.sh"
-else
-       f_log "err" "system libraries not found"
-fi
-
-if [ "${ban_action}" = "suspend" ] || [ "${ban_action}" = "resume" ] ||
-       [ "${ban_action}" = "report" ] || [ "${ban_action}" = "query" ]; then
-       json_init
-       json_load_file "${ban_rtfile}" >/dev/null 2>&1
-       json_get_var ban_status "status"
-fi
-
-# handle different banIP actions
-#
-f_load
-case "${ban_action}" in
-       "stop")
-               f_bgsrv "stop"
-               f_ipset "destroy"
-               f_jsnup "stopped"
-               f_rmbckp
-               ;;
-       "restart")
-               f_ipset "destroy"
-               f_rmbckp
-               f_env
-               f_main
-               ;;
-       "suspend")
-               if [ "${ban_status}" = "enabled" ] && [ "${ban_whitelistonly}" = "0" ]; then
-                       f_bgsrv "stop"
-                       f_jsnup "running"
-                       f_ipset "suspend"
-                       f_jsnup "paused"
-               fi
-               f_rmtmp
-               ;;
-       "resume")
-               if [ "${ban_status}" = "paused" ] && [ "${ban_whitelistonly}" = "0" ]; then
-                       f_env
-                       f_main
-               else
-                       f_rmtmp
-               fi
-               ;;
-       "query")
-               if [ "${ban_status}" = "enabled" ]; then
-                       f_query "${2}"
-               fi
-               ;;
-       "report")
-               if [ "${ban_status}" = "enabled" ] || [ "${2}" = "json" ]; then
-                       f_report "${2}"
-               fi
-               ;;
-       "start" | "reload" | "refresh")
-               f_env
-               f_main
-               ;;
-esac
diff --git a/net/banip/files/banip.sources b/net/banip/files/banip.sources
deleted file mode 100644 (file)
index 5597c06..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-{
-       "asn": {
-               "url_4": "https://asn.ipinfo.app/api/text/list/",
-               "url_6": "https://asn.ipinfo.app/api/text/list/",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add asn_4 \"$1}",
-               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add asn_6 \"$1}",
-               "focus": "ASN blocks",
-               "descurl": "https://asn.ipinfo.app"
-       },
-       "bogon": {
-               "url_4": "https://www.team-cymru.org/Services/Bogons/fullbogons-ipv4.txt",
-               "url_6": "https://www.team-cymru.org/Services/Bogons/fullbogons-ipv6.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add bogon_4 \"$1}",
-               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add bogon_6 \"$1}",
-               "focus": "Bogon prefixes",
-               "descurl": "https://team-cymru.com"
-       },
-       "country": {
-               "url_4": "http://www.ipdeny.com/ipblocks/data/aggregated/",
-               "url_6": "http://www.ipdeny.com/ipv6/ipaddresses/aggregated/",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add country_4 \"$1}",
-               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add country_6 \"$1}",
-               "focus": "Country blocks",
-               "descurl": "http://www.ipdeny.com/ipblocks"
-       },
-       "darklist": {
-               "url_4": "https://darklist.de/raw.php",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add darklist_4 \"$1}",
-               "focus": "Blocks suspicious attacker IPs",
-               "descurl": "https://darklist.de"
-       },
-       "debl": {
-               "url_4": "https://www.blocklist.de/downloads/export-ips_all.txt",
-               "url_6": "https://www.blocklist.de/downloads/export-ips_all.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add debl_4 \"$1}",
-               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add debl_6 \"$1}",
-               "focus": "Fail2ban IP blacklist",
-               "descurl": "https://www.blocklist.de"
-       },
-       "doh": {
-               "url_4": "https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv4.txt",
-               "url_6": "https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv6.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add doh_4 \"$1}",
-               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add doh_6 \"$1}",
-               "focus": "Public DoH-Provider",
-               "descurl": "https://github.com/dibdot/DoH-IP-blocklists"
-       },
-       "drop": {
-               "url_4": "https://www.spamhaus.org/drop/drop.txt",
-               "url_6": "https://www.spamhaus.org/drop/dropv6.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add drop_4 \"$1}",
-               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add drop_6 \"$1}",
-               "focus": "Spamhaus drop compilation",
-               "descurl": "https://www.spamhaus.org"
-       },
-       "dshield": {
-               "url_4": "https://feeds.dshield.org/block.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add dshield_4 \"$1 \"/\"$3}",
-               "focus": "Dshield IP blocklist",
-               "descurl": "https://www.dshield.org"
-       },
-       "edrop": {
-               "url_4": "https://www.spamhaus.org/drop/edrop.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add edrop_4 \"$1}",
-               "focus": "Spamhaus edrop compilation",
-               "descurl": "https://www.spamhaus.org"
-       },
-       "feodo": {
-               "url_4": "https://feodotracker.abuse.ch/downloads/ipblocklist.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add feodo_4 \"$1}",
-               "focus": "Feodo Tracker",
-               "descurl": "https://feodotracker.abuse.ch"
-       },
-       "firehol1": {
-               "url_4": "https://iplists.firehol.org/files/firehol_level1.netset",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add firehol1_4 \"$1}",
-               "focus": "Firehol Level 1 compilation",
-               "descurl": "https://iplists.firehol.org/?ipset=firehol_level1"
-       },
-       "firehol2": {
-               "url_4": "https://iplists.firehol.org/files/firehol_level2.netset",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add firehol2_4 \"$1}",
-               "focus": "Firehol Level 2 compilation",
-               "descurl": "https://iplists.firehol.org/?ipset=firehol_level2"
-       },
-       "firehol3": {
-               "url_4": "https://iplists.firehol.org/files/firehol_level3.netset",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add firehol3_4 \"$1}",
-               "focus": "Firehol Level 3 compilation",
-               "descurl": "https://iplists.firehol.org/?ipset=firehol_level3"
-       },
-       "firehol4": {
-               "url_4": "https://iplists.firehol.org/files/firehol_level4.netset",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add firehol4_4 \"$1}",
-               "focus": "Firehol Level 4 compilation",
-               "descurl": "https://iplists.firehol.org/?ipset=firehol_level4"
-       },
-       "greensnow": {
-               "url_4": "https://blocklist.greensnow.co/greensnow.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add greensnow_4 \"$1}",
-               "focus": "Blocks suspicious server IPs",
-               "descurl": "https://greensnow.co"
-       },
-       "iblockads": {
-               "url_4": "https://list.iblocklist.com/?list=dgxtneitpuvgqqcpfulq&fileformat=cidr&archiveformat=gz",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add iblockads_4 \"$1}",
-               "focus": "Advertising blocklist",
-               "descurl": "https://www.iblocklist.com",
-               "comp": "gz"
-       },
-       "iblockspy": {
-               "url_4": "https://list.iblocklist.com/?list=llvtlsjyoyiczbkjsxpf&fileformat=cidr&archiveformat=gz",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add iblockspy_4 \"$1}",
-               "focus": "Malicious spyware blocklist",
-               "descurl": "https://www.iblocklist.com",
-               "comp": "gz"
-       },
-       "myip": {
-               "url_4": "https://myip.ms/files/blacklist/general/latest_blacklist.txt",
-               "url_6": "https://myip.ms/files/blacklist/general/latest_blacklist.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add myip_4 \"$1}",
-               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add myip_6 \"$1}",
-               "focus": "Myip Live IP blacklist",
-               "descurl": "https://myip.ms"
-       },
-       "nixspam": {
-               "url_4": "http://www.dnsbl.manitu.net/download/nixspam-ip.dump.gz",
-               "rule_4": "/(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add nixspam_4 \"$2}",
-               "focus": "iX spam protection",
-               "descurl": "http://www.nixspam.org",
-               "comp": "gz"
-       },
-       "proxy": {
-               "url_4": "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/proxylists.ipset",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add proxy_4 \"$1}",
-               "focus": "Firehol list of open proxies",
-               "descurl": "https://iplists.firehol.org/?ipset=proxylists"
-       },
-       "sslbl": {
-               "url_4": "https://sslbl.abuse.ch/blacklist/sslipblacklist.csv",
-               "rule_4": "BEGIN{FS=\",\"}/(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)/{print \"add sslbl_4 \"$2}",
-               "focus": "SSL botnet IP blacklist",
-               "descurl": "https://sslbl.abuse.ch"
-       },
-       "talos": {
-               "url_4": "https://www.talosintelligence.com/documents/ip-blacklist",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add talos_4 \"$1}",
-               "focus": "Cisco Talos IP Blacklist",
-               "descurl": "https://talosintelligence.com/reputation_center"
-       },
-       "threat": {
-               "url_4": "https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add threat_4 \"$1}",
-               "focus": "Emerging Threats",
-               "descurl": "https://rules.emergingthreats.net"
-       },
-       "tor": {
-               "url_4": "https://lists.fissionrelays.net/tor/exits-ipv4.txt",
-               "url_6": "https://lists.fissionrelays.net/tor/exits-ipv6.txt",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add tor_4 \"$1}",
-               "rule_6": "/^(([0-9A-f]{0,4}:){1,7}[0-9A-f]{0,4}:?(\\/(1?[0-2][0-8]|[0-9][0-9]))?)([[:space:]]|$)/{print \"add tor_6 \"$1}",
-               "focus": "Tor exit nodes",
-               "descurl": "https://fissionrelays.net/lists"
-       },
-       "uceprotect1": {
-               "url_4": "http://wget-mirrors.uceprotect.net/rbldnsd-all/dnsbl-1.uceprotect.net.gz",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)$/{print \"add uceprotect1_4 \"$1}",
-               "focus": "Spam protection level 1",
-               "descurl": "http://www.uceprotect.net/en/index.php",
-               "comp": "gz"
-       },
-       "uceprotect2": {
-               "url_4": "http://wget-mirrors.uceprotect.net/rbldnsd-all/dnsbl-2.uceprotect.net.gz",
-               "rule_4": "BEGIN{IGNORECASE=1}/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]+NET[[:space:]]+)/{print \"add uceprotect2_4 \"$1}",
-               "focus": "Spam protection level 2",
-               "descurl": "http://www.uceprotect.net/en/index.php",
-               "comp": "gz"
-       },
-       "voip": {
-               "url_4": "http://www.voipbl.org/update/",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add voip_4 \"$1}",
-               "focus": "VoIP fraud blocklist",
-               "descurl": "http://www.voipbl.org"
-       },
-       "yoyo": {
-               "url_4": "https://pgl.yoyo.org/adservers/iplist.php?ipformat=plain&showintro=0&mimetype=plaintext",
-               "rule_4": "/^(([0-9]{1,3}\\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\\/(1?[0-9]|2?[0-9]|3?[0-2]))?)([[:space:]]|$)/{print \"add yoyo_4 \"$1}",
-               "focus": "Ad protection blacklist",
-               "descurl": "https://pgl.yoyo.org/adservers/"
-       }
-}
diff --git a/net/banip/files/banip.tpl b/net/banip/files/banip.tpl
new file mode 100644 (file)
index 0000000..9a614d0
--- /dev/null
@@ -0,0 +1,24 @@
+# banIP mail template/include
+# Copyright (c) 2020-2023 Dirk Brenken (dev@brenken.org)
+# This is free software, licensed under the GNU General Public License v3.
+
+# info preparation
+#
+local banip_info report_info log_info system_info mail_text
+
+banip_info="$(/etc/init.d/banip status 2>/dev/null)"
+report_info="$(cat ${ban_reportdir}/ban_report.txt 2>/dev/null)"
+log_info="$("${ban_logreadcmd}" -l 100 -e "banIP_" 2>/dev/null | awk '{NR=1;max=120;if(length($0)>max+1)while($0){if(NR==1){print substr($0,1,max)}else{print substr($0,1,max)}{$0=substr($0,max+1);NR=NR+1}}else print}')"
+system_info="$(
+       strings /etc/banner 2>/dev/null
+       ubus call system board | awk 'BEGIN{FS="[{}\"]"}{if($2=="kernel"||$2=="hostname"||$2=="system"||$2=="model"||$2=="description")printf "  + %-12s: %s\n",$2,$4}'
+)"
+
+# mail body
+#
+mail_text="$(printf "%s\n" "<html><body><pre style='display:block;font-family:monospace;font-size:1rem;padding:20;background-color:#f3eee5;white-space:pre'>")"
+mail_text="$(printf "%s\n" "${mail_text}\n<strong>++\n++ System Information ++\n++</strong>\n${system_info:-"-"}")"
+mail_text="$(printf "%s\n" "${mail_text}\n\n<strong>++\n++ banIP Status ++\n++</strong>\n${banip_info:-"-"}")"
+mail_text="$(printf "%s\n" "${mail_text}\n\n<strong>++\n++ banIP Report ++\n++</strong>\n${report_info:-"-"}")"
+mail_text="$(printf "%s\n" "${mail_text}\n\n<strong>++\n++ Logfile Information ++\n++</strong>\n${log_info}")"
+mail_text="$(printf "%s\n" "${mail_text}</pre></body></html>")"
diff --git a/net/banip/files/banip.whitelist b/net/banip/files/banip.whitelist
deleted file mode 100644 (file)
index e69de29..0000000