Community maintained packages for ImmortalWrt.
Revision | 47eca64cb82a5459668fc14df9422e365c8343b6 (tree) |
---|---|
Time | 2022-12-03 11:30:29 |
Author | Stan Grishin <stangri@melm...> |
Commiter | Stan Grishin |
pbr: initial commit
* The makefile produces the nft and iptables capable pbr package
* This replaces vpnbypass and vpn-policy-routing packages
* I'm soliciting feedback on this package and my intention is to
Signed-off-by: Stan Grishin <stangri@melmac.ca>
@@ -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)) |
@@ -0,0 +1,3 @@ | ||
1 | +# README | |
2 | + | |
3 | +README is available at [https://docs.openwrt.melmac.net/pbr/](https://docs.openwrt.melmac.net/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' |
@@ -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' |
@@ -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 |
@@ -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 |
@@ -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 | +} |
@@ -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 |
@@ -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 |
@@ -0,0 +1 @@ | ||
1 | +jump pbr_forward comment "Jump into pbr forward chain"; |
@@ -0,0 +1 @@ | ||
1 | +jump pbr_input comment "Jump into pbr input chain"; |
@@ -0,0 +1 @@ | ||
1 | +jump pbr_output comment "Jump into pbr output chain"; |
@@ -0,0 +1 @@ | ||
1 | +jump pbr_postrouting comment "Jump into pbr postrouting chain"; |
@@ -0,0 +1 @@ | ||
1 | +jump pbr_prerouting comment "Jump into pbr prerouting chain"; |
@@ -0,0 +1,5 @@ | ||
1 | +chain pbr_forward {} | |
2 | +chain pbr_input {} | |
3 | +chain pbr_output {} | |
4 | +chain pbr_prerouting {} | |
5 | +chain pbr_postrouting {} |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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)) |
@@ -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/). |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 | -} |
@@ -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 |
@@ -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)) |
@@ -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/). |
@@ -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' |
@@ -1,2 +0,0 @@ | ||
1 | -#!/bin/sh | |
2 | -[ "$ACTION" = "reload" ] && /etc/init.d/vpnbypass reload |
@@ -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 | -} |
@@ -1,3 +0,0 @@ | ||
1 | -#!/bin/sh | |
2 | - | |
3 | -/etc/init.d/"$1" version 2>&1 | grep "$2" |