• R/O
  • HTTP
  • SSH
  • HTTPS

packages: Commit

Community maintained packages for ImmortalWrt.


Commit MetaInfo

Revision47eca64cb82a5459668fc14df9422e365c8343b6 (tree)
Time2022-12-03 11:30:29
AuthorStan Grishin <stangri@melm...>
CommiterStan Grishin

Log Message

pbr: initial commit

* 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>

Change Summary

Incremental Difference

--- /dev/null
+++ b/net/pbr/Makefile
@@ -0,0 +1,201 @@
1+# Copyright 2017-2022 Stan Grishin (stangri@melmac.ca)
2+# This is free software, licensed under the GNU General Public License v3.
3+
4+include $(TOPDIR)/rules.mk
5+
6+PKG_NAME:=pbr
7+PKG_VERSION:=1.0.0
8+PKG_RELEASE:=1
9+PKG_LICENSE:=GPL-3.0-or-later
10+PKG_MAINTAINER:=Stan Grishin <stangri@melmac.ca>
11+
12+include $(INCLUDE_DIR)/package.mk
13+
14+define Package/pbr/default
15+ SECTION:=net
16+ CATEGORY:=Network
17+ SUBMENU:=VPN
18+ PROVIDES:=pbr
19+ TITLE:=Policy Based Routing Service
20+ URL:=https://docs.openwrt.melmac.net/pbr/
21+ DEPENDS:=+ip-full +jshn +jsonfilter +resolveip
22+ CONFLICTS:=vpnbypass vpn-policy-routing
23+ PROVIDES:=vpnbypass vpn-policy-routing
24+ PKGARCH:=all
25+endef
26+
27+define Package/pbr
28+$(call Package/pbr/default)
29+ TITLE+= with nft/nft set support
30+ DEPENDS+=+firewall4 +kmod-nft-core +kmod-nft-nat +nftables-json
31+endef
32+
33+define Package/pbr-iptables
34+$(call Package/pbr/default)
35+ TITLE+= with iptables/ipset support
36+ DEPENDS+=+ipset +iptables +kmod-ipt-ipset +iptables-mod-ipopt
37+endef
38+
39+define Package/pbr-netifd
40+$(call Package/pbr/default)
41+ TITLE+= with netifd support
42+endef
43+
44+define Package/pbr/description
45+This service enables policy-based routing for WAN interfaces and various VPN tunnels.
46+This version supports OpenWrt with both fw3/ipset/iptables and fw4/nft.
47+endef
48+
49+define Package/pbr-iptables/description
50+This service enables policy-based routing for WAN interfaces and various VPN tunnels.
51+This version supports OpenWrt with fw3/ipset/iptables.
52+endef
53+
54+define Package/pbr-netifd/description
55+This service enables policy-based routing for WAN interfaces and various VPN tunnels.
56+This version supports OpenWrt with both fw3/ipset/iptables and fw4/nft.
57+This version uses OpenWrt native netifd/tables to set up interfaces. This is WIP.
58+endef
59+
60+define Package/pbr/conffiles
61+/etc/config/pbr
62+endef
63+
64+Package/pbr-iptables/conffiles = $(Package/pbr/conffiles)
65+Package/pbr-netifd/conffiles = $(Package/pbr/conffiles)
66+
67+define Build/Configure
68+endef
69+
70+define Build/Compile
71+endef
72+
73+define Package/pbr/default/install
74+ $(INSTALL_DIR) $(1)/etc/init.d
75+ $(INSTALL_BIN) ./files/etc/init.d/pbr.init $(1)/etc/init.d/pbr
76+ $(SED) "s|^\(readonly PKG_VERSION\).*|\1='$(PKG_VERSION)-$(PKG_RELEASE)'|" $(1)/etc/init.d/pbr
77+ $(INSTALL_DIR) $(1)/etc/hotplug.d/firewall
78+ $(INSTALL_DIR) $(1)/etc/hotplug.d/iface
79+ $(INSTALL_DATA) ./files/etc/hotplug.d/iface/70-pbr $(1)/etc/hotplug.d/iface/70-pbr
80+ $(INSTALL_DIR) $(1)/etc/uci-defaults
81+ $(INSTALL_BIN) ./files/etc/uci-defaults/90-pbr $(1)/etc/uci-defaults/90-pbr
82+ $(INSTALL_DIR) $(1)/usr/share/pbr
83+ $(INSTALL_DATA) ./files/usr/share/pbr/pbr.firewall.include $(1)/usr/share/pbr/pbr.firewall.include
84+ $(INSTALL_DATA) ./files/usr/share/pbr/pbr.user.aws $(1)/usr/share/pbr/pbr.user.aws
85+ $(INSTALL_DATA) ./files/usr/share/pbr/pbr.user.netflix $(1)/usr/share/pbr/pbr.user.netflix
86+endef
87+
88+define Package/pbr/install
89+$(call Package/pbr/default/install,$(1))
90+ $(INSTALL_DIR) $(1)/etc/config
91+ $(INSTALL_CONF) ./files/etc/config/pbr $(1)/etc/config/pbr
92+ $(INSTALL_DIR) $(1)/usr/share/nftables.d
93+ $(CP) ./files/usr/share/nftables.d/* $(1)/usr/share/nftables.d/
94+endef
95+
96+define Package/pbr-iptables/install
97+$(call Package/pbr/default/install,$(1))
98+ $(INSTALL_DIR) $(1)/etc/config
99+ $(INSTALL_CONF) ./files/etc/config/pbr.iptables $(1)/etc/config/pbr
100+endef
101+
102+define Package/pbr-netifd/install
103+$(call Package/pbr/default/install,$(1))
104+ $(INSTALL_DIR) $(1)/etc/config
105+ $(INSTALL_CONF) ./files/etc/config/pbr $(1)/etc/config/pbr
106+ $(INSTALL_DIR) $(1)/etc/uci-defaults
107+ $(INSTALL_BIN) ./files/etc/uci-defaults/91-pbr $(1)/etc/uci-defaults/91-pbr
108+endef
109+
110+define Package/pbr/postinst
111+ #!/bin/sh
112+ # check if we are on real system
113+ if [ -z "$${IPKG_INSTROOT}" ]; then
114+ chmod -x /etc/init.d/pbr || true
115+ fw4 -q reload || true
116+ chmod +x /etc/init.d/pbr || true
117+ echo -n "Installing rc.d symlink for pbr... "
118+ /etc/init.d/pbr enable && echo "OK" || echo "FAIL"
119+ fi
120+ exit 0
121+endef
122+
123+define Package/pbr/prerm
124+ #!/bin/sh
125+ # check if we are on real system
126+ if [ -z "$${IPKG_INSTROOT}" ]; then
127+ uci -q delete firewall.pbr || true
128+ echo "Stopping pbr service... "
129+ /etc/init.d/pbr stop || true
130+ echo -n "Removing rc.d symlink for pbr... "
131+ /etc/init.d/pbr disable && echo "OK" || echo "FAIL"
132+ fi
133+ exit 0
134+endef
135+
136+define Package/pbr/postrm
137+ #!/bin/sh
138+ # check if we are on real system
139+ if [ -z "$${IPKG_INSTROOT}" ]; then
140+ fw4 -q reload || true
141+ fi
142+ exit 0
143+endef
144+
145+define Package/pbr-iptables/postinst
146+ #!/bin/sh
147+ # check if we are on real system
148+ if [ -z "$${IPKG_INSTROOT}" ]; then
149+ echo -n "Installing rc.d symlink for pbr... "
150+ /etc/init.d/pbr enable && echo "OK" || echo "FAIL"
151+ fi
152+ exit 0
153+endef
154+
155+define Package/pbr-iptables/prerm
156+ #!/bin/sh
157+ # check if we are on real system
158+ if [ -z "$${IPKG_INSTROOT}" ]; then
159+ uci -q delete firewall.pbr || true
160+ echo "Stopping pbr service... "
161+ /etc/init.d/pbr stop || true
162+ echo -n "Removing rc.d symlink for pbr... "
163+ /etc/init.d/pbr disable && echo "OK" || echo "FAIL"
164+ fi
165+ exit 0
166+endef
167+
168+define Package/pbr-netifd/postinst
169+ #!/bin/sh
170+ # check if we are on real system
171+ if [ -z "$${IPKG_INSTROOT}" ]; then
172+ echo -n "Installing rc.d symlink for pbr... "
173+ /etc/init.d/pbr enable && echo "OK" || echo "FAIL"
174+ # echo -n "Installing netifd support for pbr... "
175+ # /etc/init.d/pbr netifd install && echo "OK" || echo "FAIL"
176+ # echo -n "Restarting network... "
177+ # /etc/init.d/network restart && echo "OK" || echo "FAIL"
178+ fi
179+ exit 0
180+endef
181+
182+define Package/pbr-netifd/prerm
183+ #!/bin/sh
184+ # check if we are on real system
185+ if [ -z "$${IPKG_INSTROOT}" ]; then
186+ uci -q delete firewall.pbr || true
187+ echo "Stopping pbr service... "
188+ /etc/init.d/pbr stop || true
189+ # echo -n "Removing netifd support for pbr... "
190+ # /etc/init.d/pbr netifd remove && echo "OK" || echo "FAIL"
191+ echo -n "Removing rc.d symlink for pbr... "
192+ /etc/init.d/pbr disable && echo "OK" || echo "FAIL"
193+ # echo -n "Restarting network... "
194+ # /etc/init.d/network restart && echo "OK" || echo "FAIL"
195+ fi
196+ exit 0
197+endef
198+
199+$(eval $(call BuildPackage,pbr))
200+$(eval $(call BuildPackage,pbr-iptables))
201+#$(eval $(call BuildPackage,pbr-netifd))
--- /dev/null
+++ b/net/pbr/files/README.md
@@ -0,0 +1,3 @@
1+# README
2+
3+README is available at [https://docs.openwrt.melmac.net/pbr/](https://docs.openwrt.melmac.net/pbr/).
--- /dev/null
+++ b/net/pbr/files/etc/config/pbr
@@ -0,0 +1,45 @@
1+config pbr 'config'
2+ option enabled '0'
3+ option verbosity '2'
4+ option strict_enforcement '1'
5+ option resolver_set 'none'
6+ option ipv6_enabled '0'
7+ list ignored_interface 'vpnserver'
8+ list ignored_interface 'wgserver'
9+ option boot_timeout '30'
10+ option rule_create_option 'add'
11+ option procd_reload_delay '1'
12+ option webui_show_ignore_target '0'
13+ list webui_supported_protocol 'all'
14+ list webui_supported_protocol 'tcp'
15+ list webui_supported_protocol 'udp'
16+ list webui_supported_protocol 'tcp udp'
17+ list webui_supported_protocol 'icmp'
18+
19+config include
20+ option path '/usr/share/pbr/pbr.user.aws'
21+ option enabled 0
22+
23+config include
24+ option path '/usr/share/pbr/pbr.user.netflix'
25+ option enabled 0
26+
27+config policy
28+ option name 'Plex/Emby Local Server'
29+ option interface 'wan'
30+ option src_port '8096 8920 32400'
31+ option enabled '0'
32+
33+config policy
34+ option name 'Plex/Emby Remote Servers'
35+ option interface 'wan'
36+ option dest_addr 'plex.tv my.plexapp.com emby.media app.emby.media tv.emby.media'
37+ option enabled '0'
38+
39+config policy
40+ option name 'WireGuard Server'
41+ option interface 'wan'
42+ option src_port '51820'
43+ option chain 'OUTPUT'
44+ option proto 'udp'
45+ option enabled '0'
--- /dev/null
+++ b/net/pbr/files/etc/config/pbr.iptables
@@ -0,0 +1,45 @@
1+config pbr 'config'
2+ option enabled '0'
3+ option verbosity '2'
4+ option strict_enforcement '1'
5+ option resolver_set 'dnsmasq.ipset'
6+ option ipv6_enabled '0'
7+ list ignored_interface 'vpnserver'
8+ list ignored_interface 'wgserver'
9+ option boot_timeout '30'
10+ option rule_create_option 'add'
11+ option procd_reload_delay '1'
12+ option webui_show_ignore_target '0'
13+ list webui_supported_protocol 'all'
14+ list webui_supported_protocol 'tcp'
15+ list webui_supported_protocol 'udp'
16+ list webui_supported_protocol 'tcp udp'
17+ list webui_supported_protocol 'icmp'
18+
19+config include
20+ option path '/usr/share/pbr/pbr.user.aws'
21+ option enabled 0
22+
23+config include
24+ option path '/usr/share/pbr/pbr.user.netflix'
25+ option enabled 0
26+
27+config policy
28+ option name 'Plex/Emby Local Server'
29+ option interface 'wan'
30+ option src_port '8096 8920 32400'
31+ option enabled '0'
32+
33+config policy
34+ option name 'Plex/Emby Remote Servers'
35+ option interface 'wan'
36+ option dest_addr 'plex.tv my.plexapp.com emby.media app.emby.media tv.emby.media'
37+ option enabled '0'
38+
39+config policy
40+ option name 'WireGuard Server'
41+ option interface 'wan'
42+ option src_port '51820'
43+ option chain 'OUTPUT'
44+ option proto 'udp'
45+ option enabled '0'
--- /dev/null
+++ b/net/pbr/files/etc/hotplug.d/firewall/70-pbr
@@ -0,0 +1,6 @@
1+#!/bin/sh
2+[ "$ACTION" = "reload" ] ||[ "$ACTION" = "restart" ] || exit 0
3+if [ -x /etc/init.d/pbr ] && /etc/init.d/pbr enabled; then
4+ logger -t "pbr" "Reloading pbr due to $ACTION of firewall"
5+ /etc/init.d/pbr reload
6+fi
--- /dev/null
+++ b/net/pbr/files/etc/hotplug.d/iface/70-pbr
@@ -0,0 +1,8 @@
1+#!/bin/sh
2+# shellcheck disable=SC1091,SC3060
3+[ -s /etc/openwrt_release ] && . /etc/openwrt_release
4+[ "${DISTRIB_RELEASE//19.07}" = "$DISTRIB_RELEASE" ] && exit 0
5+if [ -x /etc/init.d/pbr ] && /etc/init.d/pbr enabled; then
6+ logger -t pbr "Reloading pbr $INTERFACE due to $ACTION of $INTERFACE ($DEVICE)"
7+ /etc/init.d/pbr reload_interface "$INTERFACE"
8+fi
--- /dev/null
+++ b/net/pbr/files/etc/init.d/pbr.init
@@ -0,0 +1,2394 @@
1+#!/bin/sh /etc/rc.common
2+# Copyright 2020-2022 Stan Grishin (stangri@melmac.ca)
3+# shellcheck disable=SC1091,SC2018,SC2019,SC3043,SC3057,SC3060
4+
5+# sysctl net.ipv4.conf.default.rp_filter=1
6+# sysctl net.ipv4.conf.all.rp_filter=1
7+
8+# shellcheck disable=SC2034
9+START=94
10+# shellcheck disable=SC2034
11+USE_PROCD=1
12+
13+if type extra_command >/dev/null 2>&1; then
14+ extra_command 'status' "Generates output required to troubleshoot routing issues
15+ Use '-d' option for more detailed output
16+ Use '-p' option to automatically upload data under VPR paste.ee account
17+ WARNING: while paste.ee uploads are unlisted, they are still publicly available
18+ List domain names after options to include their lookup in report"
19+ extra_command 'version' 'Show version information'
20+ extra_command 'on_firewall_reload' ' Run service on firewall reload'
21+ extra_command 'on_interface_reload' ' Run service on indicated interface reload'
22+else
23+# shellcheck disable=SC2034
24+ EXTRA_COMMANDS='on_firewall_reload on_interface_reload status version'
25+# shellcheck disable=SC2034
26+ EXTRA_HELP=" status Generates output required to troubleshoot routing issues
27+ Use '-d' option for more detailed output
28+ Use '-p' option to automatically upload data under VPR paste.ee account
29+ WARNING: while paste.ee uploads are unlisted, they are still publicly available
30+ List domain names after options to include their lookup in report"
31+fi
32+
33+readonly PKG_VERSION='dev-test'
34+readonly packageName='pbr'
35+readonly serviceName="$packageName $PKG_VERSION"
36+readonly serviceTrapSignals='exit SIGHUP SIGQUIT SIGKILL'
37+readonly packageConfigFile="/etc/config/${packageName}"
38+readonly nftTempFile="/var/run/${packageName}.nft"
39+#readonly nftPermFile="/etc/nftables.d/table-post/30-pbr.nft"
40+readonly dnsmasqFile="/var/dnsmasq.d/${packageName}"
41+readonly sharedMemoryOutput="/dev/shm/$packageName-output"
42+readonly _OK_='\033[0;32m\xe2\x9c\x93\033[0m'
43+readonly _FAIL_='\033[0;31m\xe2\x9c\x97\033[0m'
44+readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
45+readonly __FAIL__='\033[0;31m[\xe2\x9c\x97]\033[0m'
46+readonly _ERROR_='\033[0;31mERROR\033[0m'
47+readonly _WARNING_='\033[0;33mWARNING\033[0m'
48+readonly ip_full='/usr/libexec/ip-full'
49+readonly ipTablePrefix='pbr'
50+# shellcheck disable=SC2155
51+readonly iptables="$(command -v iptables)"
52+# shellcheck disable=SC2155
53+readonly ip6tables="$(command -v ip6tables)"
54+# shellcheck disable=SC2155
55+readonly ipset="$(command -v ipset)"
56+readonly ipsPrefix='pbr'
57+readonly iptPrefix='PBR'
58+# shellcheck disable=SC2155
59+readonly agh="$(command -v AdGuardHome)"
60+readonly aghConfigFile='/etc/adguardhome.yaml'
61+readonly aghIpsetFile="/var/run/${packageName}.adguardhome.ipsets"
62+# shellcheck disable=SC2155
63+readonly nft="$(command -v nft)"
64+readonly nftTable="fw4"
65+readonly nftPrefix='pbr'
66+readonly chainsList='forward input output postrouting prerouting'
67+
68+# package config options
69+boot_timeout=
70+enabled=
71+fw_mask=
72+icmp_interface=
73+ignored_interface=
74+ipv6_enabled=
75+procd_boot_delay=
76+procd_reload_delay=
77+resolver_set=
78+rule_create_option=
79+secure_reload=
80+strict_enforcement=
81+supported_interface=
82+verbosity=
83+wan_ip_rules_priority=
84+wan_mark=
85+
86+# run-time
87+gatewaySummary=
88+errorSummary=
89+warningSummary=
90+wanIface4=
91+wanIface6=
92+ifaceMark=
93+ifaceTableID=
94+ifacePriority=
95+ifacesAll=
96+ifacesSupported=
97+wanGW4=
98+wanGW6=
99+serviceStartTrigger=
100+processPolicyError=
101+processPolicyWarning=
102+resolver_set_supported=
103+nftPrevParam4=
104+nftPrevParam6=
105+
106+
107+get_text() {
108+ local r
109+ case "$1" in
110+ errorConfigValidation) r="Config ($packageConfigFile) validation failure!";;
111+ errorNoIpFull) r="ip-full binary cannot be found!";;
112+ errorNoIpset) r="Resolver set support (${resolver_set}) requires ipset, but ipset binary cannot be found!";;
113+ errorNoNft) r="Resolver set support (${resolver_set}) requires nftables, but nft binary cannot be found!";;
114+ errorResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system!";;
115+ errorServiceDisabled) r="The ${packageName} service is currently disabled!";;
116+ errorNoWanGateway) r="The ${serviceName} service failed to discover WAN gateway!";;
117+ errorIpsetNameTooLong) r="The ipset name '%s' is longer than allowed 31 characters!";;
118+ errorNftsetNameTooLong) r="The nft set name '%s' is longer than allowed 31 characters!";;
119+ errorUnexpectedExit) r="Unexpected exit or service termination: '%s'!";;
120+ errorPolicyNoSrcDest) r="Policy '%s' has no source/destination parameters!";;
121+ errorPolicyNoInterface) r="Policy '%s' has no assigned interface!";;
122+ errorPolicyUnknownInterface) r="Policy '%s' has an unknown interface!";;
123+ errorPolicyProcess) r="%s";;
124+ errorFailedSetup) r="Failed to set up '%s'!";;
125+ errorFailedReload) r="Failed to reload '%s'!";;
126+ errorUserFileNotFound) r="Custom user file '%s' not found or empty!";;
127+ ererrorUserFileSyntax) r="Syntax error in custom user file '%s'!";;
128+ errorUserFileRunning) r="Error running custom user file '%s'!";;
129+ errorUserFileNoCurl) r="Use of 'curl' is detected in custom user file '%s', but 'curl' isn't installed!";;
130+ errorNoGateways) r="Failed to set up any gateway!";;
131+ warningResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system.";;
132+ warningAGHVersionTooLow) r="Installed AdGuardHome (%s) doesn't support 'ipset_file' option.";;
133+ warningPolicyProcess) r="%s";;
134+ esac
135+ echo "$r"
136+}
137+
138+version() { echo "$PKG_VERSION"; }
139+output_ok() { output 1 "$_OK_"; output 2 "$__OK__\\n"; }
140+output_okn() { output 1 "$_OK_\\n"; output 2 "$__OK__\\n"; }
141+output_fail() { s=1; output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; }
142+output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; }
143+str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; }
144+str_replace() { echo "${1//$2/$3}"; }
145+str_contains() { [ -n "$1" ] &&[ -n "$2" ] && [ "${1//$2}" != "$1" ]; }
146+is_greater() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
147+is_greater_or_equal() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" = "$2"; }
148+str_contains_word() { echo "$1" | grep -q -w "$2"; }
149+str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
150+str_to_upper() { echo "$1" | tr 'a-z' 'A-Z'; }
151+str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; }
152+str_extras_to_space() { echo "$1" | tr ';{}' ' '; }
153+debug() { local i j; for i in "$@"; do eval "j=\$$i"; echo "${i}: ${j} "; done; }
154+output() {
155+# Can take a single parameter (text) to be output at any verbosity
156+# Or target verbosity level and text to be output at specifc verbosity
157+ local msg memmsg logmsg
158+ verbosity="${verbosity:-2}"
159+ if [ "$#" -ne 1 ]; then
160+ if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; else return 0; fi
161+ fi
162+ [ -t 1 ] && printf "%b" "$1"
163+ msg="${1//$serviceName /service }";
164+ if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then
165+ [ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
166+ logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
167+ logger -t "${packageName:-service}" "$(printf "%b" "$logmsg")"
168+ rm -f "$sharedMemoryOutput"
169+ else
170+ printf "%b" "$msg" >> "$sharedMemoryOutput"
171+ fi
172+}
173+is_present() { command -v "$1" >/dev/null 2>&1; }
174+is_installed() { [ -s "/usr/lib/opkg/info/${1}.control" ]; }
175+is_variant_installed() { [ "$(echo /usr/lib/opkg/info/"${1}"*.control)" != "/usr/lib/opkg/info/${1}*.control" ]; }
176+is_nft() { [ -x "$nft" ] && ! str_contains "$resolver_set" 'ipset' && "$nft" list chains inet | grep -q "${nftPrefix}_prerouting"; }
177+_build_ifaces_all() { ifacesAll="${ifacesAll}${1} "; }
178+_build_ifaces_supported() { is_supported_interface "$1" && ifacesSupported="${ifacesSupported}${1} "; }
179+pbr_find_iface() {
180+ local iface i param="$2"
181+ [ "$param" = 'wan6' ] || param='wan'
182+ "network_find_${param}" iface
183+ is_tunnel "$iface" && unset iface
184+ if [ -z "$iface" ]; then
185+ for i in $ifacesAll; do
186+ if "is_${param}" "$i"; then break; else unset i; fi
187+ done
188+ fi
189+ eval "$1"='${iface:-$i}'
190+}
191+pbr_get_gateway() {
192+ local iface="$2" dev="$3" gw
193+ network_get_gateway gw "$iface" true
194+# if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
195+# gw="$(ubus call "network.interface.${iface}" status | jsonfilter -e "@.route[0].nexthop")"
196+# fi
197+ if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
198+ gw="$($ip_full -4 a list dev "$dev" 2>/dev/null | grep inet | awk '{print $2}' | awk -F "/" '{print $1}')"
199+ fi
200+ eval "$1"='$gw'
201+}
202+pbr_get_gateway6() {
203+ local iface="$2" dev="$3" gw
204+ network_get_gateway6 gw "$iface" true
205+ if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
206+ gw="$($ip_full -6 a list dev "$dev" 2>/dev/null | grep inet6 | awk '{print $2}')"
207+ fi
208+ eval "$1"='$gw'
209+}
210+is_dslite() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:6}" = "dslite" ]; }
211+is_l2tp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "l2tp" ]; }
212+is_oc() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:11}" = "openconnect" ]; }
213+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" ]; }
214+is_pptp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "pptp" ]; }
215+is_softether() { local dev; network_get_device dev "$1"; [ "${dev:0:4}" = "vpn_" ]; }
216+is_tor() { [ "$(str_to_lower "$1")" = "tor" ]; }
217+is_tor_running() {
218+ local ret=0
219+ if [ -s "/etc/tor/torrc" ]; then
220+ json_load "$(ubus call service list "{ 'name': 'tor' }")"
221+ json_select 'tor'; json_select 'instances'; json_select 'instance1';
222+ json_get_var ret 'running'; json_cleanup
223+ fi
224+ if [ "$ret" = "0" ]; then return 1; else return 0; fi
225+}
226+is_wg() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:9}" = "wireguard" ]; }
227+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"; }
228+is_wan() { [ "$1" = "$wanIface4" ] || { [ "${1##wan}" != "$1" ] && [ "${1##wan6}" = "$1" ]; } || [ "${1%%wan}" != "$1" ]; }
229+is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1/#wan6}" != "$1" ] || [ "${1/%wan6}" != "$1" ]; }
230+is_ignored_interface() { str_contains_word "$ignored_interface" "$1"; }
231+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"; }
232+is_ignore_target() { [ "$(str_to_lower "$1")" = 'ignore' ]; }
233+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; }
234+is_ipv4() { expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; }
235+is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ":"; }
236+is_family_mismatch() { ( is_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_netmask "${2//!}" ); }
237+is_ipv6_link_local() { [ "${1:0:4}" = "fe80" ]; }
238+is_ipv6_unique_local() { [ "${1:0:2}" = "fc" ] || [ "${1:0:2}" = "fd" ]; }
239+is_ipv6_global() { [ "${1:0:4}" = "2001" ]; }
240+# is_ipv6_global() { is_ipv6 "$1" && ! is_ipv6_link_local "$1" && ! is_ipv6_link_local "$1"; }
241+is_list() { str_contains "$1" "," || str_contains "$1" " "; }
242+is_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; }
243+is_domain() { str_contains "$1" '[a-zA-Z]'; }
244+is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; }
245+dnsmasq_kill() { killall -q -s HUP dnsmasq; }
246+dnsmasq_restart() { output 3 'Restarting dnsmasq '; if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then output_okn; else output_failn; fi; }
247+is_default_dev() { [ "$1" = "$($ip_full -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
248+is_supported_iface_dev() { local n dev; for n in $ifacesSupported; do network_get_device dev "$n"; [ "$1" = "$dev" ] && return 0; done; return 1; }
249+is_supported_protocol() { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; }
250+is_service_running_iptables() { [ -x "$iptables" ] && "$iptables" -t mangle -L | grep -q "${iptPrefix}_PREROUTING" >/dev/null 2>&1; }
251+is_service_running_nft() { [ -x "$nft" ] && [ -n "$(get_mark_nft_chains)" ]; }
252+# atomic
253+# is_service_running_nft() { [ -x "$nft" ] && [ -s "$nftPermFile" ]; }
254+is_service_running() { if is_nft; then is_service_running_nft; else is_service_running_iptables; fi; }
255+is_netifd_table() { local iface="$1"; [ "$(uci -q get "network.${iface}.ip4table")" = "${packageName}_${iface%6}" ]; }
256+get_rt_tables_id() { grep "${packageName}_${iface}" /etc/iproute2/rt_tables | awk '{print $1;}'; }
257+get_rt_tables_next_id() { echo "$(($(sort -r -n /etc/iproute2/rt_tables | grep -o -E -m 1 "^[0-9]+")+1))"; }
258+_check_config() { local en; config_get_bool en "$1" 'enabled' 1; [ "$en" -gt 0 ] && _cfg_enabled=0; }
259+is_config_enabled() {
260+ local cfg="$1" _cfg_enabled=1
261+ [ -n "$1" ] || return 1
262+ config_load "$packageName"
263+ config_foreach _check_config "$cfg"
264+ return "$_cfg_enabled"
265+}
266+# shellcheck disable=SC2016
267+resolveip_to_ipt() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d'; }
268+# shellcheck disable=SC2016
269+resolveip_to_nftset() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
270+resolveip_to_nftset4() { resolveip_to_nftset -4 "$@"; }
271+resolveip_to_nftset6() { [ -n "$ipv6_enabled" ] && resolveip_to_nftset -6 "$@"; }
272+# shellcheck disable=SC2016
273+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' ' '; }
274+# shellcheck disable=SC2016
275+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' ' '; }
276+# shellcheck disable=SC3037
277+ports_to_nftset() { echo -ne "$value"; }
278+get_mark_ipt_chains() { [ -n "$(command -v iptables-save)" ] && iptables-save | grep ":${iptPrefix}_MARK_" | awk '{ print $1 }' | sed 's/://'; }
279+get_mark_nft_chains() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep chain | grep "${nftPrefix}_mark_" | awk '{ print $2 }'; }
280+get_ipsets() { [ -x "$(command -v ipset)" ] && ipset list | grep "${ipsPrefix}_" | awk '{ print $2 }'; }
281+get_nft_sets() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep 'set' | grep "${nftPrefix}_" | awk '{ print $2 }'; }
282+is_ipset_type_supported() { ipset help hash:"$1" >/dev/null 2>&1; }
283+ubus_get_status() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "@.${packageName}.instances.main.data.status.${1}"; }
284+ubus_get_iface() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "@.${packageName}.instances.main.data.interfaces[@.name='${1}']${2:+.$2}"; }
285+
286+load_package_config() {
287+ config_load "$packageName"
288+ config_get boot_timeout 'config' 'boot_timeout' '30'
289+ config_get_bool enabled 'config' 'enabled' '0'
290+ config_get fw_mask 'config' 'fw_mask' 'ff0000'
291+ config_get icmp_interface 'config' 'icmp_interface'
292+ config_get ignored_interface 'config' 'ignored_interface'
293+ config_get_bool ipv6_enabled 'config' 'ipv6_enabled' '0'
294+ config_get procd_boot_delay 'config' 'procd_boot_delay' '0'
295+ config_get resolver_set 'config' 'resolver_set'
296+ config_get rule_create_option 'config' 'rule_create_option' 'add'
297+ config_get_bool secure_reload 'config' 'secure_reload' '1'
298+ config_get_bool strict_enforcement 'config' 'strict_enforcement' '0'
299+ config_get supported_interface 'config' 'supported_interface'
300+ config_get verbosity 'config' 'verbosity' '2'
301+ config_get wan_ip_rules_priority 'config' 'wan_ip_rules_priority' '30000'
302+ config_get wan_mark 'config' 'wan_mark' '010000'
303+ fw_mask="0x${fw_mask}"
304+ wan_mark="0x${wan_mark}"
305+ [ -n "$ipv6_enabled" ] && [ "$ipv6_enabled" -eq 0 ] && unset ipv6_enabled
306+ . /lib/functions/network.sh
307+ . /usr/share/libubox/jshn.sh
308+ mkdir -p "${dnsmasqFile%/*}"
309+ if is_nft; then
310+ fw_maskXor="$(printf '%#x' "$((fw_mask ^ 0xffffffff))")"
311+ fw_maskXor="${fw_maskXor:-0xff00ffff}"
312+ else
313+ case $rule_create_option in
314+ insert|-i|-I) rule_create_option='-I';;
315+ add|-a|-A|*) rule_create_option='-A';;
316+ esac
317+ fi
318+}
319+
320+load_environment() {
321+ local param="$1" validation_result="$2"
322+ load_package_config
323+
324+ if [ "$param" = 'on_start' ]; then
325+ if [ -n "$validation_result" ] && [ "$validation_result" != '0' ]; then
326+ output "${_ERROR_}: The $packageName config validation failed!\\n"
327+ output "Please check if the '$packageConfigFile' contains correct values for config options.\\n"
328+ state add 'errorSummary' 'errorConfigValidation'
329+ return 1
330+ fi
331+ if [ "$enabled" -eq 0 ]; then
332+ state add 'errorSummary' 'errorServiceDisabled'
333+ return 1
334+ fi
335+ if [ ! -x "$ip_full" ]; then
336+ state add 'errorSummary' 'errorNoIpFull'
337+ return 1
338+ fi
339+ resolver 'check_support'
340+ fi
341+
342+ load_network "$param"
343+}
344+
345+load_network() {
346+ config_load 'network'
347+ [ -z "$ifacesAll" ] && config_foreach _build_ifaces_all 'interface'
348+ [ -z "$ifacesSupported" ] && config_foreach _build_ifaces_supported 'interface'
349+ pbr_find_iface wanIface4 'wan'
350+ [ -n "$ipv6_enabled" ] && pbr_find_iface wanIface6 'wan6'
351+ [ -n "$wanIface4" ] && network_get_gateway wanGW4 "$wanIface4"
352+ [ -n "$wanIface6" ] && network_get_gateway6 wanGW6 "$wanIface6"
353+ wanGW="${wanGW4:-$wanGW6}"
354+}
355+
356+is_wan_up() {
357+ local sleepCount='1'
358+ load_network
359+ while [ -z "$wanGW" ] ; do
360+ load_network
361+ if [ $((sleepCount)) -gt $((boot_timeout)) ] || [ -n "$wanGW" ]; then break; fi
362+ output "$serviceName waiting for wan gateway...\\n"
363+ sleep 1
364+ network_flush_cache
365+ sleepCount=$((sleepCount+1))
366+ done
367+ if [ -n "$wanGW" ]; then
368+ return 0
369+ else
370+ state add 'errorSummary' 'errorNoWanGateway'
371+ return 1
372+ fi
373+}
374+
375+# shellcheck disable=SC2086
376+ipt4() {
377+ local d
378+ [ -x "$iptables" ] || return 1
379+ for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
380+ [ "$d" != "$*" ] && "$iptables" $d >/dev/null 2>&1
381+ done
382+ d="$*"; "$iptables" $d >/dev/null 2>&1
383+}
384+
385+# shellcheck disable=SC2086
386+ipt6() {
387+ local d
388+ [ -n "$ipv6_enabled" ] || return 0
389+ [ -x "$ip6tables" ] || return 1
390+ for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
391+ [ "$d" != "$*" ] && "$ip6tables" $d >/dev/null 2>&1
392+ done
393+ d="$*"
394+ "$ip6tables" $d >/dev/null 2>&1
395+}
396+
397+# shellcheck disable=SC2086
398+ipt() {
399+ local d failFlagIpv4=1 failFlagIpv6=1
400+ [ -x "$iptables" ] || return 1
401+ for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
402+ if [ "$d" != "$*" ]; then
403+ "$iptables" $d >/dev/null 2>&1
404+ [ -x "$ip6tables" ] && "$ip6tables" $d >/dev/null 2>&1
405+ fi
406+ done
407+ d="$*"; "$iptables" $d >/dev/null 2>&1 && failFlagIpv4=0;
408+ if [ -n "$ipv6_enabled" ] && [ -x "$ip6tables" ]; then
409+ "$ip6tables" $d >/dev/null 2>&1 && failFlagIpv6=0
410+ fi
411+ [ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ]
412+}
413+
414+# shellcheck disable=SC2086
415+ips4() { [ -x "$ipset" ] && "$ipset" "$@" >/dev/null 2>&1; }
416+ips6() { [ -x "$ipset" ] && { if [ -n "$ipv6_enabled" ] && [ -n "$*" ]; then "$ipset" "$@" >/dev/null 2>&1; else return 1; fi; }; }
417+ips() {
418+ local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7"
419+ local ipset4 ipset6 i
420+ local ipv4_error=1 ipv6_error=1
421+ ipset4="${ipsPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}"
422+ ipset6="${ipsPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}"
423+
424+ [ -x "$ipset" ] || return 1
425+
426+ if [ "${#ipset4}" -gt 31 ]; then
427+ state add 'errorSummary' 'errorIpsetNameTooLong' "$ipset4"
428+ return 1
429+ fi
430+
431+ case "$command" in
432+ add)
433+ ips4 -q -! add "$ipset4" comment "$comment" && ipv4_error=0
434+ ips6 -q -! add "$ipset6" comment "$comment" && ipv6_error=0
435+ ;;
436+ add_agh_element)
437+ [ -n "$ipv6_enabled" ] || unset ipset6
438+ echo "${param}/${ipset4}${ipset6:+,$ipset6}" >> "$aghIpsetFile" && ipv4_error=0
439+ ;;
440+ add_dnsmasq_element)
441+ [ -n "$ipv6_enabled" ] || unset ipset6
442+ echo "ipset=/${param}/${ipset4}${ipset6:+,$ipset6} # $comment" >> "$dnsmasqFile" && ipv4_error=0
443+ ;;
444+ create)
445+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
446+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
447+ ;;
448+ create_agh_set)
449+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
450+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
451+ ;;
452+ create_dnsmasq_set)
453+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
454+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
455+ ;;
456+ create_user_set)
457+ case "$type" in
458+ ip|net)
459+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
460+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv4_error=0
461+ case "$target" in
462+ dst)
463+ ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
464+ ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
465+ ;;
466+ src)
467+ ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
468+ ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
469+ ;;
470+ esac
471+ ;;
472+ mac)
473+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
474+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv4_error=0
475+ ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
476+ ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
477+ ;;
478+ esac
479+ ;;
480+ delete|destroy)
481+ ips4 -q -! destroy "$ipset4" && ipv4_error=0
482+ ips6 -q -! destroy "$ipset6" && ipv6_error=0
483+ ;;
484+ delete_user_set)
485+ ips4 -q -! destroy "$ipset4" && ipv4_error=0
486+ ips6 -q -! destroy "$ipset6" family inet6 && ipv6_error=0
487+ case "$type" in
488+ ip|net)
489+ case "$target" in
490+ dst)
491+ ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
492+ ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
493+ ;;
494+ src)
495+ ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
496+ ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
497+ ;;
498+ esac
499+ ;;
500+ mac)
501+ ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
502+ ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
503+ ;;
504+ esac
505+ ;;
506+ flush|flush_user_set)
507+ ips4 -q -! flush "$ipset4" && ipv4_error=0
508+ ips6 -q -! flush "$ipset6" && ipv6_error=0
509+ ;;
510+ esac
511+ return $ipv4_error || $ipv6_error
512+}
513+
514+# atomic
515+#nfta() { echo "$@" >> "$nftTempFile"; }
516+#nfta4() { echo "$@" >> "$nftTempFile"; }
517+#nfta6() { [ -z "$ipv6_enabled" ] || echo "$@" >> "$nftTempFile"; }
518+#nft() { nfta "$@"; [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
519+#nft4() { nfta "$@"; [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
520+#nft6() { nfta "$@"; [ -n "$ipv6_enabled" ] || return 0; [ -x "$nft" ] && [ -n "$*" ] && "$nft" "$@" >/dev/null 2>&1; }
521+nft() { [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
522+nft4() { [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
523+nft6() { [ -n "$ipv6_enabled" ] || return 0; [ -x "$nft" ] && [ -n "$*" ] && "$nft" "$@" >/dev/null 2>&1; }
524+nftset() {
525+ local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7"
526+ local nftset4 nftset6 i param4 param6
527+ local ipv4_error=1 ipv6_error=1
528+ nftset4="${nftPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}"
529+ nftset6="${nftPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}"
530+
531+ [ -x "$nft" ] || return 1
532+
533+ if [ "${#nftset4}" -gt 255 ]; then
534+ state add 'errorSummary' 'errorNftsetNameTooLong' "$nftset4"
535+ return 1
536+ fi
537+
538+ case "$command" in
539+ add)
540+ if is_netmask "$param" || is_ipv4 "$param" || is_ipv6 "$param" \
541+ || is_mac_address "$param" || is_list "$param"; then
542+ nft4 add element inet "$nftTable" "$nftset4" "{ $param }" && ipv4_error=0
543+ nft6 add element inet "$nftTable" "$nftset6" "{ $param }" && ipv6_error=0
544+ else
545+# elif is_domain "$param"; then
546+ if [ "$target" = 'src' ]; then
547+ param4="$(ipv4_leases_to_nftset "$param")"
548+ param6="$(ipv6_leases_to_nftset "$param")"
549+ fi
550+ [ -z "$param4" ] && param4="$(resolveip_to_nftset4 "$param")"
551+ [ -z "$param6" ] && param6="$(resolveip_to_nftset6 "$param")"
552+ nft4 add element inet "$nftTable" "$nftset4" "{ $param4 }" && ipv4_error=0
553+ nft6 add element inet "$nftTable" "$nftset6" "{ $param6 }" && ipv6_error=0
554+ fi
555+ ;;
556+ add_dnsmasq_element)
557+ [ -n "$ipv6_enabled" ] || unset nftset6
558+ echo "nftset=/${param}/4#inet#${nftTable}#${nftset4}${nftset6:+,6#inet#${nftTable}#$nftset6} # $comment" >> "$dnsmasqFile" && ipv4_error=0
559+ ;;
560+ create)
561+ case "$type" in
562+ ip|net)
563+ nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv4_error=0
564+ nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv6_error=0
565+ ;;
566+ mac)
567+ nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv4_error=0
568+ nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv6_error=0
569+ ;;
570+ esac
571+ ;;
572+ create_dnsmasq_set)
573+ nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv4_error=0
574+ nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv6_error=0
575+ ;;
576+ create_user_set)
577+ case "$type" in
578+ ip|net)
579+ nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv4_error=0
580+ nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv6_error=0
581+ case "$target" in
582+ dst)
583+ nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
584+ nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
585+ ;;
586+ src)
587+ nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
588+ nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
589+ ;;
590+ esac
591+ ;;
592+ mac)
593+ nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv4_error=0
594+ nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv6_error=0
595+ nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
596+ nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
597+ ;;
598+ esac
599+ ;;
600+ delete|destroy)
601+ nft delete set inet "$nftTable" "$nftset4" && ipv4_error=0
602+ nft delete set inet "$nftTable" "$nftset6" && ipv6_error=0
603+ ;;
604+ delete_user_set)
605+ nft delete set inet "$nftTable" "$nftset4" && ipv4_error=0
606+ nft delete set inet "$nftTable" "$nftset6" && ipv6_error=0
607+ case "$type" in
608+ ip|net)
609+ case "$target" in
610+ dst)
611+ nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
612+ nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
613+ ;;
614+ src)
615+ nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
616+ nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
617+ ;;
618+ esac
619+ ;;
620+ mac)
621+ nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
622+ nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
623+ ;;
624+ esac
625+ ;;
626+ flush|flush_user_set)
627+ nft flush set inet "$nftTable" "$nftset4" && ipv4_error=0
628+ nft flush set inet "$nftTable" "$nftset6" && ipv6_error=0
629+ ;;
630+ esac
631+# nft6 returns true if IPv6 support is not enabled
632+ [ -z "$ipv6_enabled" ] && ipv6_error='1'
633+ return $ipv4_error || $ipv6_error
634+}
635+
636+cleanup_dnsmasq() { [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')" && rm "$dnsmasqFile" >/dev/null 2>&1; }
637+cleanup_main_chains() {
638+ local i
639+ for i in $chainsList; do
640+ i="$(str_to_lower "$i")"
641+ nft flush chain inet "$nftTable" "${nftPrefix}_${i}"
642+ done
643+ for i in $chainsList; do
644+ i="$(str_to_upper "$i")"
645+ ipt -t mangle -D "${i}" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
646+ ipt -t mangle -F "${iptPrefix}_${i}"
647+ ipt -t mangle -X "${iptPrefix}_${i}"
648+ done
649+}
650+
651+cleanup_marking_chains() {
652+ local i
653+ for i in $(get_mark_nft_chains); do
654+ nft flush chain inet "$nftTable" "$i"
655+ nft delete chain inet "$nftTable" "$i"
656+ done
657+ for i in $(get_mark_ipt_chains); do
658+ ipt -t mangle -F "$i"
659+ ipt -t mangle -X "$i"
660+ done
661+}
662+
663+cleanup_sets() {
664+ local i
665+ for i in $(get_nft_sets); do
666+ nft flush set inet "$nftTable" "$i"
667+ nft delete set inet "$nftTable" "$i"
668+ done
669+ for i in $(get_ipsets); do
670+ ipset -q -! flush "$i" >/dev/null 2>&1
671+ ipset -q -! destroy "$i" >/dev/null 2>&1
672+ done
673+}
674+
675+state() {
676+ local action="$1" param="$2" value="${3//#/_}"
677+ shift 3
678+# shellcheck disable=SC2124
679+ local extras="$@"
680+ local line error_id error_extra label
681+ case "$action" in
682+ add)
683+ line="$(eval echo "\$$param")"
684+ eval "$param"='${line:+$line#}${value}${extras:+ $extras}'
685+ ;;
686+ json)
687+ case "$param" in
688+ errorSummary)
689+ json_add_array 'errors';;
690+ warningSummary)
691+ json_add_array 'warnings';;
692+ esac
693+ if [ -n "$(eval echo "\$$param")" ]; then
694+ while read -r line; do
695+ if str_contains "$line" ' '; then
696+# url="${c##*|}"
697+# c="${c%|*}"
698+ error_id="${line% *}"
699+ error_extra="${line#* }"
700+ else
701+ error_id="$line"
702+ fi
703+ json_add_object
704+ json_add_string 'id' "$error_id"
705+ json_add_string 'extra' "$error_extra"
706+ json_close_object
707+ done <<EOF
708+$(eval echo "\$$param" | tr \# \\n)
709+EOF
710+ fi
711+ json_close_array
712+ ;;
713+ print)
714+ [ -z "$(eval echo "\$$param")" ] && return 0
715+ case "$param" in
716+ errorSummary)
717+ label="${_ERROR_}:";;
718+ warningSummary)
719+ label="${_WARNING_}:";;
720+ esac
721+ while read -r line; do
722+ if str_contains "$line" ' '; then
723+ error_id="${line% *}"
724+ error_extra="${line#* }"
725+ printf "%b $(get_text "$error_id")\\n" "$label" "$error_extra"
726+ else
727+ error_id="$line"
728+ printf "%b $(get_text "$error_id")\\n" "$label"
729+ fi
730+ done <<EOF
731+$(eval echo "\$$param" | tr \# \\n)
732+EOF
733+ ;;
734+ set)
735+ eval "$param"='${value}${extras:+ $extras}'
736+ ;;
737+ esac
738+}
739+
740+resolver() {
741+ local agh_version
742+ local param="$1"
743+ shift
744+
745+ if [ "$param" = 'cleanup_all' ]; then
746+ sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev/null 2>&1
747+ rm -f "$aghIpsetFile"
748+ rm -f "$dnsmasqFile"
749+ return 0
750+ fi
751+
752+ case "$resolver_set" in
753+ ''|none)
754+ case "$param" in
755+ add_resolver_element) return 1;;
756+ create_resolver_set) return 1;;
757+ check_support) return 0;;
758+ cleanup) return 0;;
759+ configure) return 0;;
760+ init) return 0;;
761+ init_end) return 0;;
762+ kill) return 0;;
763+ reload) return 0;;
764+ restart) return 0;;
765+ compare_hash) return 0;;
766+ store_hash) return 0;;
767+ esac
768+ ;;
769+ adguardhome.ipset)
770+ case "$param" in
771+ add_resolver_element)
772+ [ -n "$resolver_set_supported" ] && ips 'add_agh_element' "$@";;
773+ create_resolver_set)
774+ [ -n "$resolver_set_supported" ] && ips 'create_agh_set' "$@";;
775+ check_support)
776+ if [ ! -x "$ipset" ]; then
777+ state add 'errorSummary' 'errorNoIpset'
778+ return 1
779+ fi
780+ if [ -n "$agh" ] && [ -s "$aghConfigFile" ]; then
781+ agh_version="$($agh --version | sed 's|AdGuard Home, version v\(.*\)|\1|')"
782+ if is_greater_or_equal "$agh_version" '0.107.13'; then
783+ resolver_set_supported='true'
784+ return 0
785+ else
786+ state add 'warningSummary' 'warningAGHVersionTooLow' "$agh_version"
787+ return 1
788+ fi
789+ else
790+ state add 'warningSummary' 'warningResolverNotSupported'
791+ return 1
792+ fi
793+ ;;
794+ cleanup)
795+ [ -z "$resolver_set_supported" ] && return 0
796+ rm -f "$aghIpsetFile"
797+ sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev/null 2>&1
798+ ;;
799+ configure)
800+ [ -z "$resolver_set_supported" ] && return 1
801+ mkdir -p "${aghIpsetFile%/*}"
802+ touch "$aghIpsetFile"
803+ sed -i '/ipset_file/d' "$aghConfigFile" >/dev/null 2>&1
804+ sed -i "/ ipset:/a \ \ ipset_file: $aghIpsetFile" "$aghConfigFile"
805+ ;;
806+ init) :;;
807+ init_end) :;;
808+ kill)
809+ [ -n "$resolver_set_supported" ] && [ -n "$agh" ] && killall -q -s HUP "$agh";;
810+ reload)
811+ [ -z "$resolver_set_supported" ] && return 1
812+ output 3 'Reloading adguardhome '
813+ if /etc/init.d/adguardhome reload >/dev/null 2>&1; then
814+ output_okn
815+ return 0
816+ else
817+ output_failn
818+ return 1
819+ fi
820+ ;;
821+ restart)
822+ [ -z "$resolver_set_supported" ] && return 1
823+ output 3 'Restarting adguardhome '
824+ if /etc/init.d/adguardhome restart >/dev/null 2>&1; then
825+ output_okn
826+ return 0
827+ else
828+ output_failn
829+ return 1
830+ fi
831+ ;;
832+ compare_hash)
833+ [ -z "$resolver_set_supported" ] && return 1
834+ local resolverNewHash
835+ if [ -s "$aghIpsetFile" ]; then
836+ resolverNewHash="$(md5sum $aghIpsetFile | awk '{ print $1; }')"
837+ fi
838+ [ "$resolverNewHash" != "$resolverStoredHash" ]
839+ ;;
840+ store_hash)
841+ [ -s "$aghIpsetFile" ] && resolverStoredHash="$(md5sum $aghIpsetFile | awk '{ print $1; }')";;
842+ esac
843+ ;;
844+ dnsmasq.ipset)
845+ case "$param" in
846+ add_resolver_element)
847+ [ -n "$resolver_set_supported" ] && ips 'add_dnsmasq_element' "$@";;
848+ create_resolver_set)
849+ [ -n "$resolver_set_supported" ] && ips 'create_dnsmasq_set' "$@";;
850+ check_support)
851+ if [ ! -x "$ipset" ]; then
852+ state add 'errorSummary' 'errorNoIpset'
853+ return 1
854+ fi
855+ if ! dnsmasq -v 2>/dev/null | grep -q 'no-ipset' && dnsmasq -v 2>/dev/null | grep -q 'ipset'; then
856+ resolver_set_supported='true'
857+ return 0
858+ else
859+ state add 'warningSummary' 'warningResolverNotSupported'
860+ return 1
861+ fi
862+ ;;
863+ cleanup)
864+ [ -n "$resolver_set_supported" ] && rm -f "$dnsmasqFile";;
865+ configure)
866+ [ -n "$resolver_set_supported" ] && mkdir -p "${dnsmasqFile%/*}";;
867+ init) :;;
868+ init_end) :;;
869+ kill)
870+ [ -n "$resolver_set_supported" ] && killall -q -s HUP dnsmasq;;
871+ reload)
872+ [ -z "$resolver_set_supported" ] && return 1
873+ output 3 'Reloading dnsmasq '
874+ if /etc/init.d/dnsmasq reload >/dev/null 2>&1; then
875+ output_okn
876+ return 0
877+ else
878+ output_failn
879+ return 1
880+ fi
881+ ;;
882+ restart)
883+ [ -z "$resolver_set_supported" ] && return 1
884+ output 3 'Restarting dnsmasq '
885+ if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then
886+ output_okn
887+ return 0
888+ else
889+ output_failn
890+ return 1
891+ fi
892+ ;;
893+ compare_hash)
894+ [ -z "$resolver_set_supported" ] && return 1
895+ local resolverNewHash
896+ if [ -s "$dnsmasqFile" ]; then
897+ resolverNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
898+ fi
899+ [ "$resolverNewHash" != "$resolverStoredHash" ]
900+ ;;
901+ store_hash)
902+ [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')";;
903+ esac
904+ ;;
905+ dnsmasq.nftset)
906+ case "$param" in
907+ add_resolver_element)
908+ [ -n "$resolver_set_supported" ] && nftset 'add_dnsmasq_element' "$@";;
909+ create_resolver_set)
910+ [ -n "$resolver_set_supported" ] && nftset 'create_dnsmasq_set' "$@";;
911+ check_support)
912+ if [ ! -x "$nft" ]; then
913+ state add 'errorSummary' 'errorNoNft'
914+ return 1
915+ fi
916+ if ! dnsmasq -v 2>/dev/null | grep -q 'no-nftset' && dnsmasq -v 2>/dev/null | grep -q 'nftset'; then
917+ resolver_set_supported='true'
918+ return 0
919+ else
920+ state add 'warningSummary' 'warningResolverNotSupported'
921+ return 1
922+ fi
923+ ;;
924+ cleanup)
925+ [ -n "$resolver_set_supported" ] && rm -f "$dnsmasqFile";;
926+ configure)
927+ [ -n "$resolver_set_supported" ] && mkdir -p "${dnsmasqFile%/*}";;
928+ init) :;;
929+ init_end) :;;
930+ kill)
931+ [ -n "$resolver_set_supported" ] && killall -q -s HUP dnsmasq;;
932+ reload)
933+ [ -z "$resolver_set_supported" ] && return 1
934+ output 3 'Reloading dnsmasq '
935+ if /etc/init.d/dnsmasq reload >/dev/null 2>&1; then
936+ output_okn
937+ return 0
938+ else
939+ output_failn
940+ return 1
941+ fi
942+ ;;
943+ restart)
944+ [ -z "$resolver_set_supported" ] && return 1
945+ output 3 'Restarting dnsmasq '
946+ if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then
947+ output_okn
948+ return 0
949+ else
950+ output_failn
951+ return 1
952+ fi
953+ ;;
954+ compare_hash)
955+ [ -z "$resolver_set_supported" ] && return 1
956+ local resolverNewHash
957+ if [ -s "$dnsmasqFile" ]; then
958+ resolverNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
959+ fi
960+ [ "$resolverNewHash" != "$resolverStoredHash" ]
961+ ;;
962+ store_hash)
963+ [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')";;
964+ esac
965+ ;;
966+ unbound.ipset)
967+ case "$param" in
968+ add_resolver_element) :;;
969+ create_resolver_set) :;;
970+ check_support) :;;
971+ cleanup) :;;
972+ configure) :;;
973+ init) :;;
974+ init_end) :;;
975+ kill) :;;
976+ reload) :;;
977+ restart) :;;
978+ compare_hash) :;;
979+ store_hash) :;;
980+ esac
981+ ;;
982+ unbound.nftset)
983+ case "$param" in
984+ add_resolver_element) :;;
985+ create_resolver_set) :;;
986+ check_support) :;;
987+ cleanup) :;;
988+ configure) :;;
989+ init) :;;
990+ init_end) :;;
991+ kill) :;;
992+ reload) :;;
993+ restart) :;;
994+ compare_hash) :;;
995+ store_hash) :;;
996+ esac
997+ ;;
998+ esac
999+}
1000+
1001+trap_process() {
1002+# verbosity=0
1003+ output "\\n"
1004+ output "Unexpected exit or service termination: '${1}'!\\n"
1005+ state add 'errorSummary' 'errorUnexpectedExit' "$1"
1006+ traffic_killswitch 'remove'
1007+}
1008+
1009+traffic_killswitch() {
1010+ local s=0
1011+ case "$1" in
1012+ insert)
1013+ local lan_subnet wan_device
1014+ [ "$secure_reload" -ne 0 ] || return 0
1015+ for i in $serviceTrapSignals; do
1016+# shellcheck disable=SC2064
1017+ trap "trap_process $i" "$i"
1018+ done
1019+ output 3 'Activating traffic killswitch '
1020+ network_get_subnet lan_subnet 'lan'
1021+ network_get_physdev wan_device 'wan'
1022+ if is_nft; then
1023+ nft add chain inet "$nftTable" "${nftPrefix}_killswitch" '{ type filter hook forward priority 0; policy accept; }' || s=1
1024+ nft add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname "$wan_device" ip saddr "$lan_subnet" counter reject || s=1
1025+# nft add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname '$wan_devices' ip saddr '$lan_subnet' counter reject || s=1
1026+ else
1027+ ipt -N "${iptPrefix}_KILLSWITCH" || s=1
1028+ ipt -A "${iptPrefix}_KILLSWITCH" -s "$lan_subnet" -o "$wan_device" -j REJECT || s=1
1029+ ipt -I FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1
1030+ fi
1031+ if [ "$s" -eq 0 ]; then
1032+ output_okn
1033+ else
1034+ output_failn
1035+ fi
1036+ ;;
1037+ remove)
1038+ if [ "$secure_reload" -ne 0 ]; then
1039+ output 3 'Deactivating traffic killswitch '
1040+ fi
1041+ if is_nft; then
1042+ nft flush chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
1043+ nft delete chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
1044+ else
1045+ ipt -D FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1
1046+ ipt -F "${iptPrefix}_KILLSWITCH" || s=1
1047+ ipt -X "${iptPrefix}_KILLSWITCH" || s=1
1048+ fi
1049+ if [ "$secure_reload" -ne 0 ]; then
1050+ if [ "$s" -eq 0 ]; then
1051+ output_okn
1052+ else
1053+ output_failn
1054+ fi
1055+ fi
1056+# shellcheck disable=SC2086
1057+ trap - $serviceTrapSignals
1058+ ;;
1059+ esac
1060+}
1061+
1062+policy_routing_tor() { if is_nft; then policy_routing_tor_nft "$@"; else policy_routing_tor_iptables "$@"; fi; }
1063+policy_routing_tor_iptables() {
1064+ local comment="$1" iface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto chain uid="$9"
1065+ proto="$(str_to_lower "$7")"
1066+ chain="$(str_to_upper "$8")"
1067+ chain="${chain:-PREROUTING}"
1068+ if [ -n "${src_addr}${src_port}${dest_port}" ]; then
1069+ processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n"
1070+ fi
1071+ if [ -n "$proto" ] && [ "$proto" != "all" ]; then
1072+ processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n"
1073+ fi
1074+ if [ "$chain" != "PREROUTING" ]; then
1075+ processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '$comment'\\n"
1076+ fi
1077+ resolver 'add_resolver_element' "$iface" 'dst' 'ip' '' "${comment}: $dest_addr" "$dest_addr" || \
1078+ processPolicyError="${processPolicyError}${_ERROR_}: resolver 'add_resolver_element' '$iface' 'dst' 'ip' '${comment}: $dest_addr' '$dest_addr'\\n"
1079+ return 0
1080+}
1081+policy_routing_tor_nft() {
1082+ local comment="$1" iface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto chain uid="$9"
1083+ proto="$(str_to_lower "$7")"
1084+ chain="$(str_to_lower "$8")"
1085+ chain="${chain:-prerouting}"
1086+ if [ -n "${src_addr}${src_port}${dest_port}" ]; then
1087+ processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n"
1088+ fi
1089+ if [ -n "$proto" ] && [ "$proto" != "all" ]; then
1090+ processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n"
1091+ fi
1092+ if [ "$chain" != "prerouting" ]; then
1093+ processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'prerouting' for policy '$comment'\\n"
1094+ fi
1095+ resolver 'add_resolver_element' "$iface" 'dst' 'ip' '' "${comment}: $dest_addr" "$dest_addr" || \
1096+ processPolicyError="${processPolicyError}${_ERROR_}: resolver 'add_resolver_element' '$iface' 'dst' 'ip' '${comment}: $dest_addr' '$dest_addr'\\n"
1097+ return 0
1098+}
1099+
1100+policy_routing() { if is_nft; then policy_routing_nft "$@"; else policy_routing_iptables "$@"; fi; }
1101+policy_routing_iptables() {
1102+ local mark param4 param6 i negation value dest ipInsertOption="-A"
1103+ local ip4error='1' ip6error='1'
1104+ local name="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain uid="$9"
1105+ proto="$(str_to_lower "$7")"
1106+ chain="$(str_to_upper "$8")"
1107+ chain="${chain:-PREROUTING}"
1108+ mark=$(eval echo "\$mark_${iface//-/_}")
1109+
1110+ if [ -n "$ipv6_enabled" ] && { is_ipv6 "$laddr" || is_ipv6 "$raddr"; }; then
1111+ processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$name' as IPv6 support is disabled\\n"
1112+ return 1
1113+ fi
1114+
1115+ if [ -n "$mark" ]; then
1116+ dest="-g ${iptPrefix}_MARK_${mark}"
1117+ elif [ "$iface" = "ignore" ]; then
1118+ dest="-j RETURN"
1119+ else
1120+ processPolicyError="${processPolicyError}${_ERROR_}: Unknown fw_mark for ${iface}\\n"
1121+ return 1
1122+ fi
1123+
1124+ if [ -z "$proto" ]; then
1125+ if [ -n "$lport" ] || [ -n "$rport" ]; then
1126+ proto='tcp udp'
1127+ else
1128+ proto='all'
1129+ fi
1130+ fi
1131+
1132+ if is_family_mismatch "$laddr" "$raddr"; then
1133+ processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$laddr' and '$raddr' in policy '$name'\\n"
1134+ return 1
1135+ fi
1136+
1137+ for i in $proto; do
1138+ if [ "$i" = 'all' ]; then
1139+ param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest"
1140+ param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest"
1141+ elif ! is_supported_protocol "$i"; then
1142+ processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$name'\\n"
1143+ return 1
1144+ else
1145+ param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest -p $i"
1146+ param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest -p $i"
1147+ fi
1148+
1149+ if [ -n "$laddr" ]; then
1150+ if [ "${laddr:0:1}" = "!" ]; then
1151+ negation='!'; value="${laddr:1}"
1152+ else
1153+ unset negation; value="$laddr";
1154+ fi
1155+ if is_phys_dev "$value"; then
1156+ param4="$param4 $negation -m physdev --physdev-in ${value:1}"
1157+ param6="$param6 $negation -m physdev --physdev-in ${value:1}"
1158+ elif is_netmask "$value"; then
1159+ local target='src' type='net'
1160+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
1161+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
1162+ param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1163+ param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1164+ else
1165+ param4="$param4 $negation -s $value"
1166+ param6="$param6 $negation -s $value"
1167+ fi
1168+ elif is_mac_address "$value"; then
1169+ local target='src' type='mac'
1170+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
1171+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
1172+ param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1173+ param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1174+ else
1175+ param4="$param4 -m mac $negation --mac-source $value"
1176+ param6="$param6 -m mac $negation --mac-source $value"
1177+ fi
1178+ else
1179+ local target='src' type='ip'
1180+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
1181+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
1182+ param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1183+ param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1184+ else
1185+ param4="$param4 $negation -s $(resolveip_to_ipt -4 "$value")"
1186+ param6="$param6 $negation -s $(resolveip_to_ipt -6 "$value")"
1187+ fi
1188+ fi
1189+ fi
1190+
1191+ if [ -n "$lport" ]; then
1192+ if [ "${lport:0:1}" = "!" ]; then
1193+ negation='!'; value="${lport:1}"
1194+ else
1195+ unset negation; value="$lport";
1196+ fi
1197+ param4="$param4 -m multiport $negation --sport ${value//-/:}"
1198+ param6="$param6 -m multiport $negation --sport ${value//-/:}"
1199+ fi
1200+
1201+ if [ -n "$raddr" ]; then
1202+ if [ "${raddr:0:1}" = "!" ]; then
1203+ negation='!'; value="${raddr:1}"
1204+ else
1205+ unset negation; value="$raddr";
1206+ fi
1207+ if is_netmask "$value"; then
1208+ local target='dst' type='net'
1209+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
1210+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
1211+ param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1212+ param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1213+ else
1214+ param4="$param4 $negation -d $value"
1215+ param6="$param6 $negation -d $value"
1216+ fi
1217+ elif is_domain "$value"; then
1218+ local target='dst' type='ip'
1219+ if resolver 'create_resolver_set' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
1220+ resolver 'add_resolver_element' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
1221+ param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1222+ param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1223+ elif ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
1224+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
1225+ param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1226+ param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1227+ else
1228+ param4="$param4 $negation -d $(resolveip_to_ipt -4 "$value")"
1229+ param6="$param6 $negation -d $(resolveip_to_ipt -6 "$value")"
1230+ fi
1231+ else
1232+ local target='dst' type='ip'
1233+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
1234+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
1235+ param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1236+ param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1237+ else
1238+ param4="$param4 $negation -d $value"
1239+ param6="$param6 $negation -d $value"
1240+ fi
1241+ fi
1242+ fi
1243+
1244+ if [ -n "$rport" ]; then
1245+ if [ "${rport:0:1}" = "!" ]; then
1246+ negation='!'; value="${rport:1}"
1247+ else
1248+ unset negation; value="$rport";
1249+ fi
1250+ param4="$param4 -m multiport $negation --dport ${value//-/:}"
1251+ param6="$param6 -m multiport $negation --dport ${value//-/:}"
1252+ fi
1253+
1254+ if [ -n "$name" ]; then
1255+ param4="$param4 -m comment --comment $(str_extras_to_underscore "$name")"
1256+ param6="$param6 -m comment --comment $(str_extras_to_underscore "$name")"
1257+ fi
1258+
1259+ local ipv4_error='0' ipv6_error='0'
1260+ if [ "$param4" = "$param6" ]; then
1261+ ipt4 "$param4" || ipv4_error='1'
1262+ else
1263+ ipt4 "$param4" || ipv4_error='1'
1264+ ipt6 "$param6" || ipv6_error='1'
1265+ fi
1266+
1267+# ipt6 returns true if IPv6 support is not enabled
1268+ [ -z "$ipv6_enabled" ] && ipv6_error='1'
1269+ if [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
1270+ if [ -n "$ipv6_enabled" ]; then
1271+ processPolicyError="${processPolicyError}${_ERROR_}: Policy insertion failed for both IPv4 and IPv6!\\n"
1272+ processPolicyError="${processPolicyError}${_ERROR_}: iptables $param4\\n"
1273+ processPolicyError="${processPolicyError}${_ERROR_}: iptables $param6\\n"
1274+ else
1275+ processPolicyError="${processPolicyError}${_ERROR_}: iptables $param4\\n"
1276+ fi
1277+ fi
1278+
1279+ done
1280+}
1281+policy_routing_nft() {
1282+ local mark param4 param6 i negation value dest nftInsertOption='add'
1283+ local ip4Flag='ip' ip6Flag='ip6'
1284+ local name="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain uid="$9"
1285+ proto="$(str_to_lower "$7")"
1286+ chain="$(str_to_lower "$8")"
1287+ chain="${chain:-prerouting}"
1288+ mark=$(eval echo "\$mark_${iface//-/_}")
1289+
1290+ if [ -z "$ipv6_enabled" ] && { is_ipv6 "$src_addr" || is_ipv6 "$dest_addr"; }; then
1291+ processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$name' as IPv6 support is disabled\\n"
1292+ return 1
1293+ fi
1294+
1295+ if [ -n "$mark" ]; then
1296+ dest="goto ${nftPrefix}_mark_${mark}"
1297+ elif [ "$iface" = "ignore" ]; then
1298+ dest="return"
1299+ else
1300+ processPolicyError="${processPolicyError}${_ERROR_}: Unknown packet mark for ${iface}\\n"
1301+ return 1
1302+ fi
1303+
1304+ if is_family_mismatch "$src_addr" "$dest_addr"; then
1305+ processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$src_addr' and '$dest_addr' in policy '$name'\\n"
1306+ return 1
1307+ fi
1308+
1309+ if [ -n "$proto" ] && ! is_supported_protocol "$proto"; then
1310+ processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$name'\\n"
1311+ return 1
1312+ fi
1313+
1314+ if [ -n "$src_addr" ]; then
1315+ if [ "${src_addr:0:1}" = "!" ]; then
1316+ negation='!='; value="${src_addr:1}"
1317+ else
1318+ unset negation; value="$src_addr";
1319+ fi
1320+ if is_phys_dev "$value"; then
1321+ param4="$param4 iifname $negation ${value:1}"
1322+ param6="$param6 iifname $negation ${value:1}"
1323+ elif is_mac_address "$value"; then
1324+ local target='src' type='mac'
1325+ if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
1326+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1327+ param4="$param4 ether saddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1328+ param6="$param6 ether saddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1329+ else
1330+ param4="$param4 ether saddr $negation $value"
1331+ param6="$param6 ether saddr $negation $value"
1332+ fi
1333+ else
1334+ local target='src' type='ip'
1335+ if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
1336+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1337+ param4="$param4 $ip4Flag saddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1338+ param6="$param6 $ip6Flag saddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1339+ else
1340+ param4="$param4 $ip4Flag saddr $negation $value"
1341+ param6="$param6 $ip6Flag saddr $negation $value"
1342+ fi
1343+ fi
1344+ fi
1345+
1346+ if [ -n "$dest_addr" ]; then
1347+ if [ "${dest_addr:0:1}" = "!" ]; then
1348+ negation='!='; value="${dest_addr:1}"
1349+ else
1350+ unset negation; value="$dest_addr";
1351+ fi
1352+ if is_phys_dev "$value"; then
1353+ param4="$param4 oifname $negation ${value:1}"
1354+ param6="$param6 oifname $negation ${value:1}"
1355+ elif is_domain "$value"; then
1356+ local target='dst' type='ip'
1357+ if resolver 'create_resolver_set' "$iface" "$target" "$type" "$uid" "$name" && \
1358+ resolver 'add_resolver_element' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1359+ param4="$param4 $ip4Flag daddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1360+ param6="$param6 $ip6Flag daddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1361+ elif nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
1362+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1363+ param4="$param4 $ip4Flag daddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1364+ param6="$param6 $ip6Flag daddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1365+ else
1366+ param4="$param4 $ip4Flag daddr $negation {$(resolveip_to_nftset4 "$value")}"
1367+ param6="$param6 $ip6Flag daddr $negation {$(resolveip_to_nftset6 "$value")}"
1368+ fi
1369+ else
1370+ local target='dst' type='ip'
1371+ if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
1372+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1373+ param4="$param4 $ip4Flag daddr $negation @${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1374+ param6="$param6 $ip6Flag daddr $negation @${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1375+ else
1376+ param4="$param4 $ip4Flag daddr $negation $value"
1377+ param6="$param6 $ip6Flag daddr $negation $value"
1378+ fi
1379+ fi
1380+ fi
1381+
1382+ if [ -n "${src_port}${dest_port}" ]; then
1383+ proto="${proto:-tcp}"
1384+ fi
1385+
1386+ if [ -n "$src_port" ]; then
1387+ if [ "${src_port:0:1}" = "!" ]; then
1388+ negation='!='; value="${src_port:1}"
1389+ else
1390+ unset negation; value="$src_port";
1391+ fi
1392+ param4="$param4 ${proto:+$proto }sport $negation {$(ports_to_nftset "$value")}"
1393+ param6="$param6 ${proto:+$proto }sport $negation {$(ports_to_nftset "$value")}"
1394+ fi
1395+
1396+ if [ -n "$dest_port" ]; then
1397+ if [ "${dest_port:0:1}" = "!" ]; then
1398+ negation='!='; value="${dest_port:1}"
1399+ else
1400+ unset negation; value="$dest_port";
1401+ fi
1402+ param4="$param4 ${proto:+$proto }dport $negation {$(ports_to_nftset "$value")}"
1403+ param6="$param6 ${proto:+$proto }dport $negation {$(ports_to_nftset "$value")}"
1404+ fi
1405+
1406+ param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param4 $dest comment \"$name\""
1407+ param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param6 $dest comment \"$name\""
1408+
1409+ local ipv4_error='0' ipv6_error='0'
1410+ if [ "$nftPrevParam4" != "$param4" ]; then
1411+ nft4 "$param4" || ipv4_error='1'
1412+ nftPrevParam4="$param4"
1413+ fi
1414+ if [ "$nftPrevParam6" != "$param6" ]; then
1415+ nft6 "$param6" || ipv6_error='1'
1416+ nftPrevParam6="$param6"
1417+ fi
1418+
1419+# nft6 returns true if IPv6 support is not enabled
1420+ [ -z "$ipv6_enabled" ] && ipv6_error='1'
1421+ if [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
1422+ if [ -n "$ipv6_enabled" ]; then
1423+ processPolicyError="${processPolicyError}${_ERROR_}: Policy insertion failed for both IPv4 and IPv6!\\n"
1424+ processPolicyError="${processPolicyError}${_ERROR_}: nft '$param4'\\n"
1425+ processPolicyError="${processPolicyError}${_ERROR_}: nft '$param6'\\n"
1426+ else
1427+ processPolicyError="${processPolicyError}${_ERROR_}: nft '$param4'\\n"
1428+ fi
1429+ fi
1430+}
1431+
1432+policy_process() {
1433+ local i j uid="$9"
1434+ if [ -z "$uid" ]; then # first non-recursive call
1435+ [ "$enabled" -gt 0 ] || return 0
1436+ unset processPolicyWarning
1437+ unset processPolicyError
1438+ uid="$1"
1439+ if is_nft; then
1440+ chain="$(str_to_lower "$chain")"
1441+ else
1442+ chain="$(str_to_upper "$chain")"
1443+ fi
1444+ proto="$(str_to_lower "$proto")"
1445+ [ "$proto" = 'auto' ] && unset proto
1446+ [ "$proto" = 'all' ] && unset proto
1447+ output 2 "Routing '$name' via $interface "
1448+ if [ -z "${src_addr}${src_port}${dest_addr}${dest_port}" ]; then
1449+ state add 'errorSummary' 'errorPolicyNoSrcDest' "$name"
1450+ output_fail; return 1;
1451+ fi
1452+ if [ -z "$interface" ]; then
1453+ state add 'errorSummary' 'errorPolicyNoInterface' "$name"
1454+ output_fail; return 1;
1455+ fi
1456+ if ! is_supported_interface "$interface"; then
1457+ state add 'errorSummary' 'errorPolicyUnknownInterface' "$name"
1458+ output_fail; return 1;
1459+ fi
1460+ src_port="${src_port// / }"; src_port="${src_port// /,}"; src_port="${src_port//,\!/ !}";
1461+ dest_port="${dest_port// / }"; dest_port="${dest_port// /,}"; dest_port="${dest_port//,\!/ !}";
1462+ if is_nft; then
1463+ nftset 'flush' "$interface" "dst" "ip" "$uid"
1464+ nftset 'flush' "$interface" "src" "ip" "$uid"
1465+ nftset 'flush' "$interface" "src" "mac" "$uid"
1466+ else
1467+ ips 'flush' "$interface" "dst" "ip" "$uid"
1468+ ips 'flush' "$interface" "src" "ip" "$uid"
1469+ ips 'flush' "$interface" "src" "mac" "$uid"
1470+ fi
1471+ policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
1472+ if [ -n "$processPolicyWarning" ]; then
1473+ state add 'warningSummary' 'warningPolicyProcess' "$processPolicyWarning"
1474+ fi
1475+ if [ -n "$processPolicyError" ]; then
1476+ output_fail
1477+ state add 'errorSummary' 'errorPolicyProcess' "$processPolicyError"
1478+ else
1479+ output_ok
1480+ fi
1481+ else # recursive call, get options from passed variables
1482+ local name="$1" interface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto="$7" chain="$8"
1483+ if str_contains "$src_addr" '[ ;\{\}]'; then
1484+ 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
1485+ elif str_contains "$src_port" '[ ;\{\}]'; then
1486+ 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
1487+ elif str_contains "$dest_addr" '[ ;\{\}]'; then
1488+ 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
1489+ elif str_contains "$dest_port" '[ ;\{\}]'; then
1490+ 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
1491+ elif str_contains "$proto" '[ ;\{\}]'; then
1492+ 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
1493+ else
1494+ if is_tor "$interface"; then
1495+ policy_routing_tor "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
1496+ else
1497+ policy_routing "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
1498+ fi
1499+ fi
1500+ fi
1501+}
1502+
1503+interface_process_tor() { if is_nft; then interface_process_tor_nft "$@"; else interface_process_tor_iptables "$@"; fi; }
1504+interface_process_tor_iptables() {
1505+ local s=0 iface="$1" action="$2"
1506+ local displayText set_name4 set_name6
1507+ local dnsPort trafficPort
1508+ case "$action" in
1509+ reload)
1510+ displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}"
1511+ gatewaySummary="${gatewaySummary}${displayText}\\n"
1512+ ;;
1513+ destroy)
1514+ for i in $chainsList; do
1515+ i="$(str_to_upper "$i")"
1516+ ipt -t nat -D "${i}" -m mark --mark "0x0/${fw_mask}" -j "${nftPrefix}_${i}"
1517+ ipt -t nat -F "${nftPrefix}_${i}"; ipt -t nat -X "${nftPrefix}_${i}";
1518+ done
1519+ ;;
1520+ create)
1521+ output 2 "Creating TOR redirects "
1522+ dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')"
1523+ trafficPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')"
1524+ dnsPort="${dnsPort:-9053}"; trafficPort="${trafficPort:-9040}";
1525+ for i in $chainsList; do
1526+ ipt -t nat -N "${nftPrefix}_${i}"
1527+ ipt -t nat -A "$i" -m mark --mark "0x0/${fw_mask}" -j "${nftPrefix}_${i}"
1528+ done
1529+ if resolver 'create_resolver_set' "$iface" 'dst' 'ip' && ips 'flush' "$iface" 'dst' 'ip'; then
1530+ set_name4="${ipsPrefix}_${iface}_4_dst_ip"
1531+ for i in $chainsList; do
1532+ i="$(str_to_lower "$i")"
1533+ 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
1534+ 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
1535+ 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
1536+ 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
1537+ 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
1538+ done
1539+ else
1540+ s=1
1541+ fi
1542+ displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}"
1543+ if [ "$s" -eq 0 ]; then
1544+ gatewaySummary="${gatewaySummary}${displayText}\\n"
1545+ output_ok
1546+ else
1547+ state add 'errorSummary' 'errorFailedSetup' "$displayText"
1548+ output_fail
1549+ fi
1550+ ;;
1551+ esac
1552+ return $s
1553+}
1554+interface_process_tor_nft() {
1555+ local s=0 iface="$1" action="$2"
1556+ local displayText set_name4 set_name6
1557+ local dnsPort trafficPort
1558+ case "$action" in
1559+ reload)
1560+ displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}"
1561+ gatewaySummary="${gatewaySummary}${displayText}\\n"
1562+ ;;
1563+ destroy)
1564+ ;;
1565+ create)
1566+ output 2 "Creating TOR redirects "
1567+ dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')"
1568+ trafficPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')"
1569+ dnsPort="${dnsPort:-9053}"; trafficPort="${trafficPort:-9040}";
1570+ if resolver 'create_resolver_set' "$iface" 'dst' 'ip' && nftset 'flush' "$iface" 'dst' 'ip'; then
1571+ set_name4="${nftPrefix}_${iface}_4_dst_ip"
1572+ set_name6="${nftPrefix}_${iface}_6_dst_ip"
1573+ nft meta nfproto ipv4 udp daddr "@${set_name4}" dport 53 counter redirect to :"$dnsPort" comment "Tor-DNS-UDP-ipv4" || s=1
1574+ nft meta nfproto ipv4 tcp daddr "@${set_name4}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-TCP-ipv4" || s=1
1575+ nft meta nfproto ipv4 udp daddr "@${set_name4}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-UDP-ipv4" || s=1
1576+ nft meta nfproto ipv4 tcp daddr "@${set_name4}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-TCP-ipv4" || s=1
1577+ nft meta nfproto ipv4 udp daddr "@${set_name4}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-UDP-ipv4" || s=1
1578+ nft6 meta nfproto ipv6 udp daddr "@${set_name6}" dport 53 counter redirect to :"$dnsPort" comment "Tor-DNS-UDP-ipv6" || s=1
1579+ nft6 meta nfproto ipv6 tcp daddr "@${set_name6}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-TCP-ipv6" || s=1
1580+ nft6 meta nfproto ipv6 udp daddr "@${set_name6}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-UDP-ipv6" || s=1
1581+ nft6 meta nfproto ipv6 tcp daddr "@${set_name6}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-TCP-ipv6" || s=1
1582+ nft6 meta nfproto ipv6 udp daddr "@${set_name6}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-UDP-ipv6" || s=1
1583+ else
1584+ s=1
1585+ fi
1586+ displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}"
1587+ if [ "$s" -eq 0 ]; then
1588+ gatewaySummary="${gatewaySummary}${displayText}\\n"
1589+ output_ok
1590+ else
1591+ state add 'errorSummary' 'errorFailedSetup' "$displayText"
1592+ output_fail
1593+ fi
1594+ ;;
1595+ esac
1596+ return $s
1597+}
1598+
1599+interface_routing() {
1600+ local action="$1" tid="$2" mark="$3" iface="$4" gw4="$5" dev="$6" gw6="$7" dev6="$8" priority="$9"
1601+ local dscp s=0 i ipv4_error=1 ipv6_error=1
1602+ if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
1603+ return 1
1604+ fi
1605+ case "$action" in
1606+ create)
1607+ if is_netifd_table "$iface"; then
1608+ ipv4_error=0
1609+ $ip_full -4 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
1610+ $ip_full -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
1611+ if is_nft; then
1612+ nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1
1613+ nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
1614+ nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
1615+ else
1616+ ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1
1617+ ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1
1618+ ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1
1619+ fi
1620+ if [ -n "$ipv6_enabled" ]; then
1621+ ipv6_error=0
1622+ $ip_full -6 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
1623+ $ip_full -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
1624+ fi
1625+ else
1626+ sed -i "/${ipTablePrefix}_${iface}/d" /etc/iproute2/rt_tables
1627+ $ip_full -4 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
1628+ $ip_full -4 route flush table "$tid" >/dev/null 2>&1
1629+ if [ -n "$gw4" ] || [ "$strict_enforcement" -ne 0 ]; then
1630+ ipv4_error=0
1631+ echo "$tid ${ipTablePrefix}_${iface}" >> /etc/iproute2/rt_tables
1632+ if [ -z "$gw4" ]; then
1633+ $ip_full -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
1634+ else
1635+ $ip_full -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
1636+ fi
1637+ # shellcheck disable=SC2086
1638+ while read -r i; do
1639+ i="$(echo "$i" | sed 's/ linkdown$//')"
1640+ i="$(echo "$i" | sed 's/ onlink$//')"
1641+ idev="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')"
1642+ if ! is_supported_iface_dev "$idev"; then
1643+ $ip_full -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1
1644+ fi
1645+ done << EOF
1646+ $($ip_full -4 route list table main)
1647+EOF
1648+ $ip_full -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
1649+ if is_nft; then
1650+ nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1
1651+ nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
1652+ nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
1653+ else
1654+ ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1
1655+ ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1
1656+ ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1
1657+ fi
1658+ fi
1659+ if [ -n "$ipv6_enabled" ]; then
1660+ ipv6_error=0
1661+ $ip_full -6 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
1662+ $ip_full -6 route flush table "$tid" >/dev/null 2>&1
1663+ if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne 0 ]; then
1664+ if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
1665+ $ip_full -6 route add unreachable default table "$tid" || ipv6_error=1
1666+ elif $ip_full -6 route list table main | grep -q " dev $dev6 "; then
1667+ while read -r i; do
1668+ i="$(echo "$i" | sed 's/ linkdown$//')"
1669+ i="$(echo "$i" | sed 's/ onlink$//')"
1670+ $ip_full -6 route add "$i" table "$tid" >/dev/null 2>&1 || ipv6_error=1
1671+ done << EOF
1672+ $($ip_full -6 route list table main | grep " dev $dev6 ")
1673+EOF
1674+ else
1675+ $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
1676+ $ip_full -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
1677+ fi
1678+ fi
1679+ $ip_full -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
1680+ fi
1681+ fi
1682+ if [ "$ipv4_error" -eq 0 ] || [ "$ipv6_error" -eq 0 ]; then
1683+ dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)"
1684+ if is_nft; then
1685+ if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
1686+ nft add rule inet "$nftTable" "${nftPrefix}_prerouting ip dscp ${dscp} goto ${nftPrefix}_mark_${mark}" || s=1
1687+ fi
1688+ if [ "$iface" = "$icmp_interface" ]; then
1689+ nft add rule inet "$nftTable" "${nftPrefix}_output ip protocol icmp goto ${nftPrefix}_mark_${mark}" || s=1
1690+ fi
1691+ else
1692+ if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
1693+ ipt -t mangle -I "${iptPrefix}_PREROUTING" -m dscp --dscp "${dscp}" -g "${iptPrefix}_MARK_${mark}" || s=1
1694+ fi
1695+ if [ "$iface" = "$icmp_interface" ]; then
1696+ ipt -t mangle -I "${iptPrefix}_OUTPUT" -p icmp -g "${iptPrefix}_MARK_${mark}" || s=1
1697+ fi
1698+ fi
1699+ else
1700+ s=1
1701+ fi
1702+ return "$s"
1703+ ;;
1704+ create_user_set)
1705+ [ -z "$createUserSets" ] && return 0;
1706+ if is_nft; then
1707+ nftset 'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s=1
1708+ nftset 'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s=1
1709+ nftset 'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s=1
1710+ else
1711+ ips 'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s=1
1712+ ips 'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s=1
1713+ ips 'create_user_set' "$iface" 'dst' 'net' 'user' '' "$mark" || s=1
1714+ ips 'create_user_set' "$iface" 'src' 'net' 'user' '' "$mark" || s=1
1715+ ips 'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s=1
1716+ fi
1717+ return "$s"
1718+ ;;
1719+ delete|destroy)
1720+ $ip_full rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
1721+ if ! is_netifd_table "$iface"; then
1722+ $ip_full route flush table "$tid" >/dev/null 2>&1
1723+ sed -i "/${ipTablePrefix}_${iface}/d" /etc/iproute2/rt_tables
1724+ fi
1725+ return "$s"
1726+ ;;
1727+ reload_interface)
1728+ is_netifd_table "$iface" && return 0;
1729+ ipv4_error=0
1730+ $ip_full -4 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
1731+ $ip_full -4 route flush table "$tid" >/dev/null 2>&1
1732+ if [ -n "$gw4" ] || [ "$strict_enforcement" -ne 0 ]; then
1733+ if [ -z "$gw4" ]; then
1734+ $ip_full -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
1735+ else
1736+ $ip_full -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
1737+ fi
1738+ $ip_full rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
1739+ fi
1740+ if [ -n "$ipv6_enabled" ]; then
1741+ ipv6_error=0
1742+ $ip_full -6 rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
1743+ $ip_full -6 route flush table "$tid" >/dev/null 2>&1
1744+ if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne 0 ]; then
1745+ if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
1746+ $ip_full -6 route add unreachable default table "$tid" || ipv6_error=1
1747+ elif $ip_full -6 route list table main | grep -q " dev $dev6 "; then
1748+ while read -r i; do
1749+ $ip_full -6 route add "$i" table "$tid" >/dev/null 2>&1 || ipv6_error=1
1750+ done << EOF
1751+ $($ip_full -6 route list table main | grep " dev $dev6 ")
1752+EOF
1753+ else
1754+ $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
1755+ $ip_full -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
1756+ fi
1757+ fi
1758+ $ip_full -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
1759+ fi
1760+ if [ "$ipv4_error" -eq 0 ] || [ "$ipv6_error" -eq 0 ]; then
1761+ s=0
1762+ else
1763+ s=1
1764+ fi
1765+ return "$s"
1766+ ;;
1767+ esac
1768+}
1769+
1770+json_add_gateway() {
1771+ local action="$1" tid="$2" mark="$3" iface="$4" gw4="$5" dev4="$6" gw6="$7" dev6="$8" priority="$9" default="${10}"
1772+ json_add_object ''
1773+ json_add_string name "$iface"
1774+ json_add_string device_ipv4 "$dev4"
1775+ json_add_string gateway_ipv4 "$gw4"
1776+ json_add_string device_ipv6 "$dev6"
1777+ json_add_string gateway_ipv6 "$gw6"
1778+ if [ -n "$default" ]; then
1779+ json_add_boolean default true
1780+ else
1781+ json_add_boolean default false
1782+ fi
1783+ json_add_string action "$action"
1784+ json_add_string table_id "$tid"
1785+ json_add_string mark "$mark"
1786+ json_add_string priority "$priority"
1787+ json_close_object
1788+}
1789+
1790+interface_process() {
1791+ local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" reloadedIface="$3"
1792+ local displayText dispDev dispGw4 dispGw6 dispStatus
1793+
1794+ if [ "$iface" = 'all' ] && [ "$action" = 'prepare' ]; then
1795+ config_load 'network'
1796+ ifaceMark="$(printf '0x%06x' "$wan_mark")"
1797+ ifacePriority="$wan_ip_rules_priority"
1798+ return 0
1799+ fi
1800+
1801+ is_supported_interface "$iface" || return 0
1802+ is_wan6 "$iface" && return 0
1803+ [ $((ifaceMark)) -gt $((fw_mask)) ] && return 1
1804+
1805+ network_get_device dev "$iface"
1806+ if is_wan "$iface" && [ -n "$wanIface6" ] && str_contains "$wanIface6" "$iface"; then
1807+ network_get_device dev6 "$wanIface6"
1808+ fi
1809+
1810+ [ -z "$dev6" ] && dev6="$dev"
1811+ [ -z "$ifaceMark" ] && ifaceMark="$(printf '0x%06x' "$wan_mark")"
1812+ [ -z "$ifacePriority" ] && ifacePriority="$wan_ip_rules_priority"
1813+
1814+ ifaceTableID="$(get_rt_tables_id "$iface")"
1815+ [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
1816+ eval "mark_${iface//-/_}"='$ifaceMark'
1817+ eval "tid_${iface//-/_}"='$ifaceTableID'
1818+ pbr_get_gateway gw4 "$iface" "$dev"
1819+ pbr_get_gateway6 gw6 "$iface" "$dev6"
1820+ dispGw4="${gw4:-0.0.0.0}"
1821+ dispGw6="${gw6:-::/0}"
1822+ [ "$iface" != "$dev" ] && dispDev="$dev"
1823+ is_default_dev "$dev" && dispStatus="${__OK__}"
1824+ displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
1825+
1826+ case "$action" in
1827+ create)
1828+ output 2 "Setting up routing for '$displayText' "
1829+ if interface_routing 'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" && \
1830+ interface_routing 'create_user_set' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
1831+ json_add_gateway 'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
1832+ gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
1833+ output_ok
1834+ else
1835+ state add 'errorSummary' 'errorFailedSetup' "$displayText"
1836+ output_fail
1837+ fi
1838+ ;;
1839+# create_user_set)
1840+# interface_routing 'create_user_set' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"
1841+# ;;
1842+ destroy)
1843+ displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
1844+ output 2 "Removing routing for '$displayText' "
1845+ interface_routing 'destroy' "${ifaceTableID}" "${ifaceMark}" "${iface}"
1846+ output_ok
1847+ ;;
1848+ reload)
1849+ gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
1850+ ;;
1851+ reload_interface)
1852+ if [ "$iface" = "$reloadedIface" ]; then
1853+ output 2 "Reloading routing for '$displayText' "
1854+ if interface_routing 'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
1855+ json_add_gateway 'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
1856+ gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
1857+ output_ok
1858+ else
1859+ state add 'errorSummary' 'errorFailedReload' "$displayText"
1860+ output_fail
1861+ fi
1862+ else
1863+ gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
1864+ fi
1865+ ;;
1866+ esac
1867+# ifaceTableID="$((ifaceTableID + 1))"
1868+ ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))"
1869+ ifacePriority="$((ifacePriority + 1))"
1870+ return $s
1871+}
1872+
1873+user_file_process() {
1874+ local shellBin="${SHELL:-/bin/ash}"
1875+ [ "$enabled" -gt 0 ] || return 0
1876+ if [ ! -s "$path" ]; then
1877+ state add 'errorSummary' 'errorUserFileNotFound' "$path"
1878+ output_fail
1879+ return 1
1880+ fi
1881+ if ! $shellBin -n "$path"; then
1882+ state add 'errorSummary' 'ererrorUserFileSyntax' "$path"
1883+ output_fail
1884+ return 1
1885+ fi
1886+ output 2 "Running $path "
1887+# shellcheck disable=SC1090
1888+ if ! . "$path"; then
1889+ state add 'errorSummary' 'errorUserFileRunning' "$path"
1890+ if grep -q -w 'curl' "$path" && ! is_present 'curl'; then
1891+ state add 'errorSummary' 'errorUserFileNoCurl' "$path"
1892+ fi
1893+ output_fail
1894+ return 1
1895+ else
1896+ output_ok
1897+ return 0
1898+ fi
1899+}
1900+
1901+on_firewall_reload() {
1902+ if [ -z "$(ubus_get_status 'gateways')" ]; then # service is not running, do not start it on firewall reload
1903+ logger -t "$packageName" "Reload on firewall action aborted: service not running."
1904+ return 0;
1905+ else
1906+ rc_procd start_service 'on_firewall_reload' "$1"
1907+ fi
1908+}
1909+on_interface_reload() { rc_procd start_service 'on_interface_reload' "$1"; }
1910+
1911+start_service() {
1912+ local resolverStoredHash resolverNewHash i reloadedIface param="$1"
1913+ local createUserSets
1914+
1915+ load_environment 'on_start' "$(load_validate_config)" || return 1
1916+ is_wan_up || return 1
1917+ rm -f "$nftTempFile"
1918+
1919+ case "$param" in
1920+ on_boot)
1921+ serviceStartTrigger='on_start'
1922+ ;;
1923+ on_firewall_reload)
1924+ serviceStartTrigger='on_start'
1925+ ;;
1926+ on_interface_reload)
1927+ serviceStartTrigger='on_interface_reload'
1928+ reloadedIface="$2"
1929+ ;;
1930+ on_reload)
1931+ serviceStartTrigger='on_reload'
1932+ ;;
1933+ on_restart)
1934+ serviceStartTrigger='on_start'
1935+ ;;
1936+ esac
1937+
1938+ if [ -n "$reloadedIface" ] && ! is_supported_interface "$reloadedIface"; then
1939+ return 0
1940+ fi
1941+
1942+ if [ -n "$(ubus_get_status error)" ] || [ -n "$(ubus_get_status warning)" ]; then
1943+ serviceStartTrigger='on_start'
1944+ unset reloadedIface
1945+ elif ! is_service_running; then
1946+ serviceStartTrigger='on_start'
1947+ unset reloadedIface
1948+ elif [ -z "$(ubus_get_status gateway)" ]; then
1949+ serviceStartTrigger='on_start'
1950+ unset reloadedIface
1951+ elif [ "$serviceStartTrigger" = 'on_interface_reload' ] && \
1952+ [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_4')" ] && \
1953+ [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_6')" ]; then
1954+ serviceStartTrigger='on_start'
1955+ unset reloadedIface
1956+ else
1957+ serviceStartTrigger="${serviceStartTrigger:-on_start}"
1958+ fi
1959+
1960+ if is_config_enabled 'include'; then
1961+ createUserSets='true'
1962+ fi
1963+
1964+ procd_open_instance "main"
1965+ procd_set_param command /bin/true
1966+ procd_set_param stdout 1
1967+ procd_set_param stderr 1
1968+ procd_open_data
1969+
1970+ case $serviceStartTrigger in
1971+ on_interface_reload)
1972+ output 1 "Reloading Interface: $reloadedIface "
1973+ json_add_array 'gateways'
1974+ interface_process 'all' 'prepare'
1975+ config_foreach interface_process 'interface' 'reload_interface' "$reloadedIface"
1976+ json_close_array
1977+ output 1 '\n'
1978+ ;;
1979+ on_reload)
1980+ traffic_killswitch 'insert'
1981+ resolver 'store_hash'
1982+ resolver 'cleanup_all'
1983+ resolver 'configure'
1984+ resolver 'init'
1985+ cleanup_main_chains
1986+ cleanup_sets
1987+ if ! is_nft; then
1988+ for i in $chainsList; do
1989+ i="$(str_to_upper "$i")"
1990+ ipt -t mangle -N "${iptPrefix}_${i}"
1991+ ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
1992+ done
1993+ fi
1994+ json_add_array 'gateways'
1995+ interface_process 'all' 'prepare'
1996+ config_foreach interface_process 'interface' 'reload'
1997+ interface_process_tor 'tor' 'destroy'
1998+ is_tor_running && interface_process_tor 'tor' 'reload'
1999+ json_close_array
2000+ if is_config_enabled 'policy'; then
2001+ output 1 'Processing policies '
2002+ config_load "$packageName"
2003+ config_foreach load_validate_policy 'policy' policy_process
2004+ output 1 '\n'
2005+ fi
2006+ if is_config_enabled 'include'; then
2007+ traffic_killswitch 'remove'
2008+ output 1 'Processing user file(s) '
2009+ config_load "$packageName"
2010+ config_foreach load_validate_include 'include' user_file_process
2011+ output 1 '\n'
2012+ resolver 'init_end'
2013+ resolver 'compare_hash' && resolver 'restart'
2014+ else
2015+ resolver 'init_end'
2016+ resolver 'compare_hash' && resolver 'restart'
2017+ traffic_killswitch 'remove'
2018+ fi
2019+ ;;
2020+ on_start|*)
2021+ traffic_killswitch 'insert'
2022+ resolver 'store_hash'
2023+ resolver 'cleanup_all'
2024+ resolver 'configure'
2025+ resolver 'init'
2026+ cleanup_main_chains
2027+ cleanup_sets
2028+ cleanup_marking_chains
2029+ if ! is_nft; then
2030+ for i in $chainsList; do
2031+ i="$(str_to_upper "$i")"
2032+ ipt -t mangle -N "${iptPrefix}_${i}"
2033+ ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
2034+ done
2035+ fi
2036+ output 1 'Processing interfaces '
2037+ json_add_array 'gateways'
2038+ interface_process 'all' 'prepare'
2039+ config_foreach interface_process 'interface' 'create'
2040+ interface_process_tor 'tor' 'destroy'
2041+ is_tor_running && interface_process_tor 'tor' 'create'
2042+ json_close_array
2043+ ip route flush cache
2044+ output 1 '\n'
2045+ if is_config_enabled 'policy'; then
2046+ output 1 'Processing policies '
2047+ config_load "$packageName"
2048+ config_foreach load_validate_policy 'policy' policy_process
2049+ output 1 '\n'
2050+ fi
2051+ if is_config_enabled 'include'; then
2052+ traffic_killswitch 'remove'
2053+ output 1 'Processing user file(s) '
2054+ config_load "$packageName"
2055+ config_foreach load_validate_include 'include' user_file_process
2056+ output 1 '\n'
2057+ resolver 'init_end'
2058+ resolver 'compare_hash' && resolver 'restart'
2059+ else
2060+ resolver 'init_end'
2061+ resolver 'compare_hash' && resolver 'restart'
2062+ traffic_killswitch 'remove'
2063+ fi
2064+ ;;
2065+ esac
2066+
2067+ if [ -z "$gatewaySummary" ]; then
2068+ state add 'errorSummary' 'errorNoGateways'
2069+ fi
2070+ json_add_object 'status'
2071+ [ -n "$gatewaySummary" ] && json_add_string 'gateways' "$gatewaySummary"
2072+ [ -n "$errorSummary" ] && json_add_string 'errors' "$errorSummary"
2073+ [ -n "$warningSummary" ] && json_add_string 'warnings' "$warningSummary"
2074+ if [ "$strict_enforcement" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then
2075+ json_add_string 'mode' "strict"
2076+ fi
2077+ json_close_object
2078+ procd_close_data
2079+ procd_close_instance
2080+}
2081+
2082+service_started() {
2083+ if is_nft; then
2084+ [ -n "$gatewaySummary" ] && output "$serviceName (nft) started with gateways:\\n${gatewaySummary}"
2085+ else
2086+ [ -n "$gatewaySummary" ] && output "$serviceName (iptables) started with gateways:\\n${gatewaySummary}"
2087+ fi
2088+ state print 'errorSummary'
2089+ state print 'warningSummary'
2090+ if [ -n "$errorSummary" ]; then
2091+ return 2
2092+ elif [ -n "$warningSummary" ]; then
2093+ return 1
2094+ else
2095+ return 0
2096+ fi
2097+}
2098+
2099+service_triggers() {
2100+ local n
2101+ load_environment 'on_triggers'
2102+# shellcheck disable=SC2034
2103+ PROCD_RELOAD_DELAY=$(( procd_reload_delay * 1000 ))
2104+ procd_open_validate
2105+ load_validate_config
2106+ load_validate_policy
2107+ load_validate_include
2108+ procd_close_validate
2109+ procd_open_trigger
2110+ procd_add_reload_trigger 'openvpn'
2111+ procd_add_config_trigger "config.change" "${packageName}" /etc/init.d/${packageName} reload
2112+ for n in $ifacesSupported; do
2113+ procd_add_interface_trigger "interface.*" "$n" /etc/init.d/${packageName} on_interface_reload "$n"
2114+ done
2115+ procd_close_trigger
2116+ if [ "$serviceStartTrigger" = 'on_start' ]; then
2117+ output 3 "$serviceName monitoring interfaces: ${ifacesSupported}\\n"
2118+ fi
2119+}
2120+
2121+stop_service() {
2122+ local i
2123+ load_environment 'on_stop'
2124+ is_service_running || return 0
2125+ traffic_killswitch 'insert'
2126+ cleanup_main_chains
2127+ cleanup_sets
2128+ cleanup_marking_chains
2129+ output 1 'Resetting interfaces '
2130+ config_load 'network'
2131+ config_foreach interface_process 'interface' 'destroy'
2132+ interface_process_tor 'tor' 'destroy'
2133+ output 1 "\\n"
2134+ ip route flush cache
2135+ unset ifaceMark
2136+ unset ifaceTableID
2137+ resolver 'store_hash'
2138+ resolver 'cleanup_all'
2139+ resolver 'compare_hash' && resolver 'restart'
2140+ traffic_killswitch 'remove'
2141+ if [ "$enabled" -ne 0 ]; then
2142+ if is_nft; then
2143+ output "$serviceName (nft) stopped "; output_okn;
2144+ else
2145+ output "$serviceName (iptables) stopped "; output_okn;
2146+ fi
2147+ fi
2148+}
2149+
2150+status_service() {
2151+ local _SEPARATOR_='============================================================'
2152+ load_environment 'on_status'
2153+ if is_nft; then
2154+ status_service_nft "$@"
2155+ else
2156+ status_service_iptables "$@"
2157+ fi
2158+}
2159+
2160+status_service_nft() {
2161+ local i dev dev6 wan_tid
2162+
2163+ json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
2164+ if [ -n "$wanIface4" ]; then
2165+ network_get_gateway wanGW4 "$wanIface4"
2166+ network_get_device dev "$wanIface4"
2167+ fi
2168+ if [ -n "$wanIface6" ]; then
2169+ network_get_device dev6 "$wanIface6"
2170+ wanGW6=$($ip_full -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
2171+ [ "$wanGW6" = "default" ] && wanGW6=$($ip_full -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
2172+ fi
2173+ while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
2174+ [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
2175+ status="$serviceName running on $dist $vers."
2176+ [ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
2177+ [ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
2178+
2179+ echo "$_SEPARATOR_"
2180+ echo "$packageName - environment"
2181+ echo "$status"
2182+ echo "$_SEPARATOR_"
2183+ dnsmasq --version 2>/dev/null | sed '/^$/,$d'
2184+ echo "$_SEPARATOR_"
2185+ echo "$packageName chains - policies"
2186+ for i in forward input output prerouting postrouting; do
2187+ "$nft" list table inet "$nftTable" | sed -n "/chain ${nftPrefix}_${i} {/,/\t}/p"
2188+ done
2189+ echo "$_SEPARATOR_"
2190+ echo "$packageName chains - marking"
2191+ for i in $(get_mark_nft_chains); do
2192+ "$nft" list table inet "$nftTable" | sed -n "/chain ${i} {/,/\t}/p"
2193+ done
2194+ echo "$_SEPARATOR_"
2195+ echo "$packageName nft sets"
2196+ for i in $(get_nft_sets); do
2197+ "$nft" list table inet "$nftTable" | sed -n "/set ${i} {/,/\t}/p"
2198+ done
2199+ if [ -s "$dnsmasqFile" ]; then
2200+ echo "$_SEPARATOR_"
2201+ echo "dnsmasq sets"
2202+ cat "$dnsmasqFile"
2203+ fi
2204+# echo "$_SEPARATOR_"
2205+# ip rule list | grep "${packageName}_"
2206+ echo "$_SEPARATOR_"
2207+ tableCount="$(grep -c "${packageName}_" /etc/iproute2/rt_tables)" || tableCount=0
2208+ wan_tid=$(($(get_rt_tables_next_id)-tableCount))
2209+ i=0; while [ $i -lt $tableCount ]; do
2210+ echo "IPv4 table $((wan_tid + i)) route: $($ip_full -4 route show table $((wan_tid + i)) | grep default)"
2211+ echo "IPv4 table $((wan_tid + i)) rule(s):"
2212+ $ip_full -4 rule list table "$((wan_tid + i))"
2213+ i=$((i + 1))
2214+ done
2215+}
2216+
2217+status_service_iptables() {
2218+ local dist vers out id s param status set_d set_p tableCount i=0 dev dev6 j wan_tid
2219+
2220+ json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
2221+ if [ -n "$wanIface4" ]; then
2222+ network_get_gateway wanGW4 "$wanIface4"
2223+ network_get_device dev "$wanIface4"
2224+ fi
2225+ if [ -n "$wanIface6" ]; then
2226+ network_get_device dev6 "$wanIface6"
2227+ wanGW6=$($ip_full -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
2228+ [ "$wanGW6" = "default" ] && wanGW6=$($ip_full -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
2229+ fi
2230+ while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
2231+ [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
2232+ status="$serviceName running on $dist $vers."
2233+ [ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
2234+ [ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
2235+ {
2236+ echo "$status"
2237+ echo "$_SEPARATOR_"
2238+ dnsmasq --version 2>/dev/null | sed '/^$/,$d'
2239+ if [ -n "$1" ]; then
2240+ echo "$_SEPARATOR_"
2241+ echo "Resolving domains"
2242+ for i in $1; do
2243+ echo "$i: $(resolveip "$i" | tr '\n' ' ')"
2244+ done
2245+ fi
2246+
2247+ echo "$_SEPARATOR_"
2248+ echo "Routes/IP Rules"
2249+ tableCount="$(grep -c "${packageName}_" /etc/iproute2/rt_tables)" || tableCount=0
2250+ if [ -n "$set_d" ]; then route; else route | grep '^default'; fi
2251+ if [ -n "$set_d" ]; then ip rule list; fi
2252+ wan_tid=$(($(get_rt_tables_next_id)-tableCount))
2253+ i=0; while [ $i -lt $tableCount ]; do
2254+ echo "IPv4 table $((wan_tid + i)) route: $($ip_full -4 route show table $((wan_tid + i)) | grep default)"
2255+ echo "IPv4 table $((wan_tid + i)) rule(s):"
2256+ $ip_full -4 rule list table "$((wan_tid + i))"
2257+ i=$((i + 1))
2258+ done
2259+
2260+ if [ -n "$ipv6_enabled" ]; then
2261+ i=0; while [ $i -lt $tableCount ]; do
2262+ $ip_full -6 route show table $((wan_tid + i)) | while read -r param; do
2263+ echo "IPv6 Table $((wan_tid + i)): $param"
2264+ done
2265+ i=$((i + 1))
2266+ done
2267+ fi
2268+
2269+ for j in Mangle NAT; do
2270+ if [ -z "$set_d" ]; then
2271+ for i in $chainsList; do
2272+ i="$(str_to_upper "$i")"
2273+ if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}" >/dev/null 2>&1; then
2274+ echo "$_SEPARATOR_"
2275+ echo "$j IP Table: $i"
2276+ iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
2277+ if [ -n "$ipv6_enabled" ]; then
2278+ echo "$_SEPARATOR_"
2279+ echo "$j IPv6 Table: $i"
2280+ iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
2281+ fi
2282+ fi
2283+ done
2284+ else
2285+ echo "$_SEPARATOR_"
2286+ echo "$j IP Table"
2287+ iptables -L -t "$(str_to_lower $j)"
2288+ if [ -n "$ipv6_enabled" ]; then
2289+ echo "$_SEPARATOR_"
2290+ echo "$j IPv6 Table"
2291+ iptables -L -t "$(str_to_lower $j)"
2292+ fi
2293+ fi
2294+ i=0; ifaceMark="$wan_mark";
2295+ while [ $i -lt $tableCount ]; do
2296+ if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}" >/dev/null 2>&1; then
2297+ echo "$_SEPARATOR_"
2298+ echo "$j IP Table MARK Chain: ${iptPrefix}_MARK_${ifaceMark}"
2299+ iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}"
2300+ ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))";
2301+ fi
2302+ i=$((i + 1))
2303+ done
2304+ done
2305+
2306+ echo "$_SEPARATOR_"
2307+ echo "Current ipsets"
2308+ ipset save
2309+ if [ -s "$dnsmasqFile" ]; then
2310+ echo "$_SEPARATOR_"
2311+ echo "DNSMASQ sets"
2312+ cat "$dnsmasqFile"
2313+ fi
2314+ if [ -s "$aghIpsetFile" ]; then
2315+ echo "$_SEPARATOR_"
2316+ echo "AdGuardHome sets"
2317+ cat "$aghIpsetFile"
2318+ fi
2319+ echo "$_SEPARATOR_"
2320+ } | tee -a /var/${packageName}-support
2321+ if [ -n "$set_p" ]; then
2322+ printf "%b" "Pasting to paste.ee... "
2323+ if is_present 'curl' && is_variant_installed 'libopenssl' && is_installed 'ca-bundle'; then
2324+ json_init; json_add_string "description" "${packageName}-support"
2325+ json_add_array "sections"; json_add_object '0'
2326+ json_add_string "name" "$(uci -q get system.@system[0].hostname)"
2327+ json_add_string "contents" "$(cat /var/${packageName}-support)"
2328+ json_close_object; json_close_array; payload=$(json_dump)
2329+ out=$(curl -s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload")
2330+ json_load "$out"; json_get_var id id; json_get_var s success
2331+ [ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" || printf "%b" "$__FAIL__\\n"
2332+ [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
2333+ else
2334+ printf "%b" "${__FAIL__}\\n"
2335+ 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"
2336+ fi
2337+ else
2338+ printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n"
2339+ fi
2340+}
2341+
2342+# shellcheck disable=SC2120
2343+load_validate_config() {
2344+ uci_load_validate "$packageName" "$packageName" "$1" "${2}${3:+ $3}" \
2345+ 'enabled:bool:0' \
2346+ 'procd_boot_delay:integer:0' \
2347+ 'strict_enforcement:bool:1' \
2348+ 'secure_reload:bool:0' \
2349+ 'ipv6_enabled:bool:0' \
2350+ 'resolver_set:or("", "none", "dnsmasq.ipset", "dnsmasq.nftset")' \
2351+ 'verbosity:range(0,2):1' \
2352+ "wan_mark:regex('0x[A-Fa-f0-9]{8}'):0x010000" \
2353+ "fw_mask:regex('0x[A-Fa-f0-9]{8}'):0xff0000" \
2354+ 'icmp_interface:or("","ignore", uci("network", "@interface"))' \
2355+ 'ignored_interface:list(uci("network", "@interface"))' \
2356+ 'supported_interface:list(uci("network", "@interface"))' \
2357+ 'boot_timeout:integer:30' \
2358+ 'wan_ip_rules_priority:uinteger:30000' \
2359+ 'rule_create_option:or("", "add", "insert"):add' \
2360+ 'procd_reload_delay:integer:0' \
2361+ 'webui_supported_protocol:list(string)'
2362+}
2363+
2364+# shellcheck disable=SC2120
2365+load_validate_policy() {
2366+ local name
2367+ local enabled
2368+ local interface
2369+ local proto
2370+ local chain
2371+ local src_addr
2372+ local src_port
2373+ local dest_addr
2374+ local dest_port
2375+ uci_load_validate "$packageName" 'policy' "$1" "${2}${3:+ $3}" \
2376+ 'name:string:Untitled' \
2377+ 'enabled:bool:1' \
2378+ 'interface:or(uci("network", "@interface"),"ignore"):wan' \
2379+ 'proto:or(string)' \
2380+ 'chain:or("", "forward", "input", "output", "prerouting", "postrouting", "FORWARD", "INPUT", "OUTPUT", "PREROUTING", "POSTROUTING"):prerouting' \
2381+ 'src_addr:list(neg(or(host,network,macaddr,string)))' \
2382+ 'src_port:list(neg(or(portrange,string)))' \
2383+ 'dest_addr:list(neg(or(host,network,string)))' \
2384+ 'dest_port:list(neg(or(portrange,string)))'
2385+}
2386+
2387+# shellcheck disable=SC2120
2388+load_validate_include() {
2389+ local path=
2390+ local enabled=
2391+ uci_load_validate "$packageName" 'include' "$1" "${2}${3:+ $3}" \
2392+ 'path:file' \
2393+ 'enabled:bool:0'
2394+}
--- /dev/null
+++ b/net/pbr/files/etc/uci-defaults/90-pbr
@@ -0,0 +1,34 @@
1+#!/bin/sh
2+# shellcheck disable=SC1091,SC3037,SC3043
3+
4+readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
5+
6+# Transition from vpn-policy-routing
7+if [ -s '/etc/config/vpn-policy-routing' ] && [ ! -s '/etc/config/pbr-opkg' ]; then
8+ echo "Migrating vpn-policy-routing config file."
9+ mv '/etc/config/pbr' '/etc/config/pbr-opkg'
10+ sed 's/vpn-policy-routing/pbr/g' /etc/config/vpn-policy-routing > /etc/config/pbr
11+ uci set vpn-policy-routing.config.enabled=0; uci commit vpn-policy-routing;
12+fi
13+
14+# Transition from older versions of pbr
15+sed -i 's/resolver_ipset/resolver_set/g' /etc/config/pbr
16+sed -i 's/iptables_rule_option/rule_create_option/g' /etc/config/pbr
17+sed -i "s/'FORWARD'/'forward'/g" /etc/config/pbr
18+sed -i "s/'INPUT'/'input'/g" /etc/config/pbr
19+sed -i "s/'OUTPUT'/'output'/g" /etc/config/pbr
20+sed -i "s/'PREROUTING'/'prerouting'/g" /etc/config/pbr
21+sed -i "s/'POSTROUTING'/'postrouting'/g" /etc/config/pbr
22+sed -i "s/option fw_mask '0x\(.*\)'/option fw_mask '\1'/g" /etc/config/pbr
23+sed -i "s/option wan_mark '0x\(.*\)'/option wan_mark '\1'/g" /etc/config/pbr
24+
25+uci -q batch <<-EOT
26+ delete firewall.pbr
27+ set firewall.pbr='include'
28+ set firewall.pbr.fw4_compatible='1'
29+ set firewall.pbr.type='script'
30+ set firewall.pbr.path='/usr/share/pbr/pbr.firewall.include'
31+ commit firewall
32+EOT
33+
34+exit 0
--- /dev/null
+++ b/net/pbr/files/etc/uci-defaults/91-pbr
@@ -0,0 +1,58 @@
1+#!/bin/sh
2+# shellcheck disable=SC1091,SC3037,SC3043
3+
4+readonly packageName='pbr'
5+readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
6+
7+pbr_iface_setup() {
8+ local iface="${1}"
9+ local proto
10+ config_get proto "${iface}" proto
11+ case "${iface}" in
12+ (lan|loopback) return 0 ;;
13+ esac
14+ case "${proto}" in
15+ (gre*|nebula|relay|vti*|vxlan|xfrm) return 0 ;;
16+ (none)
17+ uci -q set "network.${iface}_rt=route"
18+ uci -q set "network.${iface}_rt.interface=${iface}"
19+ uci -q set "network.${iface}_rt.target=0.0.0.0/0"
20+ uci -q set "network.${iface}_rt6=route6"
21+ uci -q set "network.${iface}_rt6.interface=${iface}"
22+ uci -q set "network.${iface}_rt6.target=::/0"
23+ ;;
24+ esac
25+ echo -en "Setting up ${packageName} routing tables for ${iface}... "
26+ uci -q set "network.${iface}.ip4table=${packageName}_${iface%6}"
27+ uci -q set "network.${iface}.ip6table=${packageName}_${iface%6}"
28+ if ! grep -q -E -e "^[0-9]+\s+${packageName}_${iface%6}$" /etc/iproute2/rt_tables; then
29+ sed -i -e "\$a $(($(sort -r -n /etc/iproute2/rt_tables | grep -o -E -m 1 "^[0-9]+")+1))\t${packageName}_${iface%6}" \
30+ /etc/iproute2/rt_tables
31+ fi
32+ echo -e "${__OK__}"
33+}
34+
35+. /lib/functions.sh
36+. /lib/functions/network.sh
37+config_load network
38+config_foreach pbr_iface_setup interface
39+network_flush_cache
40+network_find_wan iface
41+network_find_wan6 iface6
42+# shellcheck disable=SC2154
43+[ -n "$iface" ] && uci -q batch << EOF
44+set network.default='rule'
45+set network.default.lookup='${packageName}_${iface%6}'
46+set network.default.priority='80000'
47+EOF
48+[ -n "$iface6" ] && uci -q batch << EOF
49+set network.default6='rule6'
50+set network.default6.lookup='${packageName}_${iface6%6}'
51+set network.default6.priority='80000'
52+EOF
53+uci commit network
54+echo -en "Restarting network... "
55+/etc/init.d/network restart
56+echo -e "${__OK__}"
57+
58+exit 0
--- /dev/null
+++ b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_forward/30-pbr.nft
@@ -0,0 +1 @@
1+jump pbr_forward comment "Jump into pbr forward chain";
--- /dev/null
+++ b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_input/30-pbr.nft
@@ -0,0 +1 @@
1+jump pbr_input comment "Jump into pbr input chain";
--- /dev/null
+++ b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_output/30-pbr.nft
@@ -0,0 +1 @@
1+jump pbr_output comment "Jump into pbr output chain";
--- /dev/null
+++ b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_postrouting/30-pbr.nft
@@ -0,0 +1 @@
1+jump pbr_postrouting comment "Jump into pbr postrouting chain";
--- /dev/null
+++ b/net/pbr/files/usr/share/nftables.d/chain-post/mangle_prerouting/30-pbr.nft
@@ -0,0 +1 @@
1+jump pbr_prerouting comment "Jump into pbr prerouting chain";
--- /dev/null
+++ b/net/pbr/files/usr/share/nftables.d/table-post/30-pbr.nft
@@ -0,0 +1,5 @@
1+chain pbr_forward {}
2+chain pbr_input {}
3+chain pbr_output {}
4+chain pbr_prerouting {}
5+chain pbr_postrouting {}
--- /dev/null
+++ b/net/pbr/files/usr/share/pbr/pbr.firewall.include
@@ -0,0 +1,5 @@
1+#!/bin/sh
2+if [ -x /etc/init.d/pbr ] && /etc/init.d/pbr enabled; then
3+ logger -t "pbr" "Reloading pbr due to $ACTION of firewall"
4+ /etc/init.d/pbr on_firewall_reload "$ACTION"
5+fi
--- /dev/null
+++ b/net/pbr/files/usr/share/pbr/pbr.user.aws
@@ -0,0 +1,33 @@
1+#!/bin/sh
2+# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh
3+
4+TARGET_SET='pbr_wan_4_dst_ip_user'
5+TARGET_IPSET='pbr_wan_4_dst_net_user'
6+TARGET_TABLE='inet fw4'
7+TARGET_URL="https://ip-ranges.amazonaws.com/ip-ranges.json"
8+TARGET_DL_FILE="/var/pbr_tmp_aws_ip_ranges"
9+TARGET_NFT_FILE="/var/pbr_tmp_aws_ip_ranges.nft"
10+[ -z "$nft" ] && nft="$(command -v nft)"
11+_ret=1
12+
13+if [ ! -s "$TARGET_DL_FILE" ]; then
14+ uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | grep "ip_prefix" | sed 's/^.*\"ip_prefix\": \"//; s/\",//' > "$TARGET_DL_FILE"
15+fi
16+
17+if [ -s "$TARGET_DL_FILE" ]; then
18+ if ipset -q list "$TARGET_IPSET" >/dev/null 2>&1; then
19+ if awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_DL_FILE" | ipset restore -!; then
20+ _ret=0
21+ fi
22+ elif [ -n "$nft" ] && [ -x "$nft" ] && "$nft" list set "$TARGET_TABLE" "$TARGET_SET" >/dev/null 2>&1; then
23+ printf "add element %s %s { " "$TARGET_TABLE" "$TARGET_SET" > "$TARGET_NFT_FILE"
24+ awk '{printf $1 ", "}' "$TARGET_DL_FILE" >> "$TARGET_NFT_FILE"
25+ printf " } " >> "$TARGET_NFT_FILE"
26+ if "$nft" -f "$TARGET_NFT_FILE"; then
27+ rm -f "$TARGET_NFT_FILE"
28+ _ret=0
29+ fi
30+ fi
31+fi
32+
33+return $_ret
--- /dev/null
+++ b/net/pbr/files/usr/share/pbr/pbr.user.netflix
@@ -0,0 +1,49 @@
1+#!/bin/sh
2+# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh
3+# Credits to https://forum.openwrt.org/u/dscpl for api.hackertarget.com code.
4+# Credits to https://github.com/kkeker and https://github.com/tophirsch for api.bgpview.io code.
5+
6+TARGET_SET='pbr_wan_4_dst_ip_user'
7+TARGET_IPSET='pbr_wan_4_dst_net_user'
8+TARGET_TABLE='inet fw4'
9+TARGET_ASN='2906'
10+TARGET_DL_FILE="/var/pbr_tmp_AS${TARGET_ASN}"
11+TARGET_NFT_FILE="/var/pbr_tmp_AS${TARGET_ASN}.nft"
12+#DB_SOURCE='ipinfo.io'
13+#DB_SOURCE='api.hackertarget.com'
14+DB_SOURCE='api.bgpview.io'
15+[ -z "$nft" ] && nft="$(command -v nft)"
16+_ret=1
17+
18+if [ ! -s "$TARGET_DL_FILE" ]; then
19+ if [ "$DB_SOURCE" = "ipinfo.io" ]; then
20+ TARGET_URL="https://ipinfo.io/AS${TARGET_ASN}"
21+ 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"
22+ fi
23+ if [ "$DB_SOURCE" = "api.hackertarget.com" ]; then
24+ TARGET_URL="https://api.hackertarget.com/aslookup/?q=AS${TARGET_ASN}"
25+ uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | sed '1d' > "$TARGET_DL_FILE"
26+ fi
27+ if [ "$DB_SOURCE" = "api.bgpview.io" ]; then
28+ TARGET_URL="https://api.bgpview.io/asn/${TARGET_ASN}/prefixes"
29+ uclient-fetch --no-check-certificate -qO- "$TARGET_URL" 2>/dev/null | jsonfilter -e '@.data.ipv4_prefixes[*].prefix' > "$TARGET_DL_FILE"
30+ fi
31+fi
32+
33+if [ -s "$TARGET_DL_FILE" ]; then
34+ if ipset -q list "$TARGET_IPSET" >/dev/null 2>&1; then
35+ if awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_DL_FILE" | ipset restore -!; then
36+ _ret=0
37+ fi
38+ elif [ -n "$nft" ] && [ -x "$nft" ] && "$nft" list set "$TARGET_TABLE" "$TARGET_SET" >/dev/null 2>&1; then
39+ printf "add element %s %s { " "$TARGET_TABLE" "$TARGET_SET" > "$TARGET_NFT_FILE"
40+ awk '{printf $1 ", "}' "$TARGET_DL_FILE" >> "$TARGET_NFT_FILE"
41+ printf " } " >> "$TARGET_NFT_FILE"
42+ if "$nft" -f "$TARGET_NFT_FILE"; then
43+ rm -f "$TARGET_NFT_FILE"
44+ _ret=0
45+ fi
46+ fi
47+fi
48+
49+return $_ret
--- a/net/vpn-policy-routing/Makefile
+++ /dev/null
@@ -1,68 +0,0 @@
1-# Copyright 2017-2018 Stan Grishin (stangri@melmac.net)
2-# This is free software, licensed under the GNU General Public License v3.
3-
4-include $(TOPDIR)/rules.mk
5-
6-PKG_NAME:=vpn-policy-routing
7-PKG_VERSION:=0.3.4
8-PKG_RELEASE:=8
9-PKG_LICENSE:=GPL-3.0-or-later
10-PKG_MAINTAINER:=Stan Grishin <stangri@melmac.net>
11-
12-include $(INCLUDE_DIR)/package.mk
13-
14-define Package/vpn-policy-routing
15- SECTION:=net
16- CATEGORY:=Network
17- TITLE:=VPN Policy-Based Routing Service
18- URL:=https://docs.openwrt.melmac.net/vpn-policy-routing/
19- DEPENDS:=+jshn +ipset +iptables +resolveip +kmod-ipt-ipset +iptables-mod-ipopt +ip-full
20- PKGARCH:=all
21-endef
22-
23-define Package/vpn-policy-routing/description
24-This service allows policy-based routing for L2TP, Openconnect, OpenVPN, PPTP and Wireguard tunnels and WAN interface.
25-Policies can specify domains, local IPs/subnets and ports, as well as remote IPs/subnets and ports.
26-endef
27-
28-define Package/vpn-policy-routing/conffiles
29-/etc/config/vpn-policy-routing
30-endef
31-
32-define Build/Configure
33-endef
34-
35-define Build/Compile
36-endef
37-
38-define Package/vpn-policy-routing/install
39- $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/etc/hotplug.d/firewall $(1)/etc/
40- $(INSTALL_BIN) ./files/vpn-policy-routing.init $(1)/etc/init.d/vpn-policy-routing
41- $(SED) "s|^\(PKG_VERSION\).*|\1='$(PKG_VERSION)-$(PKG_RELEASE)'|" $(1)/etc/init.d/vpn-policy-routing
42- $(INSTALL_CONF) ./files/vpn-policy-routing.config $(1)/etc/config/vpn-policy-routing
43- $(INSTALL_DATA) ./files/vpn-policy-routing.firewall.hotplug $(1)/etc/hotplug.d/firewall/70-vpn-policy-routing
44- $(INSTALL_DATA) ./files/vpn-policy-routing.aws.user $(1)/etc/vpn-policy-routing.aws.user
45- $(INSTALL_DATA) ./files/vpn-policy-routing.netflix.user $(1)/etc/vpn-policy-routing.netflix.user
46-endef
47-
48-define Package/vpn-policy-routing/postinst
49- #!/bin/sh
50- # check if we are on real system
51- if [ -z "$${IPKG_INSTROOT}" ]; then
52- /etc/init.d/vpn-policy-routing enable
53- fi
54- exit 0
55-endef
56-
57-define Package/vpn-policy-routing/prerm
58- #!/bin/sh
59- # check if we are on real system
60- if [ -z "$${IPKG_INSTROOT}" ]; then
61- echo "Stopping service and removing rc.d symlink for vpn-policy-routing"
62- /etc/init.d/vpn-policy-routing stop || true
63- /etc/init.d/vpn-policy-routing disable || true
64- fi
65- exit 0
66-endef
67-
68-$(eval $(call BuildPackage,vpn-policy-routing))
--- a/net/vpn-policy-routing/files/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
1-# README
2-
3-README has been moved to [https://docs.openwrt.melmac.net/vpn-policy-routing/](https://docs.openwrt.melmac.net/vpn-policy-routing/).
--- a/net/vpn-policy-routing/files/vpn-policy-routing.aws.user
+++ /dev/null
@@ -1,19 +0,0 @@
1-#!/bin/sh
2-# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh
3-
4-TARGET_IPSET='wan'
5-
6-TARGET_URL="https://ip-ranges.amazonaws.com/ip-ranges.json"
7-TARGET_FNAME="/var/vpn-policy-routing_tmp_aws_ip_ranges"
8-
9-_ret=1
10-
11-if [ ! -s "$TARGET_FNAME" ]; then
12- curl "$TARGET_URL" 2>/dev/null | grep "ip_prefix" | sed 's/^.*\"ip_prefix\": \"//; s/\",//' > "$TARGET_FNAME"
13-fi
14-if [ -s "$TARGET_FNAME" ]; then
15- awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_FNAME" | ipset restore -! && _ret=0
16-fi
17-rm -f "$TARGET_FNAME"
18-
19-return $_ret
--- a/net/vpn-policy-routing/files/vpn-policy-routing.config
+++ /dev/null
@@ -1,30 +0,0 @@
1-config vpn-policy-routing 'config'
2- option enabled '0'
3- option verbosity '2'
4- option strict_enforcement '1'
5- option src_ipset '0'
6- option dest_ipset '0'
7- option resolver_ipset 'dnsmasq.ipset'
8- option ipv6_enabled '0'
9- list ignored_interface 'vpnserver wgserver'
10- option boot_timeout '30'
11- option iptables_rule_option 'append'
12- option procd_reload_delay '1'
13- option webui_enable_column '0'
14- option webui_protocol_column '0'
15- option webui_chain_column '0'
16- option webui_show_ignore_target '0'
17- option webui_sorting '1'
18- list webui_supported_protocol 'tcp'
19- list webui_supported_protocol 'udp'
20- list webui_supported_protocol 'tcp udp'
21- list webui_supported_protocol 'icmp'
22- list webui_supported_protocol 'all'
23-
24-config include
25- option path '/etc/vpn-policy-routing.netflix.user'
26- option enabled 0
27-
28-config include
29- option path '/etc/vpn-policy-routing.aws.user'
30- option enabled 0
--- a/net/vpn-policy-routing/files/vpn-policy-routing.firewall.hotplug
+++ /dev/null
@@ -1,6 +0,0 @@
1-#!/bin/sh
2-
3-[ "$ACTION" = "reload" ] || exit 0
4-
5-logger -t "vpn-policy-routing" "Reloading vpn-policy-routing due to $ACTION of firewall"
6-/etc/init.d/vpn-policy-routing reload
--- a/net/vpn-policy-routing/files/vpn-policy-routing.init
+++ /dev/null
@@ -1,1322 +0,0 @@
1-#!/bin/sh /etc/rc.common
2-# Copyright 2017-2020 Stan Grishin (stangri@melmac.net)
3-# shellcheck disable=SC2039,SC1091,SC2018,SC2019,SC3043,SC3057,SC3060
4-PKG_VERSION='dev-test'
5-
6-# sysctl net.ipv4.conf.default.rp_filter=1
7-# sysctl net.ipv4.conf.all.rp_filter=1
8-
9-# shellcheck disable=SC2034
10-START=94
11-# shellcheck disable=SC2034
12-USE_PROCD=1
13-
14-if type extra_command 1>/dev/null 2>&1; then
15- extra_command 'support' "Generates output required to troubleshoot routing issues
16- Use '-d' option for more detailed output
17- Use '-p' option to automatically upload data under VPR paste.ee account
18- WARNING: while paste.ee uploads are unlisted, they are still publicly available
19- List domain names after options to include their lookup in report"
20- extra_command 'version' 'Show version information'
21- extra_command 'reload_interface' 'Reload specific interface only'
22-else
23-# shellcheck disable=SC2034
24- EXTRA_COMMANDS='support version'
25-# shellcheck disable=SC2034
26- EXTRA_HELP=" support Generates output required to troubleshoot routing issues
27- Use '-d' option for more detailed output
28- Use '-p' option to automatically upload data under VPR paste.ee account
29- WARNING: while paste.ee uploads are unlisted, they are still publicly available
30- List domain names after options to include their lookup in report"
31-fi
32-
33-readonly packageName='vpn-policy-routing'
34-readonly serviceName="$packageName $PKG_VERSION"
35-readonly PIDFile="/var/run/${packageName}.pid"
36-readonly jsonFile="/var/run/${packageName}.json"
37-readonly dnsmasqFile="/var/dnsmasq.d/${packageName}"
38-readonly sharedMemoryOutput="/dev/shm/$packageName-output"
39-readonly _OK_='\033[0;32m\xe2\x9c\x93\033[0m'
40-readonly _FAIL_='\033[0;31m\xe2\x9c\x97\033[0m'
41-readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
42-readonly __FAIL__='\033[0;31m[\xe2\x9c\x97]\033[0m'
43-readonly _ERROR_='\033[0;31mERROR\033[0m'
44-readonly _WARNING_='\033[0;33mWARNING\033[0m'
45-
46-gatewaySummary=''; errorSummary=''; warningSummary='';
47-serviceEnabled=''; verbosity=''; strictMode='';
48-wanTableID=''; wanMark=''; fwMask='';
49-ipv6Enabled=''; srcIpset=''; destIpset=''; resolverIpset='';
50-wanIface4=''; wanIface6=''; ifaceMark=''; ifaceTableID='';
51-ifAll=''; ifSupported=''; ignoredIfaces=''; supportedIfaces=''; icmpIface='';
52-wanGW4=''; wanGW6=''; bootTimeout=''; insertOption='';
53-webuiChainColumn=''; webuiShowIgnore=''; dnsmasqIpsetSupported='';
54-procdReloadDelay='';
55-usedChainsList='PREROUTING'
56-ipsetSupported='true'
57-configLoaded='false'
58-
59-version() { echo "$PKG_VERSION"; }
60-output_ok() { output 1 "$_OK_"; output 2 "$__OK__\\n"; }
61-output_okn() { output 1 "$_OK_\\n"; output 2 "$__OK__\\n"; }
62-output_fail() { s=1; output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; }
63-output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; }
64-str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; }
65-str_replace() { echo "${1//$2/$3}"; }
66-str_contains() { [ -n "$2" ] && [ "${1//$2}" != "$1" ]; }
67-str_contains_word() { echo "$1" | grep -q -w "$2"; }
68-str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
69-str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; }
70-str_extras_to_space() { echo "$1" | tr ';{}' ' '; }
71-
72-output() {
73-# Can take a single parameter (text) to be output at any verbosity
74-# Or target verbosity level and text to be output at specifc verbosity
75- local msg memmsg logmsg
76- if [ $# -ne 1 ]; then
77- if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; else return 0; fi
78- fi
79- [ -t 1 ] && printf "%b" "$1"
80- msg="${1//$serviceName /service }";
81- if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then
82- [ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
83- logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
84- logger -t "${packageName:-service} [$$]" "$(printf "%b" "$logmsg")"
85- rm -f "$sharedMemoryOutput"
86- else
87- printf "%b" "$msg" >> "$sharedMemoryOutput"
88- fi
89-}
90-is_present() { command -v "$1" >/dev/null 2>&1; }
91-is_installed() { [ -s "/usr/lib/opkg/info/${1}.control" ]; }
92-is_variant_installed() { [ "$(echo /usr/lib/opkg/info/"${1}"*.control)" != "/usr/lib/opkg/info/${1}*.control" ]; }
93-
94-build_ifAll() { ifAll="${ifAll}${1} "; }
95-build_ifSupported() { is_supported_interface "$1" && ifSupported="${ifSupported}${1} "; }
96-vpr_find_iface() {
97- local iface i param="$2"
98- [ "$param" = 'wan6' ] || param='wan'
99- "network_find_${param}" iface
100- is_tunnel "$iface" && unset iface
101- if [ -z "$iface" ]; then
102- for i in $ifAll; do
103- if "is_${param}" "$i"; then break; else unset i; fi
104- done
105- fi
106- eval "$1"='${iface:-$i}'
107-}
108-vpr_get_gateway() {
109- local iface="$2" dev="$3" gw
110- network_get_gateway gw "$iface"
111- if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
112- gw="$(ip -4 a list dev "$dev" 2>/dev/null | grep inet | awk '{print $2}' | awk -F "/" '{print $1}')"
113- fi
114- eval "$1"='$gw'
115-}
116-vpr_get_gateway6() {
117- local iface="$2" dev="$3" gw
118- network_get_gateway6 gw "$iface"
119- if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
120- gw="$(ip -6 a list dev "$dev" 2>/dev/null | grep inet6 | awk '{print $2}')"
121- fi
122- eval "$1"='$gw'
123-}
124-is_l2tp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "l2tp" ]; }
125-is_oc() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:11}" = "openconnect" ]; }
126-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" ]; }
127-is_pptp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "pptp" ]; }
128-is_tor() { [ "$(str_to_lower "$1")" = "tor" ]; }
129-is_tor_running() {
130- local ret=0
131- if [ -s "/etc/tor/torrc" ]; then
132- json_load "$(ubus call service list "{ 'name': 'tor' }")"
133- json_select 'tor'; json_select 'instances'; json_select 'instance1';
134- json_get_var ret 'running'; json_cleanup
135- fi
136- if [ "$ret" = "0" ]; then return 1; else return 0; fi
137-}
138-is_wg() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:9}" = "wireguard" ]; }
139-is_tunnel() { is_l2tp "$1" || is_oc "$1" || is_ovpn "$1" || is_pptp "$1" || is_tor "$1" || is_wg "$1"; }
140-is_wan() { [ "$1" = "$wanIface4" ] || { [ "${1##wan}" != "$1" ] && [ "${1##wan6}" = "$1" ]; } || [ "${1%%wan}" != "$1" ]; }
141-is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1/#wan6}" != "$1" ] || [ "${1/%wan6}" != "$1" ]; }
142-is_ignored_interface() { str_contains_word "$ignoredIfaces" "$1"; }
143-is_supported_interface() { str_contains_word "$supportedIfaces" "$1" || { ! is_ignored_interface "$1" && { is_wan "$1" || is_wan6 "$1" || is_tunnel "$1"; }; }; }
144-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; }
145-is_ipv4() { expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; }
146-is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ":"; }
147-is_family_mismatch() { ( is_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_netmask "${2//!}" ); }
148-is_ipv6_link_local() { [ "${1:0:4}" = "fe80" ]; }
149-is_ipv6_unique_local() { [ "${1:0:2}" = "fc" ] || [ "${1:0:2}" = "fd" ]; }
150-is_ipv6_global() { [ "${1:0:4}" = "2001" ]; }
151-# is_ipv6_global() { is_ipv6 "$1" && ! is_ipv6_link_local "$1" && ! is_ipv6_link_local "$1"; }
152-is_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; }
153-is_domain() { str_contains "$1" '[a-zA-Z]'; }
154-is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; }
155-is_turris() { /bin/ubus -S call system board | /bin/grep 'Turris' | /bin/grep -q '15.05'; }
156-is_chaos_calmer() { ubus -S call system board | grep -q 'Chaos Calmer'; }
157-dnsmasq_kill() { killall -q -s HUP dnsmasq; }
158-dnsmasq_restart() { output 3 'Restarting DNSMASQ '; if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then output_okn; else output_failn; fi; }
159-is_default_dev() { [ "$1" = "$(ip -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
160-is_supported_iface_dev() {
161- for n in $ifSupported; do
162- if [ "$1" = "$(uci -q get "network.${n}.ifname" || echo "$n")" ] || \
163- [ "$1" = "$(uci -q get "network.${n}.device" || echo "$n")" ] || \
164- [ "$1" = "$(uci -q get "network.${n}.proto")-${n}" ] ; then return 0; fi
165- done
166- return 1
167-}
168-is_supported_protocol () { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; }
169-append_chains_targets() {
170- local chain iface name
171- config_get name "$1" 'name' 'blank'
172- config_get chain "$1" 'chain' 'PREROUTING'
173- config_get iface "$1" 'interface'
174- if ! str_contains_word "$usedChainsList" "$chain"; then
175- usedChainsList="$usedChainsList $chain"
176- if [ "$chain" != 'PREROUTING' ] && [ "$webuiChainColumn" != '1' ]; then
177- 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"
178- fi
179- fi
180- if [ "$iface" = 'ignore' ] && ! str_contains_word "$supportedIfaces" 'ignore'; then
181- supportedIfaces="$supportedIfaces ignore"
182- if [ "$webuiShowIgnore" != '1' ]; then
183- 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"
184- fi
185- fi
186-}
187-
188-load_package_config() {
189- [ "$configLoaded" = 'false' ] || return 0
190-
191- config_load "$packageName"
192- config_get_bool serviceEnabled 'config' 'enabled' 0
193- config_get_bool strictMode 'config' 'strict_enforcement' 1
194- config_get_bool ipv6Enabled 'config' 'ipv6_enabled' 0
195- config_get_bool srcIpset 'config' 'src_ipset' 0
196- config_get_bool destIpset 'config' 'dest_ipset' 0
197- config_get resolverIpset 'config' 'resolver_ipset' 'dnsmasq.ipset'
198- config_get verbosity 'config' 'verbosity' '2'
199- config_get wanTableID 'config' 'wan_tid' '201'
200- config_get wanMark 'config' 'wan_mark' '0x010000'
201- config_get fwMask 'config' 'fw_mask' '0xff0000'
202- config_get icmpIface 'config' 'icmp_interface'
203- config_get ignoredIfaces 'config' 'ignored_interface'
204- config_get supportedIfaces 'config' 'supported_interface'
205- config_get bootTimeout 'config' 'boot_timeout' '30'
206- config_get insertOption 'config' 'iptables_rule_option' 'append'
207- config_get procdReloadDelay 'config' 'procd_reload_delay' '0'
208- config_get_bool webuiChainColumn 'config' 'webui_chain_column' '0'
209- config_get_bool webuiShowIgnore 'config' 'webui_show_ignore_target' '0'
210- config_foreach append_chains_targets 'policy'
211-
212- if [ -z "${verbosity##*[!0-9]*}" ] || [ "$verbosity" -lt 0 ] || [ "$verbosity" -gt 2 ]; then
213- verbosity=2
214- fi
215-
216- . /lib/functions/network.sh
217- . /usr/share/libubox/jshn.sh
218- mkdir -p "${PIDFile%/*}"
219- mkdir -p "${jsonFile%/*}"
220- mkdir -p "${dnsmasqFile%/*}"
221-
222- if [ -n "$icmpIface" ] && ! str_contains_word "$usedChainsList" 'OUTPUT'; then
223- usedChainsList="$usedChainsList OUTPUT"
224- fi
225-
226- case $insertOption in
227- insert|-i|-I) insertOption='-I';;
228- append|-a|-A|*) insertOption='-A';;
229- esac
230-
231- [ "$resolverIpset" = 'dnsmasq.ipset' ] && dnsmasqIpsetSupported='true'
232- if dnsmasq -v 2>/dev/null | grep -q 'no-ipset' || ! dnsmasq -v 2>/dev/null | grep -q -w 'ipset'; then
233- unset dnsmasqIpsetSupported
234- if [ -n "$dnsmasqIpsetSupported" ]; then
235- errorSummary="${errorSummary}${_ERROR_}: Resolver ipset support (dnsmasq.ipset) is enabled in $packageName, but DNSMASQ ipsets are not supported on this system!\\n"
236- fi
237- fi
238- if ! ipset help hash:net >/dev/null 2>&1; then
239- unset ipsetSupported
240- if [ -n "$dnsmasqIpsetSupported" ]; then
241- errorSummary="${errorSummary}${_ERROR_}: DNSMASQ ipsets are supported, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n"
242- unset dnsmasqIpsetSupported
243- fi
244- if [ "$destIpset" -ne 0 ]; then
245- 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"
246- destIpset=0
247- fi
248- if [ "$srcIpset" -ne 0 ]; then
249- 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"
250- srcIpset=0
251- fi
252- fi
253- if ! ipset help hash:mac >/dev/null 2>&1; then
254- if [ "$srcIpset" -ne 0 ]; then
255- 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"
256- srcIpset=0
257- fi
258- fi
259-
260- configLoaded='true'
261-}
262-
263-is_enabled() {
264- load_package_config
265- if [ "$serviceEnabled" -eq 0 ]; then
266- if [ "$1" = 'on_start' ]; then
267- errorSummary="${errorSummary}${_ERROR_}: ${packageName} is currently disabled.\\n"
268- errorSummary="${errorSummary}Enable ${packageName} from WebUI or run the following commands:\\n"
269- errorSummary="${errorSummary}uci set $packageName.config.enabled='1'; uci commit $packageName;\\n"
270- fi
271- return 1
272- fi
273-}
274-
275-load_network() {
276- if [ -z "$ifAll" ]; then
277- config_load 'network'
278- config_foreach build_ifAll 'interface'
279- fi
280- vpr_find_iface wanIface4 'wan'
281- [ "$ipv6Enabled" -ne 0 ] && vpr_find_iface wanIface6 'wan6'
282- [ -n "$wanIface4" ] && network_get_gateway wanGW4 "$wanIface4"
283- [ -n "$wanIface6" ] && network_get_gateway6 wanGW6 "$wanIface6"
284- wanGW="${wanGW4:-$wanGW6}"
285- unset ifSupported
286- config_load 'network'
287- config_foreach build_ifSupported 'interface'
288-}
289-
290-is_wan_up() {
291- local sleepCount=1
292- load_network
293- while [ -z "$wanGW" ] ; do
294- load_network
295- if [ $((sleepCount)) -gt $((bootTimeout)) ] || [ -n "$wanGW" ]; then break; fi
296- output "$serviceName waiting for wan gateway...\\n"
297- sleep 1
298- network_flush_cache
299- sleepCount=$((sleepCount+1))
300- done
301- if [ -n "$wanGW" ]; then
302- return 0
303- else
304- errorSummary="${errorSummary}${_ERROR_}: ${serviceName} failed to discover WAN gateway!\\n"
305- return 1
306- fi
307-}
308-
309-ipt_cleanup() {
310- local i
311- for i in PREROUTING FORWARD INPUT OUTPUT; do
312- while iptables -t mangle -D $i -m mark --mark 0x0/0xff0000 -j VPR_${i} >/dev/null 2>&1; do : ; done
313- done
314- for i in PREROUTING FORWARD INPUT OUTPUT; do
315- while iptables -t mangle -D $i -j VPR_${i} >/dev/null 2>&1; do : ; done
316- done
317-}
318-
319-# shellcheck disable=SC2086
320-ipt() {
321- local d failFlagIpv4=1 failFlagIpv6=1
322- for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
323- [ "$d" != "$*" ] && { iptables $d >/dev/null 2>&1; ip6tables $d >/dev/null 2>&1; }
324- done
325-
326- d="$*"; iptables $d >/dev/null 2>&1 && failFlagIpv4=0;
327- if [ "$ipv6Enabled" -gt 0 ]; then ip6tables $d >/dev/null 2>&1 && failFlagIpv6=0; fi
328-
329- [ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ]
330-}
331-
332-# shellcheck disable=SC2086
333-ips() {
334- local command="$1" ipset="${2//-/_}" param="$3" comment="$4" appendix failFlag=0
335- if str_contains "$ipset" '_ip'; then
336- ipset="${ipset//_ip}"; appendix='_ip';
337- elif str_contains "$ipset" '_mac'; then
338- ipset="${ipset//_mac}"; appendix='_mac';
339- fi
340-
341- case "$command" in
342- add_dnsmasq)
343- [ "$resolverIpset" = "dnsmasq.ipset" ] || return 1
344- if [ -z "$dnsmasqIpsetSupported" ]; then
345- warningSummary="${warningSummary}${_WARNING_}: The 'resolver_ipset' is set to 'dnsmasq.ipset', but DNSMASQ ipsets are not supported on this system!\\n"
346- failFlag=1
347- elif [ "$ipv6Enabled" -ne 0 ]; then
348- echo "ipset=/${param}/${ipset},${ipset}6 # $comment" >> "$dnsmasqFile" || failFlag=1
349- else
350- echo "ipset=/${param}/${ipset} # $comment" >> "$dnsmasqFile" || failFlag=1
351- fi
352- ;;
353- add)
354- if [ -z "$appendix" ] && [ "$destIpset" -eq 0 ]; then return 1; fi
355- if [ -n "$appendix" ] && [ "$srcIpset" -eq 0 ]; then return 1; fi
356- if [ "$ipv6Enabled" -ne 0 ] && [ "$appendix" != "_mac" ]; then
357- ipset -q -! $command "${ipset}6${appendix}" $param comment "$comment" || failFlag=1
358- fi
359- ipset -q -! $command "${ipset}${appendix}" $param comment "$comment" || failFlag=1
360- ;;
361- create)
362- if [ "$ipv6Enabled" -ne 0 ] && [ "$appendix" != "_mac" ]; then
363- ipset -q -! "$command" "${ipset}6${appendix}" $param family inet6 || failFlag=1
364- fi
365- ipset -q -! "$command" "${ipset}${appendix}" $param || failFlag=1
366- ;;
367- destroy|flush)
368- ipset -q -! "$command" "${ipset}6${appendix}" 2>/dev/null || failFlag=1
369- ipset -q -! "$command" "${ipset}${appendix}" 2>/dev/null || failFlag=1
370- return 0
371- ;;
372- esac
373- return $failFlag
374-}
375-
376-insert_tor_policy() {
377- local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain
378- proto="$(str_to_lower "$7")"
379- chain="${8:-PREROUTING}"
380- if [ -n "${laddr}${lport}${rport}" ]; then
381- processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n"
382- fi
383- if [ -n "$proto" ] && [ "$proto" != "all" ]; then
384- processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n"
385- fi
386- if [ "$chain" != "PREROUTING" ]; then
387- processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '$comment'\\n"
388- fi
389- ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || processPolicyError="${processPolicyError}${_ERROR_}: ipset 'add' $iface $raddr\\n"
390- return 0
391-}
392-
393-insert_policy() {
394- local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain
395- local mark param i valueNeg value dest ipInsertOption="-A"
396- proto="$(str_to_lower "$7")"
397- chain="${8:-PREROUTING}"
398- mark=$(eval echo "\$mark_${iface//-/_}")
399- if [ "$ipv6Enabled" -eq 0 ] && ( is_ipv6 "$laddr" || is_ipv6 "$raddr" ); then
400- processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$comment' as IPv6 support is disabled\\n"
401- return 1
402- fi
403-
404- if [ -n "$mark" ]; then
405- dest="-g VPR_MARK${mark}"
406- elif [ "$iface" = "ignore" ]; then
407- dest="-j RETURN"
408- else
409- processPolicyError="${processPolicyError}${_ERROR_}: Unknown fw_mark for ${iface}\\n"
410- return 0
411- fi
412-
413- if [ -z "$proto" ]; then
414- if [ -n "$lport" ] || [ -n "$rport" ]; then
415- proto='tcp udp'
416- else
417- proto='all'
418- fi
419- fi
420-
421- if is_family_mismatch "$laddr" "$raddr"; then
422- processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$laddr' and '$raddr' in policy '$comment'\\n"
423- return 0
424- fi
425-
426- for i in $proto; do
427- if [ "$i" = 'all' ]; then
428- param="-t mangle ${ipInsertOption} VPR_${chain} $dest"
429- elif ! is_supported_protocol "$i"; then
430- processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$comment'\\n"
431- return 0
432- else
433- param="-t mangle ${ipInsertOption} VPR_${chain} $dest -p $i"
434- fi
435-
436- if [ -n "$laddr" ]; then
437- if [ "${laddr:0:1}" = "!" ]; then
438- valueNeg='!'; value="${laddr:1}"
439- else
440- unset valueNeg; value="$laddr";
441- fi
442- if is_phys_dev "$value"; then
443- param="$param $valueNeg -m physdev --physdev-in ${value:1}"
444- elif is_mac_address "$value"; then
445- param="$param -m mac $valueNeg --mac-source $value"
446- else
447- param="$param $valueNeg -s $value"
448- fi
449- fi
450-
451- if [ -n "$lport" ]; then
452- if [ "${lport:0:1}" = "!" ]; then
453- valueNeg='!'; value="${lport:1}"
454- else
455- unset valueNeg; value="$lport";
456- fi
457- param="$param -m multiport $valueNeg --sport ${value//-/:}"
458- fi
459-
460- if [ -n "$raddr" ]; then
461- if [ "${raddr:0:1}" = "!" ]; then
462- valueNeg='!'; value="${raddr:1}"
463- else
464- unset valueNeg; value="$raddr";
465- fi
466- param="$param $valueNeg -d $value"
467- fi
468-
469- if [ -n "$rport" ]; then
470- if [ "${rport:0:1}" = "!" ]; then
471- valueNeg='!'; value="${rport:1}"
472- else
473- unset valueNeg; value="$rport";
474- fi
475- param="$param -m multiport $valueNeg --dport ${value//-/:}"
476- fi
477-
478- [ -n "$comment" ] && param="$param -m comment --comment $(str_extras_to_underscore "$comment")"
479- ipt "$param" || processPolicyError="${processPolicyError}${_ERROR_}: iptables $param\\n"
480- done
481- return 0
482-}
483-
484-r_process_policy(){
485- local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto="$7" chain="$8" resolved_laddr resolved_raddr i ipsFailFlag
486- if str_contains "$laddr" '[ ;\{\}]'; then
487- for i in $(str_extras_to_space "$laddr"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$i" "$lport" "$raddr" "$rport" "$proto" "$chain"; done
488- return 0
489- elif str_contains "$lport" '[ ;\{\}]'; then
490- for i in $(str_extras_to_space "$lport"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$i" "$raddr" "$rport" "$proto" "$chain"; done
491- return 0
492- elif str_contains "$raddr" '[ ;\{\}]'; then
493- for i in $(str_extras_to_space "$raddr"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$lport" "$i" "$rport" "$proto" "$chain"; done
494- return 0
495- elif str_contains "$rport" '[ ;\{\}]'; then
496- for i in $(str_extras_to_space "$rport"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$i" "$proto" "$chain"; done
497- return 0
498- fi
499-
500- # start non-recursive processing
501- # process TOR, netmask, physical device and mac-address separately, so we don't send them to resolveip
502- if is_tor "$iface"; then
503- insert_tor_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
504- elif is_phys_dev "$laddr"; then
505- insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
506- elif [ -n "$laddr" ] && [ -z "${lport}${raddr}${rport}" ] && [ "$chain" = 'PREROUTING' ]; then
507- if is_mac_address "$laddr"; then
508- if [ -n "$proto" ] && [ "$proto" != 'all' ] && [ "$srcIpset" -ne 0 ]; then
509- processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', mac-address: '$laddr'\\n"
510- fi
511- ips 'add' "${iface}_mac" "$laddr" "${comment}: $laddr" || ipsFailFlag=1
512- else
513- if [ -n "$proto" ] && [ "$proto" != "all" ] && [ "$srcIpset" -ne 0 ]; then
514- processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', source: '$laddr'\\n"
515- fi
516- ips 'add' "${iface}_ip" "$laddr" "${comment}: $laddr" || ipsFailFlag=1
517- fi
518- elif [ -n "$raddr" ] && [ -z "${laddr}${lport}${rport}" ] && [ "$chain" = 'PREROUTING' ]; then
519- if [ -n "$proto" ] && [ "$proto" != 'all' ]; then
520- processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', destination: '$raddr'\\n"
521- fi
522- if is_domain "$raddr"; then
523- ips 'add_dnsmasq' "${iface}" "$raddr" "${comment}" || ipsFailFlag=1
524- else
525- ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || ipsFailFlag=1
526- fi
527- else
528- ipsFailFlag=1
529- fi
530- [ -n "$ipsFailFlag" ] || return 0;
531- if is_mac_address "$laddr"; then
532- insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
533- elif is_netmask "$laddr" || is_netmask "$raddr"; then
534- insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
535- else
536- [ -n "$laddr" ] && resolved_laddr="$(resolveip "$laddr")"
537- [ -n "$raddr" ] && resolved_raddr="$(resolveip "$raddr")"
538- if [ -n "$resolved_laddr" ] && [ "$resolved_laddr" != "$laddr" ]; then
539- for i in $resolved_laddr; do [ -n "$i" ] && r_process_policy "$comment $laddr" "$iface" "$i" "$lport" "$raddr" "$rport" "$proto" "$chain"; done
540- elif [ -n "$resolved_raddr" ] && [ "$resolved_raddr" != "$raddr" ]; then
541- for i in $resolved_raddr; do [ -n "$i" ] && r_process_policy "$comment $raddr" "$iface" "$laddr" "$lport" "$i" "$rport" "$proto" "$chain"; done
542- else
543- insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
544- fi
545- fi
546-}
547-
548-process_policy(){
549- local name comment iface laddr lport raddr rport param mark processPolicyError processPolicyWarning proto chain enabled
550- config_get comment "$1" 'comment'
551- config_get name "$1" 'name' 'blank'
552- config_get iface "$1" 'interface'
553- config_get laddr "$1" 'src_addr'
554- config_get lport "$1" 'src_port'
555- config_get raddr "$1" 'dest_addr'
556- config_get rport "$1" 'dest_port'
557- config_get proto "$1" 'proto'
558- config_get chain "$1" 'chain' 'PREROUTING'
559- config_get_bool enabled "$1" 'enabled' 1
560-
561- [ "$enabled" -gt 0 ] || return 0
562- proto="$(str_to_lower "$proto")"
563- [ "$proto" = 'auto' ] && unset proto
564-
565- comment="${comment:-$name}"
566- output 2 "Routing '$comment' via $iface "
567-
568- if [ -z "$comment" ]; then
569- errorSummary="${errorSummary}${_ERROR_}: Policy name is empty\\n"
570- output_fail; return 1;
571- fi
572- if [ -z "${laddr}${lport}${raddr}${rport}" ]; then
573- errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' missing all IPs/ports\\n"
574- output_fail; return 1;
575- fi
576- if [ -z "$iface" ]; then
577- errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' has no assigned interface\\n"
578- output_fail; return 1;
579- fi
580- if ! is_supported_interface "$iface"; then
581- errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' has unknown interface: '${iface}'\\n"
582- output_fail; return 1;
583- fi
584-
585- lport="${lport// / }"; lport="${lport// /,}"; lport="${lport//,\!/ !}";
586- rport="${rport// / }"; rport="${rport// /,}"; rport="${rport//,\!/ !}";
587- r_process_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain"
588- if [ -n "$processPolicyWarning" ]; then
589- warningSummary="${warningSummary}${processPolicyWarning}\\n"
590- fi
591- if [ -n "$processPolicyError" ]; then
592- output_fail
593- errorSummary="${errorSummary}${processPolicyError}\\n"
594- else
595- output_ok
596- fi
597-}
598-
599-table_destroy(){
600- local tid="$1" iface="$2" mark="$3"
601- if [ -n "$tid" ] && [ -n "$iface" ] && [ -n "$mark" ]; then
602- ipt -t mangle -F "VPR_MARK${mark}"
603- ipt -t mangle -X "VPR_MARK${mark}"
604- ip -4 rule del fwmark "$mark" table "$tid" >/dev/null 2>&1
605- ip -6 rule del fwmark "$mark" table "$tid" >/dev/null 2>&1
606- ip -4 rule del table "$tid" >/dev/null 2>&1
607- ip -6 rule del table "$tid" >/dev/null 2>&1
608- ip -4 route flush table "$tid" >/dev/null 2>&1
609- ip -6 route flush table "$tid" >/dev/null 2>&1
610- ips 'flush' "${iface}"; ips 'destroy' "${iface}";
611- ips 'flush' "${iface}_ip"; ips 'destroy' "${iface}_ip";
612- ips 'flush' "${iface}_mac"; ips 'destroy' "${iface}_mac";
613- ip -4 route flush cache
614- ip -6 route flush cache
615- sed -i "/$iface/d" /etc/iproute2/rt_tables
616- return 0
617- else
618- return 1
619- fi
620-}
621-
622-# shellcheck disable=SC2086
623-table_create(){
624- 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
625-
626- if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
627- return 1
628- fi
629-
630- table_destroy "$tid" "$iface" "$mark"
631-
632- if [ -n "$gw4" ] || [ "$strictMode" -ne 0 ]; then
633- echo "$tid" "$iface" >> /etc/iproute2/rt_tables
634- if [ -z "$gw4" ]; then
635- ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
636- else
637- ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
638- fi
639-# ip -4 route list table main | grep -v 'br-lan' | while read -r i; do
640- ip -4 route list table main | while read -r i; do
641- idev="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')"
642- if ! is_supported_iface_dev "$idev"; then
643- ip -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1
644- fi
645- done
646- ip -4 route flush cache || ipv4_error=1
647- ip -4 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv4_error=1
648- ipt -t mangle -N "VPR_MARK${mark}" || ipv4_error=1
649- ipt -t mangle -A "VPR_MARK${mark}" -j MARK --set-xmark "${mark}/${fwMask}" || ipv4_error=1
650- ipt -t mangle -A "VPR_MARK${mark}" -j RETURN || ipv4_error=1
651- fi
652-
653- if [ "$ipv6Enabled" -ne 0 ]; then
654- ipv6_error=0
655- if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strictMode" -ne 0 ]; then
656- if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
657- ip -6 route add unreachable default table "$tid" || ipv6_error=1
658- else
659- ip -6 route list table main | grep " dev $dev6 " | while read -r i; do
660- ip -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1
661- done
662- fi
663- ip -6 route flush cache || ipv6_error=1
664- ip -6 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv6_error=1
665- fi
666- fi
667-
668- if [ $ipv4_error -eq 0 ] || [ $ipv6_error -eq 0 ]; then
669- dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)"
670- if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
671- ipt -t mangle -I VPR_PREROUTING -m dscp --dscp "${dscp}" -g "VPR_MARK${mark}" || s=1
672- fi
673- if [ -n "$ipsetSupported" ] && { [ -n "$dnsmasqIpsetSupported" ] || [ "$destIpset" -ne 0 ]; }; then
674- if ips 'create' "${iface}" 'hash:net comment' && ips 'flush' "${iface}"; then
675- for i in $usedChainsList; do
676- ipt -t mangle -I VPR_${i} -m set --match-set "${iface}" dst -g "VPR_MARK${mark}" || s=1
677- 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
678- done
679- else
680- s=1
681- fi
682- fi
683- if [ -n "$ipsetSupported" ] && [ "$srcIpset" -ne 0 ]; then
684- if ips 'create' "${iface}_ip" 'hash:net comment' && ips 'flush' "${iface}_ip"; then
685- ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_ip" src -g "VPR_MARK${mark}" || s=1
686- 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
687- else
688- s=1
689- fi
690- if ips 'create' "${iface}_mac" 'hash:mac comment' && ips 'flush' "${iface}_mac"; then
691- ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_mac" src -g "VPR_MARK${mark}" || s=1
692- else
693- s=1
694- fi
695- fi
696- if [ "$iface" = "$icmpIface" ]; then
697- ipt -t mangle -I VPR_OUTPUT -p icmp -g "VPR_MARK${mark}" || s=1
698- fi
699- else
700- s=1
701- fi
702-
703- return $s
704-}
705-
706-table_reload() {
707- 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
708-
709- if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
710- return 1
711- fi
712-
713- ip -4 route del default table "$tid" >/dev/null 2>&1
714- if [ -n "$gw4" ] || [ "$strictMode" -ne 0 ]; then
715- if [ -z "$gw4" ]; then
716- ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
717- else
718- ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
719- fi
720- ip -4 route flush cache || ipv4_error=1
721- ip -4 rule del fwmark "${mark}/${fwMask}" table "$tid" >/dev/null 2>&1
722- ip -4 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv4_error=1
723- fi
724-
725- if [ "$ipv6Enabled" -ne 0 ]; then
726- ip -6 route del default table "$tid" >/dev/null 2>&1
727- ipv6_error=0
728- if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strictMode" -ne 0 ]; then
729- if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
730- ip -6 route add unreachable default table "$tid" || ipv6_error=1
731- else
732- ip -6 route list table main | grep " dev $dev6 " | while read -r i; do
733- ip -6 route add "$i" table "$tid" >/dev/null 2>&1 || ipv6_error=1
734- done
735- fi
736- ip -6 route flush cache || ipv6_error=1
737- ip -6 rule del fwmark "${mark}/${fwMask}" table "$tid" >/dev/null 2>&1
738- ip -6 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv6_error=1
739- fi
740- fi
741-
742- if [ $ipv4_error -eq 0 ] || [ $ipv6_error -eq 0 ]; then
743- dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)"
744- if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
745- ipt -t mangle -I VPR_PREROUTING -m dscp --dscp "${dscp}" -g "VPR_MARK${mark}" || s=1
746- fi
747- if [ "$iface" = "$icmpIface" ]; then
748- ipt -t mangle -I VPR_OUTPUT -p icmp -g "VPR_MARK${mark}" || s=1
749- fi
750- else
751- s=1
752- fi
753-
754- return $s
755-}
756-
757-process_interface(){
758- local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" match="$3" displayText
759-
760- is_supported_interface "$iface" || return 0
761- is_wan6 "$iface" && return 0
762- [ $((ifaceMark)) -gt $((fwMask)) ] && return 1
763-
764- network_get_device dev "$iface"
765- [ -z "$dev" ] && config_get dev "$iface" 'ifname'
766- [ -z "$dev" ] && config_get dev "$iface" 'device'
767- if is_wan "$iface" && [ -n "$wanIface6" ]; then
768- network_get_device dev6 "$wanIface6"
769- [ -z "$dev6" ] && config_get dev6 "$wanIface6" 'ifname'
770- [ -z "$dev6" ] && config_get dev6 "$wanIface6" 'device'
771- fi
772- [ -z "$dev6" ] && dev6="$dev"
773-
774- [ -z "$ifaceTableID" ] && ifaceTableID="$wanTableID"; [ -z "$ifaceMark" ] && ifaceMark="$wanMark";
775-
776- case "$action" in
777- destroy)
778- table_destroy "${ifaceTableID}" "${iface}" "${ifaceMark}"
779- ifaceTableID="$((ifaceTableID + 1))"; ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))";
780- ;;
781- create)
782- eval "mark_${iface//-/_}"='$ifaceMark'
783- eval "tid_${iface//-/_}"='$ifaceTableID'
784- if [ -z "$match" ]; then
785- table_destroy "$ifaceTableID" "$iface"
786- fi
787- vpr_get_gateway gw4 "$iface" "$dev"
788- vpr_get_gateway6 gw6 "$iface" "$dev6"
789- if [ "$iface" = "$dev" ]; then
790- displayText="${iface}/${gw4:-0.0.0.0}"
791- else
792- displayText="${iface}/${dev}/${gw4:-0.0.0.0}"
793- fi
794- [ "$ipv6Enabled" -ne 0 ] && displayText="${displayText}/${gw6:-::/0}"
795- if [ -z "$match" ]; then
796- output 2 "Creating table '$displayText' "
797- is_default_dev "$dev" && displayText="${displayText} ${__OK__}"
798- if table_create "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$match"; then
799- gatewaySummary="${gatewaySummary}${displayText}\\n"
800- output_ok
801- else
802- errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n"
803- output_fail
804- fi
805- elif [ "$iface" = "$match" ]; then
806- output 2 "Reloading table '$displayText' "
807- is_default_dev "$dev" && displayText="${displayText} ${__OK__}"
808- if table_reload "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$match"; then
809- gatewaySummary="${gatewaySummary}${displayText}\\n"
810- output_ok
811- else
812- errorSummary="${errorSummary}${_ERROR_}: Failed to reload '$displayText'\\n"
813- output_fail
814- fi
815- else
816- is_default_dev "$dev" && displayText="${displayText} ${__OK__}"
817- gatewaySummary="${gatewaySummary}${displayText}\\n"
818- fi
819- ifaceTableID="$((ifaceTableID + 1))"; ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))";
820- ;;
821- esac
822- return $s
823-}
824-
825-process_tor_interface(){
826- local s=0 iface="$1" action="$2" displayText
827- case "$action" in
828- destroy)
829- for i in PREROUTING FORWARD INPUT OUTPUT; do
830- ipt -t nat -D "${i}" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
831- ipt -t nat -F "VPR_${i}"; ipt -t nat -X "VPR_${i}";
832- done
833- ;;
834- create)
835- output 2 "Creating TOR redirects "
836- dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')"
837- transPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')"
838- dnsPort="${dnsPort:-9053}"; transPort="${transPort:-9040}";
839- for i in $usedChainsList; do
840- ipt -t nat -N "VPR_${i}"
841- ipt -t nat "$insertOption" "$i" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
842- done
843- if ips 'create' "${iface}" 'hash:net comment' && ips 'flush' "${iface}"; then
844- for i in $usedChainsList; do
845- 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
846- 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
847- 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
848- 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
849- 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
850- done
851- else
852- s=1
853- fi
854- displayText="${iface}/53->${dnsPort}/80,443->${transPort}"
855- if [ "$s" -eq "0" ]; then
856- gatewaySummary="${gatewaySummary}${displayText}\\n"
857- output_ok
858- else
859- errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n"
860- output_fail
861- fi
862- ;;
863- esac
864- return $s
865-}
866-
867-convert_config(){
868- local i src_ipset dest_ipset resolver_ipset
869- [ -s "/etc/config/${packageName}" ] || return 0
870- grep -q "ignored_interfaces" "/etc/config/${packageName}" && sed -i 's/ignored_interfaces/ignored_interface/g' "/etc/config/${packageName}"
871- grep -q "supported_interfaces" "/etc/config/${packageName}" && sed -i 's/supported_interfaces/supported_interface/g' "/etc/config/${packageName}"
872- grep -q "local_addresses" "/etc/config/${packageName}" && sed -i 's/local_addresses/local_address/g' "/etc/config/${packageName}"
873- grep -q "local_ports" "/etc/config/${packageName}" && sed -i 's/local_ports/local_port/g' "/etc/config/${packageName}"
874- grep -q "remote_addresses" "/etc/config/${packageName}" && sed -i 's/remote_addresses/remote_address/g' "/etc/config/${packageName}"
875- grep -q "remote_ports" "/etc/config/${packageName}" && sed -i 's/remote_ports/remote_port/g' "/etc/config/${packageName}"
876- grep -q "ipset_enabled" "/etc/config/${packageName}" && sed -i 's/ipset_enabled/dest_ipset/g' "/etc/config/${packageName}"
877- grep -q "dnsmasq_enabled" "/etc/config/${packageName}" && sed -i 's/dnsmasq_enabled/resolver_ipset/g' "/etc/config/${packageName}"
878- grep -q "enable_control" "/etc/config/${packageName}" && sed -i 's/enable_control/webui_enable_column/g' "/etc/config/${packageName}"
879- grep -q "proto_control" "/etc/config/${packageName}" && sed -i 's/proto_control/webui_protocol_column/g' "/etc/config/${packageName}"
880- grep -q "chain_control" "/etc/config/${packageName}" && sed -i 's/chain_control/webui_chain_column/g' "/etc/config/${packageName}"
881- grep -q "sort_control" "/etc/config/${packageName}" && sed -i 's/sort_control/webui_sorting/g' "/etc/config/${packageName}"
882- grep -q "local_address" "/etc/config/${packageName}" && sed -i 's/local_address/src_addr/g' "/etc/config/${packageName}"
883- grep -q "local_port" "/etc/config/${packageName}" && sed -i 's/local_port/src_port/g' "/etc/config/${packageName}"
884- grep -q "remote_address" "/etc/config/${packageName}" && sed -i 's/remote_address/dest_addr/g' "/etc/config/${packageName}"
885- grep -q "remote_port" "/etc/config/${packageName}" && sed -i 's/remote_port/dest_port/g' "/etc/config/${packageName}"
886- grep -q "local_ipset" "/etc/config/${packageName}" && sed -i 's/local_ipset/src_ipset/g' "/etc/config/${packageName}"
887- grep -q "remote_ipset" "/etc/config/${packageName}" && sed -i 's/remote_ipset/dest_ipset/g' "/etc/config/${packageName}"
888- dest_ipset="$(uci -q get $packageName.config.dest_ipset)"
889- src_ipset="$(uci -q get $packageName.config.src_ipset)"
890- resolver_ipset="$(uci -q get $packageName.config.resolver_ipset)"
891-
892- if [ -n "$dest_ipset" ] && [ "$dest_ipset" != "0" ] && [ "$dest_ipset" != "1" ]; then
893- uci set "$packageName".config.dest_ipset='0'
894- if [ -z "$resolver_ipset" ]; then
895- uci set "$packageName".config.resolver_ipset='dnsmasq.ipset'
896- fi
897- uci commit "$packageName"
898- fi
899- if [ -n "$src_ipset" ] && [ "$src_ipset" != "0" ] && [ "$src_ipset" != "1" ]; then
900- uci set "$packageName".config.src_ipset='1'
901- uci commit "$packageName"
902- fi
903- if [ -z "$(uci -q get $packageName.config.webui_supported_protocol)" ]; then
904- uci add_list "$packageName".config.webui_supported_protocol='tcp'
905- uci add_list "$packageName".config.webui_supported_protocol='udp'
906- uci add_list "$packageName".config.webui_supported_protocol='tcp udp'
907- uci add_list "$packageName".config.webui_supported_protocol='icmp'
908- uci add_list "$packageName".config.webui_supported_protocol='all'
909- uci commit "$packageName"
910- fi
911- for i in append_local_rules append_src_rules \
912- append_remote_rules append_dest_rules; do
913- if [ -n "$(uci -q get $packageName.config.$i)" ]; then
914- warningSummary="${warningSummary}$_WARNING_: $i setting is not supported in ${serviceName}.\\n"
915- fi
916- done
917- for i in udp_proto_enabled forward_chain_enabled input_chain_enabled \
918- output_chain_enabled iprule_enabled; do
919- if [ "$(uci -q get $packageName.config.$i)" = "1" ]; then
920- warningSummary="${warningSummary}$_WARNING_: $i setting is not supported in ${serviceName}.\\n"
921- fi
922- done
923-}
924-
925-check_config(){ local en; config_get_bool en "$1" 'enabled' 1; [ "$en" -gt 0 ] && _cfg_enabled=0; }
926-is_config_enabled(){
927- local cfg="$1" _cfg_enabled=1
928- [ -n "$1" ] || return 1
929- config_load "$packageName"
930- config_foreach check_config "$cfg"
931- return "$_cfg_enabled"
932-}
933-
934-process_user_file(){
935- local path enabled shellBin="${SHELL:-/bin/ash}"
936- config_get_bool enabled "$1" 'enabled' 1
937- config_get path "$1" 'path'
938- [ "$enabled" -gt 0 ] || return 0
939- if [ ! -s "$path" ]; then
940- errorSummary="${errorSummary}${_ERROR_}: Custom user file '$path' not found or empty!\\n"
941- output_fail
942- return 1
943- fi
944- if ! $shellBin -n "$path"; then
945- errorSummary="${errorSummary}${_ERROR_}: Syntax error in custom user file '$path'!\\n"
946- output_fail
947- return 1
948- fi
949- output 2 "Running $path "
950-# shellcheck disable=SC1090
951- if ! . "$path"; then
952- errorSummary="${errorSummary}${_ERROR_}: Error running custom user file '$path'!\\n"
953- if grep -q -w 'curl' "$path" && ! is_present 'curl'; then
954- errorSummary="${errorSummary}${_ERROR_}: Use of 'curl' is detected in custom user file '$path', but 'curl' isn't installed!\\n"
955- errorSummary="${errorSummary}${_ERROR_}: If 'curl' is needed, install it with 'opkg update; opkg install curl;' command in CLI.\\n"
956- fi
957- output_fail
958- return 1
959- else
960- output_ok
961- return 0
962- fi
963-}
964-
965-boot() { rc_procd start_service && rc_procd service_triggers; }
966-
967-start_service() {
968- local dnsmasqStoredHash dnsmasqNewHash i modprobeStatus=0 reloadedIface="$1"
969- convert_config
970- is_enabled 'on_start' || return 1
971- is_wan_up || return 1
972-
973- iptables -t 'mangle' --list 'VPR_PREROUTING' >/dev/null 2>&1 || unset reloadedIface
974- [ -n "$(tmpfs get gateway)" ] || unset reloadedIface
975-
976- if [ -s "$dnsmasqFile" ]; then
977- dnsmasqStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
978- rm -f "$dnsmasqFile"
979- fi
980-
981- for i in xt_set ip_set ip_set_hash_ip; do
982- modprobe "$i" >/dev/null 2>/dev/null || modprobeStatus=$((modprobeStatus + 1))
983- done
984-
985- if [ "$modprobeStatus" -gt 0 ] && ! is_chaos_calmer; then
986- errorSummary="${errorSummary}${_ERROR_}: Failed to load kernel modules\\n"
987- fi
988-
989- if [ -z "$reloadedIface" ]; then
990- for i in $usedChainsList; do
991- ipt -t mangle -N "VPR_${i}"
992- ipt -t mangle "$insertOption" "$i" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
993- done
994- fi
995-
996- if [ -z "$reloadedIface" ]; then
997- output 1 'Processing Interfaces '
998- config_load 'network'; config_foreach process_interface 'interface' 'create';
999- process_tor_interface 'tor' 'destroy'; is_tor_running && process_tor_interface 'tor' 'create';
1000- output 1 '\n'
1001- if is_config_enabled 'policy'; then
1002- output 1 'Processing Policies '
1003- config_load "$packageName"; config_foreach process_policy 'policy' "$reloadedIface";
1004- output 1 '\n'
1005- fi
1006- if is_config_enabled 'include'; then
1007- output 1 'Processing User File(s) '
1008- config_load "$packageName"; config_foreach process_user_file 'include';
1009- output 1 '\n'
1010- fi
1011- else
1012- output 1 "Reloading Interface: $reloadedIface "
1013- config_load 'network'; config_foreach process_interface 'interface' 'create' "$reloadedIface";
1014- output 1 '\n'
1015- fi
1016-
1017- if [ -s "$dnsmasqFile" ]; then
1018- dnsmasqNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')"
1019- fi
1020- [ "$dnsmasqNewHash" != "$dnsmasqStoredHash" ] && dnsmasq_restart
1021-
1022- if [ -z "$gatewaySummary" ]; then
1023- errorSummary="${errorSummary}${_ERROR_}: failed to set up any gateway!\\n"
1024- fi
1025- procd_open_instance "main"
1026- procd_set_param command /bin/true
1027- procd_set_param stdout 1
1028- procd_set_param stderr 1
1029- procd_open_data
1030- json_add_array 'status'
1031- json_add_object ''
1032- [ -n "$gatewaySummary" ] && json_add_string gateway "$gatewaySummary"
1033- [ -n "$errorSummary" ] && json_add_string error "$errorSummary"
1034- [ -n "$warningSummary" ] && json_add_string warning "$warningSummary"
1035- if [ "$strictMode" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then
1036- json_add_string mode "strict"
1037- fi
1038- json_close_object
1039- json_close_array
1040- procd_close_data
1041- procd_close_instance
1042-}
1043-
1044-tmpfs() {
1045- local action="$1" param="$2" value="$3"
1046-# shellcheck disable=SC2034
1047- local gateway error warning mode i
1048- if [ -s "$jsonFile" ]; then
1049- json_load_file "$jsonFile" 2>/dev/null
1050- json_select 'status' 2>/dev/null
1051- for i in gateway error warning mode; do
1052- json_get_var $i "$i" 2>/dev/null
1053- done
1054- fi
1055- case "$action" in
1056- get)
1057- printf "%b" "$(eval echo "\$$param")"; return;;
1058- add)
1059- eval "$param"='$(eval echo "\$$param")${value}';;
1060- del)
1061- case "$param" in
1062- all)
1063- unset gateway error warning mode;;
1064- *)
1065- unset "$param";;
1066- esac
1067- ;;
1068- set)
1069- eval "$param"='$value';;
1070- esac
1071- json_init
1072- json_add_object 'status'
1073- json_add_string version "$PKG_VERSION"
1074- for i in gateway error warning mode; do
1075- json_add_string "$i" "$(eval echo "\$$i")"
1076- done
1077- json_close_object
1078- json_dump > "$jsonFile"
1079- sync
1080-}
1081-
1082-service_started() {
1083- tmpfs set 'gateway' "$gatewaySummary"
1084- tmpfs set 'error' "$errorSummary"
1085- tmpfs set 'warning' "$warningSummary"
1086- if [ "$strictMode" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then
1087- tmpfs set 'mode' 'strict'
1088- fi
1089- [ -n "$gatewaySummary" ] && output "$serviceName started with gateways:\\n${gatewaySummary}"
1090- [ -n "$errorSummary" ] && output "${errorSummary}"
1091- [ -n "$warningSummary" ] && output "${warningSummary}"
1092- if [ -n "$errorSummary" ]; then
1093- return 2
1094- elif [ -n "$warningSummary" ]; then
1095- return 1
1096- else
1097- return 0
1098- fi
1099-}
1100-
1101-stop_service() {
1102- local i
1103- iptables -t mangle -L | grep -q VPR_PREROUTING || return 0
1104- load_package_config
1105- for i in PREROUTING FORWARD INPUT OUTPUT; do
1106- ipt -t mangle -D "${i}" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}"
1107- ipt -t mangle -F "VPR_${i}"; ipt -t mangle -X "VPR_${i}";
1108- done
1109- config_load 'network'; config_foreach process_interface 'interface' 'destroy';
1110- process_tor_interface 'tor' 'destroy'
1111- unset ifaceTableID; unset ifaceMark;
1112- if [ -s "$dnsmasqFile" ]; then
1113- rm -f "$dnsmasqFile"
1114- dnsmasq_restart
1115- fi
1116- if [ "$serviceEnabled" -ne 0 ]; then
1117- output "$serviceName stopped "; output_okn;
1118- fi
1119-}
1120-
1121-reload_interface() { rc_procd start_service "$1"; }
1122-
1123-service_triggers() {
1124- local n
1125- is_enabled || return 1
1126-
1127- if [ "$procdReloadDelay" -gt 0 ] && [ "$procdReloadDelay" -lt 100 ]; then
1128-# shellcheck disable=SC2034
1129- PROCD_RELOAD_DELAY=$(( procdReloadDelay * 1000 ))
1130- fi
1131-
1132- procd_open_validate
1133- validate_config
1134- validate_policy
1135- validate_include
1136- procd_close_validate
1137-
1138- procd_open_trigger
1139- procd_add_reload_trigger 'openvpn'
1140- if type procd_add_service_trigger 1>/dev/null 2>&1; then
1141- procd_add_service_trigger "service.restart" "firewall" /etc/init.d/${packageName} reload
1142- fi
1143- procd_add_config_trigger "config.change" "${packageName}" /etc/init.d/${packageName} reload
1144- for n in $ifSupported; do
1145- procd_add_interface_trigger "interface.*" "$n" /etc/init.d/${packageName} reload_interface "$n"
1146- done
1147- procd_close_trigger
1148-
1149- output 3 "$serviceName monitoring interfaces: $ifSupported"; output_okn;
1150-}
1151-
1152-status_service() { support "$@"; }
1153-support() {
1154- local dist vers out id s param status set_d set_p tableCount i=0 dev dev6 j
1155- readonly _SEPARATOR_='============================================================'
1156- is_enabled
1157-
1158- json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
1159- if [ -n "$wanIface4" ]; then
1160- network_get_gateway wanGW4 "$wanIface4"
1161- [ -z "$dev" ] && dev="$(uci -q get network."${wanIface4}".ifname)"
1162- [ -z "$dev" ] && dev="$(uci -q get network."${wanIface4}".device)"
1163- fi
1164- if [ -n "$wanIface6" ]; then
1165- [ -z "$dev6" ] && dev6="$(uci -q get network."${wanIface6}".ifname)"
1166- [ -z "$dev6" ] && dev6="$(uci -q get network."${wanIface6}".device)"
1167- wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
1168- [ "$wanGW6" = "default" ] && wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
1169- fi
1170- while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
1171- [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
1172- status="$serviceName running on $dist $vers."
1173- [ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
1174- [ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
1175- {
1176- echo "$status"
1177- echo "$_SEPARATOR_"
1178- dnsmasq --version 2>/dev/null | sed '/^$/,$d'
1179- if [ -n "$1" ]; then
1180- echo "$_SEPARATOR_"
1181- echo "Resolving domains"
1182- for i in $1; do
1183- echo "$i: $(resolveip "$i" | tr '\n' ' ')"
1184- done
1185- fi
1186-
1187- echo "$_SEPARATOR_"
1188- echo "Routes/IP Rules"
1189- tableCount=$(ip rule list | grep -c 'fwmark') || tableCount=0
1190- if [ -n "$set_d" ]; then route; else route | grep '^default'; fi
1191- if [ -n "$set_d" ]; then ip rule list; fi
1192- i=0; while [ $i -lt $tableCount ]; do
1193- echo ""
1194- echo "IPv4 Table $((wanTableID + i)): $(ip -4 route show table $((wanTableID + i)))"
1195- echo "IPv4 Table $((wanTableID + i)) Rules:"
1196- ip -4 rule list table "$((wanTableID + i))"
1197- i=$((i + 1))
1198- done
1199-
1200- if [ "$ipv6Enabled" -ne 0 ]; then
1201- i=0; while [ $i -lt $tableCount ]; do
1202- ip -6 route show table $((wanTableID + i)) | while read -r param; do
1203- echo "IPv6 Table $((wanTableID + i)): $param"
1204- done
1205- i=$((i + 1))
1206- done
1207- fi
1208-
1209- for j in Mangle NAT; do
1210- if [ -z "$set_d" ]; then
1211- for i in $usedChainsList; do
1212- if iptables -v -t "$(str_to_lower $j)" -S "VPR_${i}" 1>/dev/null 2>&1; then
1213- echo "$_SEPARATOR_"
1214- echo "$j IP Table: $i"
1215- iptables -v -t "$(str_to_lower $j)" -S "VPR_${i}"
1216- if [ "$ipv6Enabled" -ne 0 ]; then
1217- echo "$_SEPARATOR_"
1218- echo "$j IPv6 Table: $i"
1219- ip6tables -v -t "$(str_to_lower $j)" -S "VPR_${i}"
1220- fi
1221- fi
1222- done
1223- else
1224- echo "$_SEPARATOR_"
1225- echo "$j IP Table"
1226- iptables -L -t "$(str_to_lower $j)"
1227- if [ "$ipv6Enabled" -ne 0 ]; then
1228- echo "$_SEPARATOR_"
1229- echo "$j IPv6 Table"
1230- ip6tables -L -t "$(str_to_lower $j)"
1231- fi
1232- fi
1233- i=0; ifaceMark="$wanMark";
1234- while [ $i -lt $tableCount ]; do
1235- if iptables -v -t "$(str_to_lower $j)" -S "VPR_MARK${ifaceMark}" 1>/dev/null 2>&1; then
1236- echo "$_SEPARATOR_"
1237- echo "$j IP Table MARK Chain: VPR_MARK${ifaceMark}"
1238- iptables -v -t "$(str_to_lower $j)" -S "VPR_MARK${ifaceMark}"
1239- ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))";
1240- fi
1241- i=$((i + 1))
1242- done
1243- done
1244-
1245- echo "$_SEPARATOR_"
1246- echo "Current ipsets"
1247- ipset save
1248- if [ -s "$dnsmasqFile" ]; then
1249- echo "$_SEPARATOR_"
1250- echo "DNSMASQ ipsets"
1251- cat "$dnsmasqFile"
1252- fi
1253- echo "$_SEPARATOR_"
1254- } | tee -a /var/${packageName}-support
1255- if [ -n "$set_p" ]; then
1256- printf "%b" "Pasting to paste.ee... "
1257- if is_present 'curl' && is_variant_installed 'libopenssl' && is_installed 'ca-bundle'; then
1258- json_init; json_add_string "description" "${packageName}-support"
1259- json_add_array "sections"; json_add_object '0'
1260- json_add_string "name" "$(uci -q get system.@system[0].hostname)"
1261- json_add_string "contents" "$(cat /var/${packageName}-support)"
1262- json_close_object; json_close_array; payload=$(json_dump)
1263- out=$(curl -s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload")
1264- json_load "$out"; json_get_var id id; json_get_var s success
1265- [ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" || printf "%b" "$__FAIL__\\n"
1266- [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
1267- else
1268- printf "%b" "$__FAIL__\\n"
1269- printf "%b" "$_ERROR_: curl, libopenssl or ca-bundle were not found!\\nRun 'opkg update; opkg install curl libopenssl ca-bundle' to install them.\\n"
1270- fi
1271- else
1272- printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n"
1273- fi
1274-}
1275-
1276-# shellcheck disable=SC2120
1277-validate_config() {
1278- uci_validate_section "${packageName}" config "${1}" \
1279- 'enabled:bool:0' \
1280- 'strict_enforcement:bool:1' \
1281- 'ipv6_enabled:bool:0' \
1282- 'src_ipset:bool:0' \
1283- 'dest_ipset:bool:0' \
1284- 'resolver_ipset::or("", "none", "dnsmasq.ipset")' \
1285- 'verbosity:range(0,2):1' \
1286- 'wan_tid:integer:201' \
1287- 'wan_fw_mark:hex(8)' \
1288- 'fw_mask:hex(8)' \
1289- 'icmp_interface:string' \
1290- 'ignored_interface:list(string)' \
1291- 'supported_interface:list(string)' \
1292- 'boot_timeout:integer:30' \
1293- 'iptables_rule_option:or("", "append", "insert")' \
1294- 'procd_reload_delay:integer:0' \
1295- 'webui_enable_column:bool:0' \
1296- 'webui_protocol_column:bool:0' \
1297- 'webui_supported_protocol:list(string)' \
1298- 'webui_chain_column:bool:0' \
1299- 'webui_sorting:bool:1' \
1300- 'webui_show_ignore_target:bool:0'
1301-}
1302-
1303-# shellcheck disable=SC2120
1304-validate_policy() {
1305- uci_validate_section "${packageName}" policy "${1}" \
1306- 'name:string' \
1307- 'enabled:bool:0' \
1308- 'interface:network' \
1309- 'proto:or(string)' \
1310- 'chain:or("", "PREROUTING", "FORWARD", "INPUT", "OUTPUT")' \
1311- 'src_addr:list(neg(or(host,network,macaddr)))' \
1312- 'src_port:list(neg(or(portrange, string)))' \
1313- 'dest_addr:list(neg(host))' \
1314- 'dest_port:list(neg(or(portrange, string)))'
1315-}
1316-
1317-# shellcheck disable=SC2120
1318-validate_include() {
1319- uci_validate_section "${packageName}" include "${1}" \
1320- 'path:string' \
1321- 'enabled:bool:0'
1322-}
--- a/net/vpn-policy-routing/files/vpn-policy-routing.netflix.user
+++ /dev/null
@@ -1,37 +0,0 @@
1-#!/bin/sh
2-# This file is heavily based on code from https://github.com/Xentrk/netflix-vpn-bypass/blob/master/IPSET_Netflix.sh
3-# Credits to https://forum.openwrt.org/u/dscpl for api.hackertarget.com code.
4-# Credits to https://github.com/kkeker and https://github.com/tophirsch for api.bgpview.io code.
5-
6-TARGET_IPSET='wan'
7-TARGET_ASN='2906'
8-TARGET_FNAME="/var/vpn-policy-routing_tmp_AS${TARGET_ASN}"
9-#DB_SOURCE='ipinfo.io'
10-#DB_SOURCE='api.hackertarget.com'
11-DB_SOURCE='api.bgpview.io'
12-
13-_ret=1
14-
15-if [ ! -s "$TARGET_FNAME" ]; then
16- if [ "$DB_SOURCE" = "ipinfo.io" ]; then
17- TARGET_URL="https://ipinfo.io/AS${TARGET_ASN}"
18- curl "$TARGET_URL" 2>/dev/null | grep -E "a href.*${TARGET_ASN}\/" | grep -v ":" | sed "s/^.*<a href=\"\/AS${TARGET_ASN}\///; s/\" >//" > "$TARGET_FNAME"
19- fi
20-
21- if [ "$DB_SOURCE" = "api.hackertarget.com" ]; then
22- TARGET_URL="https://api.hackertarget.com/aslookup/?q=AS${TARGET_ASN}"
23- curl "$TARGET_URL" 2>/dev/null | sed '1d' > "$TARGET_FNAME"
24- fi
25-
26- if [ "$DB_SOURCE" = "api.bgpview.io" ]; then
27- TARGET_URL="https://api.bgpview.io/asn/${TARGET_ASN}/prefixes"
28- curl -s "$TARGET_URL" 2>/dev/null | jsonfilter -e '@.data.ipv4_prefixes[*].prefix' > "$TARGET_FNAME"
29- fi
30-fi
31-
32-if [ -s "$TARGET_FNAME" ]; then
33- awk -v ipset="$TARGET_IPSET" '{print "add " ipset " " $1}' "$TARGET_FNAME" | ipset restore -! && _ret=0
34-fi
35-rm -f "$TARGET_FNAME"
36-
37-return $_ret
--- a/net/vpnbypass/Makefile
+++ /dev/null
@@ -1,69 +0,0 @@
1-# Copyright 2017-2018 Stan Grishin (stangri@melmac.net)
2-# This is free software, licensed under the GNU General Public License v3.
3-
4-include $(TOPDIR)/rules.mk
5-
6-PKG_NAME:=vpnbypass
7-PKG_VERSION:=1.3.2
8-PKG_RELEASE:=1
9-PKG_LICENSE:=GPL-3.0-or-later
10-PKG_MAINTAINER:=Stan Grishin <stangri@melmac.net>
11-
12-include $(INCLUDE_DIR)/package.mk
13-
14-define Package/vpnbypass
15- SECTION:=net
16- CATEGORY:=Network
17- TITLE:=VPN Bypass Service
18- URL:=https://docs.openwrt.melmac.net/vpnbypass/
19- DEPENDS:=+ipset +iptables
20- PKGARCH:=all
21-endef
22-
23-define Package/vpnbypass/description
24-This service can be used to enable simple VPN split tunnelling.
25-Supports accessing domains, IP ranges outside of your VPN tunnel.
26-Also supports dedicating local ports/IP ranges for direct
27-internet access (outside of your VPN tunnel).
28-Please see the README for further information.
29-endef
30-
31-define Package/vpnbypass/conffiles
32-/etc/config/vpnbypass
33-endef
34-
35-define Build/Configure
36-endef
37-
38-define Build/Compile
39-endef
40-
41-define Package/vpnbypass/install
42- $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/etc/hotplug.d/firewall
43- $(INSTALL_BIN) ./files/vpnbypass.init $(1)/etc/init.d/vpnbypass
44- $(SED) "s|^\(PKG_VERSION\).*|\1='$(PKG_VERSION)-$(PKG_RELEASE)'|" $(1)/etc/init.d/vpnbypass
45- $(INSTALL_CONF) ./files/vpnbypass.config $(1)/etc/config/vpnbypass
46- $(INSTALL_DATA) ./files/vpnbypass.hotplug $(1)/etc/hotplug.d/firewall/94-vpnbypass
47-endef
48-
49-define Package/vpnbypass/postinst
50- #!/bin/sh
51- # check if we are on real system
52- if [ -z "$${IPKG_INSTROOT}" ]; then
53- /etc/init.d/vpnbypass enable
54- fi
55- exit 0
56-endef
57-
58-define Package/vpnbypass/prerm
59- #!/bin/sh
60- # check if we are on real system
61- if [ -z "$${IPKG_INSTROOT}" ]; then
62- echo "Stopping service and removing rc.d symlink for vpnbypass"
63- /etc/init.d/vpnbypass stop || true
64- /etc/init.d/vpnbypass disable || true
65- fi
66- exit 0
67-endef
68-
69-$(eval $(call BuildPackage,vpnbypass))
--- a/net/vpnbypass/files/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
1-# README
2-
3-README has been moved to [https://docs.openwrt.melmac.net/vpnbypass/](https://docs.openwrt.melmac.net/vpnbypass/).
--- a/net/vpnbypass/files/vpnbypass.config
+++ /dev/null
@@ -1,5 +0,0 @@
1-config vpnbypass 'config'
2- option enabled '0'
3- list localport '32400'
4- list localsubnet '192.168.1.81/29'
5- list remotesubnet '25.0.0.0/8'
--- a/net/vpnbypass/files/vpnbypass.hotplug
+++ /dev/null
@@ -1,2 +0,0 @@
1-#!/bin/sh
2-[ "$ACTION" = "reload" ] && /etc/init.d/vpnbypass reload
--- a/net/vpnbypass/files/vpnbypass.init
+++ /dev/null
@@ -1,146 +0,0 @@
1-#!/bin/sh /etc/rc.common
2-# Copyright 2017-2020 Stan Grishin (stangri@melmac.net)
3-# shellcheck disable=SC2039,SC1091,SC2086,SC3043,SC3057,SC3060
4-PKG_VERSION='dev-test'
5-
6-# shellcheck disable=SC2034
7-START=94
8-# shellcheck disable=SC2034
9-USE_PROCD=1
10-
11-if type extra_command 1>/dev/null 2>&1; then
12- extra_command 'version' 'Show version information'
13-else
14-# shellcheck disable=SC2034
15- EXTRA_COMMANDS='version'
16-fi
17-
18-version() { echo "$PKG_VERSION"; }
19-
20-readonly __ERROR__='\033[0;31mERROR\033[0m'
21-
22-# shellcheck disable=SC2034
23-serviceEnabled=0
24-verbosity=2
25-TID='200'
26-IPSET='vpnbypass'
27-FW_MARK='0x010000'
28-FW_MASK='0xff0000'
29-wan_if4=''
30-wan_gw=''
31-
32-readonly packageName='vpnbypass'
33-readonly serviceName="$packageName $PKG_VERSION"
34-readonly sharedMemoryOutput="/dev/shm/$packageName-output"
35-
36-output() {
37-# Can take a single parameter (text) to be output at any verbosity
38-# Or target verbosity level and text to be output at specifc verbosity
39- local msg memmsg logmsg
40- if [ $# -ne 1 ]; then
41- if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; else return 0; fi
42- fi
43- [ -t 1 ] && printf "%b" "$1"
44- msg="${1//$serviceName /service }";
45- if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then
46- [ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
47- logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
48- logger -t "${packageName:-service} [$$]" "$(printf "%b" "$logmsg")"
49- rm -f "$sharedMemoryOutput"
50- else
51- printf "%b" "$msg" >> "$sharedMemoryOutput"
52- fi
53-}
54-load_package_config() {
55- config_load "$packageName"
56- config_get_bool serviceEnabled 'config' 'enabled' 1
57- config_get verbosity 'config' 'verbosity' '2'
58- if [ -z "${verbosity##*[!0-9]*}" ] || [ "$verbosity" -lt 0 ] || [ "$verbosity" -gt 2 ]; then
59- verbosity=1
60- fi
61- . /lib/functions/network.sh
62-}
63-
64-is_enabled() {
65- local sleepCount=1
66- load_package_config
67- while : ; do
68- network_find_wan wan_if4
69- [ "$serviceEnabled" -gt 0 ] || return 1
70- [ -n "$wan_if4" ] && network_get_gateway wan_gw "$wan_if4"
71- if [ $sleepCount -ge 25 ] || [ -n "$wan_gw" ]; then break; fi
72- output "$serviceName waiting for wan gateway...\\n"
73- sleep 2; network_flush_cache; sleepCount=$((sleepCount+1));
74- done
75- [ -n "$wan_gw" ] && return 0
76- output "$__ERROR__: $serviceName failed to discover WAN gateway.\\n"; return 1;
77-}
78-
79-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; }
80-is_wan() { if [ -n "$wan_if4" ] && [ "$1" = "$wan_if4" ]; then return 0; else return 1; fi; }
81-is_supported_interface() { if is_wan "$1" || is_ovpn "$1"; then return 0; else return 1; fi; }
82-
83-ipt() {
84- local d;
85- d="${*//-A/-D}"; [ "$d" != "$*" ] && iptables $d >/dev/null 2>&1
86- d="${*//-I/-D}"; [ "$d" != "$*" ] && iptables $d >/dev/null 2>&1
87- d="${*//-N/-F}"; [ "$d" != "$*" ] && iptables $d >/dev/null 2>&1
88- d="${*//-N/-X}"; [ "$d" != "$*" ] && iptables $d >/dev/null 2>&1
89- d="$*"; iptables $d >/dev/null 2>&1 || output "\\n$__ERROR__: iptables $d\\n"
90-}
91-
92-start_service() {
93- local ll lports rports routes ranges
94- is_enabled || return 1
95- config_get lports 'config' 'localport'
96- config_get rports 'config' 'remoteport'
97- config_get routes 'config' 'remotesubnet'
98- config_get ranges 'config' 'localsubnet'
99-
100- procd_open_instance "main"
101- procd_set_param command /bin/true
102- procd_set_param stdout 1
103- procd_set_param stderr 1
104- procd_close_instance
105-
106- ip rule del fwmark "$FW_MARK" table "$TID" >/dev/null 2>&1;
107- ipset -q flush "$IPSET"; ipset -q destroy "$IPSET";
108- ip route flush table "$TID"; ip route flush cache;
109- ip route add default via "$wan_gw" table "$TID"; ip route flush cache;
110- ip rule add fwmark "$FW_MARK" table "$TID"
111- ipset -q -exist create "$IPSET" hash:ip; ipset -q flush "$IPSET"
112- { modprobe xt_set; modprobe ip_set; modprobe ip_set_hash_ip; } >/dev/null 2>&1
113- ipt -t mangle -D PREROUTING -m mark --mark 0x00/${FW_MASK} -g VPNBYPASS >/dev/null 2>&1
114- { ipt -t mangle -N VPNBYPASS; ipt -t mangle -A PREROUTING -m mark --mark 0x00/${FW_MASK} -g VPNBYPASS; } >/dev/null 2>&1
115- ipt -t mangle -A VPNBYPASS -m set --match-set $IPSET dst -j MARK --set-mark ${FW_MARK}/${FW_MASK} >/dev/null 2>&1
116- for ll in ${ranges}; do ipt -t mangle -A VPNBYPASS -j MARK --set-mark ${FW_MARK}/${FW_MASK} -s "$ll"; done
117- 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
118- for ll in ${routes}; do ipt -t mangle -A VPNBYPASS -j MARK --set-mark ${FW_MARK}/${FW_MASK} -d "$ll"; done
119- 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
120- output "$serviceName started with TID: $TID; FW_MARK: $FW_MARK\\n"
121-}
122-
123-stop_service() {
124- load_package_config
125- ip rule del fwmark "$FW_MARK" table "$TID" >/dev/null 2>&1;
126- ipset -q flush "$IPSET"; ipset -q destroy "$IPSET";
127- ip route flush table "$TID"; ip route flush cache;
128- ipt -t mangle -D PREROUTING -m mark --mark 0x00/${FW_MASK} -g VPNBYPASS >/dev/null 2>&1
129- { ipt -t mangle -F VPNBYPASS; ipt -t mangle -X VPNBYPASS; } >/dev/null 2>&1
130- output "$serviceName stopped\\n"
131-}
132-
133-service_triggers_load_interface() { is_supported_interface "$1" && ifaces="${ifaces}${1} "; }
134-service_triggers() {
135- local ifaces n
136- config_load network; config_foreach service_triggers_load_interface 'interface';
137- procd_open_trigger
138- procd_add_reload_trigger 'openvpn'
139- if type procd_add_service_trigger 1>/dev/null 2>&1; then
140- procd_add_service_trigger "service.restart" "firewall" /etc/init.d/${packageName} reload
141- fi
142- procd_add_config_trigger "config.change" "${packageName}" /etc/init.d/${packageName} reload
143- for n in $ifaces; do procd_add_reload_interface_trigger "$n"; procd_add_interface_trigger "interface.*" "$n" /etc/init.d/vpnbypass reload; done;
144- output "$serviceName monitoring interfaces: $ifaces\\n"
145- procd_close_trigger
146-}
--- a/net/vpnbypass/test.sh
+++ /dev/null
@@ -1,3 +0,0 @@
1-#!/bin/sh
2-
3-/etc/init.d/"$1" version 2>&1 | grep "$2"
Show on old repository browser