pbr: initial commit 20010/head
authorStan Grishin <stangri@melmac.ca>
Mon, 31 Oct 2022 23:08:01 +0000 (23:08 +0000)
committerStan Grishin <stangri@melmac.ca>
Sat, 3 Dec 2022 02:38:02 +0000 (02:38 +0000)
* The makefile produces the nft and iptables capable `pbr` package
  and the `pbr-iptables` package for legacy setups
* This replaces `vpnbypass` and `vpn-policy-routing` packages
* I'm soliciting feedback on this package and my intention is to
  update the version to 1.0.0 before this is merged, but I need the
  feedback on this and luci-app-pbr before then.

Signed-off-by: Stan Grishin <stangri@melmac.ca>
(cherry picked from commit 47eca64cb82a5459668fc14df9422e365c8343b6)

33 files changed:
net/pbr/Makefile [new file with mode: 0644]
net/pbr/files/README.md [new file with mode: 0644]
net/pbr/files/etc/config/pbr [new file with mode: 0644]
net/pbr/files/etc/config/pbr.iptables [new file with mode: 0644]
net/pbr/files/etc/hotplug.d/firewall/70-pbr [new file with mode: 0755]
net/pbr/files/etc/hotplug.d/iface/70-pbr [new file with mode: 0644]
net/pbr/files/etc/init.d/pbr.init [new file with mode: 0755]
net/pbr/files/etc/uci-defaults/90-pbr [new file with mode: 0644]
net/pbr/files/etc/uci-defaults/91-pbr [new file with mode: 0644]
net/pbr/files/usr/share/nftables.d/chain-post/mangle_forward/30-pbr.nft [new file with mode: 0644]
net/pbr/files/usr/share/nftables.d/chain-post/mangle_input/30-pbr.nft [new file with mode: 0644]
net/pbr/files/usr/share/nftables.d/chain-post/mangle_output/30-pbr.nft [new file with mode: 0644]
net/pbr/files/usr/share/nftables.d/chain-post/mangle_postrouting/30-pbr.nft [new file with mode: 0644]
net/pbr/files/usr/share/nftables.d/chain-post/mangle_prerouting/30-pbr.nft [new file with mode: 0644]
net/pbr/files/usr/share/nftables.d/table-post/30-pbr.nft [new file with mode: 0644]
net/pbr/files/usr/share/pbr/pbr.firewall.include [new file with mode: 0644]
net/pbr/files/usr/share/pbr/pbr.user.aws [new file with mode: 0644]
net/pbr/files/usr/share/pbr/pbr.user.netflix [new file with mode: 0644]
net/pbr/test.sh [new file with mode: 0644]
net/vpn-policy-routing/Makefile [deleted file]
net/vpn-policy-routing/files/README.md [deleted file]
net/vpn-policy-routing/files/vpn-policy-routing.aws.user [deleted file]
net/vpn-policy-routing/files/vpn-policy-routing.config [deleted file]
net/vpn-policy-routing/files/vpn-policy-routing.firewall.hotplug [deleted file]
net/vpn-policy-routing/files/vpn-policy-routing.init [deleted file]
net/vpn-policy-routing/files/vpn-policy-routing.netflix.user [deleted file]
net/vpn-policy-routing/test.sh [deleted file]
net/vpnbypass/Makefile [deleted file]
net/vpnbypass/files/README.md [deleted file]
net/vpnbypass/files/vpnbypass.config [deleted file]
net/vpnbypass/files/vpnbypass.hotplug [deleted file]
net/vpnbypass/files/vpnbypass.init [deleted file]
net/vpnbypass/test.sh [deleted file]

diff --git a/net/pbr/Makefile b/net/pbr/Makefile
new file mode 100644 (file)
index 0000000..0f2842e
--- /dev/null
@@ -0,0 +1,201 @@
+# Copyright 2017-2022 Stan Grishin (stangri@melmac.ca)
+# This is free software, licensed under the GNU General Public License v3.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=pbr
+PKG_VERSION:=1.0.0
+PKG_RELEASE:=1
+PKG_LICENSE:=GPL-3.0-or-later
+PKG_MAINTAINER:=Stan Grishin <stangri@melmac.ca>
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/pbr/default
+  SECTION:=net
+  CATEGORY:=Network
+  SUBMENU:=VPN
+  PROVIDES:=pbr
+  TITLE:=Policy Based Routing Service
+  URL:=https://docs.openwrt.melmac.net/pbr/
+  DEPENDS:=+ip-full +jshn +jsonfilter +resolveip
+  CONFLICTS:=vpnbypass vpn-policy-routing
+  PROVIDES:=vpnbypass vpn-policy-routing
+  PKGARCH:=all
+endef
+
+define Package/pbr
+$(call Package/pbr/default)
+       TITLE+= with nft/nft set support
+       DEPENDS+=+firewall4 +kmod-nft-core +kmod-nft-nat +nftables-json
+endef
+
+define Package/pbr-iptables
+$(call Package/pbr/default)
+       TITLE+= with iptables/ipset support
+       DEPENDS+=+ipset +iptables +kmod-ipt-ipset +iptables-mod-ipopt
+endef
+
+define Package/pbr-netifd
+$(call Package/pbr/default)
+       TITLE+= with netifd support
+endef
+
+define Package/pbr/description
+This service enables policy-based routing for WAN interfaces and various VPN tunnels.
+This version supports OpenWrt with both fw3/ipset/iptables and fw4/nft.
+endef
+
+define Package/pbr-iptables/description
+This service enables policy-based routing for WAN interfaces and various VPN tunnels.
+This version supports OpenWrt with fw3/ipset/iptables.
+endef
+
+define Package/pbr-netifd/description
+This service enables policy-based routing for WAN interfaces and various VPN tunnels.
+This version supports OpenWrt with both fw3/ipset/iptables and fw4/nft.
+This version uses OpenWrt native netifd/tables to set up interfaces. This is WIP.
+endef
+
+define Package/pbr/conffiles
+/etc/config/pbr
+endef
+
+Package/pbr-iptables/conffiles = $(Package/pbr/conffiles)
+Package/pbr-netifd/conffiles = $(Package/pbr/conffiles)
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/pbr/default/install
+       $(INSTALL_DIR) $(1)/etc/init.d
+       $(INSTALL_BIN) ./files/etc/init.d/pbr.init $(1)/etc/init.d/pbr
+       $(SED) "s|^\(readonly PKG_VERSION\).*|\1='$(PKG_VERSION)-$(PKG_RELEASE)'|" $(1)/etc/init.d/pbr
+       $(INSTALL_DIR) $(1)/etc/hotplug.d/firewall
+       $(INSTALL_DIR) $(1)/etc/hotplug.d/iface
+       $(INSTALL_DATA) ./files/etc/hotplug.d/iface/70-pbr $(1)/etc/hotplug.d/iface/70-pbr
+       $(INSTALL_DIR) $(1)/etc/uci-defaults
+       $(INSTALL_BIN)  ./files/etc/uci-defaults/90-pbr $(1)/etc/uci-defaults/90-pbr
+       $(INSTALL_DIR) $(1)/usr/share/pbr
+       $(INSTALL_DATA) ./files/usr/share/pbr/pbr.firewall.include $(1)/usr/share/pbr/pbr.firewall.include
+       $(INSTALL_DATA) ./files/usr/share/pbr/pbr.user.aws $(1)/usr/share/pbr/pbr.user.aws
+       $(INSTALL_DATA) ./files/usr/share/pbr/pbr.user.netflix $(1)/usr/share/pbr/pbr.user.netflix
+endef
+
+define Package/pbr/install
+$(call Package/pbr/default/install,$(1))
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_CONF) ./files/etc/config/pbr $(1)/etc/config/pbr
+       $(INSTALL_DIR) $(1)/usr/share/nftables.d
+       $(CP) ./files/usr/share/nftables.d/* $(1)/usr/share/nftables.d/
+endef
+
+define Package/pbr-iptables/install
+$(call Package/pbr/default/install,$(1))
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_CONF) ./files/etc/config/pbr.iptables $(1)/etc/config/pbr
+endef
+
+define Package/pbr-netifd/install
+$(call Package/pbr/default/install,$(1))
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_CONF) ./files/etc/config/pbr $(1)/etc/config/pbr
+       $(INSTALL_DIR) $(1)/etc/uci-defaults
+       $(INSTALL_BIN)  ./files/etc/uci-defaults/91-pbr $(1)/etc/uci-defaults/91-pbr
+endef
+
+define Package/pbr/postinst
+       #!/bin/sh
+       # check if we are on real system
+       if [ -z "$${IPKG_INSTROOT}" ]; then
+               chmod -x /etc/init.d/pbr || true
+               fw4 -q reload || true
+               chmod +x /etc/init.d/pbr || true
+               echo -n "Installing rc.d symlink for pbr... "
+               /etc/init.d/pbr enable && echo "OK" || echo "FAIL"
+       fi
+       exit 0
+endef
+
+define Package/pbr/prerm
+       #!/bin/sh
+       # check if we are on real system
+       if [ -z "$${IPKG_INSTROOT}" ]; then
+               uci -q delete firewall.pbr || true
+               echo "Stopping pbr service... "
+               /etc/init.d/pbr stop || true
+               echo -n "Removing rc.d symlink for pbr... "
+               /etc/init.d/pbr disable && echo "OK" || echo "FAIL"
+       fi
+       exit 0
+endef
+
+define Package/pbr/postrm
+       #!/bin/sh
+       # check if we are on real system
+       if [ -z "$${IPKG_INSTROOT}" ]; then
+               fw4 -q reload || true
+       fi
+       exit 0
+endef
+
+define Package/pbr-iptables/postinst
+       #!/bin/sh
+       # check if we are on real system
+       if [ -z "$${IPKG_INSTROOT}" ]; then
+               echo -n "Installing rc.d symlink for pbr... "
+               /etc/init.d/pbr enable && echo "OK" || echo "FAIL"
+       fi
+       exit 0
+endef
+
+define Package/pbr-iptables/prerm
+       #!/bin/sh
+       # check if we are on real system
+       if [ -z "$${IPKG_INSTROOT}" ]; then
+               uci -q delete firewall.pbr || true
+               echo "Stopping pbr service... "
+               /etc/init.d/pbr stop || true
+               echo -n "Removing rc.d symlink for pbr... "
+               /etc/init.d/pbr disable && echo "OK" || echo "FAIL"
+       fi
+       exit 0
+endef
+
+define Package/pbr-netifd/postinst
+       #!/bin/sh
+       # check if we are on real system
+       if [ -z "$${IPKG_INSTROOT}" ]; then
+               echo -n "Installing rc.d symlink for pbr... "
+               /etc/init.d/pbr enable && echo "OK" || echo "FAIL"
+       #       echo -n "Installing netifd support for pbr... "
+       #       /etc/init.d/pbr netifd install && echo "OK" || echo "FAIL"
+       #       echo -n "Restarting network... "
+       #       /etc/init.d/network restart && echo "OK" || echo "FAIL"
+       fi
+       exit 0
+endef
+
+define Package/pbr-netifd/prerm
+       #!/bin/sh
+       # check if we are on real system
+       if [ -z "$${IPKG_INSTROOT}" ]; then
+               uci -q delete firewall.pbr || true
+               echo "Stopping pbr service... "
+               /etc/init.d/pbr stop || true
+       #       echo -n "Removing netifd support for pbr... "
+       #       /etc/init.d/pbr netifd remove && echo "OK" || echo "FAIL"
+               echo -n "Removing rc.d symlink for pbr... "
+               /etc/init.d/pbr disable && echo "OK" || echo "FAIL"
+       #       echo -n "Restarting network... "
+       #       /etc/init.d/network restart && echo "OK" || echo "FAIL"
+       fi
+       exit 0
+endef
+
+$(eval $(call BuildPackage,pbr))
+$(eval $(call BuildPackage,pbr-iptables))
+#$(eval $(call BuildPackage,pbr-netifd))
diff --git a/net/pbr/files/README.md b/net/pbr/files/README.md
new file mode 100644 (file)
index 0000000..494a97c
--- /dev/null
@@ -0,0 +1,3 @@
+# README
+
+README is available at [https://docs.openwrt.melmac.net/pbr/](https://docs.openwrt.melmac.net/pbr/).
diff --git a/net/pbr/files/etc/config/pbr b/net/pbr/files/etc/config/pbr
new file mode 100644 (file)
index 0000000..355fac2
--- /dev/null
@@ -0,0 +1,45 @@
+config pbr 'config'
+       option enabled '0'
+       option verbosity '2'
+       option strict_enforcement '1'
+       option resolver_set 'none'
+       option ipv6_enabled '0'
+       list ignored_interface 'vpnserver'
+       list ignored_interface 'wgserver'
+       option boot_timeout '30'
+       option rule_create_option 'add'
+       option procd_reload_delay '1'
+       option webui_show_ignore_target '0'
+       list webui_supported_protocol 'all'
+       list webui_supported_protocol 'tcp'
+       list webui_supported_protocol 'udp'
+       list webui_supported_protocol 'tcp udp'
+       list webui_supported_protocol 'icmp'
+
+config include
+       option path '/usr/share/pbr/pbr.user.aws'
+       option enabled 0
+
+config include
+       option path '/usr/share/pbr/pbr.user.netflix'
+       option enabled 0
+
+config policy
+       option name 'Plex/Emby Local Server'
+       option interface 'wan'
+       option src_port '8096 8920 32400'
+       option enabled '0'
+
+config policy
+       option name 'Plex/Emby Remote Servers'
+       option interface 'wan'
+       option dest_addr 'plex.tv my.plexapp.com emby.media app.emby.media tv.emby.media'
+       option enabled '0'
+
+config policy
+       option name 'WireGuard Server'
+       option interface 'wan'
+       option src_port '51820'
+       option chain 'OUTPUT'
+       option proto 'udp'
+       option enabled '0'
diff --git a/net/pbr/files/etc/config/pbr.iptables b/net/pbr/files/etc/config/pbr.iptables
new file mode 100644 (file)
index 0000000..c627126
--- /dev/null
@@ -0,0 +1,45 @@
+config pbr 'config'
+       option enabled '0'
+       option verbosity '2'
+       option strict_enforcement '1'
+       option resolver_set 'dnsmasq.ipset'
+       option ipv6_enabled '0'
+       list ignored_interface 'vpnserver'
+       list ignored_interface 'wgserver'
+       option boot_timeout '30'
+       option rule_create_option 'add'
+       option procd_reload_delay '1'
+       option webui_show_ignore_target '0'
+       list webui_supported_protocol 'all'
+       list webui_supported_protocol 'tcp'
+       list webui_supported_protocol 'udp'
+       list webui_supported_protocol 'tcp udp'
+       list webui_supported_protocol 'icmp'
+
+config include
+       option path '/usr/share/pbr/pbr.user.aws'
+       option enabled 0
+
+config include
+       option path '/usr/share/pbr/pbr.user.netflix'
+       option enabled 0
+
+config policy
+       option name 'Plex/Emby Local Server'
+       option interface 'wan'
+       option src_port '8096 8920 32400'
+       option enabled '0'
+
+config policy
+       option name 'Plex/Emby Remote Servers'
+       option interface 'wan'
+       option dest_addr 'plex.tv my.plexapp.com emby.media app.emby.media tv.emby.media'
+       option enabled '0'
+
+config policy
+       option name 'WireGuard Server'
+       option interface 'wan'
+       option src_port '51820'
+       option chain 'OUTPUT'
+       option proto 'udp'
+       option enabled '0'
diff --git a/net/pbr/files/etc/hotplug.d/firewall/70-pbr b/net/pbr/files/etc/hotplug.d/firewall/70-pbr
new file mode 100755 (executable)
index 0000000..c129006
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+[ "$ACTION" = "reload" ] ||[ "$ACTION" = "restart" ] || exit 0
+if [ -x /etc/init.d/pbr ] && /etc/init.d/pbr enabled; then
+       logger -t "pbr" "Reloading pbr due to $ACTION of firewall"
+       /etc/init.d/pbr reload
+fi
diff --git a/net/pbr/files/etc/hotplug.d/iface/70-pbr b/net/pbr/files/etc/hotplug.d/iface/70-pbr
new file mode 100644 (file)
index 0000000..172385a
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+# shellcheck disable=SC1091,SC3060
+[ -s /etc/openwrt_release ] && . /etc/openwrt_release
+[ "${DISTRIB_RELEASE//19.07}" = "$DISTRIB_RELEASE" ] && exit 0
+if [ -x /etc/init.d/pbr ] && /etc/init.d/pbr enabled; then
+       logger -t pbr "Reloading pbr $INTERFACE due to $ACTION of $INTERFACE ($DEVICE)"
+       /etc/init.d/pbr reload_interface "$INTERFACE"
+fi
diff --git a/net/pbr/files/etc/init.d/pbr.init b/net/pbr/files/etc/init.d/pbr.init
new file mode 100755 (executable)
index 0000000..cf807f4
--- /dev/null
@@ -0,0 +1,2394 @@
+#!/bin/sh /etc/rc.common
+# Copyright 2020-2022 Stan Grishin (stangri@melmac.ca)
+# shellcheck disable=SC1091,SC2018,SC2019,SC3043,SC3057,SC3060
+
+# sysctl net.ipv4.conf.default.rp_filter=1
+# sysctl net.ipv4.conf.all.rp_filter=1
+
+# shellcheck disable=SC2034
+START=94
+# shellcheck disable=SC2034
+USE_PROCD=1
+
+if type extra_command >/dev/null 2>&1; then
+       extra_command 'status' "Generates output required to troubleshoot routing issues
+               Use '-d' option for more detailed output
+               Use '-p' option to automatically upload data under VPR paste.ee account
+                       WARNING: while paste.ee uploads are unlisted, they are still publicly available
+               List domain names after options to include their lookup in report"
+       extra_command 'version' 'Show version information'
+       extra_command 'on_firewall_reload' '    Run service on firewall reload'
+       extra_command 'on_interface_reload' '   Run service on indicated interface reload'
+else
+# shellcheck disable=SC2034
+       EXTRA_COMMANDS='on_firewall_reload on_interface_reload status version'
+# shellcheck disable=SC2034
+       EXTRA_HELP="    status  Generates output required to troubleshoot routing issues
+               Use '-d' option for more detailed output
+               Use '-p' option to automatically upload data under VPR paste.ee account
+                       WARNING: while paste.ee uploads are unlisted, they are still publicly available
+               List domain names after options to include their lookup in report"
+fi
+
+readonly PKG_VERSION='dev-test'
+readonly packageName='pbr'
+readonly serviceName="$packageName $PKG_VERSION"
+readonly serviceTrapSignals='exit SIGHUP SIGQUIT SIGKILL'
+readonly packageConfigFile="/etc/config/${packageName}"
+readonly nftTempFile="/var/run/${packageName}.nft"
+#readonly nftPermFile="/etc/nftables.d/table-post/30-pbr.nft"
+readonly dnsmasqFile="/var/dnsmasq.d/${packageName}"
+readonly sharedMemoryOutput="/dev/shm/$packageName-output"
+readonly _OK_='\033[0;32m\xe2\x9c\x93\033[0m'
+readonly _FAIL_='\033[0;31m\xe2\x9c\x97\033[0m'
+readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
+readonly __FAIL__='\033[0;31m[\xe2\x9c\x97]\033[0m'
+readonly _ERROR_='\033[0;31mERROR\033[0m'
+readonly _WARNING_='\033[0;33mWARNING\033[0m'
+readonly ip_full='/usr/libexec/ip-full'
+readonly ipTablePrefix='pbr'
+# shellcheck disable=SC2155
+readonly iptables="$(command -v iptables)"
+# shellcheck disable=SC2155
+readonly ip6tables="$(command -v ip6tables)"
+# shellcheck disable=SC2155
+readonly ipset="$(command -v ipset)"
+readonly ipsPrefix='pbr'
+readonly iptPrefix='PBR'
+# shellcheck disable=SC2155
+readonly agh="$(command -v AdGuardHome)"
+readonly aghConfigFile='/etc/adguardhome.yaml'
+readonly aghIpsetFile="/var/run/${packageName}.adguardhome.ipsets"
+# shellcheck disable=SC2155
+readonly nft="$(command -v nft)"
+readonly nftTable="fw4"
+readonly nftPrefix='pbr'
+readonly chainsList='forward input output postrouting prerouting'
+
+# package config options
+boot_timeout=
+enabled=
+fw_mask=
+icmp_interface=
+ignored_interface=
+ipv6_enabled=
+procd_boot_delay=
+procd_reload_delay=
+resolver_set=
+rule_create_option=
+secure_reload=
+strict_enforcement=
+supported_interface=
+verbosity=
+wan_ip_rules_priority=
+wan_mark=
+
+# run-time
+gatewaySummary=
+errorSummary=
+warningSummary=
+wanIface4=
+wanIface6=
+ifaceMark=
+ifaceTableID=
+ifacePriority=
+ifacesAll=
+ifacesSupported=
+wanGW4=
+wanGW6=
+serviceStartTrigger=
+processPolicyError=
+processPolicyWarning=
+resolver_set_supported=
+nftPrevParam4=
+nftPrevParam6=
+
+
+get_text() {
+       local r
+       case "$1" in
+               errorConfigValidation) r="Config ($packageConfigFile) validation failure!";;
+               errorNoIpFull) r="ip-full binary cannot be found!";;
+               errorNoIpset) r="Resolver set support (${resolver_set}) requires ipset, but ipset binary cannot be found!";;
+               errorNoNft) r="Resolver set support (${resolver_set}) requires nftables, but nft binary cannot be found!";;
+               errorResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system!";;
+               errorServiceDisabled) r="The ${packageName} service is currently disabled!";;
+               errorNoWanGateway) r="The ${serviceName} service failed to discover WAN gateway!";;
+               errorIpsetNameTooLong) r="The ipset name '%s' is longer than allowed 31 characters!";;
+               errorNftsetNameTooLong) r="The nft set name '%s' is longer than allowed 31 characters!";;
+               errorUnexpectedExit) r="Unexpected exit or service termination: '%s'!";;
+               errorPolicyNoSrcDest) r="Policy '%s' has no source/destination parameters!";;
+               errorPolicyNoInterface) r="Policy '%s' has no assigned interface!";;
+               errorPolicyUnknownInterface) r="Policy '%s' has an unknown interface!";;
+               errorPolicyProcess) r="%s";;
+               errorFailedSetup) r="Failed to set up '%s'!";;
+               errorFailedReload) r="Failed to reload '%s'!";;
+               errorUserFileNotFound) r="Custom user file '%s' not found or empty!";;
+               ererrorUserFileSyntax) r="Syntax error in custom user file '%s'!";;
+               errorUserFileRunning) r="Error running custom user file '%s'!";;
+               errorUserFileNoCurl) r="Use of 'curl' is detected in custom user file '%s', but 'curl' isn't installed!";;
+               errorNoGateways) r="Failed to set up any gateway!";;
+               warningResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system.";;
+               warningAGHVersionTooLow) r="Installed AdGuardHome (%s) doesn't support 'ipset_file' option.";;
+               warningPolicyProcess) r="%s";;
+       esac
+       echo "$r"
+}
+
+version() { echo "$PKG_VERSION"; }
+output_ok() { output 1 "$_OK_"; output 2 "$__OK__\\n"; }
+output_okn() { output 1 "$_OK_\\n"; output 2 "$__OK__\\n"; }
+output_fail() { s=1; output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; }
+output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; }
+str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; }
+str_replace() { echo "${1//$2/$3}"; }
+str_contains() { [ -n "$1" ] &&[ -n "$2" ] && [ "${1//$2}" != "$1" ]; }
+is_greater() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
+is_greater_or_equal() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" = "$2"; }
+str_contains_word() { echo "$1" | grep -q -w "$2"; }
+str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
+str_to_upper() { echo "$1" | tr 'a-z' 'A-Z'; }
+str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; }
+str_extras_to_space() { echo "$1" | tr ';{}' ' '; }
+debug() { local i j; for i in "$@"; do eval "j=\$$i"; echo "${i}: ${j} "; done; }
+output() {
+# Can take a single parameter (text) to be output at any verbosity
+# Or target verbosity level and text to be output at specifc verbosity
+       local msg memmsg logmsg
+       verbosity="${verbosity:-2}"
+       if [ "$#" -ne 1 ]; then
+               if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; else return 0; fi
+       fi
+       [ -t 1 ] && printf "%b" "$1"
+       msg="${1//$serviceName /service }";
+       if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then
+               [ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
+               logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
+               logger -t "${packageName:-service}" "$(printf "%b" "$logmsg")"
+               rm -f "$sharedMemoryOutput"
+       else
+               printf "%b" "$msg" >> "$sharedMemoryOutput"
+       fi
+}
+is_present() { command -v "$1" >/dev/null 2>&1; }
+is_installed() { [ -s "/usr/lib/opkg/info/${1}.control" ]; }
+is_variant_installed() { [ "$(echo /usr/lib/opkg/info/"${1}"*.control)" != "/usr/lib/opkg/info/${1}*.control" ]; }
+is_nft() { [ -x "$nft" ] && ! str_contains "$resolver_set" 'ipset' && "$nft" list chains inet | grep -q "${nftPrefix}_prerouting"; }
+_build_ifaces_all() { ifacesAll="${ifacesAll}${1} "; }
+_build_ifaces_supported() { is_supported_interface "$1" && ifacesSupported="${ifacesSupported}${1} "; }
+pbr_find_iface() {
+       local iface i param="$2"
+       [ "$param" = 'wan6' ] || param='wan'
+       "network_find_${param}" iface
+       is_tunnel "$iface" && unset iface
+       if [ -z "$iface" ]; then
+               for i in $ifacesAll; do
+                       if "is_${param}" "$i"; then break; else unset i; fi
+               done
+       fi
+       eval "$1"='${iface:-$i}'
+}
+pbr_get_gateway() {
+       local iface="$2" dev="$3" gw
+       network_get_gateway gw "$iface" true
+#      if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
+#              gw="$(ubus call "network.interface.${iface}" status | jsonfilter -e "@.route[0].nexthop")"
+#      fi
+       if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
+               gw="$($ip_full -4 a list dev "$dev" 2>/dev/null | grep inet | awk '{print $2}' | awk -F "/" '{print $1}')"
+       fi
+       eval "$1"='$gw'
+}
+pbr_get_gateway6() {
+       local iface="$2" dev="$3" gw
+       network_get_gateway6 gw "$iface" true
+       if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
+               gw="$($ip_full -6 a list dev "$dev" 2>/dev/null | grep inet6 | awk '{print $2}')"
+       fi
+       eval "$1"='$gw'
+}
+is_dslite() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:6}" = "dslite" ]; }
+is_l2tp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "l2tp" ]; }
+is_oc() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:11}" = "openconnect" ]; }
+is_ovpn() { local dev; network_get_device dev "$1"; [ "${dev:0:3}" = "tun" ] || [ "${dev:0:3}" = "tap" ] || [ -f "/sys/devices/virtual/net/${dev}/tun_flags" ]; }
+is_pptp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "pptp" ]; }
+is_softether() { local dev; network_get_device dev "$1"; [ "${dev:0:4}" = "vpn_" ]; }
+is_tor() { [ "$(str_to_lower "$1")" = "tor" ]; }
+is_tor_running() { 
+       local ret=0
+       if [ -s "/etc/tor/torrc" ]; then
+               json_load "$(ubus call service list "{ 'name': 'tor' }")"
+               json_select 'tor'; json_select 'instances'; json_select 'instance1';
+               json_get_var ret 'running'; json_cleanup
+       fi
+       if [ "$ret" = "0" ]; then return 1; else return 0; fi
+}
+is_wg() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:9}" = "wireguard" ]; }
+is_tunnel() { is_dslite "$1" || is_l2tp "$1" || is_oc "$1" || is_ovpn "$1" || is_pptp "$1" || is_softether "$1" || is_tor "$1" || is_wg "$1"; }
+is_wan() { [ "$1" = "$wanIface4" ] || { [ "${1##wan}" != "$1" ] && [ "${1##wan6}" = "$1" ]; } || [ "${1%%wan}" != "$1" ]; }
+is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1/#wan6}" != "$1" ] || [ "${1/%wan6}" != "$1" ]; }
+is_ignored_interface() { str_contains_word "$ignored_interface" "$1"; }
+is_supported_interface() { str_contains_word "$supported_interface" "$1" || { ! is_ignored_interface "$1" && { is_wan "$1" || is_wan6 "$1" || is_tunnel "$1"; }; } || is_ignore_target "$1"; }
+is_ignore_target() { [ "$(str_to_lower "$1")" = 'ignore' ]; }
+is_mac_address() { expr "$1" : '[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]$' >/dev/null; }
+is_ipv4() { expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; }
+is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ":"; }
+is_family_mismatch() { ( is_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_netmask "${2//!}" ); }
+is_ipv6_link_local() { [ "${1:0:4}" = "fe80" ]; }
+is_ipv6_unique_local() { [ "${1:0:2}" = "fc" ] || [ "${1:0:2}" = "fd" ]; }
+is_ipv6_global() { [ "${1:0:4}" = "2001" ]; }
+# is_ipv6_global() { is_ipv6 "$1" && ! is_ipv6_link_local "$1" && ! is_ipv6_link_local "$1"; }
+is_list() { str_contains "$1" "," || str_contains "$1" " "; }
+is_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; }
+is_domain() { str_contains "$1" '[a-zA-Z]'; }
+is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; }
+dnsmasq_kill() { killall -q -s HUP dnsmasq; }
+dnsmasq_restart() { output 3 'Restarting dnsmasq '; if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then output_okn; else output_failn; fi; }
+is_default_dev() { [ "$1" = "$($ip_full -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
+is_supported_iface_dev() { local n dev; for n in $ifacesSupported; do network_get_device dev "$n"; [ "$1" = "$dev" ] && return 0; done; return 1; }
+is_supported_protocol() { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; }
+is_service_running_iptables() { [ -x "$iptables" ] && "$iptables" -t mangle -L | grep -q "${iptPrefix}_PREROUTING" >/dev/null 2>&1; }
+is_service_running_nft() { [ -x "$nft" ] && [ -n "$(get_mark_nft_chains)" ]; }
+# atomic
+# is_service_running_nft() { [ -x "$nft" ] && [ -s "$nftPermFile" ]; }
+is_service_running() { if is_nft; then is_service_running_nft; else is_service_running_iptables; fi; }
+is_netifd_table() { local iface="$1"; [ "$(uci -q get "network.${iface}.ip4table")" = "${packageName}_${iface%6}" ]; }
+get_rt_tables_id() { grep "${packageName}_${iface}" /etc/iproute2/rt_tables | awk '{print $1;}'; }
+get_rt_tables_next_id() { echo "$(($(sort -r -n /etc/iproute2/rt_tables | grep -o -E -m 1 "^[0-9]+")+1))"; }
+_check_config() { local en; config_get_bool en "$1" 'enabled' 1; [ "$en" -gt 0 ] && _cfg_enabled=0; }
+is_config_enabled() {
+       local cfg="$1" _cfg_enabled=1
+       [ -n "$1" ] || return 1
+       config_load "$packageName"
+       config_foreach _check_config "$cfg"
+       return "$_cfg_enabled"
+}
+# shellcheck disable=SC2016
+resolveip_to_ipt() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d'; }
+# shellcheck disable=SC2016
+resolveip_to_nftset() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
+resolveip_to_nftset4() { resolveip_to_nftset -4 "$@"; }
+resolveip_to_nftset6() { [ -n "$ipv6_enabled" ] && resolveip_to_nftset -6 "$@"; }
+# shellcheck disable=SC2016
+ipv4_leases_to_nftset() { [ -s '/tmp/dhcp.leases' ] || return 1; grep "$1" '/tmp/dhcp.leases' | awk '{print $3}' | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
+# shellcheck disable=SC2016
+ipv6_leases_to_nftset() { [ -s '/tmp/hosts/odhcpd' ] || return 1; grep -v '^\#' '/tmp/hosts/odhcpd' | grep "$1" | awk '{print $1}' | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
+# shellcheck disable=SC3037
+ports_to_nftset() { echo -ne "$value"; }
+get_mark_ipt_chains() { [ -n "$(command -v iptables-save)" ] && iptables-save | grep ":${iptPrefix}_MARK_" | awk '{ print $1 }' | sed 's/://'; }
+get_mark_nft_chains() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep chain | grep "${nftPrefix}_mark_" | awk '{ print $2 }'; }
+get_ipsets() { [ -x "$(command -v ipset)" ] && ipset list | grep "${ipsPrefix}_" | awk '{ print $2 }'; }
+get_nft_sets() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep 'set' | grep "${nftPrefix}_" | awk '{ print $2 }'; }
+is_ipset_type_supported() { ipset help hash:"$1" >/dev/null 2>&1; }
+ubus_get_status() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "@.${packageName}.instances.main.data.status.${1}"; }
+ubus_get_iface() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "@.${packageName}.instances.main.data.interfaces[@.name='${1}']${2:+.$2}"; }
+
+load_package_config() {
+       config_load "$packageName"
+       config_get      boot_timeout             'config' 'boot_timeout' '30'
+       config_get_bool enabled                  'config' 'enabled' '0'
+       config_get      fw_mask                  'config' 'fw_mask' 'ff0000'
+       config_get      icmp_interface           'config' 'icmp_interface'
+       config_get      ignored_interface        'config' 'ignored_interface'
+       config_get_bool ipv6_enabled             'config' 'ipv6_enabled' '0'
+       config_get      procd_boot_delay         'config' 'procd_boot_delay' '0'
+       config_get      resolver_set             'config' 'resolver_set'
+       config_get      rule_create_option       'config' 'rule_create_option' 'add'
+       config_get_bool secure_reload            'config' 'secure_reload' '1'
+       config_get_bool strict_enforcement       'config' 'strict_enforcement' '0'
+       config_get      supported_interface      'config' 'supported_interface'
+       config_get      verbosity                'config' 'verbosity' '2'
+       config_get      wan_ip_rules_priority    'config' 'wan_ip_rules_priority' '30000'
+       config_get      wan_mark                 'config' 'wan_mark' '010000'
+       fw_mask="0x${fw_mask}"
+       wan_mark="0x${wan_mark}"
+       [ -n "$ipv6_enabled" ] && [ "$ipv6_enabled" -eq 0 ] && unset ipv6_enabled
+       . /lib/functions/network.sh
+       . /usr/share/libubox/jshn.sh
+       mkdir -p "${dnsmasqFile%/*}"
+       if is_nft; then
+               fw_maskXor="$(printf '%#x' "$((fw_mask ^ 0xffffffff))")"
+               fw_maskXor="${fw_maskXor:-0xff00ffff}"
+       else
+               case $rule_create_option in
+                       insert|-i|-I) rule_create_option='-I';;
+                       add|-a|-A|*) rule_create_option='-A';;
+               esac
+       fi
+}
+
+load_environment() {
+       local param="$1" validation_result="$2"
+       load_package_config
+
+       if [ "$param" = 'on_start' ]; then
+               if [ -n "$validation_result" ] && [ "$validation_result" != '0' ]; then
+                       output "${_ERROR_}: The $packageName config validation failed!\\n"
+                       output "Please check if the '$packageConfigFile' contains correct values for config options.\\n"
+                       state add 'errorSummary' 'errorConfigValidation'
+                       return 1
+               fi
+               if [ "$enabled" -eq 0 ]; then
+                       state add 'errorSummary' 'errorServiceDisabled'
+                       return 1
+               fi
+               if [ ! -x "$ip_full" ]; then
+                       state add 'errorSummary' 'errorNoIpFull'
+                       return 1
+               fi
+               resolver 'check_support'
+       fi
+
+       load_network "$param"
+}
+
+load_network() {
+       config_load 'network'
+       [ -z "$ifacesAll" ] && config_foreach _build_ifaces_all 'interface'
+       [ -z "$ifacesSupported" ] && config_foreach _build_ifaces_supported 'interface'
+       pbr_find_iface wanIface4 'wan'
+       [ -n "$ipv6_enabled" ] && pbr_find_iface wanIface6 'wan6'
+       [ -n "$wanIface4" ] && network_get_gateway wanGW4 "$wanIface4"
+       [ -n "$wanIface6" ] && network_get_gateway6 wanGW6 "$wanIface6"
+       wanGW="${wanGW4:-$wanGW6}"
+}
+
+is_wan_up() {
+       local sleepCount='1'
+       load_network
+       while [ -z "$wanGW" ] ; do
+               load_network
+               if [ $((sleepCount)) -gt $((boot_timeout)) ] || [ -n "$wanGW" ]; then break; fi
+               output "$serviceName waiting for wan gateway...\\n"
+               sleep 1
+               network_flush_cache
+               sleepCount=$((sleepCount+1))
+       done
+       if [ -n "$wanGW" ]; then
+               return 0
+       else
+               state add 'errorSummary' 'errorNoWanGateway'
+               return 1
+       fi
+}
+
+# shellcheck disable=SC2086
+ipt4() {
+       local d
+       [ -x "$iptables" ] || return 1
+       for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do 
+               [ "$d" != "$*" ] && "$iptables" $d >/dev/null 2>&1
+       done
+       d="$*"; "$iptables" $d >/dev/null 2>&1
+}
+
+# shellcheck disable=SC2086
+ipt6() {
+       local d
+       [ -n "$ipv6_enabled" ] || return 0
+       [ -x "$ip6tables" ] || return 1
+       for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do 
+               [ "$d" != "$*" ] && "$ip6tables" $d >/dev/null 2>&1
+       done
+       d="$*"
+       "$ip6tables" $d >/dev/null 2>&1
+}
+
+# shellcheck disable=SC2086
+ipt() {
+       local d failFlagIpv4=1 failFlagIpv6=1
+       [ -x "$iptables" ] || return 1
+       for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do 
+               if [ "$d" != "$*" ]; then
+                       "$iptables" $d >/dev/null 2>&1
+                       [ -x "$ip6tables" ] && "$ip6tables" $d >/dev/null 2>&1
+               fi
+       done
+       d="$*"; "$iptables" $d >/dev/null 2>&1 && failFlagIpv4=0;
+       if [ -n "$ipv6_enabled" ] && [ -x "$ip6tables" ]; then
+               "$ip6tables" $d >/dev/null 2>&1 && failFlagIpv6=0
+       fi
+       [ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ]
+}
+
+# shellcheck disable=SC2086
+ips4() { [ -x "$ipset" ] && "$ipset" "$@" >/dev/null 2>&1; }
+ips6() { [ -x "$ipset" ] && { if [ -n "$ipv6_enabled" ] && [ -n "$*" ]; then "$ipset" "$@" >/dev/null 2>&1; else return 1; fi; }; }
+ips() {
+       local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7"
+       local ipset4 ipset6 i
+       local ipv4_error=1 ipv6_error=1
+       ipset4="${ipsPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}"
+       ipset6="${ipsPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}"
+
+       [ -x "$ipset" ] || return 1
+
+       if [ "${#ipset4}" -gt 31 ]; then 
+               state add 'errorSummary' 'errorIpsetNameTooLong' "$ipset4"
+               return 1
+       fi
+
+       case "$command" in
+               add)
+                       ips4 -q -! add "$ipset4" comment "$comment" && ipv4_error=0
+                       ips6 -q -! add "$ipset6" comment "$comment" && ipv6_error=0
+               ;;
+               add_agh_element)
+                       [ -n "$ipv6_enabled" ] || unset ipset6
+                       echo "${param}/${ipset4}${ipset6:+,$ipset6}" >> "$aghIpsetFile" && ipv4_error=0
+               ;;
+               add_dnsmasq_element)
+                       [ -n "$ipv6_enabled" ] || unset ipset6
+                       echo "ipset=/${param}/${ipset4}${ipset6:+,$ipset6} # $comment" >> "$dnsmasqFile" && ipv4_error=0
+               ;;
+               create)
+                       ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+                       ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
+               ;;
+               create_agh_set)
+                       ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+                       ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
+               ;;
+               create_dnsmasq_set)
+                       ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+                       ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
+               ;;
+               create_user_set)
+                       case "$type" in
+                               ip|net)
+                                       ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+                                       ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv4_error=0
+                                       case "$target" in
+                                               dst)
+                                                       ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+                                                       ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+                                               ;;
+                                               src)
+                                                       ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+                                                       ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+                                       ;;
+                                       esac
+                               ;;
+                               mac)
+                                       ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+                                       ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv4_error=0
+                                       ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+                                       ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+                               ;;
+                               esac
+               ;;
+               delete|destroy)
+                       ips4 -q -! destroy "$ipset4" && ipv4_error=0
+                       ips6 -q -! destroy "$ipset6" && ipv6_error=0
+               ;;
+               delete_user_set)
+                       ips4 -q -! destroy "$ipset4" && ipv4_error=0
+                       ips6 -q -! destroy "$ipset6" family inet6 && ipv6_error=0
+                       case "$type" in
+                               ip|net)
+                                       case "$target" in
+                                               dst)
+                                                       ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+                                                       ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+                                               ;;
+                                               src)
+                                                       ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+                                                       ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+                                       ;;
+                                       esac
+                               ;;
+                               mac)
+                                       ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+                                       ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+                               ;;
+                               esac
+               ;;
+               flush|flush_user_set)
+                       ips4 -q -! flush "$ipset4" && ipv4_error=0
+                       ips6 -q -! flush "$ipset6" && ipv6_error=0
+               ;;
+       esac
+       return $ipv4_error || $ipv6_error
+}
+
+# atomic
+#nfta() { echo "$@" >> "$nftTempFile"; }
+#nfta4() { echo "$@" >> "$nftTempFile"; }
+#nfta6() { [ -z "$ipv6_enabled" ] || echo "$@" >> "$nftTempFile"; }
+#nft() { nfta "$@"; [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
+#nft4() { nfta "$@"; [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
+#nft6() { nfta "$@"; [ -n "$ipv6_enabled" ] || return 0; [ -x "$nft" ] && [ -n "$*" ] && "$nft" "$@" >/dev/null 2>&1; }
+nft() { [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
+nft4() { [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
+nft6() { [ -n "$ipv6_enabled" ] || return 0; [ -x "$nft" ] && [ -n "$*" ] && "$nft" "$@" >/dev/null 2>&1; }
+nftset() {
+       local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7"
+       local nftset4 nftset6 i param4 param6
+       local ipv4_error=1 ipv6_error=1
+       nftset4="${nftPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}"
+       nftset6="${nftPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}"
+
+       [ -x "$nft" ] || return 1
+
+       if [ "${#nftset4}" -gt 255 ]; then 
+               state add 'errorSummary' 'errorNftsetNameTooLong' "$nftset4"
+               return 1
+       fi
+
+       case "$command" in
+               add)
+                       if is_netmask "$param" || is_ipv4 "$param" || is_ipv6 "$param" \
+                               || is_mac_address "$param" || is_list "$param"; then
+                               nft4 add element inet "$nftTable" "$nftset4" "{ $param }" && ipv4_error=0
+                               nft6 add element inet "$nftTable" "$nftset6" "{ $param }" && ipv6_error=0
+                       else
+#                      elif is_domain "$param"; then
+                               if [ "$target" = 'src' ]; then
+                                       param4="$(ipv4_leases_to_nftset "$param")"
+                                       param6="$(ipv6_leases_to_nftset "$param")"
+                               fi
+                               [ -z "$param4" ] &&     param4="$(resolveip_to_nftset4 "$param")"
+                               [ -z "$param6" ] &&     param6="$(resolveip_to_nftset6 "$param")"
+                               nft4 add element inet "$nftTable" "$nftset4" "{ $param4 }" && ipv4_error=0
+                               nft6 add element inet "$nftTable" "$nftset6" "{ $param6 }" && ipv6_error=0
+                       fi
+               ;;
+               add_dnsmasq_element)
+                       [ -n "$ipv6_enabled" ] || unset nftset6
+                       echo "nftset=/${param}/4#inet#${nftTable}#${nftset4}${nftset6:+,6#inet#${nftTable}#$nftset6} # $comment" >> "$dnsmasqFile" && ipv4_error=0
+               ;;
+               create)
+                       case "$type" in
+                               ip|net)
+                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv4_error=0
+                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv6_error=0
+                                       ;;
+                               mac)
+                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv4_error=0
+                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv6_error=0
+                                       ;;
+                               esac
+               ;;
+               create_dnsmasq_set)
+                       nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv4_error=0
+                       nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv6_error=0
+               ;;
+               create_user_set)
+                       case "$type" in
+                               ip|net)
+                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv4_error=0
+                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv6_error=0
+                                       case "$target" in
+                                               dst)
+                                                       nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+                                                       nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+                                               ;;
+                                               src)
+                                                       nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+                                                       nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+                                               ;;
+                                       esac
+                                       ;;
+                               mac)
+                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv4_error=0
+                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv6_error=0
+                                       nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+                                       nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+                                       ;;
+                               esac
+               ;;
+               delete|destroy)
+                       nft delete set inet "$nftTable" "$nftset4" && ipv4_error=0
+                       nft delete set inet "$nftTable" "$nftset6" && ipv6_error=0
+               ;;
+               delete_user_set)
+                       nft delete set inet "$nftTable" "$nftset4" && ipv4_error=0
+                       nft delete set inet "$nftTable" "$nftset6" && ipv6_error=0
+                       case "$type" in
+                               ip|net)
+                                       case "$target" in
+                                               dst)
+                                                       nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+                                                       nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+                                               ;;
+                                               src)
+                                                       nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+                                                       nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+                                               ;;
+                                       esac
+                                       ;;
+                               mac)
+                                       nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+                                       nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+                                       ;;
+                               esac
+               ;;
+               flush|flush_user_set)
+                       nft flush set inet "$nftTable" "$nftset4" && ipv4_error=0
+                       nft flush set inet "$nftTable" "$nftset6" && ipv6_error=0
+               ;;
+       esac
+# nft6 returns true if IPv6 support is not enabled
+       [ -z "$ipv6_enabled" ] && ipv6_error='1'
+       return $ipv4_error || $ipv6_error
+}
+
+cleanup_dnsmasq() { [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')" && rm "$dnsmasqFile" >/dev/null 2>&1; }
+cleanup_main_chains() {
+       local i
+       for i in $chainsList; do
+               i="$(str_to_lower "$i")"
+               nft flush chain inet "$nftTable" "${nftPrefix}_${i}"
+       done
+       for i in $chainsList; do
+               i="$(str_to_upper "$i")"
+               ipt -t mangle -D "${i}" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
+               ipt -t mangle -F "${iptPrefix}_${i}"
+               ipt -t mangle -X "${iptPrefix}_${i}"
+       done
+}
+
+cleanup_marking_chains() {
+       local i
+       for i in $(get_mark_nft_chains); do
+               nft flush chain inet "$nftTable" "$i"
+               nft delete chain inet "$nftTable" "$i"
+       done
+       for i in $(get_mark_ipt_chains); do
+               ipt -t mangle -F "$i"
+               ipt -t mangle -X "$i"
+       done
+}
+
+cleanup_sets() {
+       local i
+       for i in $(get_nft_sets); do
+               nft flush set inet "$nftTable" "$i"
+               nft delete set inet "$nftTable" "$i"
+       done
+       for i in $(get_ipsets); do
+               ipset -q -! flush "$i" >/dev/null 2>&1
+               ipset -q -! destroy "$i" >/dev/null 2>&1
+       done
+}
+
+state() {
+       local action="$1" param="$2" value="${3//#/_}"
+       shift 3
+# shellcheck disable=SC2124
+       local extras="$@"
+       local line error_id error_extra label
+       case "$action" in
+               add)
+                       line="$(eval echo "\$$param")"
+                       eval "$param"='${line:+$line#}${value}${extras:+ $extras}'
+               ;;
+               json)
+                       case "$param" in
+                               errorSummary)
+                                       json_add_array 'errors';;
+                               warningSummary)
+                                       json_add_array 'warnings';;
+                       esac
+                       if [ -n "$(eval echo "\$$param")" ]; then
+                               while read -r line; do
+                                       if str_contains "$line" ' '; then
+#                      url="${c##*|}"
+#                      c="${c%|*}"
+                                               error_id="${line% *}"
+                                               error_extra="${line#* }"
+                                       else
+                                               error_id="$line"
+                                       fi
+                                       json_add_object
+                                       json_add_string 'id' "$error_id"
+                                       json_add_string 'extra' "$error_extra"
+                                       json_close_object
+                               done <<EOF
+$(eval echo "\$$param" | tr \# \\n)
+EOF
+                       fi
+                       json_close_array
+               ;;
+               print)
+                       [ -z "$(eval echo "\$$param")" ] && return 0
+                       case "$param" in
+                               errorSummary)
+                                       label="${_ERROR_}:";;
+                               warningSummary)
+                                       label="${_WARNING_}:";;
+                       esac
+                               while read -r line; do
+                                       if str_contains "$line" ' '; then
+                                               error_id="${line% *}"
+                                               error_extra="${line#* }"
+                                               printf "%b $(get_text "$error_id")\\n" "$label" "$error_extra"
+                                       else
+                                               error_id="$line"
+                                               printf "%b $(get_text "$error_id")\\n" "$label"
+                                       fi
+                               done <<EOF
+$(eval echo "\$$param" | tr \# \\n)
+EOF
+               ;;
+               set)
+                       eval "$param"='${value}${extras:+ $extras}'
+               ;;
+       esac
+}
+
+resolver() {
+       local agh_version
+       local param="$1"
+       shift
+
+       if [ "$param" = 'cleanup_all' ]; then
+               sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev/null 2>&1
+               rm -f "$aghIpsetFile"
+               rm -f "$dnsmasqFile"
+               return 0
+       fi
+
+       case "$resolver_set" in
+               ''|none)
+                       case "$param" in
+                               add_resolver_element) return 1;;
+                               create_resolver_set) return 1;;
+                               check_support) return 0;;
+                               cleanup) return 0;;
+                               configure) return 0;;
+                               init) return 0;;
+                               init_end) return 0;;
+                               kill) return 0;;
+                               reload) return 0;;
+                               restart) return 0;;
+                               compare_hash) return 0;;
+                               store_hash) return 0;;
+                       esac
+               ;;
+               adguardhome.ipset)
+                       case "$param" in
+                               add_resolver_element)
+                                       [ -n "$resolver_set_supported" ] && ips 'add_agh_element' "$@";;
+                               create_resolver_set)
+                                       [ -n "$resolver_set_supported" ] && ips 'create_agh_set' "$@";;
+                               check_support)
+                                       if [ ! -x "$ipset" ]; then
+                                               state add 'errorSummary' 'errorNoIpset'
+                                               return 1
+                                       fi
+                                       if [ -n "$agh" ] && [ -s "$aghConfigFile" ]; then
+                                               agh_version="$($agh --version | sed 's|AdGuard Home, version v\(.*\)|\1|')"
+                                               if is_greater_or_equal "$agh_version" '0.107.13'; then
+                                                       resolver_set_supported='true'
+                                                       return 0
+                                               else
+                                                       state add 'warningSummary' 'warningAGHVersionTooLow' "$agh_version"
+                                                       return 1
+                                               fi
+                                       else
+                                               state add 'warningSummary' 'warningResolverNotSupported'
+                                               return 1
+                                       fi
+                               ;;
+                               cleanup)
+                                       [ -z "$resolver_set_supported" ] && return 0
+                                       rm -f "$aghIpsetFile"
+                                       sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev/null 2>&1
+                                       ;;
+                               configure)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       mkdir -p "${aghIpsetFile%/*}"
+                                       touch "$aghIpsetFile"
+                                       sed -i '/ipset_file/d' "$aghConfigFile" >/dev/null 2>&1
+                                       sed -i "/  ipset:/a \ \ ipset_file: $aghIpsetFile" "$aghConfigFile"
+                               ;;
+                               init) :;;
+                               init_end) :;;
+                               kill)
+                                       [ -n "$resolver_set_supported" ] && [ -n "$agh" ] && killall -q -s HUP "$agh";;
+                               reload)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       output 3 'Reloading adguardhome '
+                                       if /etc/init.d/adguardhome reload >/dev/null 2>&1; then
+                                               output_okn
+                                               return 0
+                                       else
+                                               output_failn
+                                               return 1
+                                       fi
+                               ;;
+                               restart)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       output 3 'Restarting adguardhome '
+                                       if /etc/init.d/adguardhome restart >/dev/null 2>&1; then
+                                               output_okn
+                                               return 0
+                                       else
+                                               output_failn
+                                               return 1
+                                       fi
+                               ;;
+                               compare_hash)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       local resolverNewHash
+                                       if [ -s "$aghIpsetFile" ]; then
+                                               resolverNewHash="$(md5sum $aghIpsetFile | awk '{ print $1; }')"
+                                       fi
+                                       [ "$resolverNewHash" != "$resolverStoredHash" ]
+                               ;;
+                               store_hash)
+                                       [ -s "$aghIpsetFile" ] && resolverStoredHash="$(md5sum $aghIpsetFile | awk '{ print $1; }')";;
+                       esac
+               ;;
+               dnsmasq.ipset)
+                       case "$param" in
+                               add_resolver_element)
+                                       [ -n "$resolver_set_supported" ] && ips 'add_dnsmasq_element' "$@";;
+                               create_resolver_set)
+                                       [ -n "$resolver_set_supported" ] && ips 'create_dnsmasq_set' "$@";;
+                               check_support)
+                                       if [ ! -x "$ipset" ]; then
+                                               state add 'errorSummary' 'errorNoIpset'
+                                               return 1
+                                       fi
+                                       if ! dnsmasq -v 2>/dev/null | grep -q 'no-ipset' && dnsmasq -v 2>/dev/null | grep -q 'ipset'; then
+                                               resolver_set_supported='true'
+                                               return 0
+                                       else
+                                               state add 'warningSummary' 'warningResolverNotSupported'
+                                               return 1
+                                       fi
+                               ;;
+                               cleanup)
+                                       [ -n "$resolver_set_supported" ] && rm -f "$dnsmasqFile";;
+                               configure)
+                                       [ -n "$resolver_set_supported" ] && mkdir -p "${dnsmasqFile%/*}";;
+                               init) :;;
+                               init_end) :;;
+                               kill)
+                                       [ -n "$resolver_set_supported" ] && killall -q -s HUP dnsmasq;;
+                               reload)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       output 3 'Reloading dnsmasq '
+                                       if /etc/init.d/dnsmasq reload >/dev/null 2>&1; then
+                                               output_okn
+                                               return 0
+                                       else
+                                               output_failn
+                                               return 1
+                                       fi
+                               ;;
+                               restart)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       output 3 'Restarting dnsmasq '
+                                       if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then
+                                               output_okn
+                                               return 0
+                                       else
+                                               output_failn
+                                               return 1
+                                       fi
+                               ;;
+                               compare_hash)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       local resolverNewHash
+                                       if [ -s "$dnsmasqFile" ]; then
+                                               resolverNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
+                                       fi
+                                       [ "$resolverNewHash" != "$resolverStoredHash" ]
+                               ;;
+                               store_hash)
+                                       [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')";;
+                       esac
+               ;;
+               dnsmasq.nftset)
+                       case "$param" in
+                               add_resolver_element)
+                                       [ -n "$resolver_set_supported" ] && nftset 'add_dnsmasq_element' "$@";;
+                               create_resolver_set)
+                                       [ -n "$resolver_set_supported" ] && nftset 'create_dnsmasq_set' "$@";;
+                               check_support)
+                                       if [ ! -x "$nft" ]; then
+                                               state add 'errorSummary' 'errorNoNft'
+                                               return 1
+                                       fi
+                                       if ! dnsmasq -v 2>/dev/null | grep -q 'no-nftset' && dnsmasq -v 2>/dev/null | grep -q 'nftset'; then
+                                               resolver_set_supported='true'
+                                               return 0
+                                       else
+                                               state add 'warningSummary' 'warningResolverNotSupported'
+                                               return 1
+                                       fi
+                               ;;
+                               cleanup)
+                                       [ -n "$resolver_set_supported" ] && rm -f "$dnsmasqFile";;
+                               configure)
+                                       [ -n "$resolver_set_supported" ] && mkdir -p "${dnsmasqFile%/*}";;
+                               init) :;;
+                               init_end) :;;
+                               kill)
+                                       [ -n "$resolver_set_supported" ] && killall -q -s HUP dnsmasq;;
+                               reload)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       output 3 'Reloading dnsmasq '
+                                       if /etc/init.d/dnsmasq reload >/dev/null 2>&1; then
+                                               output_okn
+                                               return 0
+                                       else
+                                               output_failn
+                                               return 1
+                                       fi
+                               ;;
+                               restart)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       output 3 'Restarting dnsmasq '
+                                       if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then
+                                               output_okn
+                                               return 0
+                                       else
+                                               output_failn
+                                               return 1
+                                       fi
+                               ;;
+                               compare_hash)
+                                       [ -z "$resolver_set_supported" ] && return 1
+                                       local resolverNewHash
+                                       if [ -s "$dnsmasqFile" ]; then
+                                               resolverNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
+                                       fi
+                                       [ "$resolverNewHash" != "$resolverStoredHash" ]
+                               ;;
+                               store_hash)
+                                       [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')";;
+                       esac
+               ;;
+               unbound.ipset)
+                       case "$param" in
+                               add_resolver_element) :;;
+                               create_resolver_set) :;;
+                               check_support) :;;
+                               cleanup) :;;
+                               configure) :;;
+                               init) :;;
+                               init_end) :;;
+                               kill) :;;
+                               reload) :;;
+                               restart) :;;
+                               compare_hash) :;;
+                               store_hash) :;;
+                       esac
+               ;;
+               unbound.nftset)
+                       case "$param" in
+                               add_resolver_element) :;;
+                               create_resolver_set) :;;
+                               check_support) :;;
+                               cleanup) :;;
+                               configure) :;;
+                               init) :;;
+                               init_end) :;;
+                               kill) :;;
+                               reload) :;;
+                               restart) :;;
+                               compare_hash) :;;
+                               store_hash) :;;
+                       esac
+               ;;
+       esac
+}
+
+trap_process() {
+#      verbosity=0
+       output "\\n"
+       output "Unexpected exit or service termination: '${1}'!\\n"
+       state add 'errorSummary' 'errorUnexpectedExit' "$1"
+       traffic_killswitch 'remove'
+}
+
+traffic_killswitch() {
+       local s=0
+       case "$1" in
+               insert)
+                       local lan_subnet wan_device
+                       [ "$secure_reload" -ne 0 ] || return 0
+                       for i in $serviceTrapSignals; do
+# shellcheck disable=SC2064
+                               trap "trap_process $i" "$i"
+                       done
+                       output 3 'Activating traffic killswitch '
+                       network_get_subnet lan_subnet 'lan'
+                       network_get_physdev wan_device 'wan'
+                       if is_nft; then
+                               nft add chain inet "$nftTable" "${nftPrefix}_killswitch" '{ type filter hook forward priority 0; policy accept; }' || s=1
+                               nft add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname "$wan_device" ip saddr "$lan_subnet" counter reject || s=1
+#                              nft add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname '$wan_devices' ip saddr '$lan_subnet' counter reject || s=1
+                       else
+                               ipt -N "${iptPrefix}_KILLSWITCH" || s=1
+                               ipt -A "${iptPrefix}_KILLSWITCH" -s "$lan_subnet" -o "$wan_device" -j REJECT || s=1
+                               ipt -I FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1
+                       fi
+                       if [ "$s" -eq 0 ]; then
+                               output_okn
+                       else
+                               output_failn
+                       fi
+               ;;
+               remove)
+                       if [ "$secure_reload" -ne 0 ]; then
+                               output 3 'Deactivating traffic killswitch '
+                       fi
+                       if is_nft; then
+                               nft flush chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
+                               nft delete chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
+                       else
+                               ipt -D FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1
+                               ipt -F "${iptPrefix}_KILLSWITCH" || s=1
+                               ipt -X "${iptPrefix}_KILLSWITCH" || s=1
+                       fi
+                       if [ "$secure_reload" -ne 0 ]; then
+                               if [ "$s" -eq 0 ]; then
+                                       output_okn
+                               else
+                                       output_failn
+                               fi
+                       fi
+# shellcheck disable=SC2086
+                       trap - $serviceTrapSignals
+               ;;
+       esac
+}
+
+policy_routing_tor() { if is_nft; then policy_routing_tor_nft "$@"; else policy_routing_tor_iptables "$@"; fi; }
+policy_routing_tor_iptables() {
+       local comment="$1" iface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto chain uid="$9"
+       proto="$(str_to_lower "$7")"
+       chain="$(str_to_upper "$8")"
+       chain="${chain:-PREROUTING}"
+       if [ -n "${src_addr}${src_port}${dest_port}" ]; then
+               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n"
+       fi
+       if [ -n "$proto" ] && [ "$proto" != "all" ]; then
+               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n"
+       fi
+       if [ "$chain" != "PREROUTING" ]; then
+               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '$comment'\\n"
+       fi
+       resolver 'add_resolver_element' "$iface" 'dst' 'ip' '' "${comment}: $dest_addr" "$dest_addr" || \
+               processPolicyError="${processPolicyError}${_ERROR_}: resolver 'add_resolver_element' '$iface' 'dst' 'ip' '${comment}: $dest_addr' '$dest_addr'\\n"
+       return 0
+}
+policy_routing_tor_nft() {
+       local comment="$1" iface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto chain uid="$9"
+       proto="$(str_to_lower "$7")"
+       chain="$(str_to_lower "$8")"
+       chain="${chain:-prerouting}"
+       if [ -n "${src_addr}${src_port}${dest_port}" ]; then
+               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n"
+       fi
+       if [ -n "$proto" ] && [ "$proto" != "all" ]; then
+               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n"
+       fi
+       if [ "$chain" != "prerouting" ]; then
+               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'prerouting' for policy '$comment'\\n"
+       fi
+       resolver 'add_resolver_element' "$iface" 'dst' 'ip' '' "${comment}: $dest_addr" "$dest_addr" || \
+               processPolicyError="${processPolicyError}${_ERROR_}: resolver 'add_resolver_element' '$iface' 'dst' 'ip' '${comment}: $dest_addr' '$dest_addr'\\n"
+       return 0
+}
+
+policy_routing() { if is_nft; then policy_routing_nft "$@"; else policy_routing_iptables "$@"; fi; }
+policy_routing_iptables() {
+       local mark param4 param6 i negation value dest ipInsertOption="-A"
+       local ip4error='1' ip6error='1'
+       local name="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain uid="$9"
+       proto="$(str_to_lower "$7")"
+       chain="$(str_to_upper "$8")"
+       chain="${chain:-PREROUTING}"
+       mark=$(eval echo "\$mark_${iface//-/_}")
+
+       if [ -n "$ipv6_enabled" ] && { is_ipv6 "$laddr" || is_ipv6 "$raddr"; }; then
+               processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$name' as IPv6 support is disabled\\n"
+               return 1
+       fi
+
+       if [ -n "$mark" ]; then
+               dest="-g ${iptPrefix}_MARK_${mark}"
+       elif [ "$iface" = "ignore" ]; then
+               dest="-j RETURN"
+       else
+               processPolicyError="${processPolicyError}${_ERROR_}: Unknown fw_mark for ${iface}\\n"
+               return 1
+       fi
+
+       if [ -z "$proto" ]; then
+               if [ -n "$lport" ] || [ -n "$rport" ]; then 
+                       proto='tcp udp'
+               else
+                       proto='all'
+               fi
+       fi
+
+       if is_family_mismatch "$laddr" "$raddr"; then 
+               processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$laddr' and '$raddr' in policy '$name'\\n"
+               return 1
+       fi
+
+       for i in $proto; do
+               if [ "$i" = 'all' ]; then
+                       param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest"
+                       param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest"
+               elif ! is_supported_protocol "$i"; then
+                       processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$name'\\n"
+                       return 1
+               else
+                       param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest -p $i"
+                       param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest -p $i"
+               fi
+
+               if [ -n "$laddr" ]; then
+                       if [ "${laddr:0:1}" = "!" ]; then
+                               negation='!'; value="${laddr:1}"
+                       else
+                               unset negation; value="$laddr";
+                       fi
+                       if is_phys_dev "$value"; then
+                               param4="$param4 $negation -m physdev --physdev-in ${value:1}"
+                               param6="$param6 $negation -m physdev --physdev-in ${value:1}"
+                       elif is_netmask "$value"; then
+                               local target='src' type='net'
+                               if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
+                                       ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
+                                       param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+                                       param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+                               else
+                                       param4="$param4 $negation -s $value"
+                                       param6="$param6 $negation -s $value"
+                               fi
+                       elif is_mac_address "$value"; then
+                               local target='src' type='mac'
+                               if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
+                                       ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
+                                       param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+                                       param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+                               else
+                                       param4="$param4 -m mac $negation --mac-source $value"
+                                       param6="$param6 -m mac $negation --mac-source $value"
+                               fi
+                       else
+                               local target='src' type='ip'
+                               if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
+                                       ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
+                                       param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+                                       param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+                               else
+                                       param4="$param4 $negation -s $(resolveip_to_ipt -4 "$value")"
+                                       param6="$param6 $negation -s $(resolveip_to_ipt -6 "$value")"
+                               fi
+                       fi
+               fi
+
+               if [ -n "$lport" ]; then
+                       if [ "${lport:0:1}" = "!" ]; then
+                               negation='!'; value="${lport:1}"
+                       else
+                               unset negation; value="$lport";
+                       fi
+                       param4="$param4 -m multiport $negation --sport ${value//-/:}"
+                       param6="$param6 -m multiport $negation --sport ${value//-/:}"
+               fi
+
+               if [ -n "$raddr" ]; then 
+                       if [ "${raddr:0:1}" = "!" ]; then
+                               negation='!'; value="${raddr:1}"
+                       else
+                               unset negation; value="$raddr";
+                       fi
+                       if is_netmask "$value"; then
+                               local target='dst' type='net'
+                               if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
+                                       ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
+                                       param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+                                       param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+                               else
+                                       param4="$param4 $negation -d $value"
+                                       param6="$param6 $negation -d $value"
+                               fi
+                       elif is_domain "$value"; then
+                               local target='dst' type='ip'
+                               if resolver 'create_resolver_set' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
+                                       resolver 'add_resolver_element' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
+                                       param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+                                       param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+                               elif ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
+                                       ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
+                                       param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+                                       param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+                               else
+                                       param4="$param4 $negation -d $(resolveip_to_ipt -4 "$value")"
+                                       param6="$param6 $negation -d $(resolveip_to_ipt -6 "$value")"
+                               fi
+                       else
+                               local target='dst' type='ip'
+                               if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
+                                       ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
+                                       param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+                                       param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+                               else
+                                       param4="$param4 $negation -d $value"
+                                       param6="$param6 $negation -d $value"
+                               fi
+                       fi
+               fi
+
+               if [ -n "$rport" ]; then
+                       if [ "${rport:0:1}" = "!" ]; then
+                               negation='!'; value="${rport:1}"
+                       else
+                               unset negation; value="$rport";
+                       fi
+                       param4="$param4 -m multiport $negation --dport ${value//-/:}"
+                       param6="$param6 -m multiport $negation --dport ${value//-/:}"
+               fi
+
+               if [ -n "$name" ]; then
+                       param4="$param4 -m comment --comment $(str_extras_to_underscore "$name")"
+                       param6="$param6 -m comment --comment $(str_extras_to_underscore "$name")"
+               fi
+
+               local ipv4_error='0' ipv6_error='0'
+               if [ "$param4" = "$param6" ]; then
+                       ipt4 "$param4" || ipv4_error='1'
+               else
+                       ipt4 "$param4" || ipv4_error='1'
+                       ipt6 "$param6" || ipv6_error='1'
+               fi
+
+# ipt6 returns true if IPv6 support is not enabled
+       [ -z "$ipv6_enabled" ] && ipv6_error='1'
+       if [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
+               if [ -n "$ipv6_enabled" ]; then
+                       processPolicyError="${processPolicyError}${_ERROR_}: Policy insertion failed for both IPv4 and IPv6!\\n"
+                       processPolicyError="${processPolicyError}${_ERROR_}: iptables $param4\\n"
+                       processPolicyError="${processPolicyError}${_ERROR_}: iptables $param6\\n"
+               else
+                       processPolicyError="${processPolicyError}${_ERROR_}: iptables $param4\\n"
+               fi
+       fi
+
+       done
+}
+policy_routing_nft() {
+       local mark param4 param6 i negation value dest nftInsertOption='add'
+       local ip4Flag='ip' ip6Flag='ip6'
+       local name="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain uid="$9"
+       proto="$(str_to_lower "$7")"
+       chain="$(str_to_lower "$8")"
+       chain="${chain:-prerouting}"
+       mark=$(eval echo "\$mark_${iface//-/_}")
+
+       if [ -z "$ipv6_enabled" ] && { is_ipv6 "$src_addr" || is_ipv6 "$dest_addr"; }; then
+               processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$name' as IPv6 support is disabled\\n"
+               return 1
+       fi
+
+       if [ -n "$mark" ]; then
+               dest="goto ${nftPrefix}_mark_${mark}"
+       elif [ "$iface" = "ignore" ]; then
+               dest="return"
+       else
+               processPolicyError="${processPolicyError}${_ERROR_}: Unknown packet mark for ${iface}\\n"
+               return 1
+       fi
+
+       if is_family_mismatch "$src_addr" "$dest_addr"; then 
+               processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$src_addr' and '$dest_addr' in policy '$name'\\n"
+               return 1
+       fi
+
+       if [ -n "$proto" ] && ! is_supported_protocol "$proto"; then
+               processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$name'\\n"
+               return 1
+       fi
+
+       if [ -n "$src_addr" ]; then
+               if [ "${src_addr:0:1}" = "!" ]; then
+                       negation='!='; value="${src_addr:1}"
+               else
+                       unset negation; value="$src_addr";
+               fi
+               if is_phys_dev "$value"; then
+                       param4="$param4 iifname $negation ${value:1}"
+                       param6="$param6 iifname $negation ${value:1}"
+               elif is_mac_address "$value"; then
+                       local target='src' type='mac'
+                       if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+                               nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+                               param4="$param4 ether saddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+                               param6="$param6 ether saddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+                       else
+                               param4="$param4 ether saddr $negation $value"
+                               param6="$param6 ether saddr $negation $value"
+                       fi
+               else
+                       local target='src' type='ip'
+                       if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+                               nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+                               param4="$param4 $ip4Flag saddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+                               param6="$param6 $ip6Flag saddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+                       else
+                               param4="$param4 $ip4Flag saddr $negation $value"
+                               param6="$param6 $ip6Flag saddr $negation $value"
+                       fi
+               fi
+       fi
+
+       if [ -n "$dest_addr" ]; then 
+               if [ "${dest_addr:0:1}" = "!" ]; then
+                       negation='!='; value="${dest_addr:1}"
+               else
+                       unset negation; value="$dest_addr";
+               fi
+               if is_phys_dev "$value"; then
+                       param4="$param4 oifname $negation ${value:1}"
+                       param6="$param6 oifname $negation ${value:1}"
+               elif is_domain "$value"; then
+                       local target='dst' type='ip'
+                       if resolver 'create_resolver_set' "$iface" "$target" "$type" "$uid" "$name" && \
+                               resolver 'add_resolver_element' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+                               param4="$param4 $ip4Flag daddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+                               param6="$param6 $ip6Flag daddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+                       elif nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+                               nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+                               param4="$param4 $ip4Flag daddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+                               param6="$param6 $ip6Flag daddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+                       else
+                               param4="$param4 $ip4Flag daddr $negation {$(resolveip_to_nftset4 "$value")}"
+                               param6="$param6 $ip6Flag daddr $negation {$(resolveip_to_nftset6 "$value")}"
+                       fi
+               else
+                       local target='dst' type='ip'
+                       if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+                               nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+                               param4="$param4 $ip4Flag daddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+                               param6="$param6 $ip6Flag daddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+                       else
+                               param4="$param4 $ip4Flag daddr $negation $value"
+                               param6="$param6 $ip6Flag daddr $negation $value"
+                       fi
+               fi
+       fi
+
+       if [ -n "${src_port}${dest_port}" ]; then
+               proto="${proto:-tcp}"
+       fi
+
+       if [ -n "$src_port" ]; then
+               if [ "${src_port:0:1}" = "!" ]; then
+                       negation='!='; value="${src_port:1}"
+               else
+                       unset negation; value="$src_port";
+               fi
+               param4="$param4 ${proto:+$proto }sport $negation {$(ports_to_nftset "$value")}"
+               param6="$param6 ${proto:+$proto }sport $negation {$(ports_to_nftset "$value")}"
+       fi
+
+       if [ -n "$dest_port" ]; then
+               if [ "${dest_port:0:1}" = "!" ]; then
+                       negation='!='; value="${dest_port:1}"
+               else
+                       unset negation; value="$dest_port";
+               fi
+               param4="$param4 ${proto:+$proto }dport $negation {$(ports_to_nftset "$value")}"
+               param6="$param6 ${proto:+$proto }dport $negation {$(ports_to_nftset "$value")}"
+       fi
+
+       param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param4 $dest comment \"$name\""
+       param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param6 $dest comment \"$name\""
+
+       local ipv4_error='0' ipv6_error='0'
+       if [ "$nftPrevParam4" != "$param4" ]; then
+               nft4 "$param4" || ipv4_error='1'
+               nftPrevParam4="$param4"
+       fi
+       if [ "$nftPrevParam6" != "$param6" ]; then
+               nft6 "$param6" || ipv6_error='1'
+               nftPrevParam6="$param6"
+       fi
+
+# nft6 returns true if IPv6 support is not enabled
+       [ -z "$ipv6_enabled" ] && ipv6_error='1'
+       if [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
+               if [ -n "$ipv6_enabled" ]; then
+                       processPolicyError="${processPolicyError}${_ERROR_}: Policy insertion failed for both IPv4 and IPv6!\\n"
+                       processPolicyError="${processPolicyError}${_ERROR_}: nft '$param4'\\n"
+                       processPolicyError="${processPolicyError}${_ERROR_}: nft '$param6'\\n"
+               else
+                       processPolicyError="${processPolicyError}${_ERROR_}: nft '$param4'\\n"
+               fi
+       fi
+}
+
+policy_process() {
+       local i j uid="$9"
+       if [ -z "$uid" ]; then # first non-recursive call
+               [ "$enabled" -gt 0 ] || return 0
+               unset processPolicyWarning
+               unset processPolicyError
+               uid="$1"
+               if is_nft; then
+                       chain="$(str_to_lower "$chain")"
+               else
+                       chain="$(str_to_upper "$chain")"
+               fi
+               proto="$(str_to_lower "$proto")"
+               [ "$proto" = 'auto' ] && unset proto
+               [ "$proto" = 'all' ] && unset proto
+               output 2 "Routing '$name' via $interface "
+               if [ -z "${src_addr}${src_port}${dest_addr}${dest_port}" ]; then
+                       state add 'errorSummary' 'errorPolicyNoSrcDest' "$name"
+                       output_fail; return 1;
+               fi
+               if [ -z "$interface" ]; then
+                       state add 'errorSummary' 'errorPolicyNoInterface' "$name"
+                       output_fail; return 1;
+               fi
+               if ! is_supported_interface "$interface"; then
+                       state add 'errorSummary' 'errorPolicyUnknownInterface' "$name"
+                       output_fail; return 1;
+               fi
+               src_port="${src_port//  / }"; src_port="${src_port// /,}"; src_port="${src_port//,\!/ !}"; 
+               dest_port="${dest_port//  / }"; dest_port="${dest_port// /,}"; dest_port="${dest_port//,\!/ !}";
+               if is_nft; then
+                       nftset 'flush' "$interface" "dst" "ip" "$uid"
+                       nftset 'flush' "$interface" "src" "ip" "$uid"
+                       nftset 'flush' "$interface" "src" "mac" "$uid"
+               else
+                       ips 'flush' "$interface" "dst" "ip" "$uid"
+                       ips 'flush' "$interface" "src" "ip" "$uid"
+                       ips 'flush' "$interface" "src" "mac" "$uid"
+               fi
+               policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
+               if [ -n "$processPolicyWarning" ]; then
+                       state add 'warningSummary' 'warningPolicyProcess' "$processPolicyWarning"
+               fi
+               if [ -n "$processPolicyError" ]; then
+                       output_fail
+                       state add 'errorSummary' 'errorPolicyProcess' "$processPolicyError"
+               else
+                       output_ok
+               fi
+       else # recursive call, get options from passed variables
+               local name="$1" interface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto="$7" chain="$8"
+               if str_contains "$src_addr" '[ ;\{\}]'; then
+                       for i in $(str_extras_to_space "$src_addr"); do [ -n "$i" ] && policy_process "$name" "$interface" "$i" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"; done
+               elif str_contains "$src_port" '[ ;\{\}]'; then
+                       for i in $(str_extras_to_space "$src_port"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$i" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"; done
+               elif str_contains "$dest_addr" '[ ;\{\}]'; then
+                       for i in $(str_extras_to_space "$dest_addr"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$i" "$dest_port" "$proto" "$chain" "$uid"; done
+               elif str_contains "$dest_port" '[ ;\{\}]'; then
+                       for i in $(str_extras_to_space "$dest_port"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$i" "$proto" "$chain" "$uid"; done
+               elif str_contains "$proto" '[ ;\{\}]'; then
+                       for i in $(str_extras_to_space "$proto"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$i" "$chain" "$uid"; done
+               else
+                       if is_tor "$interface"; then
+                               policy_routing_tor "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
+                       else
+                               policy_routing "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
+                       fi
+               fi
+       fi
+}
+
+interface_process_tor() { if is_nft; then interface_process_tor_nft "$@"; else interface_process_tor_iptables "$@"; fi; }
+interface_process_tor_iptables() {
+       local s=0 iface="$1" action="$2"
+       local displayText set_name4 set_name6
+       local dnsPort trafficPort
+       case "$action" in
+               reload)
+                       displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}"
+                       gatewaySummary="${gatewaySummary}${displayText}\\n"
+                       ;;
+               destroy)
+                       for i in $chainsList; do
+                               i="$(str_to_upper "$i")"
+                               ipt -t nat -D "${i}" -m mark --mark "0x0/${fw_mask}" -j "${nftPrefix}_${i}"
+                               ipt -t nat -F "${nftPrefix}_${i}"; ipt -t nat -X "${nftPrefix}_${i}";
+                       done
+                       ;;
+               create)
+                       output 2 "Creating TOR redirects "
+                       dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')"
+                       trafficPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')"
+                       dnsPort="${dnsPort:-9053}"; trafficPort="${trafficPort:-9040}"; 
+                       for i in $chainsList; do
+                               ipt -t nat -N "${nftPrefix}_${i}"
+                               ipt -t nat -A "$i" -m mark --mark "0x0/${fw_mask}" -j "${nftPrefix}_${i}"
+                       done
+                       if resolver 'create_resolver_set' "$iface" 'dst' 'ip' && ips 'flush' "$iface" 'dst' 'ip'; then
+                               set_name4="${ipsPrefix}_${iface}_4_dst_ip"
+                               for i in $chainsList; do
+                                       i="$(str_to_lower "$i")"
+                                       ipt -t nat -I "${nftPrefix}_${i}" -p udp -m udp --dport 53 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$dnsPort" -m comment --comment "TorDNS-UDP" || s=1
+                                       ipt -t nat -I "${nftPrefix}_${i}" -p tcp -m tcp --dport 80 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$trafficPort" -m comment --comment "TorHTTP-TCP" || s=1
+                                       ipt -t nat -I "${nftPrefix}_${i}" -p udp -m udp --dport 80 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$trafficPort" -m comment --comment "TorHTTP-UDP" || s=1
+                                       ipt -t nat -I "${nftPrefix}_${i}" -p tcp -m tcp --dport 443 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$trafficPort" -m comment --comment "TorHTTPS-TCP" || s=1
+                                       ipt -t nat -I "${nftPrefix}_${i}" -p udp -m udp --dport 443 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$trafficPort" -m comment --comment "TorHTTPS-UDP" || s=1
+                               done
+                       else
+                               s=1
+                       fi
+                       displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}"
+                       if [ "$s" -eq 0 ]; then
+                               gatewaySummary="${gatewaySummary}${displayText}\\n"
+                               output_ok
+                       else
+                               state add 'errorSummary' 'errorFailedSetup' "$displayText"
+                               output_fail
+                       fi
+                       ;;
+       esac
+       return $s
+}
+interface_process_tor_nft() {
+       local s=0 iface="$1" action="$2"
+       local displayText set_name4 set_name6
+       local dnsPort trafficPort
+       case "$action" in
+               reload)
+                       displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}"
+                       gatewaySummary="${gatewaySummary}${displayText}\\n"
+                       ;;
+               destroy)
+                       ;;
+               create)
+                       output 2 "Creating TOR redirects "
+                       dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')"
+                       trafficPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')"
+                       dnsPort="${dnsPort:-9053}"; trafficPort="${trafficPort:-9040}"; 
+                       if resolver 'create_resolver_set' "$iface" 'dst' 'ip' && nftset 'flush' "$iface" 'dst' 'ip'; then
+                               set_name4="${nftPrefix}_${iface}_4_dst_ip"
+                               set_name6="${nftPrefix}_${iface}_6_dst_ip"
+                               nft meta nfproto ipv4 udp daddr "@${set_name4}" dport 53 counter redirect to :"$dnsPort" comment "Tor-DNS-UDP-ipv4" || s=1
+                               nft meta nfproto ipv4 tcp daddr "@${set_name4}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-TCP-ipv4" || s=1
+                               nft meta nfproto ipv4 udp daddr "@${set_name4}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-UDP-ipv4" || s=1
+                               nft meta nfproto ipv4 tcp daddr "@${set_name4}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-TCP-ipv4" || s=1
+                               nft meta nfproto ipv4 udp daddr "@${set_name4}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-UDP-ipv4" || s=1
+                               nft6 meta nfproto ipv6 udp daddr "@${set_name6}" dport 53 counter redirect to :"$dnsPort" comment "Tor-DNS-UDP-ipv6" || s=1
+                               nft6 meta nfproto ipv6 tcp daddr "@${set_name6}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-TCP-ipv6" || s=1
+                               nft6 meta nfproto ipv6 udp daddr "@${set_name6}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-UDP-ipv6" || s=1
+                               nft6 meta nfproto ipv6 tcp daddr "@${set_name6}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-TCP-ipv6" || s=1
+                               nft6 meta nfproto ipv6 udp daddr "@${set_name6}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-UDP-ipv6" || s=1
+                       else
+                               s=1
+                       fi
+                       displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}"
+                       if [ "$s" -eq 0 ]; then
+                               gatewaySummary="${gatewaySummary}${displayText}\\n"
+                               output_ok
+                       else
+                               state add 'errorSummary' 'errorFailedSetup' "$displayText"
+                               output_fail
+                       fi
+                       ;;
+       esac
+       return $s
+}
+
+interface_routing() {
+       local action="$1" tid="$2" mark="$3" iface="$4" gw4="$5" dev="$6" gw6="$7" dev6="$8" priority="$9"
+       local dscp s=0 i ipv4_error=1 ipv6_error=1
+       if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
+               return 1
+       fi
+       case "$action" in
+               create)
+                       if is_netifd_table "$iface"; then
+                               ipv4_error=0
+                               $ip_full -4 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
+                               $ip_full -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
+                               if is_nft; then
+                                       nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1 
+                                       nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
+                                       nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
+                               else
+                                       ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1
+                                       ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1
+                                       ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1
+                               fi
+                               if [ -n "$ipv6_enabled" ]; then
+                                       ipv6_error=0
+                                       $ip_full -6 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
+                                       $ip_full -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
+                               fi
+                       else
+                               sed -i "/${ipTablePrefix}_${iface}/d" /etc/iproute2/rt_tables
+                               $ip_full -4 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
+                               $ip_full -4 route flush table "$tid" >/dev/null 2>&1
+                               if [ -n "$gw4" ] || [ "$strict_enforcement" -ne 0 ]; then
+                                       ipv4_error=0
+                                       echo "$tid ${ipTablePrefix}_${iface}" >> /etc/iproute2/rt_tables
+                                       if [ -z "$gw4" ]; then
+                                               $ip_full -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
+                                       else
+                                               $ip_full -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
+                                       fi
+                                       # shellcheck disable=SC2086
+                                       while read -r i; do
+                                               i="$(echo "$i" | sed 's/ linkdown$//')"
+                                               i="$(echo "$i" | sed 's/ onlink$//')"
+                                               idev="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')"
+                                               if ! is_supported_iface_dev "$idev"; then
+                                                       $ip_full -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1
+                                               fi
+                                       done << EOF
+                                       $($ip_full -4 route list table main)
+EOF
+                                       $ip_full -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
+                                       if is_nft; then
+                                               nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1 
+                                               nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
+                                               nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
+                                       else
+                                               ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1
+                                               ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1
+                                               ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1
+                                       fi
+                               fi
+                               if [ -n "$ipv6_enabled" ]; then
+                                       ipv6_error=0
+                                       $ip_full -6 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
+                                       $ip_full -6 route flush table "$tid" >/dev/null 2>&1
+                                       if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne 0 ]; then
+                                               if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
+                                                       $ip_full -6 route add unreachable default table "$tid" || ipv6_error=1
+                                               elif $ip_full -6 route list table main | grep -q " dev $dev6 "; then
+                                                       while read -r i; do
+                                                               i="$(echo "$i" | sed 's/ linkdown$//')"
+                                                               i="$(echo "$i" | sed 's/ onlink$//')"
+                                                               $ip_full -6 route add "$i" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+                                                       done << EOF
+                                                       $($ip_full -6 route list table main | grep " dev $dev6 ")
+EOF
+                                               else
+                                                       $ip_full -6 route add "$($ip_full -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+                                                       $ip_full -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+                                               fi
+                                       fi
+                                       $ip_full -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
+                               fi
+                       fi
+                       if [ "$ipv4_error" -eq 0 ] || [ "$ipv6_error" -eq 0 ]; then
+                               dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)"
+                               if is_nft; then
+                                       if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
+                                               nft add rule inet "$nftTable" "${nftPrefix}_prerouting ip dscp ${dscp} goto ${nftPrefix}_mark_${mark}" || s=1
+                                       fi
+                                       if [ "$iface" = "$icmp_interface" ]; then
+                                               nft add rule inet "$nftTable" "${nftPrefix}_output ip protocol icmp goto ${nftPrefix}_mark_${mark}" || s=1
+                                       fi
+                               else
+                                       if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
+                                               ipt -t mangle -I "${iptPrefix}_PREROUTING" -m dscp --dscp "${dscp}" -g "${iptPrefix}_MARK_${mark}" || s=1
+                                       fi
+                                       if [ "$iface" = "$icmp_interface" ]; then
+                                               ipt -t mangle -I "${iptPrefix}_OUTPUT" -p icmp -g "${iptPrefix}_MARK_${mark}" || s=1
+                                       fi
+                               fi
+                       else
+                               s=1
+                       fi
+                       return "$s"
+               ;;
+               create_user_set)
+                       [ -z "$createUserSets" ] && return 0;
+                       if is_nft; then
+                               nftset 'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s=1
+                               nftset 'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s=1
+                               nftset 'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s=1
+                       else
+                               ips 'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s=1
+                               ips 'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s=1
+                               ips 'create_user_set' "$iface" 'dst' 'net' 'user' '' "$mark" || s=1
+                               ips 'create_user_set' "$iface" 'src' 'net' 'user' '' "$mark" || s=1
+                               ips 'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s=1
+                       fi
+                       return "$s"
+               ;;
+               delete|destroy)
+                       $ip_full rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
+                       if ! is_netifd_table "$iface"; then
+                               $ip_full route flush table "$tid" >/dev/null 2>&1
+                               sed -i "/${ipTablePrefix}_${iface}/d" /etc/iproute2/rt_tables
+                       fi
+                       return "$s"
+               ;;
+               reload_interface)
+                       is_netifd_table "$iface" && return 0;
+                       ipv4_error=0
+                       $ip_full -4 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
+                       $ip_full -4 route flush table "$tid" >/dev/null 2>&1
+                       if [ -n "$gw4" ] || [ "$strict_enforcement" -ne 0 ]; then
+                               if [ -z "$gw4" ]; then
+                                       $ip_full -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
+                               else
+                                       $ip_full -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
+                               fi
+                               $ip_full rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
+                       fi
+                       if [ -n "$ipv6_enabled" ]; then
+                               ipv6_error=0
+                               $ip_full -6 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
+                               $ip_full -6 route flush table "$tid" >/dev/null 2>&1
+                               if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne 0 ]; then
+                                       if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
+                                               $ip_full -6 route add unreachable default table "$tid" || ipv6_error=1
+                                       elif $ip_full -6 route list table main | grep -q " dev $dev6 "; then
+                                               while read -r i; do
+                                                       $ip_full -6 route add "$i" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+                                               done << EOF
+                                               $($ip_full -6 route list table main | grep " dev $dev6 ")
+EOF
+                                       else
+                                               $ip_full -6 route add "$($ip_full -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+                                               $ip_full -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+                                       fi
+                               fi
+                               $ip_full -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
+                       fi
+                       if [ "$ipv4_error" -eq 0 ] || [ "$ipv6_error" -eq 0 ]; then
+                               s=0
+                       else
+                               s=1
+                       fi
+                       return "$s"
+               ;;
+       esac
+}
+
+json_add_gateway() {
+       local action="$1" tid="$2" mark="$3" iface="$4" gw4="$5" dev4="$6" gw6="$7" dev6="$8" priority="$9" default="${10}"
+       json_add_object ''
+       json_add_string name "$iface"
+       json_add_string device_ipv4 "$dev4"
+       json_add_string gateway_ipv4 "$gw4"
+       json_add_string device_ipv6 "$dev6"
+       json_add_string gateway_ipv6 "$gw6"
+       if [ -n "$default" ]; then
+               json_add_boolean default true
+       else
+               json_add_boolean default false
+       fi
+       json_add_string action "$action"
+       json_add_string table_id "$tid"
+       json_add_string mark "$mark"
+       json_add_string priority "$priority"
+       json_close_object
+}
+
+interface_process() {
+       local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" reloadedIface="$3"
+       local displayText dispDev dispGw4 dispGw6 dispStatus
+
+       if [ "$iface" = 'all' ] && [ "$action" = 'prepare' ]; then
+               config_load 'network'
+               ifaceMark="$(printf '0x%06x' "$wan_mark")"
+               ifacePriority="$wan_ip_rules_priority"
+               return 0
+       fi
+
+       is_supported_interface "$iface" || return 0
+       is_wan6 "$iface" && return 0
+       [ $((ifaceMark)) -gt $((fw_mask)) ] && return 1
+
+       network_get_device dev "$iface"
+       if is_wan "$iface" && [ -n "$wanIface6" ] && str_contains "$wanIface6" "$iface"; then
+               network_get_device dev6 "$wanIface6"
+       fi
+
+       [ -z "$dev6" ] && dev6="$dev"
+       [ -z "$ifaceMark" ] && ifaceMark="$(printf '0x%06x' "$wan_mark")"
+       [ -z "$ifacePriority" ] && ifacePriority="$wan_ip_rules_priority"
+
+       ifaceTableID="$(get_rt_tables_id "$iface")"
+       [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
+       eval "mark_${iface//-/_}"='$ifaceMark'
+       eval "tid_${iface//-/_}"='$ifaceTableID'
+       pbr_get_gateway gw4 "$iface" "$dev"
+       pbr_get_gateway6 gw6 "$iface" "$dev6"
+       dispGw4="${gw4:-0.0.0.0}"
+       dispGw6="${gw6:-::/0}"
+       [ "$iface" != "$dev" ] && dispDev="$dev"
+       is_default_dev "$dev" && dispStatus="${__OK__}"
+       displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
+
+       case "$action" in
+               create)
+                       output 2 "Setting up routing for '$displayText' "
+                       if interface_routing 'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" && \
+                               interface_routing 'create_user_set' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
+                               json_add_gateway 'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
+                               gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
+                               output_ok
+                       else
+                               state add 'errorSummary' 'errorFailedSetup' "$displayText"
+                               output_fail
+                       fi
+               ;;
+#              create_user_set)
+#                      interface_routing 'create_user_set' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"
+#              ;;
+               destroy)
+                       displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
+                       output 2 "Removing routing for '$displayText' "
+                       interface_routing 'destroy' "${ifaceTableID}" "${ifaceMark}" "${iface}"
+                       output_ok
+               ;;
+               reload)
+                       gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
+               ;;
+               reload_interface)
+                       if [ "$iface" = "$reloadedIface" ]; then
+                               output 2 "Reloading routing for '$displayText' "
+                               if interface_routing 'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
+                                       json_add_gateway 'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
+                                       gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
+                                       output_ok
+                               else
+                                       state add 'errorSummary' 'errorFailedReload' "$displayText"
+                                       output_fail
+                               fi
+                       else
+                               gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
+                       fi
+               ;;
+       esac
+#      ifaceTableID="$((ifaceTableID + 1))"
+       ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))"
+       ifacePriority="$((ifacePriority + 1))"
+       return $s
+}
+
+user_file_process() {
+       local shellBin="${SHELL:-/bin/ash}"
+       [ "$enabled" -gt 0 ] || return 0
+       if [ ! -s "$path" ]; then
+               state add 'errorSummary' 'errorUserFileNotFound' "$path"
+               output_fail
+               return 1
+       fi
+       if ! $shellBin -n "$path"; then
+               state add 'errorSummary' 'ererrorUserFileSyntax' "$path"
+               output_fail
+               return 1
+       fi
+       output 2 "Running $path "
+# shellcheck disable=SC1090
+       if ! . "$path"; then
+               state add 'errorSummary' 'errorUserFileRunning' "$path"
+               if grep -q -w 'curl' "$path" && ! is_present 'curl'; then
+                       state add 'errorSummary' 'errorUserFileNoCurl' "$path"
+               fi
+               output_fail
+               return 1
+       else
+               output_ok
+               return 0
+       fi
+}
+
+on_firewall_reload() { 
+       if [ -z "$(ubus_get_status 'gateways')" ]; then # service is not running, do not start it on firewall reload
+               logger -t "$packageName" "Reload on firewall action aborted: service not running."
+               return 0;
+       else
+               rc_procd start_service 'on_firewall_reload' "$1"
+       fi
+}
+on_interface_reload() { rc_procd start_service 'on_interface_reload' "$1"; }
+
+start_service() {
+       local resolverStoredHash resolverNewHash i reloadedIface param="$1"
+       local createUserSets
+
+       load_environment 'on_start' "$(load_validate_config)" || return 1
+       is_wan_up || return 1
+       rm -f "$nftTempFile"
+
+       case "$param" in
+               on_boot)
+                       serviceStartTrigger='on_start'
+               ;;
+               on_firewall_reload)
+                       serviceStartTrigger='on_start'
+               ;;
+               on_interface_reload)
+                       serviceStartTrigger='on_interface_reload'
+                       reloadedIface="$2"
+               ;;
+               on_reload)
+                       serviceStartTrigger='on_reload'
+               ;;
+               on_restart)
+                       serviceStartTrigger='on_start'
+               ;;
+       esac
+
+       if [ -n "$reloadedIface" ] && ! is_supported_interface "$reloadedIface"; then
+               return 0
+       fi
+
+       if [ -n "$(ubus_get_status error)" ] || [ -n "$(ubus_get_status warning)" ]; then
+               serviceStartTrigger='on_start'
+               unset reloadedIface
+       elif ! is_service_running; then
+               serviceStartTrigger='on_start'
+               unset reloadedIface
+       elif [ -z "$(ubus_get_status gateway)" ]; then
+               serviceStartTrigger='on_start'
+               unset reloadedIface
+       elif [ "$serviceStartTrigger" = 'on_interface_reload' ] && \
+                        [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_4')" ] && \
+                        [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_6')" ]; then
+               serviceStartTrigger='on_start'
+               unset reloadedIface
+       else
+               serviceStartTrigger="${serviceStartTrigger:-on_start}"
+       fi
+
+       if is_config_enabled 'include'; then
+               createUserSets='true'
+       fi
+
+       procd_open_instance "main"
+       procd_set_param command /bin/true
+       procd_set_param stdout 1
+       procd_set_param stderr 1
+       procd_open_data
+
+       case $serviceStartTrigger in
+               on_interface_reload)
+                       output 1 "Reloading Interface: $reloadedIface "
+                       json_add_array 'gateways'
+                       interface_process 'all' 'prepare'
+                       config_foreach interface_process 'interface' 'reload_interface' "$reloadedIface"
+                       json_close_array
+                       output 1 '\n'
+               ;;
+               on_reload)
+                       traffic_killswitch 'insert'
+                       resolver 'store_hash'
+                       resolver 'cleanup_all'
+                       resolver 'configure'
+                       resolver 'init'
+                       cleanup_main_chains
+                       cleanup_sets
+                       if ! is_nft; then
+                               for i in $chainsList; do
+                                       i="$(str_to_upper "$i")"
+                                       ipt -t mangle -N "${iptPrefix}_${i}"
+                                       ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
+                               done
+                       fi
+                       json_add_array 'gateways'
+                       interface_process 'all' 'prepare'
+                       config_foreach interface_process 'interface' 'reload'
+                       interface_process_tor 'tor' 'destroy'
+                       is_tor_running && interface_process_tor 'tor' 'reload'
+                       json_close_array
+                       if is_config_enabled 'policy'; then
+                               output 1 'Processing policies '
+                               config_load "$packageName"
+                               config_foreach load_validate_policy 'policy' policy_process
+                               output 1 '\n'
+                       fi
+                       if is_config_enabled 'include'; then
+                               traffic_killswitch 'remove'
+                               output 1 'Processing user file(s) '
+                               config_load "$packageName"
+                               config_foreach load_validate_include 'include' user_file_process
+                               output 1 '\n'
+                               resolver 'init_end'
+                               resolver 'compare_hash' && resolver 'restart'
+                       else
+                               resolver 'init_end'
+                               resolver 'compare_hash' && resolver 'restart'
+                               traffic_killswitch 'remove'
+                       fi
+               ;;
+               on_start|*)
+                       traffic_killswitch 'insert'
+                       resolver 'store_hash'
+                       resolver 'cleanup_all'
+                       resolver 'configure'
+                       resolver 'init'
+                       cleanup_main_chains
+                       cleanup_sets
+                       cleanup_marking_chains
+                       if ! is_nft; then
+                               for i in $chainsList; do
+                                       i="$(str_to_upper "$i")"
+                                       ipt -t mangle -N "${iptPrefix}_${i}"
+                                       ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
+                               done
+                       fi
+                       output 1 'Processing interfaces '
+                       json_add_array 'gateways'
+                       interface_process 'all' 'prepare'
+                       config_foreach interface_process 'interface' 'create'
+                       interface_process_tor 'tor' 'destroy'
+                       is_tor_running && interface_process_tor 'tor' 'create'
+                       json_close_array
+                       ip route flush cache
+                       output 1 '\n'
+                       if is_config_enabled 'policy'; then
+                               output 1 'Processing policies '
+                               config_load "$packageName"
+                               config_foreach load_validate_policy 'policy' policy_process
+                               output 1 '\n'
+                       fi
+                       if is_config_enabled 'include'; then
+                               traffic_killswitch 'remove'
+                               output 1 'Processing user file(s) '
+                               config_load "$packageName"
+                               config_foreach load_validate_include 'include' user_file_process
+                               output 1 '\n'
+                               resolver 'init_end'
+                               resolver 'compare_hash' && resolver 'restart'
+                       else
+                               resolver 'init_end'
+                               resolver 'compare_hash' && resolver 'restart'
+                               traffic_killswitch 'remove'
+                       fi
+               ;;
+       esac
+
+       if [ -z "$gatewaySummary" ]; then
+               state add 'errorSummary' 'errorNoGateways'
+       fi
+       json_add_object 'status'
+       [ -n "$gatewaySummary" ] && json_add_string 'gateways' "$gatewaySummary"
+       [ -n "$errorSummary" ] && json_add_string 'errors' "$errorSummary"
+       [ -n "$warningSummary" ] && json_add_string 'warnings' "$warningSummary"
+       if [ "$strict_enforcement" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then
+               json_add_string 'mode' "strict"
+       fi
+       json_close_object
+       procd_close_data
+       procd_close_instance
+}
+
+service_started() {
+       if is_nft; then
+               [ -n "$gatewaySummary" ] && output "$serviceName (nft) started with gateways:\\n${gatewaySummary}"
+       else
+               [ -n "$gatewaySummary" ] && output "$serviceName (iptables) started with gateways:\\n${gatewaySummary}"
+       fi
+       state print 'errorSummary'
+       state print 'warningSummary'
+       if [ -n "$errorSummary" ]; then
+               return 2
+       elif [ -n "$warningSummary" ]; then
+               return 1
+       else
+               return 0
+       fi
+}
+
+service_triggers() {
+       local n
+       load_environment 'on_triggers'
+# shellcheck disable=SC2034
+       PROCD_RELOAD_DELAY=$(( procd_reload_delay * 1000 ))
+       procd_open_validate
+               load_validate_config
+               load_validate_policy
+               load_validate_include
+       procd_close_validate
+       procd_open_trigger
+               procd_add_reload_trigger 'openvpn'
+               procd_add_config_trigger "config.change" "${packageName}" /etc/init.d/${packageName} reload
+               for n in $ifacesSupported; do 
+                       procd_add_interface_trigger "interface.*" "$n" /etc/init.d/${packageName} on_interface_reload "$n"
+               done
+       procd_close_trigger
+       if [ "$serviceStartTrigger" = 'on_start' ]; then
+               output 3 "$serviceName monitoring interfaces: ${ifacesSupported}\\n"
+       fi
+}
+
+stop_service() {
+       local i
+       load_environment 'on_stop'
+       is_service_running || return 0
+       traffic_killswitch 'insert'
+       cleanup_main_chains
+       cleanup_sets
+       cleanup_marking_chains
+       output 1 'Resetting interfaces '
+       config_load 'network'
+       config_foreach interface_process 'interface' 'destroy'
+       interface_process_tor 'tor' 'destroy'
+       output 1 "\\n"
+       ip route flush cache
+       unset ifaceMark
+       unset ifaceTableID
+       resolver 'store_hash'
+       resolver 'cleanup_all'
+       resolver 'compare_hash' && resolver 'restart'
+       traffic_killswitch 'remove'
+       if [ "$enabled" -ne 0 ]; then
+               if is_nft; then
+                       output "$serviceName (nft) stopped "; output_okn;
+               else
+                       output "$serviceName (iptables) stopped "; output_okn;
+               fi
+       fi
+}
+
+status_service() {
+       local _SEPARATOR_='============================================================'
+       load_environment 'on_status'
+       if is_nft; then
+               status_service_nft "$@"
+       else
+               status_service_iptables "$@"
+       fi
+}
+
+status_service_nft() {
+       local i dev dev6 wan_tid
+
+       json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
+       if [ -n "$wanIface4" ]; then
+               network_get_gateway wanGW4 "$wanIface4"
+               network_get_device dev "$wanIface4"
+       fi
+       if [ -n "$wanIface6" ]; then
+               network_get_device dev6 "$wanIface6"
+               wanGW6=$($ip_full -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
+               [ "$wanGW6" = "default" ] && wanGW6=$($ip_full -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
+       fi
+       while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
+       [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
+       status="$serviceName running on $dist $vers."
+       [ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
+       [ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
+
+       echo "$_SEPARATOR_"
+       echo "$packageName - environment"
+       echo "$status"
+       echo "$_SEPARATOR_"
+       dnsmasq --version 2>/dev/null | sed '/^$/,$d'
+       echo "$_SEPARATOR_"
+       echo "$packageName chains - policies"
+       for i in forward input output prerouting postrouting; do
+               "$nft" list table inet "$nftTable" | sed -n "/chain ${nftPrefix}_${i} {/,/\t}/p"
+       done
+       echo "$_SEPARATOR_"
+       echo "$packageName chains - marking"
+       for i in $(get_mark_nft_chains); do
+               "$nft" list table inet "$nftTable" | sed -n "/chain ${i} {/,/\t}/p"
+       done
+       echo "$_SEPARATOR_"
+       echo "$packageName nft sets"
+       for i in $(get_nft_sets); do
+               "$nft" list table inet "$nftTable" | sed -n "/set ${i} {/,/\t}/p"
+       done
+       if [ -s "$dnsmasqFile" ]; then
+               echo "$_SEPARATOR_"
+               echo "dnsmasq sets"
+               cat "$dnsmasqFile"
+       fi
+#      echo "$_SEPARATOR_"
+#      ip rule list | grep "${packageName}_"
+       echo "$_SEPARATOR_"
+       tableCount="$(grep -c "${packageName}_" /etc/iproute2/rt_tables)" || tableCount=0
+       wan_tid=$(($(get_rt_tables_next_id)-tableCount))
+       i=0; while [ $i -lt $tableCount ]; do 
+               echo "IPv4 table $((wan_tid + i)) route: $($ip_full -4 route show table $((wan_tid + i)) | grep default)"
+               echo "IPv4 table $((wan_tid + i)) rule(s):"
+               $ip_full -4 rule list table "$((wan_tid + i))"
+               i=$((i + 1))
+       done
+}
+
+status_service_iptables() {
+       local dist vers out id s param status set_d set_p tableCount i=0 dev dev6 j wan_tid
+
+       json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
+       if [ -n "$wanIface4" ]; then
+               network_get_gateway wanGW4 "$wanIface4"
+               network_get_device dev "$wanIface4"
+       fi
+       if [ -n "$wanIface6" ]; then
+               network_get_device dev6 "$wanIface6"
+               wanGW6=$($ip_full -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
+               [ "$wanGW6" = "default" ] && wanGW6=$($ip_full -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
+       fi
+       while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
+       [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
+       status="$serviceName running on $dist $vers."
+       [ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
+       [ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
+       {
+               echo "$status"
+               echo "$_SEPARATOR_"
+               dnsmasq --version 2>/dev/null | sed '/^$/,$d'
+               if [ -n "$1" ]; then
+                       echo "$_SEPARATOR_"
+                       echo "Resolving domains"
+                       for i in $1; do
+                               echo "$i: $(resolveip "$i" | tr '\n' ' ')"
+                       done
+               fi
+
+               echo "$_SEPARATOR_"
+               echo "Routes/IP Rules"
+               tableCount="$(grep -c "${packageName}_" /etc/iproute2/rt_tables)" || tableCount=0
+               if [ -n "$set_d" ]; then route; else route | grep '^default'; fi
+               if [ -n "$set_d" ]; then ip rule list; fi
+               wan_tid=$(($(get_rt_tables_next_id)-tableCount))
+               i=0; while [ $i -lt $tableCount ]; do 
+                       echo "IPv4 table $((wan_tid + i)) route: $($ip_full -4 route show table $((wan_tid + i)) | grep default)"
+                       echo "IPv4 table $((wan_tid + i)) rule(s):"
+                       $ip_full -4 rule list table "$((wan_tid + i))"
+                       i=$((i + 1))
+               done
+
+               if [ -n "$ipv6_enabled" ]; then
+                       i=0; while [ $i -lt $tableCount ]; do
+                               $ip_full -6 route show table $((wan_tid + i)) | while read -r param; do
+                                       echo "IPv6 Table $((wan_tid + i)): $param"
+                               done
+                               i=$((i + 1))
+                       done
+               fi
+
+               for j in Mangle NAT; do
+                       if [ -z "$set_d" ]; then
+                               for i in $chainsList; do
+                                       i="$(str_to_upper "$i")"
+                                       if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}" >/dev/null 2>&1; then
+                                               echo "$_SEPARATOR_"
+                                               echo "$j IP Table: $i"
+                                               iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
+                                               if [ -n "$ipv6_enabled" ]; then
+                                                       echo "$_SEPARATOR_"
+                                                       echo "$j IPv6 Table: $i"
+                                                       iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
+                                               fi
+                                       fi
+                               done
+                       else
+                               echo "$_SEPARATOR_"
+                               echo "$j IP Table"
+                               iptables -L -t "$(str_to_lower $j)"
+                               if [ -n "$ipv6_enabled" ]; then
+                                       echo "$_SEPARATOR_"
+                                       echo "$j IPv6 Table"
+                                       iptables -L -t "$(str_to_lower $j)"
+                               fi
+                       fi
+                       i=0; ifaceMark="$wan_mark";
+                       while [ $i -lt $tableCount ]; do
+                               if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}" >/dev/null 2>&1; then
+                                       echo "$_SEPARATOR_"
+                                       echo "$j IP Table MARK Chain: ${iptPrefix}_MARK_${ifaceMark}"
+                                       iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}"
+                                       ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))";
+                               fi
+                               i=$((i + 1))
+                       done
+               done
+
+               echo "$_SEPARATOR_"
+               echo "Current ipsets"
+               ipset save
+               if [ -s "$dnsmasqFile" ]; then
+                       echo "$_SEPARATOR_"
+                       echo "DNSMASQ sets"
+                       cat "$dnsmasqFile"
+               fi
+               if [ -s "$aghIpsetFile" ]; then
+                       echo "$_SEPARATOR_"
+                       echo "AdGuardHome sets"
+                       cat "$aghIpsetFile"
+               fi
+               echo "$_SEPARATOR_"
+       } | tee -a /var/${packageName}-support
+       if [ -n "$set_p" ]; then
+               printf "%b" "Pasting to paste.ee... "
+               if is_present 'curl' && is_variant_installed 'libopenssl' && is_installed 'ca-bundle'; then
+                       json_init; json_add_string "description" "${packageName}-support"
+                       json_add_array "sections"; json_add_object '0'
+                       json_add_string "name" "$(uci -q get system.@system[0].hostname)"
+                       json_add_string "contents" "$(cat /var/${packageName}-support)"
+                       json_close_object; json_close_array; payload=$(json_dump)
+                       out=$(curl -s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload")
+                       json_load "$out"; json_get_var id id; json_get_var s success
+                       [ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" || printf "%b" "$__FAIL__\\n"
+                       [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
+               else
+                       printf "%b" "${__FAIL__}\\n"
+                       printf "%b" "${_ERROR_}: The curl, libopenssl or ca-bundle packages were not found!\\nRun 'opkg update; opkg install curl libopenssl ca-bundle' to install them.\\n"
+               fi
+       else
+               printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n"
+       fi
+}
+
+# shellcheck disable=SC2120
+load_validate_config() {
+       uci_load_validate "$packageName" "$packageName" "$1" "${2}${3:+ $3}" \
+               'enabled:bool:0' \
+               'procd_boot_delay:integer:0' \
+               'strict_enforcement:bool:1' \
+               'secure_reload:bool:0' \
+               'ipv6_enabled:bool:0' \
+               'resolver_set:or("", "none", "dnsmasq.ipset", "dnsmasq.nftset")' \
+               'verbosity:range(0,2):1' \
+               "wan_mark:regex('0x[A-Fa-f0-9]{8}'):0x010000" \
+               "fw_mask:regex('0x[A-Fa-f0-9]{8}'):0xff0000" \
+               'icmp_interface:or("","ignore", uci("network", "@interface"))' \
+               'ignored_interface:list(uci("network", "@interface"))' \
+               'supported_interface:list(uci("network", "@interface"))' \
+               'boot_timeout:integer:30' \
+               'wan_ip_rules_priority:uinteger:30000' \
+               'rule_create_option:or("", "add", "insert"):add' \
+               'procd_reload_delay:integer:0' \
+               'webui_supported_protocol:list(string)'
+}
+
+# shellcheck disable=SC2120
+load_validate_policy() {
+       local name
+       local enabled
+       local interface
+       local proto
+       local chain
+       local src_addr
+       local src_port
+       local dest_addr
+       local dest_port
+       uci_load_validate "$packageName" 'policy' "$1" "${2}${3:+ $3}" \
+               'name:string:Untitled' \
+               'enabled:bool:1' \
+               'interface:or(uci("network", "@interface"),"ignore"):wan' \
+               'proto:or(string)' \
+               'chain:or("", "forward", "input", "output", "prerouting", "postrouting", "FORWARD", "INPUT", "OUTPUT", "PREROUTING", "POSTROUTING"):prerouting' \
+               'src_addr:list(neg(or(host,network,macaddr,string)))' \
+               'src_port:list(neg(or(portrange,string)))' \
+               'dest_addr:list(neg(or(host,network,string)))' \
+               'dest_port:list(neg(or(portrange,string)))'
+}
+
+# shellcheck disable=SC2120
+load_validate_include() {
+       local path=
+       local enabled=
+       uci_load_validate "$packageName" 'include' "$1" "${2}${3:+ $3}" \
+               'path:file' \
+               'enabled:bool:0'
+}
diff --git a/net/pbr/files/etc/uci-defaults/90-pbr b/net/pbr/files/etc/uci-defaults/90-pbr
new file mode 100644 (file)
index 0000000..237ebac
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/sh
+# shellcheck disable=SC1091,SC3037,SC3043
+
+readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
+
+# Transition from vpn-policy-routing
+if [ -s '/etc/config/vpn-policy-routing' ] && [ ! -s '/etc/config/pbr-opkg' ]; then
+       echo "Migrating vpn-policy-routing config file."
+       mv '/etc/config/pbr' '/etc/config/pbr-opkg'
+       sed 's/vpn-policy-routing/pbr/g' /etc/config/vpn-policy-routing > /etc/config/pbr
+       uci set vpn-policy-routing.config.enabled=0; uci commit vpn-policy-routing;
+fi
+
+# Transition from older versions of pbr
+sed -i 's/resolver_ipset/resolver_set/g' /etc/config/pbr
+sed -i 's/iptables_rule_option/rule_create_option/g' /etc/config/pbr
+sed -i "s/'FORWARD'/'forward'/g" /etc/config/pbr
+sed -i "s/'INPUT'/'input'/g" /etc/config/pbr
+sed -i "s/'OUTPUT'/'output'/g" /etc/config/pbr
+sed -i "s/'PREROUTING'/'prerouting'/g" /etc/config/pbr
+sed -i "s/'POSTROUTING'/'postrouting'/g" /etc/config/pbr
+sed -i "s/option fw_mask '0x\(.*\)'/option fw_mask '\1'/g" /etc/config/pbr
+sed -i "s/option wan_mark '0x\(.*\)'/option wan_mark '\1'/g" /etc/config/pbr
+
+uci -q batch <<-EOT
+       delete firewall.pbr
+       set firewall.pbr='include'
+       set firewall.pbr.fw4_compatible='1'
+       set firewall.pbr.type='script'
+       set firewall.pbr.path='/usr/share/pbr/pbr.firewall.include'
+       commit firewall
+EOT
+
+exit 0
diff --git a/net/pbr/files/etc/uci-defaults/91-pbr b/net/pbr/files/etc/uci-defaults/91-pbr
new file mode 100644 (file)
index 0000000..0d759c2
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/sh
+# shellcheck disable=SC1091,SC3037,SC3043
+
+readonly packageName='pbr'
+readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
+
+pbr_iface_setup() {
+       local iface="${1}"
+       local proto
+       config_get proto "${iface}" proto
+       case "${iface}" in
+               (lan|loopback) return 0 ;;
+       esac
+       case "${proto}" in
+               (gre*|nebula|relay|vti*|vxlan|xfrm) return 0 ;;
+               (none)
+                       uci -q set "network.${iface}_rt=route"
+                       uci -q set "network.${iface}_rt.interface=${iface}"
+                       uci -q set "network.${iface}_rt.target=0.0.0.0/0"
+                       uci -q set "network.${iface}_rt6=route6"
+                       uci -q set "network.${iface}_rt6.interface=${iface}"
+                       uci -q set "network.${iface}_rt6.target=::/0"
+               ;;
+       esac
+       echo -en "Setting up ${packageName} routing tables for ${iface}... "
+       uci -q set "network.${iface}.ip4table=${packageName}_${iface%6}"
+       uci -q set "network.${iface}.ip6table=${packageName}_${iface%6}"
+       if ! grep -q -E -e "^[0-9]+\s+${packageName}_${iface%6}$" /etc/iproute2/rt_tables; then
+               sed -i -e "\$a $(($(sort -r -n /etc/iproute2/rt_tables | grep -o -E -m 1 "^[0-9]+")+1))\t${packageName}_${iface%6}" \
+                       /etc/iproute2/rt_tables
+       fi
+       echo -e "${__OK__}"
+}
+
+. /lib/functions.sh
+. /lib/functions/network.sh
+config_load network
+config_foreach pbr_iface_setup interface
+network_flush_cache
+network_find_wan iface
+network_find_wan6 iface6
+# shellcheck disable=SC2154
+[ -n "$iface" ] && uci -q batch << EOF
+set network.default='rule'
+set network.default.lookup='${packageName}_${iface%6}'
+set network.default.priority='80000'
+EOF
+[ -n "$iface6" ] && uci -q batch << EOF
+set network.default6='rule6'
+set network.default6.lookup='${packageName}_${iface6%6}'
+set network.default6.priority='80000'
+EOF
+uci commit network
+echo -en "Restarting network... "
+/etc/init.d/network restart
+echo -e "${__OK__}"
+
+exit 0
diff --git a/net/pbr/files/usr/share/nftables.d/chain-post/mangle_forward/30-pbr.nft b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_forward/30-pbr.nft
new file mode 100644 (file)
index 0000000..d11ad84
--- /dev/null
@@ -0,0 +1 @@
+jump pbr_forward comment "Jump into pbr forward chain";
diff --git a/net/pbr/files/usr/share/nftables.d/chain-post/mangle_input/30-pbr.nft b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_input/30-pbr.nft
new file mode 100644 (file)
index 0000000..b3ce9db
--- /dev/null
@@ -0,0 +1 @@
+jump pbr_input comment "Jump into pbr input chain";
diff --git a/net/pbr/files/usr/share/nftables.d/chain-post/mangle_output/30-pbr.nft b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_output/30-pbr.nft
new file mode 100644 (file)
index 0000000..c98514b
--- /dev/null
@@ -0,0 +1 @@
+jump pbr_output comment "Jump into pbr output chain";
diff --git a/net/pbr/files/usr/share/nftables.d/chain-post/mangle_postrouting/30-pbr.nft b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_postrouting/30-pbr.nft
new file mode 100644 (file)
index 0000000..cd5d1b4
--- /dev/null
@@ -0,0 +1 @@
+jump pbr_postrouting comment "Jump into pbr postrouting chain";
diff --git a/net/pbr/files/usr/share/nftables.d/chain-post/mangle_prerouting/30-pbr.nft b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_prerouting/30-pbr.nft
new file mode 100644 (file)
index 0000000..a4471d3
--- /dev/null
@@ -0,0 +1 @@
+jump pbr_prerouting comment "Jump into pbr prerouting chain";
diff --git a/net/pbr/files/usr/share/nftables.d/table-post/30-pbr.nft b/net/pbr/files/usr/share/nftables.d/table-post/30-pbr.nft
new file mode 100644 (file)
index 0000000..4dd9b28
--- /dev/null
@@ -0,0 +1,5 @@
+chain pbr_forward {}
+chain pbr_input {}
+chain pbr_output {}
+chain pbr_prerouting {}
+chain pbr_postrouting {}
diff --git a/net/pbr/files/usr/share/pbr/pbr.firewall.include b/net/pbr/files/usr/share/pbr/pbr.firewall.include
new file mode 100644 (file)
index 0000000..3fe906e
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+if [ -x /etc/init.d/pbr ] && /etc/init.d/pbr enabled; then
+       logger -t "pbr" "Reloading pbr due to $ACTION of firewall"
+       /etc/init.d/pbr on_firewall_reload "$ACTION"
+fi
diff --git a/net/pbr/files/usr/share/pbr/pbr.user.aws b/net/pbr/files/usr/share/pbr/pbr.user.aws
new file mode 100644 (file)
index 0000000..bf398dd
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh
+
+TARGET_SET='pbr_wan_4_dst_ip_user'
+TARGET_IPSET='pbr_wan_4_dst_net_user'
+TARGET_TABLE='inet fw4'
+TARGET_URL="https://ip-ranges.amazonaws.com/ip-ranges.json"
+TARGET_DL_FILE="/var/pbr_tmp_aws_ip_ranges"
+TARGET_NFT_FILE="/var/pbr_tmp_aws_ip_ranges.nft"
+[ -z "$nft" ] && nft="$(command -v nft)"
+_ret=1
+
+if [ ! -s "$TARGET_DL_FILE" ]; then
+       uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | grep "ip_prefix" | sed 's/^.*\"ip_prefix\": \"//; s/\",//' > "$TARGET_DL_FILE"
+fi
+
+if [ -s "$TARGET_DL_FILE" ]; then
+       if ipset -q list "$TARGET_IPSET" >/dev/null 2>&1; then
+               if awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_DL_FILE" | ipset restore -!; then
+                       _ret=0
+               fi
+       elif [ -n "$nft" ] && [ -x "$nft" ] && "$nft" list set "$TARGET_TABLE" "$TARGET_SET" >/dev/null 2>&1; then
+               printf "add element %s %s { " "$TARGET_TABLE" "$TARGET_SET" > "$TARGET_NFT_FILE"
+               awk '{printf $1 ", "}' "$TARGET_DL_FILE" >> "$TARGET_NFT_FILE"
+               printf " } " >> "$TARGET_NFT_FILE"
+               if "$nft" -f "$TARGET_NFT_FILE"; then
+                       rm -f "$TARGET_NFT_FILE"
+                       _ret=0
+               fi
+       fi
+fi
+
+return $_ret
diff --git a/net/pbr/files/usr/share/pbr/pbr.user.netflix b/net/pbr/files/usr/share/pbr/pbr.user.netflix
new file mode 100644 (file)
index 0000000..54f54e0
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh
+# Credits to https://forum.openwrt.org/u/dscpl for api.hackertarget.com code.
+# Credits to https://github.com/kkeker and https://github.com/tophirsch for api.bgpview.io code.
+
+TARGET_SET='pbr_wan_4_dst_ip_user'
+TARGET_IPSET='pbr_wan_4_dst_net_user'
+TARGET_TABLE='inet fw4'
+TARGET_ASN='2906'
+TARGET_DL_FILE="/var/pbr_tmp_AS${TARGET_ASN}"
+TARGET_NFT_FILE="/var/pbr_tmp_AS${TARGET_ASN}.nft"
+#DB_SOURCE='ipinfo.io'
+#DB_SOURCE='api.hackertarget.com'
+DB_SOURCE='api.bgpview.io'
+[ -z "$nft" ] && nft="$(command -v nft)"
+_ret=1
+
+if [ ! -s "$TARGET_DL_FILE" ]; then
+       if [ "$DB_SOURCE" = "ipinfo.io" ]; then
+               TARGET_URL="https://ipinfo.io/AS${TARGET_ASN}"
+               uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | grep -E "a href.*${TARGET_ASN}\/" | grep -v ":" | sed "s/^.*<a href=\"\/AS${TARGET_ASN}\///; s/\" >//" > "$TARGET_DL_FILE"
+       fi
+       if [ "$DB_SOURCE" = "api.hackertarget.com" ]; then
+               TARGET_URL="https://api.hackertarget.com/aslookup/?q=AS${TARGET_ASN}"
+               uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | sed '1d' > "$TARGET_DL_FILE"
+       fi
+       if [ "$DB_SOURCE" = "api.bgpview.io" ]; then
+               TARGET_URL="https://api.bgpview.io/asn/${TARGET_ASN}/prefixes"
+               uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | jsonfilter -e '@.data.ipv4_prefixes[*].prefix' > "$TARGET_DL_FILE"
+       fi
+fi
+
+if [ -s "$TARGET_DL_FILE" ]; then
+       if ipset -q list "$TARGET_IPSET" >/dev/null 2>&1; then
+                       if awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_DL_FILE" | ipset restore -!; then
+                               _ret=0
+                       fi
+       elif [ -n "$nft" ] && [ -x "$nft" ] && "$nft" list set "$TARGET_TABLE" "$TARGET_SET" >/dev/null 2>&1; then
+               printf "add element %s %s { " "$TARGET_TABLE" "$TARGET_SET" > "$TARGET_NFT_FILE"
+               awk '{printf $1 ", "}' "$TARGET_DL_FILE" >> "$TARGET_NFT_FILE"
+               printf " } " >> "$TARGET_NFT_FILE"
+               if "$nft" -f "$TARGET_NFT_FILE"; then
+                       rm -f "$TARGET_NFT_FILE"
+                       _ret=0
+               fi
+       fi
+fi
+
+return $_ret
diff --git a/net/pbr/test.sh b/net/pbr/test.sh
new file mode 100644 (file)
index 0000000..45469ed
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+/etc/init.d/"$1" version 2>&1 | grep "$2"
diff --git a/net/vpn-policy-routing/Makefile b/net/vpn-policy-routing/Makefile
deleted file mode 100644 (file)
index 21c6b93..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright 2017-2018 Stan Grishin (stangri@melmac.net)
-# This is free software, licensed under the GNU General Public License v3.
-
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=vpn-policy-routing
-PKG_VERSION:=0.3.4
-PKG_RELEASE:=8
-PKG_LICENSE:=GPL-3.0-or-later
-PKG_MAINTAINER:=Stan Grishin <stangri@melmac.net>
-
-include $(INCLUDE_DIR)/package.mk
-
-define Package/vpn-policy-routing
-       SECTION:=net
-       CATEGORY:=Network
-       TITLE:=VPN Policy-Based Routing Service
-       URL:=https://docs.openwrt.melmac.net/vpn-policy-routing/
-       DEPENDS:=+jshn +ipset +iptables +resolveip +kmod-ipt-ipset +iptables-mod-ipopt +ip-full
-       PKGARCH:=all
-endef
-
-define Package/vpn-policy-routing/description
-This service allows policy-based routing for L2TP, Openconnect, OpenVPN, PPTP and Wireguard tunnels and WAN interface.
-Policies can specify domains, local IPs/subnets and ports, as well as remote IPs/subnets and ports.
-endef
-
-define Package/vpn-policy-routing/conffiles
-/etc/config/vpn-policy-routing
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
-endef
-
-define Package/vpn-policy-routing/install
-       $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/etc/hotplug.d/firewall $(1)/etc/
-       $(INSTALL_BIN) ./files/vpn-policy-routing.init $(1)/etc/init.d/vpn-policy-routing
-       $(SED) "s|^\(PKG_VERSION\).*|\1='$(PKG_VERSION)-$(PKG_RELEASE)'|" $(1)/etc/init.d/vpn-policy-routing
-       $(INSTALL_CONF) ./files/vpn-policy-routing.config $(1)/etc/config/vpn-policy-routing
-       $(INSTALL_DATA) ./files/vpn-policy-routing.firewall.hotplug $(1)/etc/hotplug.d/firewall/70-vpn-policy-routing
-       $(INSTALL_DATA) ./files/vpn-policy-routing.aws.user $(1)/etc/vpn-policy-routing.aws.user
-       $(INSTALL_DATA) ./files/vpn-policy-routing.netflix.user $(1)/etc/vpn-policy-routing.netflix.user
-endef
-
-define Package/vpn-policy-routing/postinst
-       #!/bin/sh
-       # check if we are on real system
-       if [ -z "$${IPKG_INSTROOT}" ]; then
-               /etc/init.d/vpn-policy-routing enable
-       fi
-       exit 0
-endef
-
-define Package/vpn-policy-routing/prerm
-       #!/bin/sh
-       # check if we are on real system
-       if [ -z "$${IPKG_INSTROOT}" ]; then
-               echo "Stopping service and removing rc.d symlink for vpn-policy-routing"
-               /etc/init.d/vpn-policy-routing stop || true
-               /etc/init.d/vpn-policy-routing disable || true
-       fi
-       exit 0
-endef
-
-$(eval $(call BuildPackage,vpn-policy-routing))
diff --git a/net/vpn-policy-routing/files/README.md b/net/vpn-policy-routing/files/README.md
deleted file mode 100644 (file)
index 98d7efc..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# README
-
-README has been moved to [https://docs.openwrt.melmac.net/vpn-policy-routing/](https://docs.openwrt.melmac.net/vpn-policy-routing/).
diff --git a/net/vpn-policy-routing/files/vpn-policy-routing.aws.user b/net/vpn-policy-routing/files/vpn-policy-routing.aws.user
deleted file mode 100644 (file)
index a00770b..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/sh
-# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh
-
-TARGET_IPSET='wan'
-
-TARGET_URL="https://ip-ranges.amazonaws.com/ip-ranges.json"
-TARGET_FNAME="/var/vpn-policy-routing_tmp_aws_ip_ranges"
-
-_ret=1
-
-if [ ! -s "$TARGET_FNAME" ]; then
-       curl "$TARGET_URL" 2>/dev/null | grep "ip_prefix" | sed 's/^.*\"ip_prefix\": \"//; s/\",//' > "$TARGET_FNAME"
-fi
-if [ -s "$TARGET_FNAME" ]; then
-       awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_FNAME" | ipset restore -! && _ret=0
-fi
-rm -f "$TARGET_FNAME"
-
-return $_ret
diff --git a/net/vpn-policy-routing/files/vpn-policy-routing.config b/net/vpn-policy-routing/files/vpn-policy-routing.config
deleted file mode 100644 (file)
index ed6f01c..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-config vpn-policy-routing 'config'
-       option enabled '0'
-       option verbosity '2'
-       option strict_enforcement '1'
-       option src_ipset '0'
-       option dest_ipset '0'
-       option resolver_ipset 'dnsmasq.ipset'
-       option ipv6_enabled '0'
-       list ignored_interface 'vpnserver wgserver'
-       option boot_timeout '30'
-       option iptables_rule_option 'append'
-       option procd_reload_delay '1'
-       option webui_enable_column '0'
-       option webui_protocol_column '0'
-       option webui_chain_column '0'
-       option webui_show_ignore_target '0'
-       option webui_sorting '1'
-       list webui_supported_protocol 'tcp'
-       list webui_supported_protocol 'udp'
-       list webui_supported_protocol 'tcp udp'
-       list webui_supported_protocol 'icmp'
-       list webui_supported_protocol 'all'
-
-config include
-       option path '/etc/vpn-policy-routing.netflix.user'
-       option enabled 0
-
-config include
-       option path '/etc/vpn-policy-routing.aws.user'
-       option enabled 0
diff --git a/net/vpn-policy-routing/files/vpn-policy-routing.firewall.hotplug b/net/vpn-policy-routing/files/vpn-policy-routing.firewall.hotplug
deleted file mode 100755 (executable)
index 3932b2b..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-[ "$ACTION" = "reload" ] || exit 0
-
-logger -t "vpn-policy-routing" "Reloading vpn-policy-routing due to $ACTION of firewall"
-/etc/init.d/vpn-policy-routing reload
diff --git a/net/vpn-policy-routing/files/vpn-policy-routing.init b/net/vpn-policy-routing/files/vpn-policy-routing.init
deleted file mode 100755 (executable)
index 1962ed5..0000000
+++ /dev/null
@@ -1,1322 +0,0 @@
-#!/bin/sh /etc/rc.common
-# Copyright 2017-2020 Stan Grishin (stangri@melmac.net)
-# shellcheck disable=SC2039,SC1091,SC2018,SC2019,SC3043,SC3057,SC3060
-PKG_VERSION='dev-test'
-
-# sysctl net.ipv4.conf.default.rp_filter=1
-# sysctl net.ipv4.conf.all.rp_filter=1
-
-# shellcheck disable=SC2034
-START=94
-# shellcheck disable=SC2034
-USE_PROCD=1
-
-if type extra_command 1>/dev/null 2>&1; then
-       extra_command 'support' "Generates output required to troubleshoot routing issues
-               Use '-d' option for more detailed output
-               Use '-p' option to automatically upload data under VPR paste.ee account
-                       WARNING: while paste.ee uploads are unlisted, they are still publicly available
-               List domain names after options to include their lookup in report"
-       extra_command 'version' 'Show version information'
-       extra_command 'reload_interface' 'Reload specific interface only'
-else
-# shellcheck disable=SC2034
-       EXTRA_COMMANDS='support version'
-# shellcheck disable=SC2034
-       EXTRA_HELP="    support Generates output required to troubleshoot routing issues
-               Use '-d' option for more detailed output
-               Use '-p' option to automatically upload data under VPR paste.ee account
-                       WARNING: while paste.ee uploads are unlisted, they are still publicly available
-               List domain names after options to include their lookup in report"
-fi
-
-readonly packageName='vpn-policy-routing'
-readonly serviceName="$packageName $PKG_VERSION"
-readonly PIDFile="/var/run/${packageName}.pid"
-readonly jsonFile="/var/run/${packageName}.json"
-readonly dnsmasqFile="/var/dnsmasq.d/${packageName}"
-readonly sharedMemoryOutput="/dev/shm/$packageName-output"
-readonly _OK_='\033[0;32m\xe2\x9c\x93\033[0m'
-readonly _FAIL_='\033[0;31m\xe2\x9c\x97\033[0m'
-readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
-readonly __FAIL__='\033[0;31m[\xe2\x9c\x97]\033[0m'
-readonly _ERROR_='\033[0;31mERROR\033[0m'
-readonly _WARNING_='\033[0;33mWARNING\033[0m'
-
-gatewaySummary=''; errorSummary=''; warningSummary='';
-serviceEnabled=''; verbosity=''; strictMode=''; 
-wanTableID=''; wanMark=''; fwMask=''; 
-ipv6Enabled=''; srcIpset=''; destIpset=''; resolverIpset='';
-wanIface4=''; wanIface6=''; ifaceMark=''; ifaceTableID='';
-ifAll=''; ifSupported=''; ignoredIfaces=''; supportedIfaces=''; icmpIface='';
-wanGW4=''; wanGW6=''; bootTimeout=''; insertOption='';
-webuiChainColumn=''; webuiShowIgnore=''; dnsmasqIpsetSupported='';
-procdReloadDelay='';
-usedChainsList='PREROUTING'
-ipsetSupported='true'
-configLoaded='false'
-
-version() { echo "$PKG_VERSION"; }
-output_ok() { output 1 "$_OK_"; output 2 "$__OK__\\n"; }
-output_okn() { output 1 "$_OK_\\n"; output 2 "$__OK__\\n"; }
-output_fail() { s=1; output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; }
-output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; }
-str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; }
-str_replace() { echo "${1//$2/$3}"; }
-str_contains() { [ -n "$2" ] && [ "${1//$2}" != "$1" ]; }
-str_contains_word() { echo "$1" | grep -q -w "$2"; }
-str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
-str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; }
-str_extras_to_space() { echo "$1" | tr ';{}' ' '; }
-
-output() {
-# Can take a single parameter (text) to be output at any verbosity
-# Or target verbosity level and text to be output at specifc verbosity
-       local msg memmsg logmsg
-       if [ $# -ne 1 ]; then
-               if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; else return 0; fi
-       fi
-       [ -t 1 ] && printf "%b" "$1"
-       msg="${1//$serviceName /service }";
-       if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then
-               [ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
-               logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
-               logger -t "${packageName:-service} [$$]" "$(printf "%b" "$logmsg")"
-               rm -f "$sharedMemoryOutput"
-       else
-               printf "%b" "$msg" >> "$sharedMemoryOutput"
-       fi
-}
-is_present() { command -v "$1" >/dev/null 2>&1; }
-is_installed() { [ -s "/usr/lib/opkg/info/${1}.control" ]; }
-is_variant_installed() { [ "$(echo /usr/lib/opkg/info/"${1}"*.control)" != "/usr/lib/opkg/info/${1}*.control" ]; }
-
-build_ifAll() { ifAll="${ifAll}${1} "; }
-build_ifSupported() { is_supported_interface "$1" && ifSupported="${ifSupported}${1} "; }
-vpr_find_iface() {
-       local iface i param="$2"
-       [ "$param" = 'wan6' ] || param='wan'
-       "network_find_${param}" iface
-       is_tunnel "$iface" && unset iface
-       if [ -z "$iface" ]; then
-               for i in $ifAll; do
-                       if "is_${param}" "$i"; then break; else unset i; fi
-               done
-       fi
-       eval "$1"='${iface:-$i}'
-}
-vpr_get_gateway() {
-       local iface="$2" dev="$3" gw
-       network_get_gateway gw "$iface"
-       if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
-               gw="$(ip -4 a list dev "$dev" 2>/dev/null | grep inet | awk '{print $2}' | awk -F "/" '{print $1}')"
-       fi
-       eval "$1"='$gw'
-}
-vpr_get_gateway6() {
-       local iface="$2" dev="$3" gw
-       network_get_gateway6 gw "$iface"
-       if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
-               gw="$(ip -6 a list dev "$dev" 2>/dev/null | grep inet6 | awk '{print $2}')"
-       fi
-       eval "$1"='$gw'
-}
-is_l2tp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "l2tp" ]; }
-is_oc() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:11}" = "openconnect" ]; }
-is_ovpn() { local dev i; for i in ifname device; do [ -z "$dev" ] && dev="$(uci -q get "network.${1}.${i}")"; done; [ "${dev:0:3}" = "tun" ] || [ "${dev:0:3}" = "tap" ] || [ -f "/sys/devices/virtual/net/${dev}/tun_flags" ]; }
-is_pptp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "pptp" ]; }
-is_tor() { [ "$(str_to_lower "$1")" = "tor" ]; }
-is_tor_running() { 
-       local ret=0
-       if [ -s "/etc/tor/torrc" ]; then
-               json_load "$(ubus call service list "{ 'name': 'tor' }")"
-               json_select 'tor'; json_select 'instances'; json_select 'instance1';
-               json_get_var ret 'running'; json_cleanup
-       fi
-       if [ "$ret" = "0" ]; then return 1; else return 0; fi
-}
-is_wg() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:9}" = "wireguard" ]; }
-is_tunnel() { is_l2tp "$1" || is_oc "$1" || is_ovpn "$1" || is_pptp "$1" || is_tor "$1" || is_wg "$1"; }
-is_wan() { [ "$1" = "$wanIface4" ] || { [ "${1##wan}" != "$1" ] && [ "${1##wan6}" = "$1" ]; } || [ "${1%%wan}" != "$1" ]; }
-is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1/#wan6}" != "$1" ] || [ "${1/%wan6}" != "$1" ]; }
-is_ignored_interface() { str_contains_word "$ignoredIfaces" "$1"; }
-is_supported_interface() { str_contains_word "$supportedIfaces" "$1" || { ! is_ignored_interface "$1" && { is_wan "$1" || is_wan6 "$1" || is_tunnel "$1"; }; }; }
-is_mac_address() { expr "$1" : '[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]$' >/dev/null; }
-is_ipv4() { expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; }
-is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ":"; }
-is_family_mismatch() { ( is_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_netmask "${2//!}" ); }
-is_ipv6_link_local() { [ "${1:0:4}" = "fe80" ]; }
-is_ipv6_unique_local() { [ "${1:0:2}" = "fc" ] || [ "${1:0:2}" = "fd" ]; }
-is_ipv6_global() { [ "${1:0:4}" = "2001" ]; }
-# is_ipv6_global() { is_ipv6 "$1" && ! is_ipv6_link_local "$1" && ! is_ipv6_link_local "$1"; }
-is_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; }
-is_domain() { str_contains "$1" '[a-zA-Z]'; }
-is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; }
-is_turris() { /bin/ubus -S call system board | /bin/grep 'Turris' | /bin/grep -q '15.05'; }
-is_chaos_calmer() { ubus -S call system board | grep -q 'Chaos Calmer'; }
-dnsmasq_kill() { killall -q -s HUP dnsmasq; }
-dnsmasq_restart() { output 3 'Restarting DNSMASQ '; if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then output_okn; else output_failn; fi; }
-is_default_dev() { [ "$1" = "$(ip -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
-is_supported_iface_dev() {
-       for n in $ifSupported; do 
-               if [ "$1" = "$(uci -q get "network.${n}.ifname" || echo "$n")" ] || \
-                       [ "$1" = "$(uci -q get "network.${n}.device" || echo "$n")" ] || \
-                       [ "$1" = "$(uci -q get "network.${n}.proto")-${n}" ] ; then return 0; fi
-       done
-       return 1
-}
-is_supported_protocol () { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; }
-append_chains_targets() {
-       local chain iface name
-       config_get name "$1" 'name' 'blank'
-       config_get chain "$1" 'chain' 'PREROUTING'
-       config_get iface "$1" 'interface'
-       if ! str_contains_word "$usedChainsList" "$chain"; then
-               usedChainsList="$usedChainsList $chain"
-               if [ "$chain" != 'PREROUTING' ] && [ "$webuiChainColumn" != '1' ]; then
-                       warningSummary="${warningSummary}$_WARNING_: Chain '$chain' is used by a policy '$name', but a WebUI setting to show chains column (webui_chain_column) is disabled!\\n"
-               fi
-       fi
-       if [ "$iface" = 'ignore' ] && ! str_contains_word "$supportedIfaces" 'ignore'; then
-               supportedIfaces="$supportedIfaces ignore"
-               if [ "$webuiShowIgnore" != '1' ]; then
-                       warningSummary="${warningSummary}$_WARNING_: The 'ignore' target is used by a policy '$name', but a WebUI setting to show 'ignore' target (webui_show_ignore_target) is disabled!\\n"
-               fi
-       fi
-}
-
-load_package_config() {
-       [ "$configLoaded" = 'false' ] || return 0
-
-       config_load "$packageName"
-       config_get_bool serviceEnabled      'config' 'enabled' 0
-       config_get_bool strictMode          'config' 'strict_enforcement' 1
-       config_get_bool ipv6Enabled         'config' 'ipv6_enabled' 0
-       config_get_bool srcIpset            'config' 'src_ipset' 0
-       config_get_bool destIpset           'config' 'dest_ipset' 0
-       config_get resolverIpset            'config' 'resolver_ipset' 'dnsmasq.ipset'
-       config_get verbosity                'config' 'verbosity' '2'
-       config_get wanTableID               'config' 'wan_tid' '201'
-       config_get wanMark                  'config' 'wan_mark' '0x010000'
-       config_get fwMask                   'config' 'fw_mask' '0xff0000'
-       config_get icmpIface                'config' 'icmp_interface'
-       config_get ignoredIfaces            'config' 'ignored_interface'
-       config_get supportedIfaces          'config' 'supported_interface'
-       config_get bootTimeout              'config' 'boot_timeout' '30'
-       config_get insertOption             'config' 'iptables_rule_option' 'append'
-       config_get procdReloadDelay         'config' 'procd_reload_delay' '0'
-       config_get_bool webuiChainColumn    'config' 'webui_chain_column' '0'
-       config_get_bool webuiShowIgnore     'config' 'webui_show_ignore_target' '0'
-       config_foreach append_chains_targets 'policy'
-
-       if [ -z "${verbosity##*[!0-9]*}" ] || [ "$verbosity" -lt 0 ] || [ "$verbosity" -gt 2 ]; then
-               verbosity=2
-       fi
-
-       . /lib/functions/network.sh
-       . /usr/share/libubox/jshn.sh
-       mkdir -p "${PIDFile%/*}"
-       mkdir -p "${jsonFile%/*}"
-       mkdir -p "${dnsmasqFile%/*}"
-
-       if [ -n "$icmpIface" ] && ! str_contains_word "$usedChainsList" 'OUTPUT'; then
-               usedChainsList="$usedChainsList OUTPUT"
-       fi
-
-       case $insertOption in
-               insert|-i|-I) insertOption='-I';;
-               append|-a|-A|*) insertOption='-A';;
-       esac
-
-       [ "$resolverIpset" = 'dnsmasq.ipset' ] && dnsmasqIpsetSupported='true'
-       if dnsmasq -v 2>/dev/null | grep -q 'no-ipset' || ! dnsmasq -v 2>/dev/null | grep -q -w 'ipset'; then
-               unset dnsmasqIpsetSupported
-               if [ -n "$dnsmasqIpsetSupported" ]; then
-                       errorSummary="${errorSummary}${_ERROR_}: Resolver ipset support (dnsmasq.ipset) is enabled in $packageName, but DNSMASQ ipsets are not supported on this system!\\n"
-               fi
-       fi
-       if ! ipset help hash:net >/dev/null 2>&1; then
-               unset ipsetSupported
-               if [ -n "$dnsmasqIpsetSupported" ]; then
-                       errorSummary="${errorSummary}${_ERROR_}: DNSMASQ ipsets are supported, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n"
-                       unset dnsmasqIpsetSupported
-               fi
-               if [ "$destIpset" -ne 0 ]; then
-                       errorSummary="${errorSummary}${_ERROR_}: Destination ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n"
-                       destIpset=0
-               fi
-               if [ "$srcIpset" -ne 0 ]; then
-                       errorSummary="${errorSummary}${_ERROR_}: Source ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n"
-                       srcIpset=0
-               fi
-       fi
-       if ! ipset help hash:mac >/dev/null 2>&1; then
-               if [ "$srcIpset" -ne 0 ]; then
-                       errorSummary="${errorSummary}${_ERROR_}: Source ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:mac' type!\\n"
-                       srcIpset=0
-               fi
-       fi
-
-       configLoaded='true'
-}
-
-is_enabled() {
-       load_package_config
-       if [ "$serviceEnabled" -eq 0 ]; then
-               if [ "$1" = 'on_start' ]; then
-                       errorSummary="${errorSummary}${_ERROR_}: ${packageName} is currently disabled.\\n"
-                       errorSummary="${errorSummary}Enable ${packageName} from WebUI or run the following commands:\\n"
-                       errorSummary="${errorSummary}uci set $packageName.config.enabled='1'; uci commit $packageName;\\n"
-               fi
-               return 1
-       fi
-}
-
-load_network() {
-       if [ -z "$ifAll" ]; then
-               config_load 'network'
-               config_foreach build_ifAll 'interface'
-       fi
-       vpr_find_iface wanIface4 'wan'
-       [ "$ipv6Enabled" -ne 0 ] && vpr_find_iface wanIface6 'wan6'
-       [ -n "$wanIface4" ] && network_get_gateway wanGW4 "$wanIface4"
-       [ -n "$wanIface6" ] && network_get_gateway6 wanGW6 "$wanIface6"
-       wanGW="${wanGW4:-$wanGW6}"
-       unset ifSupported
-       config_load 'network'
-       config_foreach build_ifSupported 'interface'
-}
-
-is_wan_up() {
-       local sleepCount=1
-       load_network
-       while [ -z "$wanGW" ] ; do
-               load_network
-               if [ $((sleepCount)) -gt $((bootTimeout)) ] || [ -n "$wanGW" ]; then break; fi
-               output "$serviceName waiting for wan gateway...\\n"
-               sleep 1
-               network_flush_cache
-               sleepCount=$((sleepCount+1))
-       done
-       if [ -n "$wanGW" ]; then
-               return 0
-       else
-               errorSummary="${errorSummary}${_ERROR_}: ${serviceName} failed to discover WAN gateway!\\n"
-               return 1
-       fi
-}
-
-ipt_cleanup() {
-       local i
-       for i in PREROUTING FORWARD INPUT OUTPUT; do
-               while iptables -t mangle -D $i -m mark --mark 0x0/0xff0000 -j VPR_${i} >/dev/null 2>&1; do : ; done
-       done
-       for i in PREROUTING FORWARD INPUT OUTPUT; do
-               while iptables -t mangle -D $i -j VPR_${i} >/dev/null 2>&1; do : ; done
-       done
-}
-
-# shellcheck disable=SC2086
-ipt() {
-       local d failFlagIpv4=1 failFlagIpv6=1
-       for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do 
-               [ "$d" != "$*" ] && { iptables $d >/dev/null 2>&1; ip6tables $d >/dev/null 2>&1; }
-       done
-
-       d="$*"; iptables $d >/dev/null 2>&1 && failFlagIpv4=0;
-       if [ "$ipv6Enabled" -gt 0 ]; then ip6tables $d >/dev/null 2>&1 && failFlagIpv6=0; fi
-
-       [ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ]
-}
-
-# shellcheck disable=SC2086
-ips() {
-       local command="$1" ipset="${2//-/_}" param="$3" comment="$4" appendix failFlag=0
-       if str_contains "$ipset" '_ip'; then
-               ipset="${ipset//_ip}"; appendix='_ip';
-       elif str_contains "$ipset" '_mac'; then
-               ipset="${ipset//_mac}"; appendix='_mac';
-       fi
-
-       case "$command" in
-               add_dnsmasq)
-                       [ "$resolverIpset" = "dnsmasq.ipset" ] || return 1
-                       if [ -z "$dnsmasqIpsetSupported" ]; then
-                               warningSummary="${warningSummary}${_WARNING_}: The 'resolver_ipset' is set to 'dnsmasq.ipset', but DNSMASQ ipsets are not supported on this system!\\n"
-                               failFlag=1
-                       elif [ "$ipv6Enabled" -ne 0 ]; then
-                               echo "ipset=/${param}/${ipset},${ipset}6 # $comment" >> "$dnsmasqFile" || failFlag=1
-                       else
-                               echo "ipset=/${param}/${ipset} # $comment" >> "$dnsmasqFile" || failFlag=1
-                       fi
-                       ;;
-               add)
-                       if [ -z "$appendix" ] && [ "$destIpset" -eq 0 ]; then return 1; fi
-                       if [ -n "$appendix" ] && [ "$srcIpset" -eq 0 ]; then return 1; fi
-                       if [ "$ipv6Enabled" -ne 0 ] && [ "$appendix" != "_mac" ]; then
-                               ipset -q -! $command "${ipset}6${appendix}" $param comment "$comment" || failFlag=1
-                       fi
-                       ipset -q -! $command "${ipset}${appendix}" $param comment "$comment" || failFlag=1
-                       ;;
-               create)
-                       if [ "$ipv6Enabled" -ne 0 ] && [ "$appendix" != "_mac" ]; then
-                               ipset -q -! "$command" "${ipset}6${appendix}" $param family inet6 || failFlag=1
-                       fi
-                       ipset -q -! "$command" "${ipset}${appendix}" $param || failFlag=1
-                       ;;
-               destroy|flush)
-                       ipset -q -! "$command" "${ipset}6${appendix}" 2>/dev/null || failFlag=1
-                       ipset -q -! "$command" "${ipset}${appendix}" 2>/dev/null || failFlag=1
-                       return 0
-                       ;;
-       esac
-       return $failFlag
-}
-
-insert_tor_policy() {
-       local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain
-       proto="$(str_to_lower "$7")"
-       chain="${8:-PREROUTING}"
-       if [ -n "${laddr}${lport}${rport}" ]; then
-               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n"
-       fi
-       if [ -n "$proto" ] && [ "$proto" != "all" ]; then
-               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n"
-       fi
-       if [ "$chain" != "PREROUTING" ]; then
-               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '$comment'\\n"
-       fi
-       ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || processPolicyError="${processPolicyError}${_ERROR_}: ipset 'add' $iface $raddr\\n"
-       return 0
-}
-
-insert_policy() {
-       local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain
-       local mark param i valueNeg value dest ipInsertOption="-A"
-       proto="$(str_to_lower "$7")"
-       chain="${8:-PREROUTING}"
-       mark=$(eval echo "\$mark_${iface//-/_}")
-       if [ "$ipv6Enabled" -eq 0 ] && ( is_ipv6 "$laddr" || is_ipv6 "$raddr" ); then
-               processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$comment' as IPv6 support is disabled\\n"
-               return 1
-       fi
-
-       if [ -n "$mark" ]; then
-               dest="-g VPR_MARK${mark}"
-       elif [ "$iface" = "ignore" ]; then
-                       dest="-j RETURN"
-       else
-               processPolicyError="${processPolicyError}${_ERROR_}: Unknown fw_mark for ${iface}\\n"
-               return 0
-       fi
-
-       if [ -z "$proto" ]; then
-               if [ -n "$lport" ] || [ -n "$rport" ]; then 
-                       proto='tcp udp'
-               else
-                       proto='all'
-               fi
-       fi
-
-       if is_family_mismatch "$laddr" "$raddr"; then 
-               processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$laddr' and '$raddr' in policy '$comment'\\n"
-               return 0
-       fi
-
-       for i in $proto; do
-               if [ "$i" = 'all' ]; then
-                       param="-t mangle ${ipInsertOption} VPR_${chain} $dest"
-               elif ! is_supported_protocol "$i"; then
-                       processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$comment'\\n"
-                       return 0
-               else
-                       param="-t mangle ${ipInsertOption} VPR_${chain} $dest -p $i"
-               fi
-
-               if [ -n "$laddr" ]; then
-                       if [ "${laddr:0:1}" = "!" ]; then
-                               valueNeg='!'; value="${laddr:1}"
-                       else
-                               unset valueNeg; value="$laddr";
-                       fi
-                       if is_phys_dev "$value"; then
-                               param="$param $valueNeg -m physdev --physdev-in ${value:1}"
-                       elif is_mac_address "$value"; then
-                               param="$param -m mac $valueNeg --mac-source $value"
-                       else
-                               param="$param $valueNeg -s $value"
-                       fi
-               fi
-
-               if [ -n "$lport" ]; then
-                       if [ "${lport:0:1}" = "!" ]; then
-                               valueNeg='!'; value="${lport:1}"
-                       else
-                               unset valueNeg; value="$lport";
-                       fi
-                       param="$param -m multiport $valueNeg --sport ${value//-/:}"
-               fi
-
-               if [ -n "$raddr" ]; then 
-                       if [ "${raddr:0:1}" = "!" ]; then
-                               valueNeg='!'; value="${raddr:1}"
-                       else
-                               unset valueNeg; value="$raddr";
-                       fi
-                       param="$param $valueNeg -d $value"
-               fi
-
-               if [ -n "$rport" ]; then
-                       if [ "${rport:0:1}" = "!" ]; then
-                               valueNeg='!'; value="${rport:1}"
-                       else
-                               unset valueNeg; value="$rport";
-                       fi
-                       param="$param -m multiport $valueNeg --dport ${value//-/:}"
-               fi
-
-               [ -n "$comment" ] && param="$param -m comment --comment $(str_extras_to_underscore "$comment")"
-               ipt "$param" || processPolicyError="${processPolicyError}${_ERROR_}: iptables $param\\n"
-       done
-       return 0
-}
-
-r_process_policy(){
-       local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto="$7" chain="$8" resolved_laddr resolved_raddr i ipsFailFlag
-       if str_contains "$laddr" '[ ;\{\}]'; then
-               for i in $(str_extras_to_space "$laddr"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$i" "$lport" "$raddr" "$rport" "$proto" "$chain"; done
-               return 0
-       elif str_contains "$lport" '[ ;\{\}]'; then
-               for i in $(str_extras_to_space "$lport"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$i" "$raddr" "$rport" "$proto" "$chain"; done
-               return 0
-       elif str_contains "$raddr" '[ ;\{\}]'; then
-               for i in $(str_extras_to_space "$raddr"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$lport" "$i" "$rport" "$proto" "$chain"; done
-               return 0
-       elif str_contains "$rport" '[ ;\{\}]'; then
-               for i in $(str_extras_to_space "$rport"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$i" "$proto" "$chain"; done
-               return 0
-       fi
-
-       # start non-recursive processing 
-       # process TOR, netmask, physical device and mac-address separately, so we don't send them to resolveip
-       if is_tor "$iface"; then
-               insert_tor_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
-       elif is_phys_dev "$laddr"; then
-               insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
-       elif [ -n "$laddr" ] && [ -z "${lport}${raddr}${rport}" ] && [ "$chain" = 'PREROUTING' ]; then
-               if is_mac_address "$laddr"; then
-                       if [ -n "$proto" ] && [ "$proto" != 'all' ] && [ "$srcIpset" -ne 0 ]; then
-                               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', mac-address: '$laddr'\\n"
-                       fi
-                       ips 'add' "${iface}_mac" "$laddr" "${comment}: $laddr" || ipsFailFlag=1
-               else
-                       if [ -n "$proto" ] && [ "$proto" != "all" ] && [ "$srcIpset" -ne 0 ]; then
-                               processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', source: '$laddr'\\n"
-                       fi
-                       ips 'add' "${iface}_ip" "$laddr" "${comment}: $laddr" || ipsFailFlag=1
-               fi
-       elif [ -n "$raddr" ] && [ -z "${laddr}${lport}${rport}" ] && [ "$chain" = 'PREROUTING' ]; then
-               if [ -n "$proto" ] && [ "$proto" != 'all' ]; then
-                       processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', destination: '$raddr'\\n"
-               fi
-               if is_domain "$raddr"; then
-                       ips 'add_dnsmasq' "${iface}" "$raddr" "${comment}" || ipsFailFlag=1
-               else 
-                       ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || ipsFailFlag=1
-               fi
-       else
-               ipsFailFlag=1
-       fi
-       [ -n "$ipsFailFlag" ] || return 0;
-       if is_mac_address "$laddr"; then
-               insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
-       elif is_netmask "$laddr" || is_netmask "$raddr"; then
-               insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
-       else
-               [ -n "$laddr" ] && resolved_laddr="$(resolveip "$laddr")"
-               [ -n "$raddr" ] && resolved_raddr="$(resolveip "$raddr")"
-               if [ -n "$resolved_laddr" ] && [ "$resolved_laddr" != "$laddr" ]; then
-                       for i in $resolved_laddr; do [ -n "$i" ] && r_process_policy "$comment $laddr" "$iface" "$i" "$lport" "$raddr" "$rport" "$proto" "$chain"; done
-               elif [ -n "$resolved_raddr" ] && [ "$resolved_raddr" != "$raddr" ]; then
-                               for i in $resolved_raddr; do [ -n "$i" ] && r_process_policy "$comment $raddr" "$iface" "$laddr" "$lport" "$i" "$rport" "$proto" "$chain"; done
-               else
-                       insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
-               fi
-       fi
-}
-
-process_policy(){
-       local name comment iface laddr lport raddr rport param mark processPolicyError processPolicyWarning proto chain enabled
-       config_get comment "$1" 'comment'
-       config_get name    "$1" 'name' 'blank'
-       config_get iface   "$1" 'interface'
-       config_get laddr   "$1" 'src_addr'
-       config_get lport   "$1" 'src_port'
-       config_get raddr   "$1" 'dest_addr'
-       config_get rport   "$1" 'dest_port'
-       config_get proto   "$1" 'proto'
-       config_get chain   "$1" 'chain' 'PREROUTING'
-       config_get_bool enabled "$1" 'enabled' 1
-
-       [ "$enabled" -gt 0 ] || return 0
-       proto="$(str_to_lower "$proto")"
-       [ "$proto" = 'auto' ] && unset proto
-
-       comment="${comment:-$name}"
-       output 2 "Routing '$comment' via $iface "
-
-       if [ -z "$comment" ]; then
-               errorSummary="${errorSummary}${_ERROR_}: Policy name is empty\\n"
-               output_fail; return 1;
-       fi
-       if [ -z "${laddr}${lport}${raddr}${rport}" ]; then
-               errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' missing all IPs/ports\\n"
-               output_fail; return 1;
-       fi
-       if [ -z "$iface" ]; then
-               errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' has no assigned interface\\n"
-               output_fail; return 1;
-       fi
-       if ! is_supported_interface "$iface"; then
-               errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' has unknown interface: '${iface}'\\n"
-               output_fail; return 1;
-       fi
-
-       lport="${lport//  / }"; lport="${lport// /,}"; lport="${lport//,\!/ !}"; 
-       rport="${rport//  / }"; rport="${rport// /,}"; rport="${rport//,\!/ !}";
-       r_process_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
-       if [ -n "$processPolicyWarning" ]; then
-               warningSummary="${warningSummary}${processPolicyWarning}\\n"
-       fi
-       if [ -n "$processPolicyError" ]; then
-               output_fail
-               errorSummary="${errorSummary}${processPolicyError}\\n"
-       else
-               output_ok
-       fi
-}
-
-table_destroy(){
-       local tid="$1" iface="$2" mark="$3"
-       if [ -n "$tid" ] && [ -n "$iface" ] && [ -n "$mark" ]; then
-               ipt -t mangle -F "VPR_MARK${mark}"
-               ipt -t mangle -X "VPR_MARK${mark}"
-               ip -4 rule del fwmark "$mark" table "$tid" >/dev/null 2>&1
-               ip -6 rule del fwmark "$mark" table "$tid" >/dev/null 2>&1
-               ip -4 rule del table "$tid" >/dev/null 2>&1
-               ip -6 rule del table "$tid" >/dev/null 2>&1
-               ip -4 route flush table "$tid" >/dev/null 2>&1
-               ip -6 route flush table "$tid" >/dev/null 2>&1
-               ips 'flush' "${iface}"; ips 'destroy' "${iface}";
-               ips 'flush' "${iface}_ip"; ips 'destroy' "${iface}_ip";
-               ips 'flush' "${iface}_mac"; ips 'destroy' "${iface}_mac";
-               ip -4 route flush cache
-               ip -6 route flush cache
-               sed -i "/$iface/d" /etc/iproute2/rt_tables
-               return 0
-       else
-               return 1
-       fi
-}
-
-# shellcheck disable=SC2086
-table_create(){
-       local tid="$1" mark="$2" iface="$3" gw4="$4" dev="$5" gw6="$6" dev6="$7" match="$8" dscp s=0 i ipv4_error=0 ipv6_error=1
-
-       if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
-               return 1
-       fi
-
-       table_destroy "$tid" "$iface" "$mark"
-
-       if [ -n "$gw4" ] || [ "$strictMode" -ne 0 ]; then
-               echo "$tid" "$iface" >> /etc/iproute2/rt_tables
-               if [ -z "$gw4" ]; then
-                       ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
-               else
-                       ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
-               fi
-#              ip -4 route list table main | grep -v 'br-lan' | while read -r i; do
-               ip -4 route list table main | while read -r i; do
-                       idev="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')"
-                       if ! is_supported_iface_dev "$idev"; then
-                               ip -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1
-                       fi
-               done
-               ip -4 route flush cache || ipv4_error=1
-               ip -4 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv4_error=1
-               ipt -t mangle -N "VPR_MARK${mark}" || ipv4_error=1
-               ipt -t mangle -A "VPR_MARK${mark}" -j MARK --set-xmark "${mark}/${fwMask}" || ipv4_error=1
-               ipt -t mangle -A "VPR_MARK${mark}" -j RETURN || ipv4_error=1
-       fi
-
-       if [ "$ipv6Enabled" -ne 0 ]; then
-               ipv6_error=0
-               if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strictMode" -ne 0 ]; then
-                       if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
-                               ip -6 route add unreachable default table "$tid" || ipv6_error=1
-                       else
-                               ip -6 route list table main | grep " dev $dev6 " | while read -r i; do
-                                       ip -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1
-                               done
-                       fi
-                       ip -6 route flush cache || ipv6_error=1
-                       ip -6 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv6_error=1
-               fi
-       fi
-
-       if [ $ipv4_error -eq 0 ] || [ $ipv6_error -eq 0 ]; then
-               dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)"
-               if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
-                       ipt -t mangle -I VPR_PREROUTING -m dscp --dscp "${dscp}" -g "VPR_MARK${mark}" || s=1
-               fi
-               if [ -n "$ipsetSupported" ] && { [ -n "$dnsmasqIpsetSupported" ] || [ "$destIpset" -ne 0 ]; }; then
-                       if ips 'create' "${iface}" 'hash:net comment' && ips 'flush' "${iface}"; then
-                               for i in $usedChainsList; do
-                                       ipt -t mangle -I VPR_${i} -m set --match-set "${iface}" dst -g "VPR_MARK${mark}" || s=1
-                                       if [ "$ipv6Enabled" -ne 0 ]; then ipt -t mangle -I VPR_${i} -m set --match-set "${iface}6" dst -g "VPR_MARK${mark}" || s=1; fi
-                               done
-                       else
-                               s=1
-                       fi
-               fi
-               if [ -n "$ipsetSupported" ] && [ "$srcIpset" -ne 0 ]; then
-                       if ips 'create' "${iface}_ip" 'hash:net comment' && ips 'flush' "${iface}_ip"; then
-                               ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_ip" src -g "VPR_MARK${mark}" || s=1
-                               if [ "$ipv6Enabled" -ne 0 ]; then ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}6_ip" src -g "VPR_MARK${mark}" || s=1; fi
-                       else
-                               s=1
-                       fi
-                       if ips 'create' "${iface}_mac" 'hash:mac comment' && ips 'flush' "${iface}_mac"; then
-                               ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_mac" src -g "VPR_MARK${mark}" || s=1
-                       else
-                               s=1
-                       fi
-               fi
-               if [ "$iface" = "$icmpIface" ]; then
-                       ipt -t mangle -I VPR_OUTPUT -p icmp -g "VPR_MARK${mark}" || s=1
-               fi
-       else
-               s=1
-       fi
-
-       return $s
-}
-
-table_reload() {
-       local tid="$1" mark="$2" iface="$3" gw4="$4" dev="$5" gw6="$6" dev6="$7" match="$8" dscp s=0 i ipv4_error=0 ipv6_error=1
-
-       if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
-               return 1
-       fi
-
-       ip -4 route del default table "$tid" >/dev/null 2>&1
-       if [ -n "$gw4" ] || [ "$strictMode" -ne 0 ]; then
-               if [ -z "$gw4" ]; then
-                       ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
-               else
-                       ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
-               fi
-               ip -4 route flush cache || ipv4_error=1
-               ip -4 rule del fwmark "${mark}/${fwMask}" table "$tid" >/dev/null 2>&1
-               ip -4 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv4_error=1
-       fi
-
-       if [ "$ipv6Enabled" -ne 0 ]; then
-               ip -6 route del default table "$tid" >/dev/null 2>&1
-               ipv6_error=0
-               if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strictMode" -ne 0 ]; then
-                       if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
-                               ip -6 route add unreachable default table "$tid" || ipv6_error=1
-                       else
-                               ip -6 route list table main | grep " dev $dev6 " | while read -r i; do
-                                       ip -6 route add "$i" table "$tid" >/dev/null 2>&1 || ipv6_error=1
-                               done
-                       fi
-                       ip -6 route flush cache || ipv6_error=1
-                       ip -6 rule del fwmark "${mark}/${fwMask}" table "$tid" >/dev/null 2>&1
-                       ip -6 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv6_error=1
-               fi
-       fi
-
-       if [ $ipv4_error -eq 0 ] || [ $ipv6_error -eq 0 ]; then
-               dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)"
-               if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
-                       ipt -t mangle -I VPR_PREROUTING -m dscp --dscp "${dscp}" -g "VPR_MARK${mark}" || s=1
-               fi
-               if [ "$iface" = "$icmpIface" ]; then
-                       ipt -t mangle -I VPR_OUTPUT -p icmp -g "VPR_MARK${mark}" || s=1
-               fi
-       else
-               s=1
-       fi
-
-       return $s
-}
-
-process_interface(){
-       local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" match="$3" displayText
-
-       is_supported_interface "$iface" || return 0
-       is_wan6 "$iface" && return 0
-       [ $((ifaceMark)) -gt $((fwMask)) ] && return 1
-
-       network_get_device dev "$iface"
-       [ -z "$dev" ] && config_get dev "$iface" 'ifname'
-       [ -z "$dev" ] && config_get dev "$iface" 'device'
-       if is_wan "$iface" && [ -n "$wanIface6" ]; then
-               network_get_device dev6 "$wanIface6"
-               [ -z "$dev6" ] && config_get dev6 "$wanIface6" 'ifname'
-               [ -z "$dev6" ] && config_get dev6 "$wanIface6" 'device'
-       fi
-       [ -z "$dev6" ] && dev6="$dev"
-
-       [ -z "$ifaceTableID" ] && ifaceTableID="$wanTableID"; [ -z "$ifaceMark" ] && ifaceMark="$wanMark";
-
-       case "$action" in
-               destroy)
-                       table_destroy "${ifaceTableID}" "${iface}" "${ifaceMark}"
-                       ifaceTableID="$((ifaceTableID + 1))"; ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))";
-                       ;;
-               create)
-                       eval "mark_${iface//-/_}"='$ifaceMark'
-                       eval "tid_${iface//-/_}"='$ifaceTableID'
-                       if [ -z "$match" ]; then
-                               table_destroy "$ifaceTableID" "$iface"
-                       fi
-                       vpr_get_gateway gw4 "$iface" "$dev"
-                       vpr_get_gateway6 gw6 "$iface" "$dev6"
-                       if [ "$iface" = "$dev" ]; then
-                               displayText="${iface}/${gw4:-0.0.0.0}"
-                       else
-                               displayText="${iface}/${dev}/${gw4:-0.0.0.0}"
-                       fi
-                       [ "$ipv6Enabled" -ne 0 ] && displayText="${displayText}/${gw6:-::/0}"
-                       if [ -z "$match" ]; then
-                               output 2 "Creating table '$displayText' "
-                               is_default_dev "$dev" && displayText="${displayText} ${__OK__}"
-                               if table_create "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$match"; then
-                                       gatewaySummary="${gatewaySummary}${displayText}\\n"
-                                       output_ok
-                               else
-                                       errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n"
-                                       output_fail
-                               fi
-                       elif [ "$iface" = "$match" ]; then
-                               output 2 "Reloading table '$displayText' "
-                               is_default_dev "$dev" && displayText="${displayText} ${__OK__}"
-                               if table_reload "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$match"; then
-                                       gatewaySummary="${gatewaySummary}${displayText}\\n"
-                                       output_ok
-                               else
-                                       errorSummary="${errorSummary}${_ERROR_}: Failed to reload '$displayText'\\n"
-                                       output_fail
-                               fi
-                       else
-                               is_default_dev "$dev" && displayText="${displayText} ${__OK__}"
-                               gatewaySummary="${gatewaySummary}${displayText}\\n"
-                       fi
-                       ifaceTableID="$((ifaceTableID + 1))"; ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))";
-                       ;;
-       esac
-       return $s
-}
-
-process_tor_interface(){
-       local s=0 iface="$1" action="$2" displayText
-       case "$action" in
-               destroy)
-                       for i in PREROUTING FORWARD INPUT OUTPUT; do
-                               ipt -t nat -D "${i}" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
-                               ipt -t nat -F "VPR_${i}"; ipt -t nat -X "VPR_${i}";
-                       done
-                       ;;
-               create)
-                       output 2 "Creating TOR redirects "
-                       dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')"
-                       transPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')"
-                       dnsPort="${dnsPort:-9053}"; transPort="${transPort:-9040}"; 
-                       for i in $usedChainsList; do
-                               ipt -t nat -N "VPR_${i}"
-                               ipt -t nat "$insertOption" "$i" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
-                       done
-                       if ips 'create' "${iface}" 'hash:net comment' && ips 'flush' "${iface}"; then
-                               for i in $usedChainsList; do
-                                       ipt -t nat -I "VPR_${i}" -p udp -m udp --dport 53 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$dnsPort" -m comment --comment "TorDNS-UDP" || s=1
-                                       ipt -t nat -I "VPR_${i}" -p tcp -m tcp --dport 80 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTP-TCP" || s=1
-                                       ipt -t nat -I "VPR_${i}" -p udp -m udp --dport 80 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTP-UDP" || s=1
-                                       ipt -t nat -I "VPR_${i}" -p tcp -m tcp --dport 443 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTPS-TCP" || s=1
-                                       ipt -t nat -I "VPR_${i}" -p udp -m udp --dport 443 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTPS-UDP" || s=1
-                               done
-                       else
-                               s=1
-                       fi
-                       displayText="${iface}/53->${dnsPort}/80,443->${transPort}"
-                       if [ "$s" -eq "0" ]; then
-                               gatewaySummary="${gatewaySummary}${displayText}\\n"
-                               output_ok
-                       else
-                               errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n"
-                               output_fail
-                       fi
-                       ;;
-       esac
-       return $s
-}
-
-convert_config(){
-       local i src_ipset dest_ipset resolver_ipset
-       [ -s "/etc/config/${packageName}" ] || return 0
-       grep -q "ignored_interfaces" "/etc/config/${packageName}" && sed -i 's/ignored_interfaces/ignored_interface/g' "/etc/config/${packageName}"
-       grep -q "supported_interfaces" "/etc/config/${packageName}" && sed -i 's/supported_interfaces/supported_interface/g' "/etc/config/${packageName}"
-       grep -q "local_addresses" "/etc/config/${packageName}" && sed -i 's/local_addresses/local_address/g' "/etc/config/${packageName}"
-       grep -q "local_ports" "/etc/config/${packageName}" && sed -i 's/local_ports/local_port/g' "/etc/config/${packageName}"
-       grep -q "remote_addresses" "/etc/config/${packageName}" && sed -i 's/remote_addresses/remote_address/g' "/etc/config/${packageName}"
-       grep -q "remote_ports" "/etc/config/${packageName}" && sed -i 's/remote_ports/remote_port/g' "/etc/config/${packageName}"
-       grep -q "ipset_enabled" "/etc/config/${packageName}" && sed -i 's/ipset_enabled/dest_ipset/g' "/etc/config/${packageName}"
-       grep -q "dnsmasq_enabled" "/etc/config/${packageName}" && sed -i 's/dnsmasq_enabled/resolver_ipset/g' "/etc/config/${packageName}"
-       grep -q "enable_control" "/etc/config/${packageName}" && sed -i 's/enable_control/webui_enable_column/g' "/etc/config/${packageName}"
-       grep -q "proto_control" "/etc/config/${packageName}" && sed -i 's/proto_control/webui_protocol_column/g' "/etc/config/${packageName}"
-       grep -q "chain_control" "/etc/config/${packageName}" && sed -i 's/chain_control/webui_chain_column/g' "/etc/config/${packageName}"
-       grep -q "sort_control" "/etc/config/${packageName}" && sed -i 's/sort_control/webui_sorting/g' "/etc/config/${packageName}"
-       grep -q "local_address" "/etc/config/${packageName}" && sed -i 's/local_address/src_addr/g' "/etc/config/${packageName}"
-       grep -q "local_port" "/etc/config/${packageName}" && sed -i 's/local_port/src_port/g' "/etc/config/${packageName}"
-       grep -q "remote_address" "/etc/config/${packageName}" && sed -i 's/remote_address/dest_addr/g' "/etc/config/${packageName}"
-       grep -q "remote_port" "/etc/config/${packageName}" && sed -i 's/remote_port/dest_port/g' "/etc/config/${packageName}"
-       grep -q "local_ipset" "/etc/config/${packageName}" && sed -i 's/local_ipset/src_ipset/g' "/etc/config/${packageName}"
-       grep -q "remote_ipset" "/etc/config/${packageName}" && sed -i 's/remote_ipset/dest_ipset/g' "/etc/config/${packageName}"
-       dest_ipset="$(uci -q get $packageName.config.dest_ipset)"
-       src_ipset="$(uci -q get $packageName.config.src_ipset)"
-       resolver_ipset="$(uci -q get $packageName.config.resolver_ipset)"
-       
-       if [ -n "$dest_ipset" ] && [ "$dest_ipset" != "0" ] && [ "$dest_ipset" != "1" ]; then
-               uci set "$packageName".config.dest_ipset='0'
-               if [ -z "$resolver_ipset" ]; then
-                       uci set "$packageName".config.resolver_ipset='dnsmasq.ipset'
-               fi
-               uci commit "$packageName"
-       fi
-       if [ -n "$src_ipset" ] && [ "$src_ipset" != "0" ] && [ "$src_ipset" != "1" ]; then
-               uci set "$packageName".config.src_ipset='1'
-               uci commit "$packageName"
-       fi
-       if [ -z "$(uci -q get $packageName.config.webui_supported_protocol)" ]; then
-               uci add_list "$packageName".config.webui_supported_protocol='tcp'
-               uci add_list "$packageName".config.webui_supported_protocol='udp'
-               uci add_list "$packageName".config.webui_supported_protocol='tcp udp'
-               uci add_list "$packageName".config.webui_supported_protocol='icmp'
-               uci add_list "$packageName".config.webui_supported_protocol='all'
-               uci commit "$packageName"
-       fi
-       for i in append_local_rules append_src_rules \
-               append_remote_rules append_dest_rules; do
-               if [ -n "$(uci -q get $packageName.config.$i)" ]; then
-                       warningSummary="${warningSummary}$_WARNING_: $i setting is not supported in ${serviceName}.\\n"
-               fi
-       done
-       for i in udp_proto_enabled forward_chain_enabled input_chain_enabled \
-               output_chain_enabled iprule_enabled; do
-               if [ "$(uci -q get $packageName.config.$i)" = "1" ]; then
-                       warningSummary="${warningSummary}$_WARNING_: $i setting is not supported in ${serviceName}.\\n"
-               fi
-       done
-}
-
-check_config(){ local en; config_get_bool en "$1" 'enabled' 1; [ "$en" -gt 0 ] && _cfg_enabled=0; }
-is_config_enabled(){
-       local cfg="$1" _cfg_enabled=1
-       [ -n "$1" ] || return 1
-       config_load "$packageName"
-       config_foreach check_config "$cfg"
-       return "$_cfg_enabled"
-}
-
-process_user_file(){
-       local path enabled shellBin="${SHELL:-/bin/ash}"
-       config_get_bool enabled "$1" 'enabled' 1
-       config_get      path    "$1" 'path'
-       [ "$enabled" -gt 0 ] || return 0
-       if [ ! -s "$path" ]; then
-               errorSummary="${errorSummary}${_ERROR_}: Custom user file '$path' not found or empty!\\n"
-               output_fail
-               return 1
-       fi
-       if ! $shellBin -n "$path"; then
-               errorSummary="${errorSummary}${_ERROR_}: Syntax error in custom user file '$path'!\\n"
-               output_fail
-               return 1
-       fi
-       output 2 "Running $path "
-# shellcheck disable=SC1090
-       if ! . "$path"; then
-               errorSummary="${errorSummary}${_ERROR_}: Error running custom user file '$path'!\\n"
-               if grep -q -w 'curl' "$path" && ! is_present 'curl'; then
-                       errorSummary="${errorSummary}${_ERROR_}: Use of 'curl' is detected in custom user file '$path', but 'curl' isn't installed!\\n"
-                       errorSummary="${errorSummary}${_ERROR_}: If 'curl' is needed, install it with 'opkg update; opkg install curl;' command in CLI.\\n"
-               fi
-               output_fail
-               return 1
-       else
-               output_ok
-               return 0
-       fi
-}
-
-boot() { rc_procd start_service && rc_procd service_triggers; }
-
-start_service() {
-       local dnsmasqStoredHash dnsmasqNewHash i modprobeStatus=0 reloadedIface="$1"
-       convert_config
-       is_enabled 'on_start' || return 1
-       is_wan_up || return 1
-
-       iptables -t 'mangle' --list 'VPR_PREROUTING' >/dev/null 2>&1 || unset reloadedIface
-       [ -n "$(tmpfs get gateway)" ] || unset reloadedIface
-
-       if [ -s "$dnsmasqFile" ]; then
-               dnsmasqStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
-               rm -f "$dnsmasqFile"
-       fi
-
-       for i in xt_set ip_set ip_set_hash_ip; do
-               modprobe "$i" >/dev/null 2>/dev/null || modprobeStatus=$((modprobeStatus + 1))
-       done
-
-       if [ "$modprobeStatus" -gt 0 ] && ! is_chaos_calmer; then
-               errorSummary="${errorSummary}${_ERROR_}: Failed to load kernel modules\\n"
-       fi
-
-       if [ -z "$reloadedIface" ]; then
-               for i in $usedChainsList; do
-                       ipt -t mangle -N "VPR_${i}"
-                       ipt -t mangle "$insertOption" "$i" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
-               done
-       fi
-
-       if [ -z "$reloadedIface" ]; then
-               output 1 'Processing Interfaces '
-               config_load 'network'; config_foreach process_interface 'interface' 'create';
-               process_tor_interface 'tor' 'destroy'; is_tor_running && process_tor_interface 'tor' 'create';
-               output 1 '\n'
-               if is_config_enabled 'policy'; then
-                       output 1 'Processing Policies '
-                       config_load "$packageName"; config_foreach process_policy 'policy' "$reloadedIface";
-                       output 1 '\n'
-               fi
-               if is_config_enabled 'include'; then
-                       output 1 'Processing User File(s) '
-                       config_load "$packageName"; config_foreach process_user_file 'include';
-                       output 1 '\n'
-               fi
-       else
-               output 1 "Reloading Interface: $reloadedIface "
-               config_load 'network'; config_foreach process_interface 'interface' 'create' "$reloadedIface";
-               output 1 '\n'
-       fi
-
-       if [ -s "$dnsmasqFile" ]; then
-               dnsmasqNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
-       fi
-       [ "$dnsmasqNewHash" != "$dnsmasqStoredHash" ] && dnsmasq_restart
-
-       if [ -z "$gatewaySummary" ]; then
-               errorSummary="${errorSummary}${_ERROR_}: failed to set up any gateway!\\n"
-       fi
-       procd_open_instance "main"
-       procd_set_param command /bin/true
-       procd_set_param stdout 1
-       procd_set_param stderr 1
-       procd_open_data
-       json_add_array 'status'
-       json_add_object ''
-       [ -n "$gatewaySummary" ] && json_add_string gateway "$gatewaySummary"
-       [ -n "$errorSummary" ] && json_add_string error "$errorSummary"
-       [ -n "$warningSummary" ] && json_add_string warning "$warningSummary"
-       if [ "$strictMode" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then
-               json_add_string mode "strict"
-       fi
-       json_close_object
-       json_close_array
-       procd_close_data
-       procd_close_instance
-}
-
-tmpfs() {
-       local action="$1" param="$2" value="$3"
-# shellcheck disable=SC2034
-       local gateway error warning mode i
-       if [ -s "$jsonFile" ]; then
-               json_load_file "$jsonFile" 2>/dev/null
-               json_select 'status' 2>/dev/null
-               for i in gateway error warning mode; do
-                       json_get_var $i "$i" 2>/dev/null
-               done
-       fi
-       case "$action" in
-               get)
-                       printf "%b" "$(eval echo "\$$param")"; return;;
-               add)
-                       eval "$param"='$(eval echo "\$$param")${value}';;
-               del)
-                       case "$param" in
-                               all)
-                                       unset gateway error warning mode;;
-                               *)
-                                       unset "$param";;
-                       esac
-                       ;;
-               set)
-                       eval "$param"='$value';;
-       esac
-       json_init
-       json_add_object 'status'
-       json_add_string version "$PKG_VERSION"
-       for i in gateway error warning mode; do
-               json_add_string "$i" "$(eval echo "\$$i")"
-       done 
-       json_close_object
-       json_dump > "$jsonFile"
-       sync
-}
-
-service_started() {
-       tmpfs set 'gateway' "$gatewaySummary"
-       tmpfs set 'error' "$errorSummary"
-       tmpfs set 'warning' "$warningSummary"
-       if [ "$strictMode" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then
-               tmpfs set 'mode' 'strict'
-       fi
-       [ -n "$gatewaySummary" ] && output "$serviceName started with gateways:\\n${gatewaySummary}"
-       [ -n "$errorSummary" ] && output "${errorSummary}"
-       [ -n "$warningSummary" ] && output "${warningSummary}"
-       if [ -n "$errorSummary" ]; then
-               return 2
-       elif [ -n "$warningSummary" ]; then
-               return 1
-       else
-               return 0
-       fi
-}
-
-stop_service() {
-       local i
-       iptables -t mangle -L | grep -q VPR_PREROUTING || return 0
-       load_package_config
-       for i in PREROUTING FORWARD INPUT OUTPUT; do
-               ipt -t mangle -D "${i}" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
-               ipt -t mangle -F "VPR_${i}"; ipt -t mangle -X "VPR_${i}";
-       done
-       config_load 'network'; config_foreach process_interface 'interface' 'destroy';
-       process_tor_interface 'tor' 'destroy'
-       unset ifaceTableID; unset ifaceMark;
-       if [ -s "$dnsmasqFile" ]; then
-               rm -f "$dnsmasqFile"
-               dnsmasq_restart
-       fi
-       if [ "$serviceEnabled" -ne 0 ]; then
-               output "$serviceName stopped "; output_okn;
-       fi
-}
-
-reload_interface() { rc_procd start_service "$1"; }
-
-service_triggers() {
-       local n
-       is_enabled || return 1
-
-       if [ "$procdReloadDelay" -gt 0 ] && [ "$procdReloadDelay" -lt 100 ]; then
-# shellcheck disable=SC2034
-               PROCD_RELOAD_DELAY=$(( procdReloadDelay * 1000  ))
-       fi
-
-       procd_open_validate
-               validate_config
-               validate_policy
-               validate_include
-       procd_close_validate
-
-       procd_open_trigger
-               procd_add_reload_trigger 'openvpn'
-               if type procd_add_service_trigger 1>/dev/null 2>&1; then
-                       procd_add_service_trigger "service.restart" "firewall" /etc/init.d/${packageName} reload
-               fi
-               procd_add_config_trigger "config.change" "${packageName}" /etc/init.d/${packageName} reload
-               for n in $ifSupported; do 
-                       procd_add_interface_trigger "interface.*" "$n" /etc/init.d/${packageName} reload_interface "$n"
-               done
-       procd_close_trigger
-
-       output 3 "$serviceName monitoring interfaces: $ifSupported"; output_okn;
-}
-
-status_service() { support "$@"; }
-support() {
-       local dist vers out id s param status set_d set_p tableCount i=0 dev dev6 j
-       readonly _SEPARATOR_='============================================================'
-       is_enabled
-
-       json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
-       if [ -n "$wanIface4" ]; then
-               network_get_gateway wanGW4 "$wanIface4"
-               [ -z "$dev" ] && dev="$(uci -q get network."${wanIface4}".ifname)"
-               [ -z "$dev" ] && dev="$(uci -q get network."${wanIface4}".device)"
-       fi
-       if [ -n "$wanIface6" ]; then
-               [ -z "$dev6" ] && dev6="$(uci -q get network."${wanIface6}".ifname)"
-               [ -z "$dev6" ] && dev6="$(uci -q get network."${wanIface6}".device)"
-               wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
-               [ "$wanGW6" = "default" ] && wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
-       fi
-       while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
-       [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
-       status="$serviceName running on $dist $vers."
-       [ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
-       [ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
-       {
-               echo "$status"
-               echo "$_SEPARATOR_"
-               dnsmasq --version 2>/dev/null | sed '/^$/,$d'
-               if [ -n "$1" ]; then
-                       echo "$_SEPARATOR_"
-                       echo "Resolving domains"
-                       for i in $1; do
-                               echo "$i: $(resolveip "$i" | tr '\n' ' ')"
-                       done
-               fi
-
-               echo "$_SEPARATOR_"
-               echo "Routes/IP Rules"
-               tableCount=$(ip rule list | grep -c 'fwmark') || tableCount=0
-               if [ -n "$set_d" ]; then route; else route | grep '^default'; fi
-               if [ -n "$set_d" ]; then ip rule list; fi
-               i=0; while [ $i -lt $tableCount ]; do 
-                       echo ""
-                       echo "IPv4 Table $((wanTableID + i)): $(ip -4 route show table $((wanTableID + i)))"
-                       echo "IPv4 Table $((wanTableID + i)) Rules:"
-                       ip -4 rule list table "$((wanTableID + i))"
-                       i=$((i + 1))
-               done
-
-               if [ "$ipv6Enabled" -ne 0 ]; then
-                       i=0; while [ $i -lt $tableCount ]; do
-                               ip -6 route show table $((wanTableID + i)) | while read -r param; do
-                                       echo "IPv6 Table $((wanTableID + i)): $param"
-                               done
-                               i=$((i + 1))
-                       done
-               fi
-
-               for j in Mangle NAT; do
-                       if [ -z "$set_d" ]; then
-                               for i in $usedChainsList; do
-                                       if iptables -v -t "$(str_to_lower $j)" -S "VPR_${i}" 1>/dev/null 2>&1; then
-                                               echo "$_SEPARATOR_"
-                                               echo "$j IP Table: $i"
-                                               iptables -v -t "$(str_to_lower $j)" -S "VPR_${i}"
-                                               if [ "$ipv6Enabled" -ne 0 ]; then
-                                                       echo "$_SEPARATOR_"
-                                                       echo "$j IPv6 Table: $i"
-                                                       ip6tables -v -t "$(str_to_lower $j)" -S "VPR_${i}"
-                                               fi
-                                       fi
-                               done
-                       else
-                               echo "$_SEPARATOR_"
-                               echo "$j IP Table"
-                               iptables -L -t "$(str_to_lower $j)"
-                               if [ "$ipv6Enabled" -ne 0 ]; then
-                                       echo "$_SEPARATOR_"
-                                       echo "$j IPv6 Table"
-                                       ip6tables -L -t "$(str_to_lower $j)"
-                               fi
-                       fi
-                       i=0; ifaceMark="$wanMark";
-                       while [ $i -lt $tableCount ]; do
-                               if iptables -v -t "$(str_to_lower $j)" -S "VPR_MARK${ifaceMark}" 1>/dev/null 2>&1; then
-                                       echo "$_SEPARATOR_"
-                                       echo "$j IP Table MARK Chain: VPR_MARK${ifaceMark}"
-                                       iptables -v -t "$(str_to_lower $j)" -S "VPR_MARK${ifaceMark}"
-                                       ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))";
-                               fi
-                               i=$((i + 1))
-                       done
-               done
-
-               echo "$_SEPARATOR_"
-               echo "Current ipsets"
-               ipset save
-               if [ -s "$dnsmasqFile" ]; then
-                       echo "$_SEPARATOR_"
-                       echo "DNSMASQ ipsets"
-                       cat "$dnsmasqFile"
-               fi
-               echo "$_SEPARATOR_"
-       } | tee -a /var/${packageName}-support
-       if [ -n "$set_p" ]; then
-               printf "%b" "Pasting to paste.ee... "
-               if is_present 'curl' && is_variant_installed 'libopenssl' && is_installed 'ca-bundle'; then
-                       json_init; json_add_string "description" "${packageName}-support"
-                       json_add_array "sections"; json_add_object '0'
-                       json_add_string "name" "$(uci -q get system.@system[0].hostname)"
-                       json_add_string "contents" "$(cat /var/${packageName}-support)"
-                       json_close_object; json_close_array; payload=$(json_dump)
-                       out=$(curl -s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload")
-                       json_load "$out"; json_get_var id id; json_get_var s success
-                       [ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" || printf "%b" "$__FAIL__\\n"
-                       [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
-               else
-                       printf "%b" "$__FAIL__\\n"
-                       printf "%b" "$_ERROR_: curl, libopenssl or ca-bundle were not found!\\nRun 'opkg update; opkg install curl libopenssl ca-bundle' to install them.\\n"
-               fi
-       else
-               printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n"
-       fi
-}
-
-# shellcheck disable=SC2120
-validate_config() {
-       uci_validate_section "${packageName}" config "${1}" \
-               'enabled:bool:0' \
-               'strict_enforcement:bool:1' \
-               'ipv6_enabled:bool:0' \
-               'src_ipset:bool:0' \
-               'dest_ipset:bool:0' \
-               'resolver_ipset::or("", "none", "dnsmasq.ipset")' \
-               'verbosity:range(0,2):1' \
-               'wan_tid:integer:201' \
-               'wan_fw_mark:hex(8)' \
-               'fw_mask:hex(8)' \
-               'icmp_interface:string' \
-               'ignored_interface:list(string)' \
-               'supported_interface:list(string)' \
-               'boot_timeout:integer:30' \
-               'iptables_rule_option:or("", "append", "insert")' \
-               'procd_reload_delay:integer:0' \
-               'webui_enable_column:bool:0' \
-               'webui_protocol_column:bool:0' \
-               'webui_supported_protocol:list(string)' \
-               'webui_chain_column:bool:0' \
-               'webui_sorting:bool:1' \
-               'webui_show_ignore_target:bool:0'
-}
-
-# shellcheck disable=SC2120
-validate_policy() {
-       uci_validate_section "${packageName}" policy "${1}" \
-               'name:string' \
-               'enabled:bool:0' \
-               'interface:network' \
-               'proto:or(string)' \
-               'chain:or("", "PREROUTING", "FORWARD", "INPUT", "OUTPUT")' \
-               'src_addr:list(neg(or(host,network,macaddr)))' \
-               'src_port:list(neg(or(portrange, string)))' \
-               'dest_addr:list(neg(host))' \
-               'dest_port:list(neg(or(portrange, string)))'
-}
-
-# shellcheck disable=SC2120
-validate_include() {
-       uci_validate_section "${packageName}" include "${1}" \
-               'path:string' \
-               'enabled:bool:0'
-}
diff --git a/net/vpn-policy-routing/files/vpn-policy-routing.netflix.user b/net/vpn-policy-routing/files/vpn-policy-routing.netflix.user
deleted file mode 100644 (file)
index 02335c4..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/sh
-# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh
-# Credits to https://forum.openwrt.org/u/dscpl for api.hackertarget.com code.
-# Credits to https://github.com/kkeker and https://github.com/tophirsch for api.bgpview.io code.
-
-TARGET_IPSET='wan'
-TARGET_ASN='2906'
-TARGET_FNAME="/var/vpn-policy-routing_tmp_AS${TARGET_ASN}"
-#DB_SOURCE='ipinfo.io'
-#DB_SOURCE='api.hackertarget.com'
-DB_SOURCE='api.bgpview.io'
-
-_ret=1
-
-if [ ! -s "$TARGET_FNAME" ]; then
-       if [ "$DB_SOURCE" = "ipinfo.io" ]; then
-               TARGET_URL="https://ipinfo.io/AS${TARGET_ASN}"
-               curl "$TARGET_URL" 2>/dev/null | grep -E "a href.*${TARGET_ASN}\/" | grep -v ":" | sed "s/^.*<a href=\"\/AS${TARGET_ASN}\///; s/\" >//" > "$TARGET_FNAME"
-       fi
-
-       if [ "$DB_SOURCE" = "api.hackertarget.com" ]; then
-               TARGET_URL="https://api.hackertarget.com/aslookup/?q=AS${TARGET_ASN}"
-               curl "$TARGET_URL" 2>/dev/null | sed '1d' > "$TARGET_FNAME"
-       fi
-
-       if [ "$DB_SOURCE" = "api.bgpview.io" ]; then
-               TARGET_URL="https://api.bgpview.io/asn/${TARGET_ASN}/prefixes"
-               curl -s "$TARGET_URL" 2>/dev/null | jsonfilter -e '@.data.ipv4_prefixes[*].prefix' > "$TARGET_FNAME"
-       fi
-fi
-
-if [ -s "$TARGET_FNAME" ]; then
-       awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_FNAME" | ipset restore -! && _ret=0
-fi
-rm -f "$TARGET_FNAME"
-
-return $_ret
diff --git a/net/vpn-policy-routing/test.sh b/net/vpn-policy-routing/test.sh
deleted file mode 100644 (file)
index 45469ed..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-/etc/init.d/"$1" version 2>&1 | grep "$2"
diff --git a/net/vpnbypass/Makefile b/net/vpnbypass/Makefile
deleted file mode 100644 (file)
index 83a4786..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright 2017-2018 Stan Grishin (stangri@melmac.net)
-# This is free software, licensed under the GNU General Public License v3.
-
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=vpnbypass
-PKG_VERSION:=1.3.2
-PKG_RELEASE:=1
-PKG_LICENSE:=GPL-3.0-or-later
-PKG_MAINTAINER:=Stan Grishin <stangri@melmac.net>
-
-include $(INCLUDE_DIR)/package.mk
-
-define Package/vpnbypass
-       SECTION:=net
-       CATEGORY:=Network
-       TITLE:=VPN Bypass Service
-       URL:=https://docs.openwrt.melmac.net/vpnbypass/
-       DEPENDS:=+ipset +iptables
-       PKGARCH:=all
-endef
-
-define Package/vpnbypass/description
-This service can be used to enable simple VPN split tunnelling.
-Supports accessing domains, IP ranges outside of your VPN tunnel.
-Also supports dedicating local ports/IP ranges for direct
-internet access (outside of your VPN tunnel).
-Please see the README for further information.
-endef
-
-define Package/vpnbypass/conffiles
-/etc/config/vpnbypass
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
-endef
-
-define Package/vpnbypass/install
-       $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/etc/hotplug.d/firewall
-       $(INSTALL_BIN) ./files/vpnbypass.init $(1)/etc/init.d/vpnbypass
-       $(SED) "s|^\(PKG_VERSION\).*|\1='$(PKG_VERSION)-$(PKG_RELEASE)'|" $(1)/etc/init.d/vpnbypass
-       $(INSTALL_CONF) ./files/vpnbypass.config $(1)/etc/config/vpnbypass
-       $(INSTALL_DATA) ./files/vpnbypass.hotplug $(1)/etc/hotplug.d/firewall/94-vpnbypass
-endef
-
-define Package/vpnbypass/postinst
-       #!/bin/sh
-       # check if we are on real system
-       if [ -z "$${IPKG_INSTROOT}" ]; then
-               /etc/init.d/vpnbypass enable
-       fi
-       exit 0
-endef
-
-define Package/vpnbypass/prerm
-       #!/bin/sh
-       # check if we are on real system
-       if [ -z "$${IPKG_INSTROOT}" ]; then
-               echo "Stopping service and removing rc.d symlink for vpnbypass"
-               /etc/init.d/vpnbypass stop || true
-               /etc/init.d/vpnbypass disable || true
-       fi
-       exit 0
-endef
-
-$(eval $(call BuildPackage,vpnbypass))
diff --git a/net/vpnbypass/files/README.md b/net/vpnbypass/files/README.md
deleted file mode 100644 (file)
index 886ac7c..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# README
-
-README has been moved to [https://docs.openwrt.melmac.net/vpnbypass/](https://docs.openwrt.melmac.net/vpnbypass/).
diff --git a/net/vpnbypass/files/vpnbypass.config b/net/vpnbypass/files/vpnbypass.config
deleted file mode 100644 (file)
index 0768e86..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-config vpnbypass 'config'
-       option enabled          '0'
-       list localport          '32400'
-       list localsubnet        '192.168.1.81/29'
-       list remotesubnet       '25.0.0.0/8'
diff --git a/net/vpnbypass/files/vpnbypass.hotplug b/net/vpnbypass/files/vpnbypass.hotplug
deleted file mode 100644 (file)
index a2874f9..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-[ "$ACTION" = "reload" ] && /etc/init.d/vpnbypass reload
diff --git a/net/vpnbypass/files/vpnbypass.init b/net/vpnbypass/files/vpnbypass.init
deleted file mode 100644 (file)
index 03a95ae..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/bin/sh /etc/rc.common
-# Copyright 2017-2020 Stan Grishin (stangri@melmac.net)
-# shellcheck disable=SC2039,SC1091,SC2086,SC3043,SC3057,SC3060
-PKG_VERSION='dev-test'
-
-# shellcheck disable=SC2034
-START=94
-# shellcheck disable=SC2034
-USE_PROCD=1
-
-if type extra_command 1>/dev/null 2>&1; then
-       extra_command 'version' 'Show version information'
-else
-# shellcheck disable=SC2034
-       EXTRA_COMMANDS='version'
-fi
-
-version() { echo "$PKG_VERSION"; }
-
-readonly __ERROR__='\033[0;31mERROR\033[0m'
-
-# shellcheck disable=SC2034
-serviceEnabled=0
-verbosity=2
-TID='200'
-IPSET='vpnbypass'
-FW_MARK='0x010000'
-FW_MASK='0xff0000'
-wan_if4=''
-wan_gw=''
-
-readonly packageName='vpnbypass'
-readonly serviceName="$packageName $PKG_VERSION"
-readonly sharedMemoryOutput="/dev/shm/$packageName-output"
-
-output() {
-# Can take a single parameter (text) to be output at any verbosity
-# Or target verbosity level and text to be output at specifc verbosity
-       local msg memmsg logmsg
-       if [ $# -ne 1 ]; then
-               if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; else return 0; fi
-       fi
-       [ -t 1 ] && printf "%b" "$1"
-       msg="${1//$serviceName /service }";
-       if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then
-               [ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
-               logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
-               logger -t "${packageName:-service} [$$]" "$(printf "%b" "$logmsg")"
-               rm -f "$sharedMemoryOutput"
-       else
-               printf "%b" "$msg" >> "$sharedMemoryOutput"
-       fi
-}
-load_package_config() {
-       config_load "$packageName"
-       config_get_bool serviceEnabled 'config' 'enabled' 1
-       config_get verbosity           'config' 'verbosity' '2'
-       if [ -z "${verbosity##*[!0-9]*}" ] || [ "$verbosity" -lt 0 ] || [ "$verbosity" -gt 2 ]; then
-               verbosity=1
-       fi
-       . /lib/functions/network.sh
-}
-
-is_enabled() {
-       local sleepCount=1
-       load_package_config
-       while : ; do
-               network_find_wan wan_if4
-               [ "$serviceEnabled" -gt 0 ] || return 1
-               [ -n "$wan_if4" ] && network_get_gateway wan_gw "$wan_if4"
-               if [ $sleepCount -ge 25 ] || [ -n "$wan_gw" ]; then break; fi
-               output "$serviceName waiting for wan gateway...\\n"
-               sleep 2; network_flush_cache; sleepCount=$((sleepCount+1));
-       done
-       [ -n "$wan_gw" ] && return 0
-       output "$__ERROR__: $serviceName failed to discover WAN gateway.\\n"; return 1;
-}
-
-is_ovpn() { local dev i; for i in ifname device; do [ -z "$dev" ] && dev="$(uci -q get "network.${1}.${i}")"; done; if [ "${dev:0:3}" = "tun" ] || [ "${dev:0:3}" = "tap" ] || [ -f "/sys/devices/virtual/net/${dev}/tun_flags" ]; then return 0; else return 1; fi; }
-is_wan() { if [ -n "$wan_if4" ] && [ "$1" = "$wan_if4" ]; then return 0; else return 1; fi; }
-is_supported_interface() { if is_wan "$1" || is_ovpn "$1"; then return 0; else return 1; fi; }
-
-ipt() {
-       local d; 
-       d="${*//-A/-D}"; [ "$d" != "$*" ] && iptables $d >/dev/null 2>&1
-       d="${*//-I/-D}"; [ "$d" != "$*" ] && iptables $d >/dev/null 2>&1
-       d="${*//-N/-F}"; [ "$d" != "$*" ] && iptables $d >/dev/null 2>&1
-       d="${*//-N/-X}"; [ "$d" != "$*" ] && iptables $d >/dev/null 2>&1
-       d="$*"; iptables $d >/dev/null 2>&1 || output "\\n$__ERROR__: iptables $d\\n"
-}
-
-start_service() {
-       local ll lports rports routes ranges
-       is_enabled || return 1
-       config_get lports   'config' 'localport'
-       config_get rports   'config' 'remoteport'
-       config_get routes   'config' 'remotesubnet'
-       config_get ranges   'config' 'localsubnet'
-
-       procd_open_instance "main"
-       procd_set_param command /bin/true
-       procd_set_param stdout 1
-       procd_set_param stderr 1
-       procd_close_instance
-
-       ip rule del fwmark "$FW_MARK" table "$TID" >/dev/null 2>&1; 
-       ipset -q flush "$IPSET"; ipset -q destroy "$IPSET";
-       ip route flush table "$TID"; ip route flush cache;
-       ip route add default via "$wan_gw" table "$TID"; ip route flush cache;
-       ip rule add fwmark "$FW_MARK" table "$TID"
-       ipset -q -exist create "$IPSET" hash:ip; ipset -q flush "$IPSET"
-       { modprobe xt_set; modprobe ip_set; modprobe ip_set_hash_ip; } >/dev/null 2>&1
-       ipt -t mangle -D PREROUTING -m mark --mark 0x00/${FW_MASK} -g VPNBYPASS >/dev/null 2>&1
-       { ipt -t mangle -N VPNBYPASS; ipt -t mangle -A PREROUTING -m mark --mark 0x00/${FW_MASK} -g VPNBYPASS; } >/dev/null 2>&1
-       ipt -t mangle -A VPNBYPASS -m set --match-set $IPSET dst -j MARK --set-mark ${FW_MARK}/${FW_MASK} >/dev/null 2>&1
-       for ll in ${ranges}; do ipt -t mangle -A VPNBYPASS -j MARK --set-mark ${FW_MARK}/${FW_MASK} -s "$ll"; done
-       for ll in ${lports}; do ipt -t mangle -A VPNBYPASS -j MARK --set-mark ${FW_MARK}/${FW_MASK} -p tcp -m multiport --sport "${ll//-/:}"; done
-       for ll in ${routes}; do ipt -t mangle -A VPNBYPASS -j MARK --set-mark ${FW_MARK}/${FW_MASK} -d "$ll"; done
-       for ll in ${rports}; do ipt -t mangle -A VPNBYPASS -j MARK --set-mark ${FW_MARK}/${FW_MASK} -p tcp -m multiport --dport "${ll//-/:}"; done
-       output "$serviceName started with TID: $TID; FW_MARK: $FW_MARK\\n"
-}
-
-stop_service() {
-       load_package_config
-       ip rule del fwmark "$FW_MARK" table "$TID" >/dev/null 2>&1; 
-       ipset -q flush "$IPSET"; ipset -q destroy "$IPSET";
-       ip route flush table "$TID"; ip route flush cache;
-       ipt -t mangle -D PREROUTING -m mark --mark 0x00/${FW_MASK} -g VPNBYPASS >/dev/null 2>&1
-       { ipt -t mangle -F VPNBYPASS; ipt -t mangle -X VPNBYPASS; } >/dev/null 2>&1
-       output "$serviceName stopped\\n"
-}
-
-service_triggers_load_interface() { is_supported_interface "$1" && ifaces="${ifaces}${1} "; }
-service_triggers() {
-       local ifaces n
-       config_load network; config_foreach service_triggers_load_interface 'interface';
-       procd_open_trigger
-               procd_add_reload_trigger 'openvpn'
-               if type procd_add_service_trigger 1>/dev/null 2>&1; then
-                       procd_add_service_trigger "service.restart" "firewall" /etc/init.d/${packageName} reload
-               fi
-               procd_add_config_trigger "config.change" "${packageName}" /etc/init.d/${packageName} reload
-               for n in $ifaces; do procd_add_reload_interface_trigger "$n"; procd_add_interface_trigger "interface.*" "$n" /etc/init.d/vpnbypass reload; done;
-               output "$serviceName monitoring interfaces: $ifaces\\n"
-       procd_close_trigger
-}
diff --git a/net/vpnbypass/test.sh b/net/vpnbypass/test.sh
deleted file mode 100644 (file)
index 45469ed..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-/etc/init.d/"$1" version 2>&1 | grep "$2"