1 #!/bin/sh /etc/rc.common
2 # Copyright 2020-2023 MOSSDeF, Stan Grishin (stangri@melmac.ca)
3 # shellcheck disable=SC2018,SC2019,SC3043,SC3057,SC3060
5 # sysctl net.ipv4.conf.default.rp_filter=1
6 # sysctl net.ipv4.conf.all.rp_filter=1
8 # shellcheck disable=SC2034
10 # shellcheck disable=SC2034
14 # Copyright 2023 MOSSDeF, Stan Grishin (stangri@melmac.ca)
15 # shellcheck disable=SC2018,SC2019,SC2034,SC3043,SC3057,SC3060
17 readonly packageName
='pbr'
18 readonly PKG_VERSION
='dev-test'
19 readonly serviceName
="$packageName $PKG_VERSION"
20 readonly serviceTrapSignals
='exit SIGHUP SIGQUIT SIGKILL'
21 readonly packageConfigFile
="/etc/config/${packageName}"
22 readonly packageLockFile
="/var/run/${packageName}.lock"
23 readonly dnsmasqFileDefault
="/var/dnsmasq.d/${packageName}"
24 readonly _OK_
='\033[0;32m\xe2\x9c\x93\033[0m'
25 readonly __OK__
='\033[0;32m[\xe2\x9c\x93]\033[0m'
26 readonly _OKB_
='\033[1;34m\xe2\x9c\x93\033[0m'
27 readonly __OKB__
='\033[1;34m[\xe2\x9c\x93]\033[0m'
28 readonly _FAIL_
='\033[0;31m\xe2\x9c\x97\033[0m'
29 readonly __FAIL__
='\033[0;31m[\xe2\x9c\x97]\033[0m'
30 readonly _ERROR_
='\033[0;31mERROR\033[0m'
31 readonly _WARNING_
='\033[0;33mWARNING\033[0m'
32 readonly ip_full
='/usr/libexec/ip-full'
33 # shellcheck disable=SC2155
34 readonly ip_bin
="$(command -v ip)"
35 readonly ipTablePrefix
='pbr'
36 # shellcheck disable=SC2155
37 readonly iptables
="$(command -v iptables)"
38 # shellcheck disable=SC2155
39 readonly ip6tables
="$(command -v ip6tables)"
40 # shellcheck disable=SC2155
41 readonly ipset
="$(command -v ipset)"
42 readonly ipsPrefix
='pbr'
43 readonly iptPrefix
='PBR'
44 # shellcheck disable=SC2155
45 readonly agh
="$(command -v AdGuardHome)"
46 readonly aghConfigFile
='/etc/adguardhome.yaml'
47 readonly aghIpsetFile
="/var/run/${packageName}.adguardhome.ipsets"
48 # shellcheck disable=SC2155
49 readonly nft
="$(command -v nft)"
50 readonly nftIPv4Flag
='ip'
51 readonly nftIPv6Flag
='ip6'
52 readonly nftTempFile
="/var/run/${packageName}.nft"
53 readonly nftPermFile
="/usr/share/nftables.d/ruleset-post/30-${packageName}.nft"
54 readonly nftPrefix
='pbr'
55 readonly nftTable
='fw4'
56 readonly chainsList
='forward input output postrouting prerouting'
57 readonly ssConfigFile
='/etc/shadowsocks'
58 readonly torConfigFile
='/etc/tor/torrc'
59 readonly xrayIfacePrefix
='xray_'
60 readonly rtTablesFile
='/etc/iproute2/rt_tables'
62 # package config options
75 procd_wan_ignore_status
=
85 wan_ip_rules_priority
=
89 nft_set_flags_interval
=
90 nft_set_flags_timeout
=
91 nft_set_flags_gc_interval
=
113 processPolicyWarning
=
114 resolver_set_supported
=
115 policy_routing_nft_prev_param4
=
116 policy_routing_nft_prev_param6
=
121 # shellcheck disable=SC1091
123 # shellcheck disable=SC1091
124 .
/lib
/functions
/network.sh
125 # shellcheck disable=SC1091
126 .
/usr
/share
/libubox
/jshn.sh
128 output_ok
() { output
1 "$_OK_"; output
2 "$__OK__\\n"; }
129 output_okn
() { output
1 "$_OK_\\n"; output
2 "$__OK__\\n"; }
130 output_okb
() { output
1 "$_OKB_"; output
2 "$__OKB__\\n"; }
131 output_okbn
() { output
1 "$_OKB_\\n"; output
2 "$__OKB__\\n"; }
132 output_fail
() { output
1 "$_FAIL_"; output
2 "$__FAIL__\\n"; }
133 output_failn
() { output
1 "$_FAIL_\\n"; output
2 "$__FAIL__\\n"; }
134 # shellcheck disable=SC2317
135 str_replace
() { printf "%b" "$1" |
sed -e "s/$(printf "%b
" "$2")/$(printf "%b
" "$3")/g"; }
136 str_replace
() { echo "${1//$2/$3}"; }
137 str_contains
() { [ -n "$1" ] && [ -n "$2" ] && [ "${1//$2}" != "$1" ]; }
138 str_contains_word
() { echo "$1" |
grep -q -w "$2"; }
139 str_to_lower
() { echo "$1" |
tr 'A-Z' 'a-z'; }
140 str_to_upper
() { echo "$1" |
tr 'a-z' 'A-Z'; }
141 str_extras_to_underscore
() { echo "$1" |
tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; }
142 str_extras_to_space
() { echo "$1" |
tr ';{}' ' '; }
143 debug
() { local i j
; for i
in "$@"; do eval "j=\$$i"; echo "${i}: ${j} "; done; }
147 off) verbosity="$
(uci_get
"$packageName" 'config' 'verbosity' '2')";;
151 # Target verbosity level with the first parameter being an integer
154 (*[!0123456789]*) return 1;;
159 local msg memmsg logmsg text
160 local sharedMemoryOutput="/dev
/shm
/$packageName-output"
161 if [ -z "$verbosity" ] && [ -n "$packageName" ]; then
162 verbosity="$
(uci_get
"$packageName" 'config' 'verbosity' '2')"
164 if [ $# -ne 1 ] && is_integer "$1"; then
165 if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; text="$
*"; else return 0; fi
168 [ -t 1 ] && printf "%b
" "$text"
169 msg="${text//$serviceName /service }";
170 if [ "$
(printf "%b" "$msg" |
wc -l)" -gt 0 ]; then
171 [ -s "$sharedMemoryOutput" ] && memmsg="$
(cat "$sharedMemoryOutput")"
172 logmsg="$
(printf "%b" "${memmsg}${msg}" |
sed 's/\x1b\[[0-9;]*m//g')"
173 logger -t "${packageName:-service} [$$
]" "$
(printf "%b" "$logmsg")"
174 rm -f "$sharedMemoryOutput"
176 printf "%b
" "$msg" >> "$sharedMemoryOutput"
180 local iface i param="$2"
182 wan6) iface="$procd_wan6_interface";;
183 wan|*) iface="$procd_wan_interface";;
188 local iface="$2" dev="$3" gw
189 network_get_gateway gw "$iface" true
190 if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
191 # gw="$
(ubus call
"network.interface.${iface}" status | jsonfilter
-e "@.route[0].nexthop")"
192 gw="$
($ip_bin -4 a list dev
"$dev" 2>/dev
/null |
grep inet |
awk '{print $2}' |
awk -F "/" '{print $1}')"
197 local iface="$2" dev="$3" gw
198 network_get_gateway6 gw "$iface" true
199 if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
200 gw="$
($ip_bin -6 a list dev
"$dev" 2>/dev
/null |
grep inet6 |
grep 'scope global' |
awk '{print $2}')"
205 # shellcheck disable=SC2016
206 is_bad_user_file_nft_call() { grep -q '"\
$nft" list' "$1" || grep '"\
$nft" -f' "$1";}
207 is_config_enabled() {
208 _check_config() { local en; config_get_bool en "$1" 'enabled' 1; [ "$en" -gt 0 ] && _cfg_enabled=0; }
209 local cfg="$1" _cfg_enabled=1
210 [ -n "$1" ] || return 1
211 config_load "$packageName"
212 config_foreach _check_config "$cfg"
213 return "$_cfg_enabled"
215 uci_get_device() { uci_get 'network' "$1" 'device' || uci_get 'network' "$1" 'dev'; }
216 uci_get_protocol() { uci_get 'network' "$1" 'proto'; }
217 is_default_dev() { [ "$1" = "$
($ip_bin -4 r |
grep -m1 'dev' |
grep -Eso 'dev [^ ]*' |
awk '{print $2}')" ]; }
218 is_domain() { ! is_ipv6 "$1" && str_contains "$1" '[a-zA-Z]'; }
219 is_dslite() { local p; network_get_protocol p "$1"; [ "${p:0:6}" = "dslite
" ]; }
220 is_family_mismatch() { ( is_ipv4_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_ipv4_netmask "${2//!}" ); }
221 is_greater
() { test "$(printf '%s\n' "$@
" | sort -V | head -n 1)" != "$1"; }
222 is_greater_or_equal
() { test "$(printf '%s\n' "$@
" | sort -V | head -n 1)" = "$2"; }
223 is_ignored_interface
() { str_contains_word
"$ignored_interface" "$1"; }
224 is_ignore_target
() { [ "$(str_to_lower "$1")" = 'ignore' ]; }
227 (*[!0123456789]*) return 1;;
232 is_ipset_type_supported
() { ipset
help hash:"$1" >/dev
/null
2>&1; }
233 is_nft_mode
() { [ -x "$nft" ] && ! str_contains
"$resolver_set" 'ipset' && "$nft" list chains inet |
grep -q "${nftPrefix}_prerouting"; }
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_ipv6_global
() { [ "${1:0:4}" = '2001' ]; }
237 is_ipv6_link_local
() { [ "${1:0:4}" = 'fe80' ]; }
238 is_ipv6_unique_local
() { [ "${1:0:2}" = 'fc' ] || [ "${1:0:2}" = 'fd' ]; }
239 is_list() { str_contains "$1" ',' || str_contains "$1" ' '; }
240 is_ipv4_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; }
241 is_lan() { local d; network_get_device d "$1"; str_contains "$d" 'br-lan'; }
242 is_l2tp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "l2tp
" ]; }
243 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; }
244 is_netifd_table() { grep -q "ip.table.
*$1" /etc/config/network; }
245 is_netifd_table_interface() { local iface="$1"; [ "$
(uci_get
'network' "$iface" 'ip4table')" = "${packageName}_${iface%6}" ]; }
246 is_oc
() { local p
; network_get_protocol p
"$1"; [ "${p:0:11}" = "openconnect" ]; }
247 is_ovpn
() { local d
; uci_get_device d
"$1"; [ "${d:0:3}" = "tun" ] || [ "${d:0:3}" = "tap" ] || [ -f "/sys/devices/virtual/net/${d}/tun_flags" ]; }
248 is_ovpn_valid() { local dev_net dev_ovpn; uci_get_device dev_net "$1"; dev_ovpn="$
(uci_get
'openvpn' "$1" 'dev')"; [ -n "$dev_net" ] && [ -n "$dev_ovpn" ] && [ "$dev_net" = "$dev_ovpn" ]; }
249 is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; }
250 is_present
() { command -v "$1" >/dev
/null
2>&1; }
251 is_service_running
() { if is_nft_mode
; then is_service_running_nft
; else is_service_running_iptables
; fi; }
252 is_service_running_iptables
() { [ -x "$iptables" ] && "$iptables" -t mangle
-L |
grep -q "${iptPrefix}_PREROUTING" >/dev
/null
2>&1; }
253 is_service_running_nft
() { [ -x "$nft" ] && [ -n "$(get_mark_nft_chains)" ]; }
254 is_supported_iface_dev
() { local n dev
; for n
in $ifacesSupported; do network_get_device dev
"$n"; [ "$1" = "$dev" ] && return 0; done; return 1; }
255 is_supported_protocol
() { grep -o '^[^#]*' /etc
/protocols |
grep -w -v '0' |
grep . |
awk '{print $1}' |
grep -q "$1"; }
256 is_pptp
() { local p
; network_get_protocol p
"$1"; [ "${p:0:4}" = "pptp" ]; }
257 is_softether
() { local d
; network_get_device d
"$1"; [ "${d:0:4}" = "vpn_" ]; }
258 is_supported_interface
() { is_lan
"$1" && return 1; str_contains_word
"$supported_interface" "$1" ||
{ ! is_ignored_interface
"$1" && { is_wan
"$1" || is_wan6
"$1" || is_tunnel
"$1"; }; } || is_ignore_target
"$1" || is_xray
"$1"; }
259 is_tailscale
() { local d
; network_get_device d
"$1"; [ "${d:0:9}" = "tailscale" ]; }
260 is_tor
() { [ "$(str_to_lower "$1")" = "tor" ]; }
263 is_ignored_interface
'tor' && return 1
264 [ -s "$torConfigFile" ] ||
return 1
265 json_load
"$(ubus call service list "{ 'name': 'tor' }")" >/dev
/null ||
return 1
266 json_select
'tor' >/dev
/null ||
return 1
267 json_select
'instances' >/dev
/null ||
return 1
268 json_select
'instance1' >/dev
/null ||
return 1
269 json_get_var ret
'running' >/dev
/null ||
return 1
271 if [ "$ret" = "0" ]; then return 1; else return 0; fi
273 is_tunnel
() { is_dslite
"$1" || is_l2tp
"$1" || is_oc
"$1" || is_ovpn
"$1" || is_pptp
"$1" || is_softether
"$1" || is_tailscale
"$1" || is_tor
"$1" || is_wg
"$1"; }
274 is_url
() { is_url_file
"$1" || is_url_dl
"$1"; }
275 is_url_dl
() { is_url_ftp
"$1" || is_url_http
"$1" || is_url_https
"$1"; }
276 is_url_file
() { [ "$1" != "${1#file://}" ];}
277 is_url_ftp
() { [ "$1" != "${1#ftp://}" ];}
278 is_url_http
() { [ "$1" != "${1#http://}" ];}
279 is_url_https
() { [ "$1" != "${1#https://}" ];}
280 is_wan
() { [ "$1" = "$wanIface4" ] ||
{ [ "${1##wan}" != "$1" ] && [ "${1##wan6}" = "$1" ]; } || [ "${1%%wan}" != "$1" ]; }
281 is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1/#wan6}" != "$1" ] || [ "${1/%wan6}" != "$1" ]; }
282 is_wg
() { local p
lp; network_get_protocol p
"$1"; uci_get_listen_port
lp "$1"; [ -z "$lp" ] && [ "${p:0:9}" = "wireguard" ]; }
283 is_xray
() { [ -n "$(get_xray_traffic_port "$1")" ]; }
284 dnsmasq_kill
() { killall
-q -s HUP dnsmasq
; }
285 dnsmasq_restart
() { output
3 'Restarting dnsmasq '; if /etc
/init.d
/dnsmasq restart
>/dev
/null
2>&1; then output_okn
; else output_failn
; fi; }
286 # shellcheck disable=SC2155
287 get_ss_traffic_ports
() { local i
="$(jsonfilter -i "$ssConfigFile" -q -e "@.inbounds
[*].port
")"; echo "${i:-443}"; }
288 # shellcheck disable=SC2155
289 get_tor_dns_port
() { local i
="$(grep -m1 DNSPort "$torConfigFile" | awk -F: '{print $2}')"; echo "${i:-9053}"; }
290 # shellcheck disable=SC2155
291 get_tor_traffic_port
() { local i
="$(grep -m1 TransPort "$torConfigFile" | awk -F: '{print $2}')"; echo "${i:-9040}"; }
292 get_xray_traffic_port
() { local i
="${1//$xrayIfacePrefix}"; [ "$i" = "$1" ] && unset i
; echo "$i"; }
293 get_rt_tables_id
() { local iface
="$1"; grep "${ipTablePrefix}_${iface}\$" "$rtTablesFile" | awk '{print $1;}'; }
294 get_rt_tables_next_id() { echo "$
(($
(sort -r -n "$rtTablesFile" |
grep -o -E -m 1 "^[0-9]+")+1))"; }
295 get_rt_tables_non_pbr_next_id() { echo "$
(($
(grep -v "${ipTablePrefix}_" "$rtTablesFile" |
sort -r -n |
grep -o -E -m 1 "^[0-9]+")+1))"; }
296 # shellcheck disable=SC2016
297 resolveip_to_ipt() { resolveip "$@
" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d'; }
298 resolveip_to_ipt4() { resolveip_to_ipt -4 "$@
"; }
299 resolveip_to_ipt6() { [ -n "$ipv6_enabled" ] && resolveip_to_ipt -6 "$@
"; }
300 # shellcheck disable=SC2016
301 resolveip_to_nftset() { resolveip "$@
" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
302 resolveip_to_nftset4() { resolveip_to_nftset -4 "$@
"; }
303 resolveip_to_nftset6() { [ -n "$ipv6_enabled" ] && resolveip_to_nftset -6 "$@
"; }
304 # shellcheck disable=SC2016
305 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' ' '; }
306 # shellcheck disable=SC2016
307 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' ' '; }
308 # shellcheck disable=SC3037
309 ports_to_nftset() { echo -ne "$1"; }
310 get_mark_ipt_chains() { [ -n "$
(command -v iptables-save
)" ] && iptables-save | grep ":${iptPrefix}_MARK_
" | awk '{ print $1 }' | sed 's/://'; }
311 get_mark_nft_chains() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep chain | grep "${nftPrefix}_mark_
" | awk '{ print $2 }'; }
312 get_ipsets() { [ -x "$
(command -v ipset
)" ] && ipset list | grep "${ipsPrefix}_
" | awk '{ print $2 }'; }
313 get_nft_sets() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep 'set' | grep "${nftPrefix}_
" | awk '{ print $2 }'; }
314 __ubus_get() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "$1"; }
315 ubus_get_status() { __ubus_get "@.
${packageName}.instances.main.data.status.${1}"; }
316 ubus_get_interface
() { __ubus_get
"@.${packageName}.instances.main.data.gateways[@.name='${1}']${2:+.$2}"; }
317 ubus_get_gateways() { __ubus_get "@.
${packageName}.instances.main.data.gateways
"; }
320 __tmp="$
(uci_get
'network' "$2" 'device')"
321 [ -z "$__tmp" ] && unset "$1" && return 1
324 uci_get_listen_port() {
326 __tmp="$
(uci_get
'network' "$2" 'listen_port')"
327 [ -z "$__tmp" ] && unset "$1" && return 1
332 is_enabled() { uci_get "$1" 'config' 'enabled'; }
333 is_running_iptables() { iptables -t mangle -L | grep -q PBR_PREROUTING >/dev/null 2>&1; }
334 is_running_nft_file() { [ -s "$nftPermFile" ]; }
335 is_running_nft() { "$nft" list table inet fw4 | grep chain | grep -q pbr_mark_ >/dev/null 2>&1; }
336 is_running() { is_running_iptables || is_running_nft; }
337 check_ipset() { { [ -n "$ipset" ] && "$ipset" help hash:net; } >/dev/null 2>&1; }
338 check_nft() { [ -n "$nft" ]; }
339 check_agh() { [ -n "$agh" ] && [ -s "$aghConfigFile" ]; }
340 check_dnsmasq() { command -v dnsmasq >/dev/null 2>&1; }
341 check_unbound() { command -v unbound >/dev/null 2>&1; }
343 check_ipset || return 1
344 check_agh || return 1
345 is_greater_or_equal "$
($agh --version |
sed 's|AdGuard Home, version v\(.*\)|\1|' |
sed 's|-.*||')" '0.107.13'
347 check_dnsmasq_ipset() {
349 check_ipset || return 1
350 check_dnsmasq || return 1
351 o="$
(dnsmasq
-v 2>/dev
/null
)"
352 ! echo "$o" | grep -q 'no-ipset' && echo "$o" | grep -q 'ipset'
354 check_dnsmasq_nftset() {
356 check_nft || return 1
357 check_dnsmasq || return 1
358 o="$
(dnsmasq
-v 2>/dev
/null
)"
359 ! echo "$o" | grep -q 'no-nftset' && echo "$o" | grep -q 'nftset'
361 print_json_bool() { json_init; json_add_boolean "$1" "$2"; json_dump; json_cleanup; }
362 print_json_string() { json_init; json_add_string "$1" "$2"; json_dump; json_cleanup; }
364 if type extra_command >/dev/null 2>&1; then
365 extra_command 'status' "Generates output required to troubleshoot routing issues
366 Use
'-d' option
for more detailed output
367 Use
'-p' option to automatically upload data under VPR
paste.ee account
368 WARNING
: while paste.ee uploads are unlisted
, they are still publicly available
369 List domain names after options to include their lookup
in report
"
370 extra_command 'version' 'Show version information'
371 extra_command 'on_firewall_reload' ' Run service on firewall reload'
372 extra_command 'on_interface_reload' ' Run service on indicated interface reload'
374 # shellcheck disable=SC2034
375 EXTRA_COMMANDS='on_firewall_reload on_interface_reload status version'
376 # shellcheck disable=SC2034
377 EXTRA_HELP=" status Generates output required to troubleshoot routing issues
378 Use
'-d' option
for more detailed output
379 Use
'-p' option to automatically upload data under VPR
paste.ee account
380 WARNING
: while paste.ee uploads are unlisted
, they are still publicly available
381 List domain names after options to include their lookup
in report
"
387 errorConfigValidation) r="Config
($packageConfigFile) validation failure
!";;
388 errorNoIpFull) r="ip-full binary cannot be found
!";;
389 errorNoIptables) r="iptables binary cannot be found
!";;
390 errorNoIpset) r="Resolver
set support
(${resolver_set}) requires ipset
, but ipset binary cannot be found
!";;
391 errorNoNft) r="Resolver
set support
(${resolver_set}) requires nftables
, but nft binary cannot be found
!";;
392 errorResolverNotSupported) r="Resolver
set (${resolver_set}) is not supported on this system
!";;
393 errorServiceDisabled) r="The
${packageName} service is currently disabled
!";;
394 errorNoWanGateway) r="The
${serviceName} service failed to discover WAN gateway
!";;
395 errorNoWanInterface) r="The
%s inteface not found
, you need to
set the
'pbr.config.procd_wan_interface' option
!";;
396 errorNoWanInterfaceHint) r="Refer to https
://docs.openwrt.melmac.net
/pbr
/#procd_wan_interface.";;
397 errorIpsetNameTooLong
) r
="The ipset name '%s' is longer than allowed 31 characters!";;
398 errorNftsetNameTooLong
) r
="The nft set name '%s' is longer than allowed 255 characters!";;
399 errorUnexpectedExit
) r
="Unexpected exit or service termination: '%s'!";;
400 errorPolicyNoSrcDest
) r
="Policy '%s' has no source/destination parameters!";;
401 errorPolicyNoInterface
) r
="Policy '%s' has no assigned interface!";;
402 errorPolicyUnknownInterface
) r
="Policy '%s' has an unknown interface!";;
403 errorPolicyProcessCMD
) r
="'%s'!";;
404 errorFailedSetup
) r
="Failed to set up '%s'!";;
405 errorFailedReload
) r
="Failed to reload '%s'!";;
406 errorUserFileNotFound
) r
="Custom user file '%s' not found or empty!";;
407 errorUserFileSyntax
) r
="Syntax error in custom user file '%s'!";;
408 errorUserFileRunning
) r
="Error running custom user file '%s'!";;
409 errorUserFileNoCurl
) r
="Use of 'curl' is detected in custom user file '%s', but 'curl' isn't installed!";;
410 errorNoGateways
) r
="Failed to set up any gateway!";;
411 errorResolver
) r
="Resolver '%s'!";;
412 errorPolicyProcessNoIpv6
) r
="Skipping IPv6 policy '%s' as IPv6 support is disabled!";;
413 errorPolicyProcessUnknownFwmark
) r
="Unknown packet mark for interface '%s'!";;
414 errorPolicyProcessMismatchFamily
) r
="Mismatched IP family between in policy '%s'!";;
415 errorPolicyProcessUnknownProtocol
) r
="Unknown protocol in policy '%s'!";;
416 errorPolicyProcessInsertionFailed
) r
="Insertion failed for both IPv4 and IPv6 for policy '%s'!";;
417 errorPolicyProcessInsertionFailedIpv4
) r
="Insertion failed for IPv4 for policy '%s'!";;
418 errorInterfaceRoutingEmptyValues
) r
="Received empty tid/mark or interface name when setting up routing!";;
419 errorFailedToResolve
) r
="Failed to resolve '%s'!";;
420 errorTryFailed
) r
="Command failed: %s";;
421 errorNftFileInstall
) r
="Failed to install fw4 nft file '%s'!";;
422 errorDownloadUrlNoHttps
) r
="Failed to download '%s', HTTPS is not supported!";;
423 errorDownloadUrl
) r
="Failed to download '%s'!";;
424 errorNoDownloadWithSecureReload
) r
="Policy '%s' refers to URL which can't be downloaded in 'secure_reload' mode!";;
425 errorFileSchemaRequiresCurl
) r
="The file:// schema requires curl, but it's not detected on this system!";;
426 warningInvalidOVPNConfig
) r
="Invalid OpenVPN config for '%s' interface.";;
427 warningResolverNotSupported
) r
="Resolver set (${resolver_set}) is not supported on this system.";;
428 warningAGHVersionTooLow
) r
="Installed AdGuardHome ('%s') doesn't support 'ipset_file' option.";;
429 warningPolicyProcessCMD
) r
="'%s'";;
430 warningTorUnsetParams
) r
="Please unset 'src_addr', 'src_port' and 'dest_port' for policy '%s'.";;
431 warningTorUnsetProto
) r
="Please unset 'proto' or set 'proto' to 'all' for policy '%s'.";;
432 warningTorUnsetChainIpt
) r
="Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '%s'.";;
433 warningTorUnsetChainNft
) r
="Please unset 'chain' or set 'chain' to 'prerouting' for policy '%s'.";;
434 warningOutdatedWebUIApp
) r
="The WebUI application is outdated (version %s), please update it.";;
435 warningBadNftCallsInUserFile
) r
="Incompatible nft calls detected in user include file, disabling fw4 nft file support.";;
436 warningDnsmasqInstanceNoConfdir
) r
="Dnsmasq instance (%s) targeted in settings, but it doesn't have its own confdir.";;
443 local dl_command dl_https_supported dl_temp_file
444 # TODO: check for FILE schema and missing curl
445 if is_present
'curl'; then
446 dl_command
="curl --silent --insecure"
448 elif is_present
'/usr/libexec/wget-ssl'; then
449 dl_command
="/usr/libexec/wget-ssl --no-check-certificate -q"
451 elif is_present wget
&& wget
--version 2>/dev
/null |
grep -q "+https"; then
452 dl_command
="wget --no-check-certificate -q"
455 dl_command
="uclient-fetch --no-check-certificate -q"
458 if curl
--version 2>/dev
/null |
grep -q "Protocols: .*https.*" \
459 || wget
--version 2>/dev
/null |
grep -q "+ssl"; then
462 unset dl_https_supported
464 while [ -z "$dl_temp_file" ] ||
[ -e "$dl_temp_file" ]; do
465 dl_temp_file
="$(mktemp -u -q -t ${packageName}_tmp.XXXXXXXX)"
467 if is_url_file
"$url" && ! is_present
'curl'; then
468 state add
'errorSummary' 'errorFileSchemaRequiresCurl' "$url"
469 elif is_url_https
"$url" && [ -z "$dl_https_supported" ]; then
470 state add
'errorSummary' 'errorDownloadUrlNoHttps' "$url"
471 elif $dl_command "$url" "$dl_flag" "$dl_temp_file" 2>/dev
/null
; then
472 sed 'N;s/\n/ /;s/\s\+/ /g;' "$dl_temp_file"
474 state add
'errorSummary' 'errorDownloadUrl' "$url"
476 rm -f "$dl_temp_file"
479 load_package_config
() {
480 _check_user_files_for_bad_nft_calls
() {
483 config_get_bool en
"$cfg" 'enabled' 1
484 config_get path
"$cfg" 'path'
485 [ "$en" -eq 0 ] && return 0
486 [ -z "$path" ] && return 0
487 [ -s "$path" ] ||
return 0
488 is_bad_user_file_nft_call
"$path" && user_file_check_result
='bad'
491 local user_file_check_result i
492 config_load
"$packageName"
493 config_get_bool enabled
'config' 'enabled' '0'
494 config_get fw_mask
'config' 'fw_mask' 'ff0000'
495 config_get icmp_interface
'config' 'icmp_interface'
496 config_get ignored_interface
'config' 'ignored_interface'
497 config_get_bool ipv6_enabled
'config' 'ipv6_enabled' '0'
498 config_get_bool nft_file_support
'config' 'nft_file_support' '1'
499 config_get_bool nft_set_auto_merge
'config' 'nft_set_auto_merge' '1'
500 config_get_bool nft_set_counter
'config' 'nft_set_counter' '1'
501 config_get_bool nft_set_flags_interval
'config' 'nft_set_flags_interval' '1'
502 config_get_bool nft_set_flags_timeout
'config' 'nft_set_flags_timeout' '0'
503 config_get nft_set_gc_interval
'config' 'nft_set_gc_interval'
504 config_get nft_set_policy
'config' 'nft_set_policy' 'performance'
505 config_get nft_set_timeout
'config' 'nft_set_timeout'
506 config_get resolver_set
'config' 'resolver_set'
507 config_get resolver_instance
'config' 'resolver_instance' '*'
508 config_get rule_create_option
'config' 'rule_create_option' 'add'
509 config_get_bool secure_reload
'config' 'secure_reload' '0'
510 config_get_bool strict_enforcement
'config' 'strict_enforcement' '1'
511 config_get supported_interface
'config' 'supported_interface'
512 config_get verbosity
'config' 'verbosity' '2'
513 config_get procd_boot_delay
'config' 'procd_boot_delay' '0'
514 config_get procd_boot_timeout
'config' 'procd_boot_timeout' '30'
515 config_get procd_lan_interface
'config' 'procd_lan_interface'
516 config_get procd_wan_ignore_status
'config' 'procd_wan_ignore_status' '0'
517 config_get procd_wan_interface
'config' 'procd_wan_interface' 'wan'
518 config_get procd_wan6_interface
'config' 'procd_wan6_interface' 'wan6'
519 config_get wan_ip_rules_priority
'config' 'wan_ip_rules_priority' '30000'
520 config_get wan_mark
'config' 'wan_mark' '010000'
521 fw_mask
="0x${fw_mask}"
522 wan_mark
="0x${wan_mark}"
523 [ -n "$ipv6_enabled" ] && [ "$ipv6_enabled" -eq 0 ] && unset ipv6_enabled
524 [ -n "$nft_file_support" ] && [ "$nft_file_support" -eq 0 ] && unset nft_file_support
525 [ -n "$nft_user_set_counter" ] && [ "$nft_user_set_counter" -eq 0 ] && unset nft_user_set_counter
526 [ -n "$secure_reload" ] && [ "$secure_reload" -eq 0 ] && unset secure_reload
527 config_foreach _check_user_files_for_bad_nft_calls
'include'
528 [ -n "$user_file_check_result" ] && unset nft_file_support
529 [ -n "$nft_file_support" ] && unset secure_reload
530 is_config_enabled
'include' && unset secure_reload
532 fw_maskXor
="$(printf '%#x' "$
((fw_mask ^
0xffffffff))")"
533 fw_maskXor
="${fw_maskXor:-0xff00ffff}"
535 case $rule_create_option in
536 insert|
-i|
-I) rule_create_option
='-I';;
537 add|
-a|
-A|
*) rule_create_option
='-A';;
541 [ "$nft_set_auto_merge" != '1' ] && unset nft_set_auto_merge
542 [ "$nft_set_counter" != '1' ] && unset nft_set_counter
543 [ "$nft_set_flags_interval" != '1' ] && unset nft_set_flags_interval
544 [ "$nft_set_flags_timeout" != '1' ] && unset nft_set_flags_timeout
545 [ -z "${nft_set_flags_timeout}${nft_set_timeout}" ] && unset nft_set_gc_interval
547 if [ -n "${nft_set_flags_interval}${nft_set_flags_timeout}" ]; then
548 [ -n "$nft_set_flags_interval" ] && nft_set_flags
='flags interval'
549 if [ -n "$nft_set_flags_timeout" ]; then
550 if [ -n "$nft_set_flags" ]; then
551 nft_set_flags
="${nft_set_flags}, timeout"
553 nft_set_flags
='flags timeout'
558 ${nft_set_auto_merge:+ auto-merge;} \
559 ${nft_set_counter:+ counter;} \
560 ${nft_set_flags:+ $nft_set_flags;} \
561 ${nft_set_gc_interval:+ gc_interval "$nft_set_gc_interval";} \
562 ${nft_set_policy:+ policy "$nft_set_policy";} \
563 ${nft_set_timeout:+ timeout "$nft_set_timeout";} \
566 resolver
'check_support' && resolver
'configure_instances'
570 local param
="$1" validation_result
="$2"
571 load_package_config
"$param"
574 if [ "$enabled" -eq 0 ]; then
575 state add
'errorSummary' 'errorServiceDisabled'
578 if [ -n "$validation_result" ] && [ "$validation_result" != '0' ]; then
579 output
"${_ERROR_}: The $packageName config validation failed!\\n"
580 output
"Please check if the '$packageConfigFile' contains correct values for config options.\\n"
581 state add
'errorSummary' 'errorConfigValidation'
584 if [ ! -x "$ip_bin" ]; then
585 state add
'errorSummary' 'errorNoIpFull'
589 if [ "$(uci_get 'firewall' 'defaults' 'auto_includes')" = '0' ]; then
590 uci_remove
'firewall' 'defaults' 'auto_includes'
594 if [ -z "$iptables" ] ||
[ ! -x "$iptables" ]; then
595 state add
'errorSummary' 'errorNoIptables'
604 load_network
"$param"
608 _build_ifaces_supported
() { is_supported_interface
"$1" && ! str_contains
"$ifacesSupported" "$1" && ifacesSupported
="${ifacesSupported}${1} "; }
609 _find_firewall_wan_zone() { [ "$
(uci_get
'firewall' "$1" 'name')" = "wan
" ] && firewallWanZone="$1"; }
612 if [ -z "$ifacesSupported" ]; then
613 config_load 'firewall'
614 config_foreach _find_firewall_wan_zone 'zone'
615 for i in $(uci_get 'firewall' "$firewallWanZone" 'network'); do
616 is_supported_interface "$i" && ! str_contains "$ifacesSupported" "$1" && ifacesSupported="${ifacesSupported}${i} "
618 config_load 'network'
619 config_foreach _build_ifaces_supported 'interface'
621 wanIface4="$procd_wan_interface"
622 network_get_device dev4 "$wanIface4"
623 [ -z "$dev4" ] && network_get_physdev dev4 "$wanIface4"
624 [ -z "$wanGW4" ] && pbr_get_gateway4 wanGW4 "$wanIface4" "$dev4"
625 if [ -n "$ipv6_enabled" ]; then
626 wanIface6="$procd_wan6_interface"
627 network_get_device dev6 "$wanIface6"
628 [ -z "$dev6" ] && network_get_physdev dev6 "$wanIface6"
629 [ -z "$wanGW6" ] && pbr_get_gateway6 wanGW6 "$wanIface6" "$dev6"
634 [ -n "$wanIface4" ] && output 2 "Using wan interface
(${param}): $wanIface4 \\n
"
635 [ -n "$wanGW4" ] && output 2 "Found wan gateway
(${param}): $wanGW4 \\n
"
636 [ -n "$wanIface6" ] && output 2 "Using wan6 interface
(${param}): $wanIface6 \\n
"
637 [ -n "$wanGW6" ] && output 2 "Found wan6 gateway
(${param}): $wanGW6 \\n
"
640 wanGW="${wanGW4:-$wanGW6}"
644 local sleepCount='1' param="$1"
645 load_network "$param"
646 [ "$procd_wan_ignore_status" -eq '0' ] || return 0
647 [ "$param" = 'on_boot' ] || procd_boot_timeout='1'
648 if [ -z "$
(uci_get network
"$procd_wan_interface")" ]; then
649 state add 'errorSummary' 'errorNoWanInterface' "$procd_wan_interface"
650 state add 'errorSummary' 'errorNoWanInterfaceHint'
653 while [ -z "$wanGW" ] ; do
654 load_network "$param"
655 if [ $((sleepCount)) -gt $((procd_boot_timeout)) ] || [ -n "$wanGW" ]; then break; fi
656 output "$serviceName waiting
for $procd_wan_interface gateway...
\\n
"
659 sleepCount=$((sleepCount+1))
661 if [ -n "$wanGW" ]; then
664 state add 'errorSummary' 'errorNoWanGateway'
669 # shellcheck disable=SC2086
672 [ -x "$iptables" ] || return 1
673 for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
674 [ "$d" != "$
*" ] && "$iptables" $d >/dev/null 2>&1
676 d="$
*"; "$iptables" $d >/dev/null 2>&1
679 # shellcheck disable=SC2086
682 [ -n "$ipv6_enabled" ] || return 0
683 [ -x "$ip6tables" ] || return 1
684 for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
685 [ "$d" != "$
*" ] && "$ip6tables" $d >/dev/null 2>&1
688 "$ip6tables" $d >/dev/null 2>&1
691 # shellcheck disable=SC2086
693 local d failFlagIpv4=1 failFlagIpv6=1
694 [ -x "$iptables" ] || return 1
695 for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
696 if [ "$d" != "$
*" ]; then
697 "$iptables" $d >/dev/null 2>&1
698 if [ -x "$ip6tables" ]; then
699 "$ip6tables" $d >/dev/null 2>&1
703 d="$
*"; "$iptables" $d >/dev/null 2>&1 && failFlagIpv4=0;
704 if [ -n "$ipv6_enabled" ] && [ -x "$ip6tables" ]; then
705 "$ip6tables" $d >/dev/null 2>&1 && failFlagIpv6=0
707 [ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ]
710 # shellcheck disable=SC2086
711 ips4() { [ -x "$ipset" ] && "$ipset" "$@
" >/dev/null 2>&1; }
712 ips6() { [ -x "$ipset" ] && { if [ -n "$ipv6_enabled" ] && [ -n "$
*" ]; then "$ipset" "$@
" >/dev/null 2>&1; else return 1; fi; }; }
714 local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7"
715 local ipset4 ipset6 i
716 local ipv4_error=1 ipv6_error=1
717 ipset4="${ipsPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}"
718 ipset6="${ipsPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}"
720 [ -x "$ipset" ] || return 1
722 if [ "${#ipset4}" -gt 31 ]; then
723 state add 'errorSummary' 'errorIpsetNameTooLong' "$ipset4"
729 ips4 -q -! add "$ipset4" ["$param"] comment "$comment" && ipv4_error=0
730 ips6 -q -! add "$ipset6" ["$param"] comment "$comment" && ipv6_error=0
733 [ -n "$ipv6_enabled" ] || unset ipset6
734 echo "${param}/${ipset4}${ipset6:+,$ipset6}" >> "$aghIpsetFile" && ipv4_error=0
737 [ -n "$ipv6_enabled" ] || unset ipset6
738 # shellcheck disable=SC2086
739 echo "ipset
=/${param}/${ipset4}${ipset6:+,$ipset6} # $comment" | tee -a $dnsmasqFileList >/dev/null 2>&1 && ipv4_error=0
742 ips4
-q -! create
"$ipset4" "hash:$type" comment
&& ipv4_error
=0
743 ips6
-q -! create
"$ipset6" "hash:$type" comment family inet6
&& ipv6_error
=0
746 ips4
-q -! create
"$ipset4" "hash:$type" comment
&& ipv4_error
=0
747 ips6
-q -! create
"$ipset6" "hash:$type" comment family inet6
&& ipv6_error
=0
750 ips4
-q -! create
"$ipset4" "hash:$type" comment
&& ipv4_error
=0
751 ips6
-q -! create
"$ipset6" "hash:$type" comment family inet6
&& ipv6_error
=0
756 ips4
-q -! create
"$ipset4" "hash:$type" comment
&& ipv4_error
=0
757 ips6
-q -! create
"$ipset6" "hash:$type" comment family inet6
&& ipv6_error
=0
760 ipt4
-t mangle
-A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error
=0
761 ipt6
-t mangle
-A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error
=0
764 ipt4
-t mangle
-A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error
=0
765 ipt6
-t mangle
-A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error
=0
770 ips4
-q -! create
"$ipset4" "hash:$type" comment
&& ipv4_error
=0
771 ips6
-q -! create
"$ipset6" "hash:$type" comment family inet6
&& ipv4_error
=0
772 ipt4
-t mangle
-A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error
=0
773 ipt6
-t mangle
-A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error
=0
778 ips4
-q -! destroy
"$ipset4" && ipv4_error
=0
779 ips6
-q -! destroy
"$ipset6" && ipv6_error
=0
782 ips4
-q -! destroy
"$ipset4" && ipv4_error
=0
783 ips6
-q -! destroy
"$ipset6" family inet6
&& ipv6_error
=0
788 ipt4
-t mangle
-D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error
=0
789 ipt6
-t mangle
-D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error
=0
792 ipt4
-t mangle
-D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error
=0
793 ipt6
-t mangle
-D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error
=0
798 ipt4
-t mangle
-D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error
=0
799 ipt6
-t mangle
-D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error
=0
803 flush|flush_user_set
)
804 ips4
-q -! flush
"$ipset4" && ipv4_error
=0
805 ips6
-q -! flush
"$ipset6" && ipv6_error
=0
808 if [ "$ipv4_error" -eq '0' ] ||
[ "$ipv6_error" -eq '0' ]; then
815 nft_call
() { [ -x "$nft" ] && "$nft" "$@" >/dev
/null
2>&1; }
818 [ -x "$nft" ] ||
return 1
821 [ -n "$nft_file_support" ] ||
return 1
823 grep -q "$*" "$nftTempFile" ||
echo "$*" >> "$nftTempFile"
826 rm -f "$nftTempFile" "$nftPermFile"
827 for i
in "$nftTempFile" "$nftPermFile"; do
830 [ -n "$nft_file_support" ] ||
return 1
831 { echo '#!/usr/sbin/nft -f'; echo ''; } > "$nftTempFile"
834 rm -f "$nftTempFile" "$nftPermFile"
837 [ -n "$nft_file_support" ] && return 0 ||
return 1
840 [ -s "$nftPermFile" ] && return 0 ||
return 1
843 [ -n "$nft_file_support" ] ||
return 1
844 [ -s "$nftTempFile" ] ||
return 1
845 output
"Installing fw4 nft file "
846 if nft_call
-c -f "$nftTempFile" && \
847 cp -f "$nftTempFile" "$nftPermFile"; then
850 state add
'errorSummary' 'errorNftFileInstall' "$nftTempFile"
856 nft
() { [ -x "$nft" ] && [ -n "$*" ] && { nft_file
'add_command' "$@" ||
"$nft" "$@" >/dev
/null
2>&1;} }
858 nft6
() { [ -n "$ipv6_enabled" ] ||
return 0; nft
"$@"; }
860 local command="$1" iface
="$2" target
="${3:-dst}" type="${4:-ip}" uid
="$5" comment
="$6" param
="$7" mark
="$7"
861 local nftset4 nftset6 i param4 param6
862 local ipv4_error
=1 ipv6_error
=1
863 nftset4
="${nftPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}"
864 nftset6
="${nftPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}"
866 [ -x "$nft" ] ||
return 1
868 if [ "${#nftset4}" -gt 255 ]; then
869 state add
'errorSummary' 'errorNftsetNameTooLong' "$nftset4"
875 if is_mac_address
"$param" || is_list
"$param"; then
876 nft4 add element inet
"$nftTable" "$nftset4" "{ $param }" && ipv4_error
=0
877 nft6 add element inet
"$nftTable" "$nftset6" "{ $param }" && ipv6_error
=0
878 elif is_ipv4_netmask
"$param" || is_ipv4
"$param"; then
879 nft4 add element inet
"$nftTable" "$nftset4" "{ $param }" && ipv4_error
=0
880 elif is_ipv6
"$param"; then
881 nft6 add element inet
"$nftTable" "$nftset6" "{ $param }" && ipv6_error
=0
883 if [ "$target" = 'src' ]; then
884 param4
="$(ipv4_leases_to_nftset "$param")"
885 param6
="$(ipv6_leases_to_nftset "$param")"
887 [ -z "$param4" ] && param4
="$(resolveip_to_nftset4 "$param")"
888 [ -z "$param6" ] && param6
="$(resolveip_to_nftset6 "$param")"
889 if [ -z "$param4" ] && [ -z "$param6" ]; then
890 state add
'errorSummary' 'errorFailedToResolve' "$param"
892 [ -n "$param4" ] && nft4 add element inet
"$nftTable" "$nftset4" "{ $param4 }" && ipv4_error
=0
893 [ -n "$param6" ] && nft6 add element inet
"$nftTable" "$nftset6" "{ $param6 }" && ipv6_error
=0
898 [ -n "$ipv6_enabled" ] ||
unset nftset6
899 # shellcheck disable=SC2086
900 echo "nftset=/${param}/4#inet#${nftTable}#${nftset4}${nftset6:+,6#inet#${nftTable}#$nftset6} # $comment" |
tee -a $dnsmasqFileList >/dev
/null
2>&1 && ipv4_error
=0
905 nft4 add
set inet
"$nftTable" "$nftset4" "{ type ipv4_addr; $nft_set_params comment \"$comment\";}" && ipv4_error
=0
906 nft6 add
set inet
"$nftTable" "$nftset6" "{ type ipv6_addr; $nft_set_params comment \"$comment\"; }" && ipv6_error
=0
909 nft4 add
set inet
"$nftTable" "$nftset4" "{ type ether_addr; $nft_set_params comment \"$comment\"; }" && ipv4_error
=0
910 nft6 add
set inet
"$nftTable" "$nftset6" "{ type ether_addr; $nft_set_params comment \"$comment\"; }" && ipv6_error
=0
915 nft4 add
set inet
"$nftTable" "$nftset4" "{ type ipv4_addr; $nft_set_params comment \"$comment\"; }" && ipv4_error
=0
916 nft6 add
set inet
"$nftTable" "$nftset6" "{ type ipv6_addr; $nft_set_params comment \"$comment\"; }" && ipv6_error
=0
921 nft4 add
set inet
"$nftTable" "$nftset4" "{ type ipv4_addr; $nft_set_params comment \"$comment\"; }" && ipv4_error
=0
922 nft6 add
set inet
"$nftTable" "$nftset6" "{ type ipv6_addr; $nft_set_params comment \"$comment\"; }" && ipv6_error
=0
925 nft4 add rule inet
"$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error
=0
926 nft6 add rule inet
"$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error
=0
929 nft4 add rule inet
"$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error
=0
930 nft6 add rule inet
"$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error
=0
935 nft4 add
set inet
"$nftTable" "$nftset4" "{ type ether_addr; $nft_set_params comment \"$comment\"; }" && ipv4_error
=0
936 nft6 add
set inet
"$nftTable" "$nftset6" "{ type ether_addr; $nft_set_params comment \"$comment\"; }" && ipv6_error
=0
937 nft4 add rule inet
"$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error
=0
938 nft6 add rule inet
"$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error
=0
943 nft_call delete
set inet
"$nftTable" "$nftset4" && ipv4_error
=0
944 nft_call delete
set inet
"$nftTable" "$nftset6" && ipv6_error
=0
947 nft_call delete
set inet
"$nftTable" "$nftset4" && ipv4_error
=0
948 nft_call delete
set inet
"$nftTable" "$nftset6" && ipv6_error
=0
953 nft_call delete rule inet
"$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error
=0
954 nft_call delete rule inet
"$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error
=0
957 nft_call delete rule inet
"$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error
=0
958 nft_call delete rule inet
"$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error
=0
963 nft_call delete rule inet
"$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error
=0
964 nft_call delete rule inet
"$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error
=0
968 flush|flush_user_set
)
969 nft_call flush
set inet
"$nftTable" "$nftset4" && ipv4_error
=0
970 nft_call flush
set inet
"$nftTable" "$nftset6" && ipv6_error
=0
973 # nft6 returns true if IPv6 support is not enabled
974 [ -z "$ipv6_enabled" ] && ipv6_error
='1'
975 if [ "$ipv4_error" -eq '0' ] ||
[ "$ipv6_error" -eq '0' ]; then
982 cleanup_rt_tables
() {
984 # shellcheck disable=SC2013
985 for i
in $
(grep -oh "${ipTablePrefix}_.*" $rtTablesFile); do
986 ! is_netifd_table
"$i" && sed -i "/${i}/d" "$rtTablesFile"
991 cleanup_main_chains
() {
993 for i
in $chainsList; do
994 i
="$(str_to_lower "$i")"
995 nft_call flush chain inet
"$nftTable" "${nftPrefix}_${i}"
997 for i
in $chainsList; do
998 i
="$(str_to_upper "$i")"
999 ipt
-t mangle
-D "${i}" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
1000 ipt
-t mangle
-F "${iptPrefix}_${i}"
1001 ipt
-t mangle
-X "${iptPrefix}_${i}"
1005 cleanup_marking_chains
() {
1007 for i
in $
(get_mark_nft_chains
); do
1008 nft_call flush chain inet
"$nftTable" "$i"
1009 nft_call delete chain inet
"$nftTable" "$i"
1011 for i
in $
(get_mark_ipt_chains
); do
1012 ipt
-t mangle
-F "$i"
1013 ipt
-t mangle
-X "$i"
1019 for i
in $
(get_nft_sets
); do
1020 nft_call flush
set inet
"$nftTable" "$i"
1021 nft_call delete
set inet
"$nftTable" "$i"
1023 for i
in $
(get_ipsets
); do
1024 ipset
-q -! flush
"$i" >/dev
/null
2>&1
1025 ipset
-q -! destroy
"$i" >/dev
/null
2>&1
1030 local action
="$1" param
="$2" value
="${3//#/_}"
1032 # shellcheck disable=SC2124
1034 local line error_id error_extra label
1037 line
="$(eval echo "\$
$param")"
1038 eval "$param"='${line:+$line#}${value}${extras:+ $extras}'
1042 json_add_object
"$packageName"
1045 json_add_array
'errors';;
1047 json_add_array
'warnings';;
1049 if [ -n "$(eval echo "\$
$param")" ]; then
1050 while read -r line
; do
1051 if str_contains
"$line" ' '; then
1052 error_id
="${line% *}"
1053 error_extra
="${line#* }"
1058 json_add_string
'id' "$error_id"
1059 json_add_string
'extra' "$error_extra"
1062 $(eval echo "\$$param" | tr \# \\n)
1070 [ -z "$(eval echo "\$
$param")" ] && return 0
1073 label
="${_ERROR_}:";;
1075 label
="${_WARNING_}:";;
1077 while read -r line
; do
1078 if str_contains
"$line" ' '; then
1079 error_id
="${line% *}"
1080 error_extra
="${line#* }"
1081 printf "%b $(get_text "$error_id")\\n" "$label" "$error_extra"
1084 printf "%b $(get_text "$error_id")\\n" "$label"
1087 $(eval echo "\$$param" | tr \# \\n)
1091 eval "$param"='${value}${extras:+ $extras}'
1096 _resolver_dnsmasq_confdir
() {
1099 [ -z "$(uci_get 'dhcp' "$cfg")" ] && return 1;
1100 config_get confdir
"$1" 'confdir'
1101 if [ -z "$confdir" ] && [ "$resolver_instance" != "*" ]; then
1102 state add
'warningSummary' 'warningDnsmasqInstanceNoConfdir' "$cfg"
1104 if [ -n "$confdir" ] && ! str_contains
"$dnsmasqFileList" "$confdir"; then
1105 dnsmasqFile
="${confdir}/${packageName}"
1106 dnsmasqFileList
="${dnsmasqFileList:+$dnsmasqFileList }${dnsmasqFile}"
1115 if [ "$param" = 'cleanup_all' ]; then
1116 sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev
/null
2>&1
1117 rm -f "$aghIpsetFile"
1119 for dfl
in $dnsmasqFileList; do
1125 case "$resolver_set" in
1128 add_resolver_element
) return 1;;
1129 create_resolver_set
) return 1;;
1130 check_support
) return 0;;
1132 configure
) return 0;;
1134 init_end
) return 0;;
1138 compare_hash
) return 0;;
1139 store_hash
) return 0;;
1144 add_resolver_element
)
1145 [ -n "$resolver_set_supported" ] && ips
'add_agh_element' "$@";;
1146 create_resolver_set
)
1147 [ -n "$resolver_set_supported" ] && ips
'create_agh_set' "$@";;
1149 if [ ! -x "$ipset" ]; then
1150 state add
'errorSummary' 'errorNoIpset'
1153 if [ -n "$agh" ] && [ -s "$aghConfigFile" ]; then
1154 agh_version
="$($agh --version | sed 's|AdGuard Home, version v\(.*\)|\1|' | sed 's|-.*||')"
1155 if is_greater_or_equal
"$agh_version" '0.107.13'; then
1156 resolver_set_supported
='true'
1159 state add
'warningSummary' 'warningAGHVersionTooLow' "$agh_version"
1163 state add
'warningSummary' 'warningResolverNotSupported'
1168 [ -z "$resolver_set_supported" ] && return 0
1169 rm -f "$aghIpsetFile"
1170 sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev
/null
2>&1
1173 [ -z "$resolver_set_supported" ] && return 1
1174 mkdir
-p "${aghIpsetFile%/*}"
1175 touch "$aghIpsetFile"
1176 sed -i '/ipset_file/d' "$aghConfigFile" >/dev
/null
2>&1
1177 sed -i "/ ipset:/a \ \ ipset_file: $aghIpsetFile" "$aghConfigFile"
1182 [ -n "$resolver_set_supported" ] && [ -n "$agh" ] && killall
-q -s HUP
"$agh";;
1184 [ -z "$resolver_set_supported" ] && return 1
1185 output
3 'Reloading adguardhome '
1186 if /etc
/init.d
/adguardhome reload
>/dev
/null
2>&1; then
1195 [ -z "$resolver_set_supported" ] && return 1
1196 output
3 'Restarting adguardhome '
1197 if /etc
/init.d
/adguardhome restart
>/dev
/null
2>&1; then
1206 [ -z "$resolver_set_supported" ] && return 1
1207 local resolverNewHash
1208 if [ -s "$aghIpsetFile" ]; then
1209 resolverNewHash
="$(md5sum "$aghIpsetFile" | awk '{ print $1; }')"
1211 [ "$resolverNewHash" != "$resolverStoredHash" ]
1214 [ -s "$aghIpsetFile" ] && resolverStoredHash
="$(md5sum "$aghIpsetFile" | awk '{ print $1; }')";;
1219 add_resolver_element
)
1220 [ -n "$resolver_set_supported" ] && ips
'add_dnsmasq_element' "$@";;
1221 create_resolver_set
)
1222 [ -n "$resolver_set_supported" ] && ips
'create_dnsmasq_set' "$@";;
1224 if [ ! -x "$ipset" ]; then
1225 state add
'errorSummary' 'errorNoIpset'
1228 if ! dnsmasq
-v 2>/dev
/null |
grep -q 'no-ipset' && dnsmasq
-v 2>/dev
/null |
grep -q 'ipset'; then
1229 resolver_set_supported
='true'
1232 state add
'warningSummary' 'warningResolverNotSupported'
1237 if [ -n "$resolver_set_supported" ]; then
1239 for dfl
in $dnsmasqFileList; do
1245 if [ -n "$resolver_set_supported" ]; then
1247 for dfl
in $dnsmasqFileList; do
1248 mkdir
-p "${dfl%/*}"
1249 chmod -R 660 "${dfl%/*}"
1250 chown
-R root
:dnsmasq
"${dfl%/*}"
1253 chown root
:dnsmasq
"$dfl"
1257 configure_instances
)
1259 if [ "$resolver_instance" = "*" ]; then
1260 config_foreach _resolver_dnsmasq_confdir
'dnsmasq'
1261 dnsmasqFile
="${dnsmasqFile:-$dnsmasqFileDefault}"
1262 str_contains
"$dnsmasqFileList" "$dnsmasqFileDefault" || \
1263 dnsmasqFileList
="${dnsmasqFileList:+$dnsmasqFileList }${dnsmasqFileDefault}"
1265 for i
in $resolver_instance; do
1266 _resolver_dnsmasq_confdir
"@dnsmasq[$i]" \
1267 || _resolver_dnsmasq_confdir
"$i"
1269 dnsmasqFile
="${dnsmasqFile:-$dnsmasqFileDefault}"
1270 str_contains
"$dnsmasqFileList" "$dnsmasqFileDefault" || \
1271 dnsmasqFileList
="${dnsmasqFileList:-$dnsmasqFileDefault}"
1277 [ -n "$resolver_set_supported" ] && killall
-q -s HUP dnsmasq
;;
1279 [ -z "$resolver_set_supported" ] && return 1
1280 output
3 'Reloading dnsmasq '
1281 if /etc
/init.d
/dnsmasq reload
>/dev
/null
2>&1; then
1290 [ -z "$resolver_set_supported" ] && return 1
1291 output
3 'Restarting dnsmasq '
1292 if /etc
/init.d
/dnsmasq restart
>/dev
/null
2>&1; then
1301 [ -z "$resolver_set_supported" ] && return 1
1302 local resolverNewHash
1303 if [ -s "$dnsmasqFile" ]; then
1304 resolverNewHash
="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')"
1306 [ "$resolverNewHash" != "$resolverStoredHash" ]
1309 [ -s "$dnsmasqFile" ] && resolverStoredHash
="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')";;
1314 add_resolver_element
)
1315 [ -n "$resolver_set_supported" ] && nftset
'add_dnsmasq_element' "$@";;
1316 create_resolver_set
)
1317 [ -n "$resolver_set_supported" ] && nftset
'create_dnsmasq_set' "$@";;
1319 if [ ! -x "$nft" ]; then
1320 state add
'errorSummary' 'errorNoNft'
1323 if ! dnsmasq
-v 2>/dev
/null |
grep -q 'no-nftset' && dnsmasq
-v 2>/dev
/null |
grep -q 'nftset'; then
1324 resolver_set_supported
='true'
1327 state add
'warningSummary' 'warningResolverNotSupported'
1332 if [ -n "$resolver_set_supported" ]; then
1334 for dfl
in $dnsmasqFileList; do
1340 if [ -n "$resolver_set_supported" ]; then
1342 for dfl
in $dnsmasqFileList; do
1343 mkdir
-p "${dfl%/*}"
1344 chmod -R 660 "${dfl%/*}"
1345 chown
-R root
:dnsmasq
"${dfl%/*}"
1348 chown root
:dnsmasq
"$dfl"
1352 configure_instances
)
1354 if [ "$resolver_instance" = "*" ]; then
1355 config_foreach _resolver_dnsmasq_confdir
'dnsmasq'
1356 dnsmasqFile
="${dnsmasqFile:-$dnsmasqFileDefault}"
1357 str_contains
"$dnsmasqFileList" "$dnsmasqFileDefault" || \
1358 dnsmasqFileList
="${dnsmasqFileList:+$dnsmasqFileList }${dnsmasqFileDefault}"
1360 for i
in $resolver_instance; do
1361 _resolver_dnsmasq_confdir
"@dnsmasq[$i]" \
1362 || _resolver_dnsmasq_confdir
"$i"
1364 dnsmasqFile
="${dnsmasqFile:-$dnsmasqFileDefault}"
1365 str_contains
"$dnsmasqFileList" "$dnsmasqFileDefault" || \
1366 dnsmasqFileList
="${dnsmasqFileList:-$dnsmasqFileDefault}"
1372 [ -n "$resolver_set_supported" ] && killall
-q -s HUP dnsmasq
;;
1374 [ -z "$resolver_set_supported" ] && return 1
1375 output
3 'Reloading dnsmasq '
1376 if /etc
/init.d
/dnsmasq reload
>/dev
/null
2>&1; then
1385 [ -z "$resolver_set_supported" ] && return 1
1386 output
3 'Restarting dnsmasq '
1387 if /etc
/init.d
/dnsmasq restart
>/dev
/null
2>&1; then
1396 [ -z "$resolver_set_supported" ] && return 1
1397 local resolverNewHash
1398 if [ -s "$dnsmasqFile" ]; then
1399 resolverNewHash
="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')"
1401 [ "$resolverNewHash" != "$resolverStoredHash" ]
1404 [ -s "$dnsmasqFile" ] && resolverStoredHash
="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')";;
1409 add_resolver_element
) :;;
1410 create_resolver_set
) :;;
1425 add_resolver_element
) :;;
1426 create_resolver_set
) :;;
1444 output
"Unexpected exit or service termination: '${1}'!\\n"
1445 state add
'errorSummary' 'errorUnexpectedExit' "$1"
1446 traffic_killswitch
'remove'
1449 traffic_killswitch
() {
1453 local lan_subnet wan_device wan6_device
1454 [ -n "$secure_reload" ] ||
return 0
1455 nft_file
'enabled' && return 0
1456 for i
in $serviceTrapSignals; do
1457 # shellcheck disable=SC2064
1458 trap "trap_process $i" "$i"
1460 output
3 'Activating traffic killswitch '
1461 network_get_subnet lan_subnet
"${procd_lan_interface:-lan}"
1462 network_get_physdev wan_device
"${wanIface4:-wan}"
1463 network_get_physdev wan6_device
"${wanIface6:-wan6}"
1464 if is_nft_mode
; then
1465 nft_call add chain inet
"$nftTable" "${nftPrefix}_killswitch" '{ type filter hook forward priority 0; policy accept; }' || s
=1
1466 nft_call add rule inet
"$nftTable" "${nftPrefix}_killswitch" oifname
"$wan_device" "$nftIPv4Flag" saddr
"$lan_subnet" counter reject || s
=1
1467 nft_call add rule inet
"$nftTable" "${nftPrefix}_killswitch" oifname
"$wan6_device" "$nftIPv6Flag" saddr
"$lan_subnet" counter reject
1469 ipt
-N "${iptPrefix}_KILLSWITCH" || s
=1
1470 ipt
-A "${iptPrefix}_KILLSWITCH" -s "$lan_subnet" -o "$wan_device" -j REJECT || s
=1
1471 ipt
-A "${iptPrefix}_KILLSWITCH" -s "$lan_subnet" -o "$wan6_device" -j REJECT
1472 ipt
-I FORWARD
-j "${iptPrefix}_KILLSWITCH" || s
=1
1474 if [ "$s" -eq 0 ]; then
1481 if [ -n "$secure_reload" ] && ! nft_file
'enabled'; then
1482 output
3 'Deactivating traffic killswitch '
1484 if is_nft_mode
; then
1485 nft_call flush chain inet
"$nftTable" "${nftPrefix}_killswitch" || s
=1
1486 nft_call delete chain inet
"$nftTable" "${nftPrefix}_killswitch" || s
=1
1488 ipt
-D FORWARD
-j "${iptPrefix}_KILLSWITCH" || s
=1
1489 ipt
-F "${iptPrefix}_KILLSWITCH" || s
=1
1490 ipt
-X "${iptPrefix}_KILLSWITCH" || s
=1
1492 if [ -n "$secure_reload" ] && ! nft_file
'enabled'; then
1493 if [ "$s" -eq 0 ]; then
1499 # shellcheck disable=SC2086
1500 trap - $serviceTrapSignals
1505 policy_routing
() { if is_nft_mode
; then policy_routing_nft
"$@"; else policy_routing_iptables
"$@"; fi; }
1506 policy_routing_iptables
() {
1507 local mark param4 param6 i negation value dest4 dest6 ipInsertOption
="-A"
1508 local ip4error
='1' ip6error
='1'
1509 local name
="$1" iface
="$2" laddr
="$3" lport
="$4" raddr
="$5" rport
="$6" proto chain uid
="$9"
1510 proto
="$(str_to_lower "$7")"
1511 chain
="$(str_to_upper "$8")"
1512 chain
="${chain:-PREROUTING}"
1513 mark
=$
(eval echo "\$mark_${iface//-/_}")
1515 if [ -n "$ipv6_enabled" ] && { is_ipv6
"$laddr" || is_ipv6
"$raddr"; }; then
1516 processPolicyError
='true'
1517 state add
'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
1521 if is_tor
"$iface"; then
1523 elif is_xray
"$iface"; then
1525 [ -z "$lport" ] && lport
='0-52,54-65535'
1527 dest4
="-j TPROXY --on-ip 0.0.0.0 --on-port $(get_xray_traffic_port "$iface")"
1528 dest6
="-j TPROXY --on-ip :: --on-port $(get_xray_traffic_port "$iface")"
1529 elif [ -n "$mark" ]; then
1530 dest4
="-g ${iptPrefix}_MARK_${mark}"
1531 dest6
="-g ${iptPrefix}_MARK_${mark}"
1532 elif [ "$iface" = "ignore" ]; then
1536 processPolicyError
='true'
1537 state add
'errorSummary' 'errorPolicyProcessUnknownFwmark' "$iface"
1541 if is_family_mismatch
"$laddr" "$raddr"; then
1542 processPolicyError
='true'
1543 state add
'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$laddr' '$raddr'"
1547 if [ -z "$proto" ]; then
1548 if [ -n "${lport}${rport}" ]; then
1556 if [ "$i" = 'all' ]; then
1557 param4
="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest4"
1558 param6
="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest6"
1559 elif ! is_supported_protocol
"$i"; then
1560 processPolicyError
='true'
1561 state add
'errorSummary' 'errorPolicyProcessUnknownProtocol' "${name}: '$i'"
1564 param4
="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest4 -p $i"
1565 param6
="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest6 -p $i"
1568 if [ -n "$laddr" ]; then
1569 if [ "${laddr:0:1}" = "!" ]; then
1570 negation
='!'; value
="${laddr:1}"
1572 unset negation
; value
="$laddr";
1574 if is_phys_dev
"$value"; then
1575 param4
="$param4 ${negation:+$negation }-m physdev --physdev-in ${value:1}"
1576 param6
="$param6 ${negation:+$negation }-m physdev --physdev-in ${value:1}"
1577 elif is_ipv4_netmask
"$value"; then
1578 local target
='src' type='net'
1579 if ips
'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
1580 ips
'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
1581 param4
="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1582 param6
="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1584 param4
="$param4 ${negation:+$negation }-s $value"
1585 param6
="$param6 ${negation:+$negation }-s $value"
1587 elif is_mac_address
"$value"; then
1588 local target
='src' type='mac'
1589 if ips
'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
1590 ips
'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
1591 param4
="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1592 param6
="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1594 param4
="$param4 -m mac ${negation:+$negation }--mac-source $value"
1595 param6
="$param6 -m mac ${negation:+$negation }--mac-source $value"
1598 local target
='src' type='ip'
1599 if ips
'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
1600 ips
'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
1601 param4
="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1602 param6
="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1604 local resolvedIP4 resolvedIP6
1605 resolvedIP4
="$(resolveip_to_ipt4 "$value")"
1606 resolvedIP6
="$(resolveip_to_ipt6 "$value")"
1607 if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
1608 state add
'errorSummary' 'errorFailedToResolve' "$value"
1610 param4
="$param4 ${negation:+$negation }-s $resolvedIP4"
1611 param6
="$param6 ${negation:+$negation }-s $resolvedIP6"
1616 if [ -n "$lport" ]; then
1617 if [ "${lport:0:1}" = "!" ]; then
1618 negation
='!'; value
="${lport:1}"
1620 unset negation
; value
="$lport";
1622 param4
="$param4 -m multiport ${negation:+$negation }--sport ${value//-/:}"
1623 param6
="$param6 -m multiport ${negation:+$negation }--sport ${value//-/:}"
1626 if [ -n "$raddr" ]; then
1627 if [ "${raddr:0:1}" = "!" ]; then
1628 negation
='!'; value
="${raddr:1}"
1630 unset negation
; value
="$raddr";
1632 if is_ipv4_netmask
"$value"; then
1633 local target
='dst' type='net'
1634 if ips
'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
1635 ips
'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
1636 param4
="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1637 param6
="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1639 param4
="$param4 ${negation:+$negation }-d ${value}"
1640 param6
="$param6 ${negation:+$negation }-d ${value}"
1642 elif is_domain
"$value"; then
1643 local target
='dst' type='ip'
1644 if resolver
'create_resolver_set' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
1645 resolver
'add_resolver_element' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
1646 param4
="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1647 param6
="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1648 elif ips
'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
1649 ips
'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
1650 param4
="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1651 param6
="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1653 local resolvedIP4 resolvedIP6
1654 resolvedIP4
="$(resolveip_to_ipt4 "$value")"
1655 resolvedIP6
="$(resolveip_to_ipt6 "$value")"
1656 if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
1657 state add
'errorSummary' 'errorFailedToResolve' "$value"
1659 param4
="$param4 ${negation:+$negation }-d $resolvedIP4"
1660 param6
="$param6 ${negation:+$negation }-d $resolvedIP6"
1663 local target
='dst' type='ip'
1664 if ips
'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
1665 ips
'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
1666 param4
="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
1667 param6
="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
1669 param4
="$param4 ${negation:+$negation }-d ${value}"
1670 param6
="$param6 ${negation:+$negation }-d ${value}"
1675 if [ -n "$rport" ]; then
1676 if [ "${rport:0:1}" = "!" ]; then
1677 negation
='!'; value
="${rport:1}"
1679 unset negation
; value
="$rport";
1681 param4
="$param4 -m multiport ${negation:+$negation }--dport ${value//-/:}"
1682 param6
="$param6 -m multiport ${negation:+$negation }--dport ${value//-/:}"
1685 if [ -n "$name" ]; then
1686 param4
="$param4 -m comment --comment $(str_extras_to_underscore "$name")"
1687 param6
="$param6 -m comment --comment $(str_extras_to_underscore "$name")"
1690 local ipv4_error
='0' ipv6_error
='0'
1691 if [ "$param4" = "$param6" ]; then
1692 ipt4
"$param4" || ipv4_error
='1'
1694 ipt4
"$param4" || ipv4_error
='1'
1695 ipt6
"$param6" || ipv6_error
='1'
1698 if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
1699 processPolicyError
='true'
1700 state add
'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
1701 state add
'errorSummary' 'errorPolicyProcessCMD' "iptables $param4"
1702 state add
'errorSummary' 'errorPolicyProcessCMD' "iptables $param6"
1703 logger
-t "$packageName" "ERROR: iptables $param4"
1704 logger
-t "$packageName" "ERROR: iptables $param6"
1705 elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
1706 processPolicyError
='true'
1707 state add
'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
1708 state add
'errorSummary' 'errorPolicyProcessCMD' "iptables $param4"
1709 logger
-t "$packageName" "ERROR: iptables $param4"
1714 policy_routing_nft
() {
1715 local mark i nftInsertOption
='add'
1716 local param4 param6 proto_i negation value dest4 dest6
1717 local name
="$1" iface
="$2" src_addr
="$3" src_port
="$4" dest_addr
="$5" dest_port
="$6" proto chain uid
="$9"
1718 proto
="$(str_to_lower "$7")"
1719 chain
="$(str_to_lower "$8")"
1720 chain
="${chain:-prerouting}"
1721 mark
=$
(eval echo "\$mark_${iface//-/_}")
1723 if [ -z "$ipv6_enabled" ] && { is_ipv6
"$src_addr" || is_ipv6
"$dest_addr"; }; then
1724 processPolicyError
='true'
1725 state add
'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
1729 if is_tor
"$iface"; then
1732 elif is_xray
"$iface"; then
1734 [ -z "$src_port" ] && src_port
='0-65535'
1735 dest4
="tproxy $nftIPv4Flag to: $(get_xray_traffic_port "$iface") accept"
1736 dest6
="tproxy $nftIPv6Flag to: $(get_xray_traffic_port "$iface") accept"
1737 elif [ -n "$mark" ]; then
1738 dest4
="goto ${nftPrefix}_mark_${mark}"
1739 dest6
="goto ${nftPrefix}_mark_${mark}"
1740 elif [ "$iface" = "ignore" ]; then
1744 processPolicyError
='true'
1745 state add
'errorSummary' 'errorPolicyProcessUnknownFwmark' "$iface"
1749 if is_family_mismatch
"$src_addr" "$dest_addr"; then
1750 processPolicyError
='true'
1751 state add
'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_addr'"
1755 if [ -z "$proto" ]; then
1756 if [ -n "${src_port}${dest_port}" ]; then
1763 for proto_i
in $proto; do
1766 if [ "$proto_i" = 'all' ]; then
1768 elif ! is_supported_protocol
"$proto_i"; then
1769 processPolicyError
='true'
1770 state add
'errorSummary' 'errorPolicyProcessUnknownProtocol' "${name}: '$proto_i'"
1774 if [ -n "$src_addr" ]; then
1775 if [ "${src_addr:0:1}" = "!" ]; then
1776 negation
='!='; value
="${src_addr:1}"
1778 unset negation
; value
="$src_addr";
1780 if is_phys_dev
"$value"; then
1781 param4
="${param4:+$param4 }iifname ${negation:+$negation }${value:1}"
1782 param6
="${param6:+$param6 }iifname ${negation:+$negation }${value:1}"
1783 elif is_mac_address
"$value"; then
1784 local target
='src' type='mac'
1785 if nftset
'create' "$iface" "$target" "$type" "$uid" "$name" && \
1786 nftset
'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1787 param4
="${param4:+$param4 }ether saddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1788 param6
="${param6:+$param6 }ether saddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1790 param4
="${param4:+$param4 }ether saddr ${negation:+$negation }${value}"
1791 param6
="${param6:+$param6 }ether saddr ${negation:+$negation }${value}"
1794 local target
='src' type='ip'
1795 if nftset
'create' "$iface" "$target" "$type" "$uid" "$name" && \
1796 nftset
'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1797 param4
="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1798 param6
="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1800 param4
="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }${value}"
1801 param6
="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }${value}"
1806 if [ -n "$dest_addr" ]; then
1807 if [ "${dest_addr:0:1}" = "!" ]; then
1808 negation
='!='; value
="${dest_addr:1}"
1810 unset negation
; value
="$dest_addr";
1812 if is_phys_dev
"$value"; then
1813 param4
="${param4:+$param4 }oifname ${negation:+$negation }${value:1}"
1814 param6
="${param6:+$param6 }oifname ${negation:+$negation }${value:1}"
1815 elif is_domain
"$value"; then
1816 local target
='dst' type='ip'
1817 if resolver
'create_resolver_set' "$iface" "$target" "$type" "$uid" "$name" && \
1818 resolver
'add_resolver_element' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1819 param4
="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1820 param6
="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1821 elif nftset
'create' "$iface" "$target" "$type" "$uid" "$name" && \
1822 nftset
'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1823 param4
="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1824 param6
="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1826 local resolvedIP4 resolvedIP6
1827 resolvedIP4
="$(resolveip_to_nftset4 "$value")"
1828 resolvedIP6
="$(resolveip_to_nftset6 "$value")"
1829 if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
1830 state add
'errorSummary' 'errorFailedToResolve' "$value"
1832 param4
="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }{ $resolvedIP4 }"
1833 param6
="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }{ $resolvedIP6 }"
1836 local target
='dst' type='ip'
1837 if nftset
'create' "$iface" "$target" "$type" "$uid" "$name" && \
1838 nftset
'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
1839 param4
="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
1840 param6
="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
1842 param4
="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }${value}"
1843 param6
="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }${value}"
1848 if [ -n "$src_port" ]; then
1849 if [ "${src_port:0:1}" = "!" ]; then
1850 negation
='!='; value
="${src_port:1}"
1852 unset negation
; value
="$src_port";
1854 param4
="${param4:+$param4 }${proto_i:+$proto_i }sport ${negation:+$negation }{$(ports_to_nftset "$value")}"
1855 param6
="${param6:+$param6 }${proto_i:+$proto_i }sport ${negation:+$negation }{$(ports_to_nftset "$value")}"
1858 if [ -n "$dest_port" ]; then
1859 if [ "${dest_port:0:1}" = "!" ]; then
1860 negation
='!='; value
="${dest_port:1}"
1862 unset negation
; value
="$dest_port";
1864 param4
="${param4:+$param4 }${proto_i:+$proto_i }dport ${negation:+$negation }{$(ports_to_nftset "$value")}"
1865 param6
="${param6:+$param6 }${proto_i:+$proto_i }dport ${negation:+$negation }{$(ports_to_nftset "$value")}"
1868 if is_tor
"$iface"; then
1869 local dest_udp_53 dest_tcp_80 dest_udp_80 dest_tcp_443 dest_udp_443
1870 local ipv4_error
='0' ipv6_error
='0'
1871 local dest_i dest4 dest6
1872 param4
="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} dstnat meta nfproto ipv4 $param4"
1873 param6
="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} dstnat meta nfproto ipv6 $param6"
1874 dest_udp_53
="udp dport 53 counter redirect to :${torDnsPort} comment 'Tor-DNS-UDP'"
1875 dest_tcp_80
="tcp dport 80 counter redirect to :${torTrafficPort} comment 'Tor-HTTP-TCP'"
1876 dest_udp_80
="udp dport 80 counter redirect to :${torTrafficPort} comment 'Tor-HTTP-UDP'"
1877 dest_tcp_443
="tcp dport 443 counter redirect to :${torTrafficPort} comment 'Tor-HTTPS-TCP'"
1878 dest_udp_443
="udp dport 443 counter redirect to :${torTrafficPort} comment 'Tor-HTTPS-UDP'"
1879 for dest_i
in dest_udp_53 dest_tcp_80 dest_udp_80 dest_tcp_443 dest_udp_443
; do
1880 eval "dest4=\$$dest_i"
1881 eval "dest6=\$$dest_i"
1882 nft4
"$param4" "$dest4" || ipv4_error
='1'
1883 nft6
"$param6" "$dest6" || ipv6_error
='1'
1884 if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
1885 processPolicyError
='true'
1886 state add
'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
1887 state add
'errorSummary' 'errorPolicyProcessCMD' "nft $param4 $dest4"
1888 state add
'errorSummary' 'errorPolicyProcessCMD' "nft $param6 $dest6"
1889 logger
-t "$packageName" "ERROR: nft $param4 $dest4"
1890 logger
-t "$packageName" "ERROR: nft $param6 $dest6"
1891 elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
1892 processPolicyError
='true'
1893 state add
'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
1894 state add
'errorSummary' 'errorPolicyProcessCMD' "nft $param4 $dest4"
1895 logger
-t "$packageName" "ERROR: nft $param4 $dest4"
1899 param4
="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param4 $dest4 comment \"$name\""
1900 param6
="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param6 $dest6 comment \"$name\""
1901 local ipv4_error
='0' ipv6_error
='0'
1902 if [ "$policy_routing_nft_prev_param4" != "$param4" ]; then
1903 nft4
"$param4" || ipv4_error
='1'
1904 policy_routing_nft_prev_param4
="$param4"
1906 if [ "$policy_routing_nft_prev_param6" != "$param6" ] && \
1907 [ "$param4" != "$param6" ]; then
1908 nft6
"$param6" || ipv6_error
='1'
1909 policy_routing_nft_prev_param6
="$param6"
1912 if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
1913 processPolicyError
='true'
1914 state add
'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
1915 state add
'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
1916 state add
'errorSummary' 'errorPolicyProcessCMD' "nft $param6"
1917 logger
-t "$packageName" "ERROR: nft $param4"
1918 logger
-t "$packageName" "ERROR: nft $param6"
1919 elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
1920 processPolicyError
='true'
1921 state add
'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
1922 state add
'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
1923 logger
-t "$packageName" "ERROR: nft $param4"
1931 if [ -z "$uid" ]; then # first non-recursive call
1932 [ "$enabled" -gt 0 ] ||
return 0
1933 unset processPolicyError
1935 if is_nft_mode
; then
1936 chain
="$(str_to_lower "$chain")"
1938 chain
="$(str_to_upper "$chain")"
1940 proto
="$(str_to_lower "$proto")"
1941 [ "$proto" = 'auto' ] && unset proto
1942 [ "$proto" = 'all' ] && unset proto
1943 output
2 "Routing '$name' via $interface "
1944 if [ -z "${src_addr}${src_port}${dest_addr}${dest_port}" ]; then
1945 state add
'errorSummary' 'errorPolicyNoSrcDest' "$name"
1946 output_fail
; return 1;
1948 if [ -z "$interface" ]; then
1949 state add
'errorSummary' 'errorPolicyNoInterface' "$name"
1950 output_fail
; return 1;
1952 if ! is_supported_interface
"$interface"; then
1953 state add
'errorSummary' 'errorPolicyUnknownInterface' "$name"
1954 output_fail
; return 1;
1956 src_port
="${src_port// / }"; src_port="${src_port// /,}"; src_port="${src_port//,\!/ !}";
1957 dest_port
="${dest_port// / }"; dest_port="${dest_port// /,}"; dest_port="${dest_port//,\!/ !}";
1958 policy_process
"$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
1959 if [ -n "$processPolicyError" ]; then
1964 else # recursive call, get options from passed variables
1965 local name
="$1" interface
="$2" src_addr
="$3" src_port
="$4" dest_addr
="$5" dest_port
="$6" proto
="$7" chain
="$8"
1966 if str_contains
"$src_addr" '[ ;\{\}]'; then
1967 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
1968 elif str_contains
"$src_port" '[ ;\{\}]'; then
1969 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
1970 elif str_contains
"$dest_addr" '[ ;\{\}]'; then
1971 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
1972 elif str_contains
"$dest_port" '[ ;\{\}]'; then
1973 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
1974 elif str_contains
"$proto" '[ ;\{\}]'; then
1975 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
1977 if [ -n "$secure_reload" ] && { is_url_dl
"$src_addr" || is_url_dl
"$dest_addr"; }; then
1978 state add
'errorSummary' 'errorNoDownloadWithSecureReload' "$name"
1979 elif is_url
"$src_addr"; then
1980 src_addr
="$(process_url "$src_addr")"
1981 [ -n "$src_addr" ] && policy_process
"$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
1982 elif is_url
"$dest_addr"; then
1983 dest_addr
="$(process_url "$dest_addr")"
1984 [ -n "$dest_addr" ] && policy_process
"$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
1986 policy_routing
"$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
1994 state add
'errorSummary' 'errorTryFailed' "$*"
1999 interface_routing
() {
2000 local action
="$1" tid
="$2" mark
="$3" iface
="$4" gw4
="$5" dev
="$6" gw6
="$7" dev6
="$8" priority
="$9"
2001 local dscp s
=0 i ipv4_error
=1 ipv6_error
=1
2002 if [ -z "$tid" ] ||
[ -z "$mark" ] ||
[ -z "$iface" ]; then
2003 state add
'errorSummary' 'errorInterfaceRoutingEmptyValues'
2008 if is_netifd_table_interface
"$iface"; then
2010 $ip_bin -4 rule del table
"$tid" >/dev
/null
2>&1
2011 try
"$ip_bin" -4 rule add fwmark
"${mark}/${fw_mask}" table
"$tid" priority
"$priority" || ipv4_error
=1
2012 if is_nft_mode
; then
2013 try nft add chain inet
"$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error
=1
2014 try nft add rule inet
"$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error
=1
2015 try nft add rule inet
"$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error
=1
2017 ipt
-t mangle
-N "${iptPrefix}_MARK_${mark}" || ipv4_error
=1
2018 ipt
-t mangle
-A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error
=1
2019 ipt
-t mangle
-A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error
=1
2021 if [ -n "$ipv6_enabled" ]; then
2023 $ip_bin -6 rule del table
"$tid" >/dev
/null
2>&1
2024 try
"$ip_bin" -6 rule add fwmark
"${mark}/${fw_mask}" table
"$tid" priority
"$((priority-1))" || ipv6_error
=1
2027 if ! grep -q "$tid ${ipTablePrefix}_${iface}" "$rtTablesFile"; then
2028 sed -i "/${ipTablePrefix}_${iface}/d" "$rtTablesFile"
2030 echo "$tid ${ipTablePrefix}_${iface}" >> "$rtTablesFile"
2033 $ip_bin -4 rule del table
"$tid" >/dev
/null
2>&1
2034 $ip_bin -4 route flush table
"$tid" >/dev
/null
2>&1
2035 if [ -n "$gw4" ] ||
[ "$strict_enforcement" -ne 0 ]; then
2037 if [ -z "$gw4" ]; then
2038 try
"$ip_bin" -4 route add unreachable default table
"$tid" >/dev
/null
2>&1 || ipv4_error
=1
2040 try
"$ip_bin" -4 route add default via
"$gw4" dev
"$dev" table
"$tid" >/dev
/null
2>&1 || ipv4_error
=1
2042 # shellcheck disable=SC2086
2044 i
="$(echo "$i" | sed 's/ linkdown$//')"
2045 i
="$(echo "$i" | sed 's/ onlink$//')"
2046 idev
="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')"
2047 if ! is_supported_iface_dev
"$idev"; then
2048 try
"$ip_bin" -4 route add
$i table
"$tid" >/dev
/null
2>&1 || ipv4_error
=1
2051 $($ip_bin -4 route list table main)
2053 try
"$ip_bin" -4 rule add fwmark
"${mark}/${fw_mask}" table
"$tid" priority
"$priority" || ipv4_error
=1
2054 if is_nft_mode
; then
2055 try nft add chain inet
"$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error
=1
2056 try nft add rule inet
"$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error
=1
2057 try nft add rule inet
"$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error
=1
2059 ipt
-t mangle
-N "${iptPrefix}_MARK_${mark}" || ipv4_error
=1
2060 ipt
-t mangle
-A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error
=1
2061 ipt
-t mangle
-A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error
=1
2064 if [ -n "$ipv6_enabled" ]; then
2066 $ip_bin -6 rule del table
"$tid" >/dev
/null
2>&1
2067 $ip_bin -6 route flush table
"$tid" >/dev
/null
2>&1
2068 if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } ||
[ "$strict_enforcement" -ne 0 ]; then
2069 if [ -z "$gw6" ] ||
[ "$gw6" = "::/0" ]; then
2070 try
"$ip_bin" -6 route add unreachable default table
"$tid" >/dev
/null
2>&1 || ipv6_error
=1
2071 elif "$ip_bin" -6 route list table main |
grep -q " dev $dev6 "; then
2072 "$ip_bin" -6 route add default via
"$gw6" dev
"$dev6" table
"$tid" >/dev
/null
2>&1 || ipv6_error
=1
2074 i
="$(echo "$i" | sed 's/ linkdown$//')"
2075 i
="$(echo "$i" | sed 's/ onlink$//')"
2076 # shellcheck disable=SC2086
2077 try
"$ip_bin" -6 route add
$i table
"$tid" >/dev
/null
2>&1 || ipv6_error
=1
2079 $($ip_bin -6 route list table main | grep " dev $dev6 ")
2082 try
"$ip_bin" -6 route add
"$($ip_bin -6 -o a show "$dev6" | awk '{print $4}')" dev
"$dev6" table
"$tid" >/dev
/null
2>&1 || ipv6_error
=1
2083 try
"$ip_bin" -6 route add default dev
"$dev6" table
"$tid" >/dev
/null
2>&1 || ipv6_error
=1
2086 try
"$ip_bin" -6 rule add fwmark
"${mark}/${fw_mask}" table
"$tid" priority
"$((priority-1))" >/dev
/null
2>&1 || ipv6_error
=1
2089 if [ "$ipv4_error" -eq 0 ] ||
[ "$ipv6_error" -eq 0 ]; then
2090 dscp
="$(uci_get "$packageName" 'config' "${iface}_dscp
")"
2091 if is_nft_mode
; then
2092 if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
2093 try nft add rule inet
"$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} goto ${nftPrefix}_mark_${mark}" || s
=1
2094 if [ -n "$ipv6_enabled" ]; then
2095 try nft add rule inet
"$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} goto ${nftPrefix}_mark_${mark}" || s
=1
2098 if [ "$iface" = "$icmp_interface" ]; then
2099 try nft add rule inet
"$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp goto ${nftPrefix}_mark_${mark}" || s
=1
2100 if [ -n "$ipv6_enabled" ]; then
2101 try nft add rule inet
"$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp goto ${nftPrefix}_mark_${mark}" || s
=1
2105 if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
2106 ipt
-t mangle
-I "${iptPrefix}_PREROUTING" -m dscp --dscp "${dscp}" -g "${iptPrefix}_MARK_${mark}" || s
=1
2108 if [ "$iface" = "$icmp_interface" ]; then
2109 ipt
-t mangle
-I "${iptPrefix}_OUTPUT" -p icmp -g "${iptPrefix}_MARK_${mark}" || s
=1
2118 if is_nft_mode
; then
2119 nftset
'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s
=1
2120 nftset
'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s
=1
2121 nftset
'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s
=1
2123 ips
'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s
=1
2124 ips
'create_user_set' "$iface" 'dst' 'net' 'user' '' "$mark" || s
=1
2125 ips
'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s
=1
2126 ips
'create_user_set' "$iface" 'src' 'net' 'user' '' "$mark" || s
=1
2127 ips
'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s
=1
2132 $ip_bin rule del table
"$tid" >/dev
/null
2>&1
2133 if ! is_netifd_table_interface
"$iface"; then
2134 $ip_bin route flush table
"$tid" >/dev
/null
2>&1
2135 sed -i "/${ipTablePrefix}_${iface}\$/d" "$rtTablesFile"
2141 is_netifd_table_interface
"$iface" && return 0;
2143 $ip_bin rule del table
"$tid" >/dev
/null
2>&1
2144 if ! is_netifd_table_interface
"$iface"; then
2145 $ip_bin route flush table
"$tid" >/dev
/null
2>&1
2147 if [ -n "$gw4" ] ||
[ "$strict_enforcement" -ne 0 ]; then
2148 if [ -z "$gw4" ]; then
2149 try
"$ip_bin" -4 route add unreachable default table
"$tid" >/dev
/null
2>&1 || ipv4_error
=1
2151 try
"$ip_bin" -4 route add default via
"$gw4" dev
"$dev" table
"$tid" >/dev
/null
2>&1 || ipv4_error
=1
2153 try
"$ip_bin" rule add fwmark
"${mark}/${fw_mask}" table
"$tid" priority
"$priority" || ipv4_error
=1
2155 if [ -n "$ipv6_enabled" ]; then
2157 if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } ||
[ "$strict_enforcement" -ne 0 ]; then
2158 if [ -z "$gw6" ] ||
[ "$gw6" = "::/0" ]; then
2159 try
"$ip_bin" -6 route add unreachable default table
"$tid" || ipv6_error
=1
2160 elif $ip_bin -6 route list table main |
grep -q " dev $dev6 "; then
2162 # shellcheck disable=SC2086
2163 try
"$ip_bin" -6 route add
$i table
"$tid" >/dev
/null
2>&1 || ipv6_error
=1
2165 $($ip_bin -6 route list table main | grep " dev $dev6 ")
2168 try
"$ip_bin" -6 route add
"$($ip_bin -6 -o a show "$dev6" | awk '{print $4}')" dev
"$dev6" table
"$tid" >/dev
/null
2>&1 || ipv6_error
=1
2169 try
"$ip_bin" -6 route add default dev
"$dev6" table
"$tid" >/dev
/null
2>&1 || ipv6_error
=1
2172 try
"$ip_bin" -6 rule add fwmark
"${mark}/${fw_mask}" table
"$tid" priority
"$priority" || ipv6_error
=1
2174 if [ "$ipv4_error" -eq 0 ] ||
[ "$ipv6_error" -eq 0 ]; then
2184 json_add_gateway
() {
2185 local action
="$1" tid
="$2" mark
="$3" iface
="$4" gw4
="$5" dev4
="$6" gw6
="$7" dev6
="$8" priority
="$9" default
="${10}"
2187 json_add_string
'name' "$iface"
2188 json_add_string
'device_ipv4' "$dev4"
2189 json_add_string
'gateway_ipv4' "$gw4"
2190 json_add_string
'device_ipv6' "$dev6"
2191 json_add_string
'gateway_ipv6' "$gw6"
2192 if [ -n "$default" ]; then
2193 json_add_boolean
'default' '1'
2195 json_add_boolean
'default' '0'
2197 json_add_string
'action' "$action"
2198 json_add_string
'table_id' "$tid"
2199 json_add_string
'mark' "$mark"
2200 json_add_string
'priority' "$priority"
2204 interface_process
() {
2205 local gw4 gw6 dev dev6 s
=0 dscp iface
="$1" action
="$2" reloadedIface
="$3"
2206 local displayText dispDev dispGw4 dispGw6 dispStatus
2208 if [ "$iface" = 'all' ] && [ "$action" = 'prepare' ]; then
2209 config_load
'network'
2210 ifaceMark
="$(printf '0x%06x' "$wan_mark")"
2211 ifacePriority
="$wan_ip_rules_priority"
2216 if [ "$iface" = 'tor' ]; then
2219 torDnsPort
="$(get_tor_dns_port)"
2220 torTrafficPort
="$(get_tor_traffic_port)"
2221 displayText
="${iface}/53->${torDnsPort}/80,443->${torTrafficPort}"
2222 gatewaySummary
="${gatewaySummary}${displayText}\\n"
2230 is_supported_interface
"$iface" ||
return 0
2231 is_wan6
"$iface" && return 0
2232 [ $
((ifaceMark
)) -gt $
((fw_mask
)) ] && return 1
2234 if is_ovpn
"$iface" && ! is_ovpn_valid
"$iface"; then
2235 : || state add
'warningSummary' 'warningInvalidOVPNConfig' "$iface"
2238 network_get_device dev
"$iface"
2239 [ -z "$dev" ] && network_get_physdev dev
"$iface"
2240 if is_wan
"$iface" && [ -n "$wanIface6" ] && str_contains
"$wanIface6" "$iface"; then
2241 network_get_device dev6
"$wanIface6"
2242 [ -z "$dev6" ] && network_get_physdev dev6
"$wanIface6"
2245 [ -z "$dev6" ] && dev6
="$dev"
2246 [ -z "$ifaceMark" ] && ifaceMark
="$(printf '0x%06x' "$wan_mark")"
2247 [ -z "$ifacePriority" ] && ifacePriority
="$wan_ip_rules_priority"
2251 [ -z "$ifaceTableID" ] && ifaceTableID
="$(get_rt_tables_non_pbr_next_id)"
2252 eval "pre_init_mark_${iface//-/_}"='$ifaceMark'
2253 eval "pre_init_priority_${iface//-/_}"='$ifacePriority'
2254 eval "pre_init_tid_${iface//-/_}"='$ifaceTableID'
2255 ifaceMark
="$(printf '0x%06x' $((ifaceMark + wan_mark)))"
2256 ifacePriority
="$((ifacePriority - 1))"
2257 ifaceTableID
="$((ifaceTableID + 1))"
2261 ifaceTableID
="$(get_rt_tables_id "$iface")"
2262 [ -z "$ifaceTableID" ] && ifaceTableID
="$(get_rt_tables_next_id)"
2263 eval "mark_${iface//-/_}"='$ifaceMark'
2264 eval "tid_${iface//-/_}"='$ifaceTableID'
2265 pbr_get_gateway4 gw4
"$iface" "$dev"
2266 pbr_get_gateway6 gw6
"$iface" "$dev6"
2267 dispGw4
="${gw4:-0.0.0.0}"
2268 dispGw6
="${gw6:-::/0}"
2269 [ "$iface" != "$dev" ] && dispDev
="$dev"
2270 if is_default_dev
"$dev"; then
2271 [ "$verbosity" = '1' ] && dispStatus
="$_OK_" || dispStatus
="$__OK__"
2273 displayText
="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
2274 output
2 "Setting up routing for '$displayText' "
2275 if interface_routing
'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
2276 json_add_gateway
'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
2277 gatewaySummary
="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
2278 if is_netifd_table_interface
"$iface"; then output_okb
; else output_ok
; fi
2280 state add
'errorSummary' 'errorFailedSetup' "$displayText"
2285 ifaceTableID
="$(get_rt_tables_id "$iface")"
2286 [ -z "$ifaceTableID" ] && ifaceTableID
="$(get_rt_tables_next_id)"
2287 eval "mark_${iface//-/_}"='$ifaceMark'
2288 eval "tid_${iface//-/_}"='$ifaceTableID'
2289 pbr_get_gateway4 gw4
"$iface" "$dev"
2290 pbr_get_gateway6 gw6
"$iface" "$dev6"
2291 dispGw4
="${gw4:-0.0.0.0}"
2292 dispGw6
="${gw6:-::/0}"
2293 [ "$iface" != "$dev" ] && dispDev
="$dev"
2294 if is_default_dev
"$dev"; then
2295 [ "$verbosity" = '1' ] && dispStatus
="$_OK_" || dispStatus
="$__OK__"
2297 displayText
="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
2298 interface_routing
'create_user_set' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"
2301 ifaceTableID
="$(get_rt_tables_id "$iface")"
2302 [ -z "$ifaceTableID" ] && ifaceTableID
="$(get_rt_tables_next_id)"
2303 eval "mark_${iface//-/_}"='$ifaceMark'
2304 eval "tid_${iface//-/_}"='$ifaceTableID'
2305 pbr_get_gateway4 gw4
"$iface" "$dev"
2306 pbr_get_gateway6 gw6
"$iface" "$dev6"
2307 dispGw4
="${gw4:-0.0.0.0}"
2308 dispGw6
="${gw6:-::/0}"
2309 [ "$iface" != "$dev" ] && dispDev
="$dev"
2310 if is_default_dev
"$dev"; then
2311 [ "$verbosity" = '1' ] && dispStatus
="$_OK_" || dispStatus
="$__OK__"
2313 displayText
="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
2314 displayText
="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
2315 output
2 "Removing routing for '$displayText' "
2316 interface_routing
'destroy' "${ifaceTableID}" "${ifaceMark}" "${iface}"
2317 if is_netifd_table_interface
"$iface"; then output_okb
; else output_ok
; fi
2320 ifaceTableID
="$(get_rt_tables_id "$iface")"
2321 [ -z "$ifaceTableID" ] && ifaceTableID
="$(get_rt_tables_next_id)"
2322 eval "mark_${iface//-/_}"='$ifaceMark'
2323 eval "tid_${iface//-/_}"='$ifaceTableID'
2324 pbr_get_gateway4 gw4
"$iface" "$dev"
2325 pbr_get_gateway6 gw6
"$iface" "$dev6"
2326 dispGw4
="${gw4:-0.0.0.0}"
2327 dispGw6
="${gw6:-::/0}"
2328 [ "$iface" != "$dev" ] && dispDev
="$dev"
2329 if is_default_dev
"$dev"; then
2330 [ "$verbosity" = '1' ] && dispStatus
="$_OK_" || dispStatus
="$__OK__"
2332 displayText
="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
2333 gatewaySummary
="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
2336 ifaceTableID
="$(get_rt_tables_id "$iface")"
2337 [ -z "$ifaceTableID" ] && ifaceTableID
="$(get_rt_tables_next_id)"
2338 eval "mark_${iface//-/_}"='$ifaceMark'
2339 eval "tid_${iface//-/_}"='$ifaceTableID'
2340 pbr_get_gateway4 gw4
"$iface" "$dev"
2341 pbr_get_gateway6 gw6
"$iface" "$dev6"
2342 dispGw4
="${gw4:-0.0.0.0}"
2343 dispGw6
="${gw6:-::/0}"
2344 [ "$iface" != "$dev" ] && dispDev
="$dev"
2345 if is_default_dev
"$dev"; then
2346 [ "$verbosity" = '1' ] && dispStatus
="$_OK_" || dispStatus
="$__OK__"
2348 displayText
="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
2349 if [ "$iface" = "$reloadedIface" ]; then
2350 output
2 "Reloading routing for '$displayText' "
2351 if interface_routing
'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
2352 json_add_gateway
'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
2353 gatewaySummary
="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
2354 if is_netifd_table_interface
"$iface"; then output_okb
; else output_ok
; fi
2356 state add
'errorSummary' 'errorFailedReload' "$displayText"
2360 gatewaySummary
="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
2364 # ifaceTableID="$((ifaceTableID + 1))"
2365 ifaceMark
="$(printf '0x%06x' $((ifaceMark + wan_mark)))"
2366 ifacePriority
="$((ifacePriority - 2))"
2370 user_file_process
() {
2371 local shellBin
="${SHELL:-/bin/ash}"
2372 [ "$enabled" -gt 0 ] ||
return 0
2373 if [ ! -s "$path" ]; then
2374 state add
'errorSummary' 'errorUserFileNotFound' "$path"
2378 if ! $shellBin -n "$path"; then
2379 state add
'errorSummary' 'errorUserFileSyntax' "$path"
2383 output
2 "Running $path "
2384 # shellcheck disable=SC1090
2385 if ! .
"$path"; then
2386 state add
'errorSummary' 'errorUserFileRunning' "$path"
2387 if grep -q -w 'curl' "$path" && ! is_present
'curl'; then
2388 state add
'errorSummary' 'errorUserFileNoCurl' "$path"
2399 local procd_boot_delay
2400 config_load
"$packageName"
2401 config_get procd_boot_delay
'config' 'procd_boot_delay' '0'
2403 ubus
-t 30 wait_for network.interface
2>/dev
/null
2404 { is_integer
"$procd_boot_delay" && sleep "$procd_boot_delay"; \
2405 rc_procd start_service
'on_boot' && service_started
'on_boot'; } &
2408 on_firewall_reload
() {
2409 if [ ! -e "$packageLockFile" ]; then
2410 logger
-t "$packageName" "Reload on firewall action aborted: service is stopped."
2413 if nft_file
'exists'; then
2414 logger
-t "$packageName" "Reusing the fw4 nft file."
2416 rc_procd start_service
'on_firewall_reload' "$1"
2421 on_interface_reload
() {
2422 if [ ! -e "$packageLockFile" ]; then
2423 logger
-t "$packageName" "Reload on interface change aborted: service is stopped."
2426 rc_procd start_service
'on_interface_reload' "$1"
2431 local resolverStoredHash resolverNewHash i param
="$1" reloadedIface
2433 load_environment
"${param:-on_start}" "$(load_validate_config)" ||
return 1
2434 is_wan_up
"$param" ||
return 1
2436 interface_process
'all' 'prepare'
2437 config_foreach interface_process
'interface' 'pre_init'
2441 serviceStartTrigger
='on_start'
2444 serviceStartTrigger
='on_start'
2446 on_interface_reload
)
2448 local tid pre_init_tid
2449 tid
="$(get_rt_tables_id "$reloadedIface")"
2450 pre_init_tid
="$(eval echo "\
$pre_init_tid_${reloadedIface//-/_}")"
2451 if [ "$tid" = "$pre_init_tid" ]; then
2452 # logger -t "$packageName" "Updated interface $reloadedIface TID: ${tid}; Pre-Init TID: ${pre_init_tid}. Reloading..."
2453 serviceStartTrigger
='on_interface_reload'
2455 # logger -t "$packageName" "Updated interface $reloadedIface TID: ${tid}; Pre-Init TID: ${pre_init_tid}. Restarting..."
2456 serviceStartTrigger
='on_start'
2459 # if is_ovpn "$reloadedIface"; then
2460 # logger -t "$packageName" "Updated interface is an OpenVPN tunnel, restarting."
2461 # serviceStartTrigger='on_start'
2462 # unset reloadedIface
2464 # serviceStartTrigger='on_interface_reload'
2468 serviceStartTrigger
='on_reload'
2471 serviceStartTrigger
='on_start'
2475 if [ -n "$reloadedIface" ] && ! is_supported_interface
"$reloadedIface"; then
2479 if [ -n "$(ubus_get_status error)" ] ||
[ -n "$(ubus_get_status warning)" ]; then
2480 serviceStartTrigger
='on_start'
2482 elif ! is_service_running
; then
2483 serviceStartTrigger
='on_start'
2485 elif [ -z "$(ubus_get_status gateways)" ]; then
2486 serviceStartTrigger
='on_start'
2488 # elif [ "$serviceStartTrigger" = 'on_interface_reload' ] && \
2489 # [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_ipv4')" ] && \
2490 # [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_ipv6')" ]; then
2491 # serviceStartTrigger='on_start'
2492 # unset reloadedIface
2494 serviceStartTrigger
="${serviceStartTrigger:-on_start}"
2497 procd_open_instance
'main'
2498 procd_set_param
command /bin
/true
2499 procd_set_param stdout
1
2500 procd_set_param stderr
1
2503 case $serviceStartTrigger in
2504 on_interface_reload
)
2505 output
1 "Reloading Interface: $reloadedIface "
2506 json_add_array
'gateways'
2507 interface_process
'all' 'prepare'
2508 config_foreach interface_process
'interface' 'reload_interface' "$reloadedIface"
2513 traffic_killswitch
'insert'
2514 resolver
'store_hash'
2515 resolver
'cleanup_all'
2516 resolver
'configure'
2521 if ! is_nft_mode
; then
2522 for i
in $chainsList; do
2523 i
="$(str_to_upper "$i")"
2524 ipt
-t mangle
-N "${iptPrefix}_${i}"
2525 ipt
-t mangle
"$rule_create_option" "$i" -m mark
--mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
2528 json_add_array
'gateways'
2529 interface_process
'all' 'prepare'
2530 config_foreach interface_process
'interface' 'reload'
2531 interface_process
'tor' 'destroy'
2532 is_tor_running
&& interface_process
'tor' 'reload'
2534 if is_config_enabled
'policy'; then
2535 output
1 'Processing policies '
2536 config_load
"$packageName"
2537 config_foreach load_validate_policy
'policy' policy_process
2540 if is_config_enabled
'include'; then
2541 interface_process
'all' 'prepare'
2542 config_foreach interface_process
'interface' 'create_user_set'
2543 output
1 'Processing user file(s) '
2544 config_load
"$packageName"
2545 config_foreach load_validate_include
'include' user_file_process
2550 ! nft_file
'exists' && resolver
'compare_hash' && resolver
'restart'
2551 traffic_killswitch
'remove'
2554 traffic_killswitch
'insert'
2555 resolver
'store_hash'
2556 resolver
'cleanup_all'
2557 resolver
'configure'
2561 cleanup_marking_chains
2564 if ! is_nft_mode
; then
2565 for i
in $chainsList; do
2566 i
="$(str_to_upper "$i")"
2567 ipt
-t mangle
-N "${iptPrefix}_${i}"
2568 ipt
-t mangle
"$rule_create_option" "$i" -m mark
--mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
2571 output
1 'Processing interfaces '
2572 json_add_array
'gateways'
2573 interface_process
'all' 'prepare'
2574 config_foreach interface_process
'interface' 'create'
2575 interface_process
'tor' 'destroy'
2576 is_tor_running
&& interface_process
'tor' 'create'
2578 ip route flush cache
2580 if is_config_enabled
'policy'; then
2581 output
1 'Processing policies '
2582 config_load
"$packageName"
2583 config_foreach load_validate_policy
'policy' policy_process
2586 if is_config_enabled
'include'; then
2587 interface_process
'all' 'prepare'
2588 config_foreach interface_process
'interface' 'create_user_set'
2589 output
1 'Processing user file(s) '
2590 config_load
"$packageName"
2591 config_foreach load_validate_include
'include' user_file_process
2596 ! nft_file
'exists' && resolver
'compare_hash' && resolver
'restart'
2597 traffic_killswitch
'remove'
2601 if [ -z "$gatewaySummary" ]; then
2602 state add
'errorSummary' 'errorNoGateways'
2604 json_add_object
'status'
2605 [ -n "$gatewaySummary" ] && json_add_string
'gateways' "$gatewaySummary"
2606 [ -n "$errorSummary" ] && json_add_string
'errors' "$errorSummary"
2607 [ -n "$warningSummary" ] && json_add_string
'warnings' "$warningSummary"
2608 if [ "$strict_enforcement" -ne 0 ] && str_contains
"$gatewaySummary" '0.0.0.0'; then
2609 json_add_string
'mode' 'strict'
2613 procd_close_instance
2617 if nft_file
'exists'; then
2618 procd_set_config_changed firewall
2619 if nft_file
'exists'; then
2620 resolver
'compare_hash' && resolver
'restart'
2621 [ -n "$gatewaySummary" ] && output
"$serviceName (fw4 nft file mode) started with gateways:\\n${gatewaySummary}"
2623 output
"$serviceName FAILED TO START in fw4 nft file mode!!!"
2624 output
"Check the output of nft -c -f $nftTempFile"
2626 elif is_nft_mode
; then
2627 [ -n "$gatewaySummary" ] && output
"$serviceName (nft mode) started with gateways:\\n${gatewaySummary}"
2629 [ -n "$gatewaySummary" ] && output
"$serviceName (iptables mode) started with gateways:\\n${gatewaySummary}"
2631 state print
'errorSummary'
2632 state print
'warningSummary'
2633 touch "$packageLockFile"
2634 if [ -n "$errorSummary" ]; then
2636 elif [ -n "$warningSummary" ]; then
2643 service_triggers
() {
2645 load_environment
'on_triggers'
2646 # shellcheck disable=SC2034
2647 PROCD_RELOAD_DELAY
=$
(( procd_reload_delay
* 1000 ))
2649 load_validate_config
2650 load_validate_policy
2651 load_validate_include
2652 procd_close_validate
2654 procd_add_config_trigger
"config.change" 'openvpn' "/etc/init.d/${packageName}" reload
'on_openvpn_change'
2655 procd_add_config_trigger
"config.change" "${packageName}" /etc
/init.d
/${packageName} reload
2656 for n
in $ifacesSupported; do
2657 procd_add_interface_trigger
"interface.*" "$n" /etc
/init.d
/${packageName} on_interface_reload
"$n"
2660 # procd_add_raw_trigger "interface.*.up" 4000 "/etc/init.d/${packageName}" restart 'on_interface_up'
2661 if [ "$serviceStartTrigger" = 'on_start' ]; then
2662 output
3 "$serviceName monitoring interfaces: ${ifacesSupported}\\n"
2667 local i nft_file_mode
2668 load_environment
'on_stop'
2669 ! is_service_running
&& [ "$(get_rt_tables_next_id)" = "$(get_rt_tables_non_pbr_next_id)" ] && return 0
2670 [ "$1" = 'quiet' ] && quiet_mode
'on'
2671 traffic_killswitch
'insert'
2672 if nft_file
'exists'; then
2678 cleanup_marking_chains
2679 output
1 'Resetting interfaces '
2680 config_load
'network'
2681 config_foreach interface_process
'interface' 'destroy'
2682 interface_process
'tor' 'destroy'
2685 ip route flush cache
2688 resolver
'store_hash'
2689 resolver
'cleanup_all'
2690 resolver
'compare_hash' && resolver
'restart'
2691 traffic_killswitch
'remove'
2692 if [ "$enabled" -ne 0 ]; then
2693 if [ -n "$nft_file_mode" ]; then
2694 output
"$serviceName (fw4 nft file mode) stopped "; output_okn
;
2695 elif is_nft_mode
; then
2696 output
"$serviceName (nft mode) stopped "; output_okn
;
2698 output
"$serviceName (iptables mode) stopped "; output_okn
;
2701 rm -f "$packageLockFile"
2704 version
() { echo "$PKG_VERSION"; }
2707 local _SEPARATOR_
='============================================================'
2708 load_environment
'on_status'
2709 if is_nft_mode
; then
2710 status_service_nft
"$@"
2712 status_service_iptables
"$@"
2716 status_service_nft
() {
2717 local i dev dev6 wan_tid
2719 json_load
"$(ubus call system board)"; json_select release
; json_get_var dist distribution
; json_get_var vers version
2720 if [ -n "$wanIface4" ]; then
2721 network_get_gateway wanGW4
"$wanIface4"
2722 network_get_device dev
"$wanIface4"
2724 if [ -n "$wanIface6" ]; then
2725 network_get_device dev6
"$wanIface6"
2726 wanGW6
=$
($ip_bin -6 route show |
grep -m1 " dev $dev6 " |
awk '{print $1}')
2727 [ "$wanGW6" = "default" ] && wanGW6
=$
($ip_bin -6 route show |
grep -m1 " dev $dev6 " |
awk '{print $3}')
2729 while [ "${1:0:1}" = "-" ]; do param
="${1//-/}"; eval "set_$param=1"; shift; done
2730 [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
2731 status
="$serviceName running on $dist $vers."
2732 [ -n "$wanIface4" ] && status
="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
2733 [ -n "$wanIface6" ] && status
="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
2736 echo "$packageName - environment"
2739 dnsmasq
--version 2>/dev
/null |
sed '/^$/,$d'
2740 if nft_file
'exists'; then
2742 echo "$packageName fw4 nft file: $nftPermFile"
2743 sed '1d;2d;' "$nftPermFile"
2746 echo "$packageName chains - policies"
2747 for i
in forward input output prerouting postrouting
; do
2748 "$nft" -a list table inet
"$nftTable" |
sed -n "/chain ${nftPrefix}_${i} {/,/\t}/p"
2751 echo "$packageName chains - marking"
2752 for i
in $
(get_mark_nft_chains
); do
2753 "$nft" -a list table inet
"$nftTable" |
sed -n "/chain ${i} {/,/\t}/p"
2756 echo "$packageName nft sets"
2757 for i
in $
(get_nft_sets
); do
2758 "$nft" -a list table inet
"$nftTable" |
sed -n "/set ${i} {/,/\t}/p"
2760 if [ -s "$dnsmasqFile" ]; then
2765 # echo "$_SEPARATOR_"
2766 # ip rule list | grep "${packageName}_"
2768 tableCount
="$(grep -c "${packageName}_
" $rtTablesFile)" || tableCount
=0
2769 wan_tid
=$
(($
(get_rt_tables_next_id
)-tableCount))
2770 i
=0; while [ $i -lt "$tableCount" ]; do
2771 echo "IPv4 table $((wan_tid + i)) route: $($ip_bin -4 route show table $((wan_tid + i)) | grep default)"
2772 echo "IPv4 table $((wan_tid + i)) rule(s):"
2773 $ip_bin -4 rule list table
"$((wan_tid + i))"
2774 if [ -n "$ipv6_enabled" ]; then
2775 echo "IPv6 table $((wan_tid + i)) route: $($ip_bin -6 route show table $((wan_tid + i)) | grep default)"
2776 echo "IPv6 table $((wan_tid + i)) rule(s):"
2777 $ip_bin -6 route show table $
((wan_tid
+ i
))
2783 status_service_iptables
() {
2784 local dist vers out id s param status set_d set_p tableCount i
=0 dev dev6 j wan_tid
2786 json_load
"$(ubus call system board)"; json_select release
; json_get_var dist distribution
; json_get_var vers version
2787 if [ -n "$wanIface4" ]; then
2788 network_get_gateway wanGW4
"$wanIface4"
2789 network_get_device dev
"$wanIface4"
2791 if [ -n "$wanIface6" ]; then
2792 network_get_device dev6
"$wanIface6"
2793 wanGW6
=$
($ip_bin -6 route show |
grep -m1 " dev $dev6 " |
awk '{print $1}')
2794 [ "$wanGW6" = "default" ] && wanGW6
=$
($ip_bin -6 route show |
grep -m1 " dev $dev6 " |
awk '{print $3}')
2796 while [ "${1:0:1}" = "-" ]; do param
="${1//-/}"; eval "set_$param=1"; shift; done
2797 [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
2798 status
="$serviceName running on $dist $vers."
2799 [ -n "$wanIface4" ] && status
="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
2800 [ -n "$wanIface6" ] && status
="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
2804 dnsmasq
--version 2>/dev
/null |
sed '/^$/,$d'
2805 if [ -n "$1" ]; then
2807 echo "Resolving domains"
2809 echo "$i: $(resolveip "$i" | tr '\n' ' ')"
2814 echo "Routes/IP Rules"
2815 tableCount
="$(grep -c "${packageName}_
" $rtTablesFile)" || tableCount
=0
2816 if [ -n "$set_d" ]; then route
; else route |
grep '^default'; fi
2817 if [ -n "$set_d" ]; then ip rule list
; fi
2818 wan_tid
=$
(($
(get_rt_tables_next_id
)-tableCount))
2819 i
=0; while [ $i -lt "$tableCount" ]; do
2820 echo "IPv4 table $((wan_tid + i)) route: $($ip_bin -4 route show table $((wan_tid + i)) | grep default)"
2821 echo "IPv4 table $((wan_tid + i)) rule(s):"
2822 $ip_bin -4 rule list table
"$((wan_tid + i))"
2826 if [ -n "$ipv6_enabled" ]; then
2827 i
=0; while [ $i -lt "$tableCount" ]; do
2828 $ip_bin -6 route show table $
((wan_tid
+ i
)) |
while read -r param
; do
2829 echo "IPv6 Table $((wan_tid + i)): $param"
2835 for j
in Mangle NAT
; do
2836 if [ -z "$set_d" ]; then
2837 for i
in $chainsList; do
2838 i
="$(str_to_upper "$i")"
2839 if iptables
-v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}" >/dev
/null
2>&1; then
2841 echo "$j IP Table: $i"
2842 iptables
-v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
2843 if [ -n "$ipv6_enabled" ]; then
2845 echo "$j IPv6 Table: $i"
2846 iptables
-v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
2853 iptables
-L -t "$(str_to_lower $j)"
2854 if [ -n "$ipv6_enabled" ]; then
2856 echo "$j IPv6 Table"
2857 iptables
-L -t "$(str_to_lower $j)"
2860 i
=0; ifaceMark
="$wan_mark";
2861 while [ $i -lt "$tableCount" ]; do
2862 if iptables
-v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}" >/dev
/null
2>&1; then
2864 echo "$j IP Table MARK Chain: ${iptPrefix}_MARK_${ifaceMark}"
2865 iptables
-v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}"
2866 ifaceMark
="$(printf '0x%06x' $((ifaceMark + wan_mark)))";
2873 echo "Current ipsets"
2875 if [ -s "$dnsmasqFile" ]; then
2880 if [ -s "$aghIpsetFile" ]; then
2882 echo "AdGuardHome sets"
2886 } |
tee -a /var
/${packageName}-support
2887 if [ -n "$set_p" ]; then
2888 printf "%b" "Pasting to paste.ee... "
2889 if curl
--version 2>/dev
/null |
grep -q "Protocols: .*https.*"; then
2890 json_init
; json_add_string
'description' "${packageName}-support"
2891 json_add_array
'sections'; json_add_object
'0'
2892 json_add_string
'name' "$(uci_get 'system' '@system[0]' 'hostname')"
2893 json_add_string
'contents' "$(cat /var/${packageName}-support)"
2894 json_close_object
; json_close_array
; payload
=$
(json_dump
)
2895 out
=$
(curl
-s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload")
2896 json_load
"$out"; json_get_var id id
; json_get_var s success
2897 [ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" ||
printf "%b" "$__FAIL__\\n"
2898 [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
2900 printf "%b" "${__FAIL__}\\n"
2901 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"
2904 printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n"
2908 # shellcheck disable=SC2120
2909 load_validate_config
() {
2910 uci_load_validate
"$packageName" "$packageName" "$1" "${2}${3:+ $3}" \
2912 'strict_enforcement:bool:1' \
2913 'secure_reload:bool:0' \
2914 'ipv6_enabled:bool:0' \
2915 'resolver_set:or("", "none", "dnsmasq.ipset", "dnsmasq.nftset")' \
2916 'resolver_instance:list(or(integer, string)):*' \
2917 'verbosity:range(0,2):2' \
2918 'wan_mark:regex("[A-Fa-f0-9]{8}"):010000' \
2919 'fw_mask:regex("[A-Fa-f0-9]{8}"):ff0000' \
2920 'icmp_interface:or("", tor, uci("network", "@interface"))' \
2921 'ignored_interface:list(or(tor, uci("network", "@interface")))' \
2922 'supported_interface:list(or(ignore, tor, regex("xray_.*"), uci("network", "@interface")))' \
2923 'procd_boot_delay:integer:0' \
2924 'procd_boot_timeout:integer:30' \
2925 'procd_lan_interface:string' \
2926 'procd_reload_delay:integer:0' \
2927 'procd_wan_ignore_status:bool:0' \
2928 'procd_wan_interface:network:wan' \
2929 'procd_wan6_interface:network:wan6' \
2930 'wan_ip_rules_priority:uinteger:30000' \
2931 'rule_create_option:or("", add, insert):add' \
2932 'webui_supported_protocol:list(string)' \
2933 'nft_file_support:bool:1'\
2934 'nft_set_auto_merge:bool:1'\
2935 'nft_set_counter:bool:1'\
2936 'nft_set_flags_interval:bool:1'\
2937 'nft_set_flags_timeout:bool:0'\
2938 'nft_set_gc_interval:or("", string)'\
2939 'nft_set_policy:or("", memory, performance):performance'\
2940 'nft_set_timeout:or("", string)'
2943 # shellcheck disable=SC2120
2944 load_validate_policy
() {
2954 uci_load_validate
"$packageName" 'policy' "$1" "${2}${3:+ $3}" \
2955 'name:string:Untitled' \
2957 'interface:or("ignore", "tor", regex("xray_.*"), uci("network", "@interface")):wan' \
2958 'proto:or(string)' \
2959 'chain:or("", "forward", "input", "output", "prerouting", "postrouting", "FORWARD", "INPUT", "OUTPUT", "PREROUTING", "POSTROUTING"):prerouting' \
2960 'src_addr:list(neg(or(host,network,macaddr,string)))' \
2961 'src_port:list(neg(or(portrange,string)))' \
2962 'dest_addr:list(neg(or(host,network,string)))' \
2963 'dest_port:list(neg(or(portrange,string)))'
2966 # shellcheck disable=SC2120
2967 load_validate_include
() {
2970 uci_load_validate
"$packageName" 'include' "$1" "${2}${3:+ $3}" \