readonly packageName='pbr'
readonly PKG_VERSION='dev-test'
-readonly packageCompat='20'
+readonly packageCompat='24'
readonly serviceName="$packageName $PKG_VERSION"
readonly packageConfigFile="/etc/config/${packageName}"
readonly packageDebugFile="/var/run/${packageName}.debug"
readonly __FAIL__='\033[0;31m[\xe2\x9c\x97]\033[0m'
readonly _ERROR_='\033[0;31mERROR:\033[0m'
readonly _WARNING_='\033[0;33mWARNING:\033[0m'
+readonly _DOT_='.'
+readonly __DOT__='[w]'
readonly ip_full='/usr/libexec/ip-full'
# shellcheck disable=SC2155
readonly ipTablePrefix="$packageName"
readonly nftIPv4Flag='ip'
readonly nftIPv6Flag='ip6'
readonly nftTempFile="/var/run/${packageName}.nft"
-readonly nftPermFile="/usr/share/nftables.d/ruleset-post/30-${packageName}.nft"
-readonly nftNetifdPermFile="/usr/share/nftables.d/ruleset-post/20-${packageName}-netifd.nft"
+readonly nftMainFile="/usr/share/nftables.d/ruleset-post/30-${packageName}.nft"
+readonly nftNetifdFile="/usr/share/nftables.d/ruleset-post/20-${packageName}-netifd.nft"
readonly nftPrefix="$packageName"
readonly nftTable='fw4'
readonly chainsList='forward output prerouting'
procd_reload_delay=
lan_device=
uplink_interface=
+uplink_interface4=
uplink_interface6=
uplink_interface6_metric='128'
resolver_set=
# run-time
aghConfigFile='/etc/AdGuardHome/AdGuardHome.yaml'
gatewaySummary=
-wanIface4=
-wanIface6=
ifaceMark=
ifaceTableID=
ifacePriority=
ifacesSupported=
ifacesTriggers=
firewallWanZone=
-wanGW4=
-wanGW6=
+uplinkGW=
+uplinkGW4=
+uplinkGW6=
pbrBootFlag=
serviceStartTrigger=
processDnsPolicyError=
nft_fw4_dump=
loadEnvironmentFlag=
loadPackageConfigFlag=
+resolverWorkingFlag=
# shellcheck disable=SC1091
. "${IPKG_INSTROOT}/lib/functions.sh"
output_okbn() { output 1 "$_OKB_\n"; output 2 "$__OKB__\n"; }
output_fail() { output 1 "$_FAIL_"; output 2 "$__FAIL__\n"; }
output_failn() { output 1 "$_FAIL_\n"; output 2 "$__FAIL__\n"; }
+output_dot() { output 1 "$_DOT_"; output 2 "$__DOT__"; }
output_error() { output "${_ERROR_} $*!\n"; }
output_warning() { output "${_WARNING_} $*.\n"; }
quiet_mode() {
local iface i param="$2"
case "$param" in
wan6) iface="$uplink_interface6";;
- wan|*) iface="$uplink_interface";;
+ wan|*) iface="$uplink_interface4";;
esac
eval "$1"='${iface}'
}
}
pbr_get_gateway6() {
local iface="$2" dev="$3" gw
- [ "$iface" = "$uplink_interface" ] && iface="$uplink_interface6"
+ is_wan "$iface" && iface="$uplink_interface6"
network_get_gateway6 gw "$iface" true
if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
gw="$(ip -6 a list dev "$dev" 2>/dev/null | grep inet6 | grep 'scope global' | awk '{print $2}')"
is_supported_protocol(){ grep -qi "^${1:--}" /etc/protocols;}
is_pptp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "pptp" ]; }
is_softether() { local d; network_get_device d "$1"; [ "${d:0:4}" = "vpn_" ]; }
+is_split_uplink() { [ -n "$ipv6_enabled" ] && [ "$uplink_interface4" != "$uplink_interface6" ]; }
is_supported_interface() { { is_lan "$1" || is_disabled_interface "$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"; }
is_netbird() { local d; network_get_device d "$1"; [ "${d:0:2}" = "wt" ]; }
is_tailscale() { local d; network_get_device d "$1"; [ "${d:0:9}" = "tailscale" ]; }
is_url_ftp() { [ "$1" != "${1#ftp://}" ]; }
is_url_http() { [ "$1" != "${1#http://}" ]; }
is_url_https() { [ "$1" != "${1#https://}" ]; }
-is_wan() { [ "$1" = "$wanIface4" ] || { [ "${1##wan}" != "$1" ] && [ "${1##wan6}" = "$1" ]; } || [ "${1%%wan}" != "$1" ]; }
-is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1##wan6}" != "$1" ] || [ "${1%%wan6}" != "$1" ]; }
+is_wan() { [ "$1" = "$uplink_interface4" ]; }
+is_wan6() { [ -n "$ipv6_enabled" ] && [ "$1" = "$uplink_interface6" ]; }
is_wg() { local p lp; network_get_protocol p "$1"; uci_get_listen_port lp "$1"; [ -z "$lp" ] && [ "${p:0:9}" = "wireguard" ]; }
is_wg_server() { local p lp; network_get_protocol p "$1"; uci_get_listen_port lp "$1"; [ -n "$lp" ] && [ "${p:0:9}" = "wireguard" ]; }
is_xray() { [ -n "$(get_xray_traffic_port "$1")" ]; }
get_rt_tables_next_id() { echo "$(($(sort -r -n "$rtTablesFile" | grep -o -E -m 1 "^[0-9]+")+1))"; }
get_rt_tables_non_pbr_next_id() { echo "$(($(grep -v "${ipTablePrefix}_" "$rtTablesFile" | sort -r -n | grep -o -E -m 1 "^[0-9]+")+1))"; }
# shellcheck disable=SC2016
-resolveip_to_nftset() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d'; }
+resolveip_to_nftset() { resolver 'wait' && resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d'; }
resolveip_to_nftset4() { resolveip_to_nftset -4 "$@"; }
resolveip_to_nftset6() { [ -n "$ipv6_enabled" ] && resolveip_to_nftset -6 "$@"; }
# shellcheck disable=SC2016
# luci app specific
is_enabled() { uci_get "$1" 'config' 'enabled'; }
-is_running_nft_file() { [ -s "$nftPermFile" ]; }
-is_running_nft() { "$nft" list table inet fw4 | grep chain | grep -q pbr_mark_ >/dev/null 2>&1; }
+is_running_nft_file() { [ -s "$nftMainFile" ]; }
+is_running_nft() { "$nft" list table inet "$nftTable" | grep chain | grep -q "${nftPrefix}_mark_" >/dev/null 2>&1; }
check_nft() { [ -x "$nft" ]; }
check_agh() { [ -x "$agh" ] && { [ -s "$aghConfigFile" ] || [ -s "${agh%/*}/AdGuardHome.yaml" ]; }; }
check_dnsmasq() { command -v dnsmasq >/dev/null 2>&1; }
errorNoNft) printf "Resolver set support (%s) requires nftables, but nft binary cannot be found" "$resolver_set";;
errorResolverNotSupported) printf "Resolver set (%s) is not supported on this system" "$resolver_set";;
errorServiceDisabled) printf "The %s service is currently disabled" "$packageName";;
- errorNoWanGateway) printf "The %s service failed to discover WAN gateway" "$serviceName";;
+ errorNoUplinkGateway) printf "The %s service failed to discover uplink gateway" "$serviceName";;
errorNoUplinkInterface) printf "The %s interface not found, you need to set the 'pbr.config.uplink_interface' option" "$1";;
errorNoUplinkInterfaceHint) printf "Refer to %s" "$1";;
errorNftsetNameTooLong) printf "The nft set name '%s' is longer than allowed 255 characters" "$1";;
errorPolicyProcessInsertionFailedIpv4) printf "Insertion failed for IPv4 for policy '%s'" "$1";;
errorPolicyProcessUnknownEntry) printf "Unknown entry in policy '%s'" "$1";;
errorInterfaceRoutingEmptyValues) printf "Received empty tid/mark or interface name when setting up routing";;
+ errorInterfaceMarkOverflow) printf "Interface mark for '%s' exceeds the fwmask value" "$1";;
errorFailedToResolve) printf "Failed to resolve '%s'" "$1";;
errorInvalidOVPNConfig) printf "Invalid OpenVPN config for '%s' interface" "$1";;
- errorNftFileInstall) printf "Failed to install fw4 nft file '%s'" "$1";;
+ errorNftMainFileInstall) printf "Failed to install fw4 nft file '%s'" "$1";;
errorTryFailed) printf "Command failed: %s" "$1";;
errorDownloadUrlNoHttps) printf "Failed to download '%s', HTTPS is not supported" "$1";;
errorDownloadUrl) printf "Failed to download '%s'" "$1";;
errorUplinkDown) printf "Uplink/WAN interface is still down, increase value of 'procd_boot_trigger_delay' option";;
errorMktempFileCreate) printf "Failed to create temporary file with mktemp mask: '%s'" "$1";;
errorSummary) printf "Errors encountered, please check %s" "$1";;
- errorNetifdNftFileInstall) printf "Netifd setup: failed to install fw4 netifd nft file '%s'" "$1";;
- errorNetifdNftFileRemove) printf "Netifd setup: failed to remove fw4 netifd nft file '%s'" "$1";;
+ errorNftNetifdFileInstall) printf "Netifd setup: failed to install fw4 netifd nft file '%s'" "$1";;
+ errorNftNetifdFileDelete) printf "Netifd setup: failed to remove fw4 netifd nft file '%s'" "$1";;
errorNetifdMissingOption) printf "Netifd setup: required option '%s' is missing" "$1";;
errorNetifdInvalidGateway4) printf "Netifd setup: invalid value of netifd_interface_default option '%s'" "$1";;
errorNetifdInvalidGateway6) printf "Netifd setup: invalid value of netifd_interface_default6 option '%s'" "$1";;
warningTorUnsetParams) printf "Please unset 'src_addr', 'src_port' and 'dest_port' for policy '%s'" "$1";;
warningTorUnsetProto) printf "Please unset 'proto' or set 'proto' to 'all' for policy '%s'" "$1";;
warningTorUnsetChainNft) printf "Please unset 'chain' or set 'chain' to 'prerouting' for policy '%s'" "$1";;
- warningOutdatedWebUIApp) printf "The WebUI application is outdated (version %s), please update it" "$1";;
+ warningOutdatedLuciPackage) printf "The WebUI application is outdated (version %s), please update it" "$1";;
warningDnsmasqInstanceNoConfdir) printf "Dnsmasq instance '%s' targeted in settings, but it doesn't have its own confdir" "$1";;
warningDhcpLanForce) printf "Please set 'dhcp.%s.force=1' to speed up service start-up" "$1";;
warningSummary) printf "Warnings encountered, please check %s" "$(get_url '#WarningMessagesDetails')";;
warningIncompatibleDHCPOption6) printf "Incompatible DHCP Option 6 for interface '%s'" "$1";;
warningNetifdMissingInterfaceLocal) printf "Netifd setup: option netifd_interface_local is missing, assuming '%s'" "$1";;
+ warningUplinkDown) printf "Uplink/WAN interface is still down, going back to boot mode";;
*) printf "Unknown error/warning '%s'" "$1";;
esac
}
config_get uplink_ip_rules_priority 'config' 'uplink_ip_rules_priority' '30000'
config_get uplink_mark 'config' 'uplink_mark' '00010000'
config_get verbosity 'config' 'verbosity' '2'
+ config_get_bool webui_show_ignore_target 'config' 'webui_show_ignore_target' '0'
config_get_bool netifd_enabled 'config' 'netifd_enabled'
config_get_bool netifd_strict_enforcement 'config' 'netifd_strict_enforcement'
config_get netifd_interface_default 'config' 'netifd_interface_default'
config_get netifd_interface_default6 'config' 'netifd_interface_default6'
config_get netifd_interface_local 'config' 'netifd_interface_local'
+ uplink_interface4="$uplink_interface"
fw_mask="0x${fw_mask}"
uplink_mark="0x${uplink_mark}"
[ "$resolver_set" = 'none' ] && unset resolver_set
[ "$enabled" = '1' ] || unset enabled
+ [ "$ipv6_enabled" = '1' ] || unset uplink_interface6
[ "$ipv6_enabled" = '1' ] || unset ipv6_enabled
[ "$strict_enforcement" = '1' ] || unset strict_enforcement
;;
on_triggers)
[ -n "$loadPackageConfigFlag" ] || load_package_config "$param"
-# load_network "$param"
;;
on_interface_reload|on_reload|on_stop|*)
output 1 "Loading environment ($param) "
# shellcheck disable=SC2329
_build_ifaces_supported() { is_supported_interface "$1" && ! str_contains "$ifacesSupported" "$1" && ifacesSupported="${ifacesSupported}${1} "; }
# shellcheck disable=SC2329
- _find_firewall_wan_zone() { [ "$(uci_get 'firewall' "$1" 'name')" = "wan" ] && firewallWanZone="$1"; }
+ _find_firewall_wan_zone() { [ "$(uci_get 'firewall' "$1" 'name')" = 'wan' ] && firewallWanZone="$1"; }
local i param="$1"
local dev4 dev6
if [ -z "$ifacesSupported" ]; then
config_load 'firewall'
config_foreach _find_firewall_wan_zone 'zone'
for i in $(uci_get 'firewall' "$firewallWanZone" 'network'); do
- is_supported_interface "$i" && ! str_contains "$ifacesSupported" "$1" && ifacesSupported="${ifacesSupported}${i} "
+ is_supported_interface "$i" && ! str_contains "$ifacesSupported" "$i" && ifacesSupported="${ifacesSupported}${i} "
done
config_load 'network'
config_foreach _build_ifaces_supported 'interface'
fi
- wanIface4="$uplink_interface"
- network_get_device dev4 "$wanIface4"
- [ -z "$dev4" ] && network_get_physdev dev4 "$wanIface4"
- [ -z "$wanGW4" ] && pbr_get_gateway4 wanGW4 "$wanIface4" "$dev4"
+ network_get_device dev4 "$uplink_interface4"
+ [ -z "$dev4" ] && network_get_physdev dev4 "$uplink_interface4"
+ [ -z "$uplinkGW4" ] && pbr_get_gateway4 uplinkGW4 "$uplink_interface4" "$dev4"
if [ -n "$ipv6_enabled" ]; then
- wanIface6="$uplink_interface6"
- network_get_device dev6 "$wanIface6"
- [ -z "$dev6" ] && network_get_physdev dev6 "$wanIface6"
- [ -z "$wanGW6" ] && pbr_get_gateway6 wanGW6 "$wanIface6" "$dev6"
+ network_get_device dev6 "$uplink_interface6"
+ [ -z "$dev6" ] && network_get_physdev dev6 "$uplink_interface6"
+ [ -z "$uplinkGW6" ] && pbr_get_gateway6 uplinkGW6 "$uplink_interface6" "$dev6"
fi
case "$param" in
on_boot|on_start)
- [ -n "$wanIface4" ] && output 2 "Using uplink${wanIface6:+ IPv4} interface (${param}): $wanIface4 $__OK__\n"
- [ -n "$wanGW4" ] && output 2 "Found uplink${wanIface6:+ IPv4} gateway (${param}): $wanGW4 $__OK__\n"
- [ -n "$wanIface6" ] && output 2 "Using uplink IPv6 interface (${param}): $wanIface6 $__OK__\n"
- [ -n "$wanGW6" ] && output 2 "Found uplink IPv6 gateway (${param}): $wanGW6 $__OK__\n"
+ [ -n "$uplink_interface4" ] && output 2 "Using uplink${uplink_interface6:+ IPv4} interface (${param}): $uplink_interface4 $__OK__\n"
+ [ -n "$uplinkGW4" ] && output 2 "Found uplink${uplink_interface6:+ IPv4} gateway (${param}): $uplinkGW4 $__OK__\n"
+ [ -n "$uplink_interface6" ] && output 2 "Using uplink IPv6 interface (${param}): $uplink_interface6 $__OK__\n"
+ [ -n "$uplinkGW6" ] && output 2 "Found uplink IPv6 gateway (${param}): $uplinkGW6 $__OK__\n"
;;
esac
- wanGW="${wanGW4:-$wanGW6}"
+ uplinkGW="${uplinkGW4:-$uplinkGW6}"
}
is_wan_up() {
local param="$1"
- if [ -z "$(uci_get network "$uplink_interface")" ]; then
- json add error 'errorNoUplinkInterface' "$uplink_interface"
+ if [ -z "$(uci_get network "$uplink_interface4")" ]; then
+ json add error 'errorNoUplinkInterface' "$uplink_interface4"
json add error 'errorNoUplinkInterfaceHint' "$(get_url '#uplink_interface')"
return 1
fi
network_flush_cache
load_network "$param"
- if [ -n "$wanGW" ]; then
+ if [ -n "$uplinkGW" ]; then
return 0
else
- json add error 'errorNoWanGateway'
+ json add error 'errorNoUplinkGateway'
return 1
fi
}
-nft() { [ -n "$*" ] && nft_file 'add_command' "$@"; }
+nft() { [ -n "$*" ] && nft_file 'add' 'main' "$@"; }
nft4() { nft "$@"; }
nft6() { [ -n "$ipv6_enabled" ] || return 0; nft "$@"; }
nft_call() { "$nft" "$@" >/dev/null 2>&1; }
esac
}
nft_file() {
- local i chain
- case "$1" in
- add|add_command)
- shift
+ local i chain command="$1" target="$2"
+ case "$command:$target" in
+ add:*)
+ shift 2
echo "$*" >> "$nftTempFile"
;;
- create)
- rm -f "$nftTempFile" "$nftPermFile"
- for i in "$nftTempFile" "$nftPermFile"; do
+ create:main)
+ rm -f "$nftTempFile" "$nftMainFile"
+ for i in "$nftTempFile" "$nftMainFile"; do
mkdir -p "${i%/*}"
done
{ echo '#!/usr/sbin/nft -f'; echo ''; } > "$nftTempFile"
- # Insert PBR guards at the top of main caller chains so first PBR match wins, while preserving foreign marks.
+ # Create pbr chains in fw4 table
+ for chain in dstnat $chainsList; do
+ echo "add chain inet $nftTable ${nftPrefix}_${chain} {}" >> "$nftTempFile"
+ done
+ echo "" >> "$nftTempFile"
+ # Add jump rules from fw4 chains to pbr chains
+ echo "add rule inet $nftTable dstnat jump ${nftPrefix}_dstnat" >> "$nftTempFile"
+ echo "add rule inet $nftTable mangle_prerouting jump ${nftPrefix}_prerouting" >> "$nftTempFile"
+ echo "add rule inet $nftTable mangle_output jump ${nftPrefix}_output" >> "$nftTempFile"
+ echo "add rule inet $nftTable mangle_forward jump ${nftPrefix}_forward" >> "$nftTempFile"
+ echo "" >> "$nftTempFile"
+ # Insert PBR guards at the top of pbr chains so first PBR match wins, while preserving foreign marks.
for chain in $chainsList; do
echo "add rule inet $nftTable ${nftPrefix}_${chain} ${nftRuleParams:+$nftRuleParams }meta mark & $fw_mask != 0 return" >> "$nftTempFile"
done
;;
- delete|rm|remove)
- rm -f "$nftTempFile" "$nftPermFile"
+ create:netifd)
+ rm -f "$nftTempFile" "$nftNetifdFile"
+ for i in "$nftTempFile" "$nftNetifdFile"; do
+ mkdir -p "${i%/*}"
+ done
+ { echo '#!/usr/sbin/nft -f'; echo ''; } > "$nftTempFile"
+ ;;
+ delete:main)
+ rm -f "$nftTempFile" "$nftMainFile"
+ ;;
+ delete:netifd)
+ output "Removing fw4 netifd nft file "
+ if rm -f "$nftNetifdFile"; then
+ output_okbn
+ else
+ json add error 'errorNftNetifdFileDelete' "$nftNetifdFile"
+ output_failn
+ fi
;;
- enabled)
- return 0
+ exists:main)
+ [ -s "$nftMainFile" ] && return 0 || return 1
;;
- exists)
- [ -s "$nftPermFile" ] && return 0 || return 1
+ exists:netifd)
+ [ -s "$nftNetifdFile" ] && return 0 || return 1
;;
- install)
+ install:main)
[ -s "$nftTempFile" ] || return 1
output "Installing fw4 nft file "
if nft_call -c -f "$nftTempFile" && \
- cp -f "$nftTempFile" "$nftPermFile"; then
+ cp -f "$nftTempFile" "$nftMainFile"; then
output_okn
else
- json add error 'errorNftFileInstall' "$nftTempFile"
+ json add error 'errorNftMainFileInstall' "$nftTempFile"
output_failn
fi
;;
- netifd_exists)
- [ -s "$nftNetifdPermFile" ] && return 0 || return 1
- ;;
- netifd_create)
- rm -f "$nftTempFile" "$nftNetifdPermFile"
- for i in "$nftTempFile" "$nftNetifdPermFile"; do
- mkdir -p "${i%/*}"
- done
- { echo '#!/usr/sbin/nft -f'; echo ''; } > "$nftTempFile"
- ;;
- netifd_delete|netifd_rm)
- rm -f "$nftNetifdPermFile"
- ;;
- netifd_install)
+ install:netifd)
[ -s "$nftTempFile" ] || return 1
output "Installing fw4 netifd nft file "
if nft_call -c -f "$nftTempFile" && \
- cp -f "$nftTempFile" "$nftNetifdPermFile"; then
+ cp -f "$nftTempFile" "$nftNetifdFile"; then
output_okbn
else
- json add error 'errorNetifdNftFileInstall' "$nftTempFile"
+ json add error 'errorNftNetifdFileInstall' "$nftTempFile"
output_failn
fi
;;
- netifd_remove)
- output "Removing fw4 netifd nft file "
- if rm -f "$nftNetifdPermFile"; then
- output_okbn
- else
- json add error 'errorNetifdNftFileRemove' "$nftTempFile"
- output_failn
- fi
+ match:temp)
+ grep -q "$3" "$nftTempFile"
+ ;;
+ sed:temp)
+ shift 2
+ sed -i "$*" "$nftTempFile" >/dev/null 2>&1
+ ;;
+ sed:netifd)
+ shift 2
+ sed -i "$*" "$nftNetifdFile" >/dev/null 2>&1
+ ;;
+ show:main)
+ echo "$packageName fw4 nft file: $nftMainFile"
+ sed '1d;2d;' "$nftMainFile"
+ ;;
+ show:netifd)
+ echo "$packageName fw4 netifd nft file: $nftNetifdFile"
+ sed '1d;2d;' "$nftNetifdFile"
;;
esac
}
fi
}
-cleanup_rt_tables() {
- local i
-# shellcheck disable=SC2013
- for i in $(grep -oh "${ipTablePrefix}_.*" "$rtTablesFile"); do
- ! is_netifd_table "$i" && sed -i "/${i}/d" "$rtTablesFile"
- done
- sync
-}
-
-cleanup_main_table() {
- for prio in $(ip -4 rule show \
- | awk -v mask="$fw_mask" '/fwmark/ && $0 ~ mask && /lookup main/ && /suppress_prefixlength 0/ {sub(":", "", $1); print $1}'
-); do
- ip -4 rule del priority "$prio"
-done
-}
-
-cleanup_main_chains() {
- local i j
- for i in $chainsList dstnat; do
- i="$(str_to_lower "$i")"
- nft_call flush chain inet "$nftTable" "${nftPrefix}_${i}"
- done
-}
-
-cleanup_marking_chains() {
- local i j
- for i in $(get_mark_nft_chains); do
- nft_call flush chain inet "$nftTable" "$i"
- nft_call delete chain inet "$nftTable" "$i"
- done
-}
-
-cleanup_sets() {
- local i
- for i in $(get_nft_sets); do
- nft_call flush set inet "$nftTable" "$i"
- nft_call delete set inet "$nftTable" "$i"
+cleanup() {
+ local action i prio
+ for action in "$@"; do
+ case "$action" in
+ rt_tables)
+ # shellcheck disable=SC2013
+ for i in $(grep -oh "${ipTablePrefix}_.*" "$rtTablesFile"); do
+ ! is_netifd_table "$i" && sed -i "/${i}/d" "$rtTablesFile"
+ done
+ sync
+ ;;
+ main_table)
+ # Get all rules to delete in one pass (format: "priority suppress_prefixlength_value table_name")
+ ip -4 rule show | awk '
+ /lookup[[:space:]]+main[[:space:]]+suppress_prefixlength[[:space:]]+[0-9]+/ {
+ sub(":", "", $1)
+ match($0, /suppress_prefixlength[[:space:]]+([0-9]+)/, arr)
+ print $1 " " arr[1] " main"
+ }
+ /lookup '"$ipTablePrefix"'_/ {
+ sub(":", "", $1)
+ match($0, /lookup[[:space:]]+([^[:space:]]+)/, arr)
+ print $1 " 0 " arr[1]
+ }
+ ' | while read -r prio len table; do
+ if [ "$table" != "main" ] && is_netifd_table "$table"; then
+ continue # Skip netifd-managed tables
+ fi
+ if [ "$len" = "0" ]; then
+ # pbr table rule - try priority deletion first
+ ip -4 rule del priority "$prio" 2>/dev/null
+ else
+ # suppress_prefixlength rule - try priority first, then full spec
+ if ! ip -4 rule del priority "$prio" 2>/dev/null; then
+ ip -4 rule del lookup main suppress_prefixlength "$len" priority "$prio" 2>/dev/null || \
+ ip -4 rule del table main suppress_prefixlength "$len" priority "$prio" 2>/dev/null
+ fi
+ fi
+ done
+ # Always attempt IPv6 cleanup regardless of current ipv6_enabled setting
+ # since rules might exist from when IPv6 was previously enabled
+ ip -6 rule show 2>/dev/null | awk '
+ /lookup[[:space:]]+main[[:space:]]+suppress_prefixlength[[:space:]]+[0-9]+/ {
+ sub(":", "", $1)
+ match($0, /suppress_prefixlength[[:space:]]+([0-9]+)/, arr)
+ print $1 " " arr[1] " main"
+ }
+ /lookup '"$ipTablePrefix"'_/ {
+ sub(":", "", $1)
+ match($0, /lookup[[:space:]]+([^[:space:]]+)/, arr)
+ print $1 " 0 " arr[1]
+ }
+ ' | while read -r prio len table; do
+ if [ "$table" != "main" ] && is_netifd_table "$table"; then
+ continue # Skip netifd-managed tables
+ fi
+ if [ "$len" = "0" ]; then
+ ip -6 rule del priority "$prio" 2>/dev/null
+ else
+ if ! ip -6 rule del priority "$prio" 2>/dev/null; then
+ ip -6 rule del lookup main suppress_prefixlength "$len" priority "$prio" 2>/dev/null || \
+ ip -6 rule del table main suppress_prefixlength "$len" priority "$prio" 2>/dev/null
+ fi
+ fi
+ done
+ ;;
+ main_chains)
+ for i in $chainsList dstnat; do
+ i="$(str_to_lower "$i")"
+ nft_call flush chain inet "$nftTable" "${i}"
+ done
+ ;;
+ marking_chains)
+ for i in $(get_mark_nft_chains); do
+ # nft_call flush chain inet "$nftTable" "$i"
+ nft_call delete chain inet "$nftTable" "$i"
+ done
+ ;;
+ sets)
+ for i in $(get_nft_sets); do
+ # nft_call flush set inet "$nftTable" "$i"
+ nft_call delete set inet "$nftTable" "$i"
+ done
+ ;;
+ esac
done
}
restart) return 0;;
compare_hash) return 0;;
store_hash) return 0;;
+ wait)
+ [ -n "$resolverWorkingFlag" ] && return 0
+ local timeout="${iface:-30}" count=0
+ local hostname="$(uci_get 'system' '@system[0]' 'hostname' 'OpenWrt')"
+ while [ "$count" -lt "$timeout" ]; do
+ if resolveip "$hostname" >/dev/null 2>&1; then
+ resolverWorkingFlag='true'
+ return 0
+ fi
+ sleep 1
+ count=$((count + 1))
+ done
+ return 1
+ ;;
esac
;;
dnsmasq.nftset)
case "$param" in
add_resolver_element)
[ -n "$resolverSetSupported" ] || return 1
+ [ "$target" = 'src' ] && return 1 # dnsmasq doesn't populate nft sets for local addresses
local d
for d in $value; do
nftset 'add_dnsmasq_element' "$iface" "$target" "$type" "$uid" "$name" "$d"
;;
create_resolver_set)
[ -n "$resolverSetSupported" ] || return 1
+ [ "$target" = 'src' ] && return 1 # dnsmasq doesn't populate nft sets for local addresses
nftset 'create_dnsmasq_set' "$iface" "$target" "$type" "$uid" "$name" "$value"
;;
check_support)
rm -f "$packageDnsmasqFile"
config_load 'dhcp'
config_foreach _dnsmasq_instance_config 'dnsmasq' 'cleanup'
+ return 0
;;
configure)
[ -n "$resolverSetSupported" ] || return 1
[ "$resolverNewHash" != "$resolverStoredHash" ]
;;
store_hash)
- [ -s "$packageDnsmasqFile" ] && resolverStoredHash="$(md5sum "$packageDnsmasqFile" | awk '{ print $1; }')";;
+ [ -s "$packageDnsmasqFile" ] && resolverStoredHash="$(md5sum "$packageDnsmasqFile" | awk '{ print $1; }')"
+ return 0
+ ;;
+ wait)
+ [ -n "$resolverWorkingFlag" ] && return 0
+ local timeout="${iface:-30}" count=0
+ local hostname="$(uci_get 'system' '@system[0]' 'hostname' 'OpenWrt')"
+ while [ "$count" -lt "$timeout" ]; do
+ if resolveip "$hostname" >/dev/null 2>&1; then
+ resolverWorkingFlag='true'
+ return 0
+ fi
+ sleep 1
+ count=$((count + 1))
+ done
+ return 1
+ ;;
esac
;;
unbound.nftset)
# Usage: netifd install [iface] | netifd remove [iface] | netifd uninstall
_netifd_process_interface() {
local iface="$1" action="${2:-install}"
- local rt_name="${ipTablePrefix}_${iface%6}"
+ # Normalize table name for split uplink scenarios
+ local rt_name="${ipTablePrefix}_${iface}"
+ if is_split_uplink && [ "$iface" = "$uplink_interface6" ]; then
+ rt_name="${ipTablePrefix}_${uplink_interface4}"
+ fi
+ # clean-up first for repeated netifd install calls in different netifd_strict_enforcement modes
+ uci_remove 'network' "${iface}" 'ip4table' 2>/dev/null
+ uci_remove 'network' "${iface}" 'ip6table' 2>/dev/null
uci_remove 'network' 'rule' "${rt_name}_ipv4" 2>/dev/null
uci_remove 'network' 'rule6' "${rt_name}_ipv6" 2>/dev/null
+ # process local interfaces and set up rules for LANs if netifd_strict_enforcement is enabled
if [ -n "$netifd_strict_enforcement" ] && str_contains "$netifd_interface_local" "$iface"; then
- if [ -n "$netifd_interface_default" ]; then
- uci_add 'network' 'rule' "${rt_name}_ipv4"
- uci_set 'network' "${rt_name}_ipv4" 'in' "${iface}"
- uci_set 'network' "${rt_name}_ipv4" 'lookup' "${ipTablePrefix}_${netifd_interface_default}"
- uci_set 'network' "${rt_name}_ipv4" 'priority' "${lan_priority}"
- fi
- if [ -n "$ipv6_enabled" ] && [ -n "$netifd_interface_default6" ]; then
- uci_add 'network' 'rule6' "${rt_name}_ipv6"
- uci_set 'network' "${rt_name}_ipv6" 'in' "${iface}"
- uci_set 'network' "${rt_name}_ipv6" 'lookup' "${ipTablePrefix}_${netifd_interface_default6}"
- uci_set 'network' "${rt_name}_ipv6" 'priority' "${lan_priority}"
- fi
- lan_priority="$((lan_priority + 1))"
+ case "$action" in
+ install)
+ if [ -n "$netifd_interface_default" ]; then
+ uci_add 'network' 'rule' "${rt_name}_ipv4"
+ uci_set 'network' "${rt_name}_ipv4" 'in' "${iface}"
+ uci_set 'network' "${rt_name}_ipv4" 'lookup' "${ipTablePrefix}_${netifd_interface_default}"
+ uci_set 'network' "${rt_name}_ipv4" 'priority' "${lan_priority}"
+ fi
+ if [ -n "$ipv6_enabled" ] && [ -n "$netifd_interface_default6" ]; then
+ uci_add 'network' 'rule6' "${rt_name}_ipv6"
+ uci_set 'network' "${rt_name}_ipv6" 'in' "${iface}"
+ uci_set 'network' "${rt_name}_ipv6" 'lookup' "${ipTablePrefix}_${netifd_interface_default6}"
+ uci_set 'network' "${rt_name}_ipv6" 'priority' "${lan_priority}"
+ fi
+ lan_priority="$((lan_priority + 1))"
+ ;;
+ remove|uninstall)
+ : # rules already removed above
+ ;;
+ esac
fi
+
+ # process only WAN and supported tunnels below
is_supported_interface "$iface" || return 0
- if [ -z "$target_iface" ] || [ "$target_ifance" = "$iface" ]; then
- is_wan6 "$iface" && return # TODO: properly process wan/wan6 at some point
- if [ -z "$netifd_strict_enforcement" ] && [ "$netifd_interface_default" = "$iface" ]; then
- rt_name='main'
+ local _mark="$mark" _priority="$priority" _tid="$tid"
+ local splitUplinkSecondIface
+
+ if is_split_uplink; then
+ if is_wan "$iface" || is_wan6 "$iface"; then
+ if [ -n "$_uplinkMark" ] && [ -n "$_uplinkPriority" ] && [ -n "$_uplinkTableID" ]; then
+ _mark="$_uplinkMark"
+ _priority="$_uplinkPriority"
+ _tid="$_uplinkTableID"
+ splitUplinkSecondIface='true'
+ else
+ _uplinkMark="$_mark"
+ _uplinkPriority="$_priority"
+ _uplinkTableID="$_tid"
+ fi
fi
+ fi
+
+ # use main table for default uplink if netifd_strict_enforcement is not enabled
+ [ -z "$netifd_strict_enforcement" ] && [ "$netifd_interface_default" = "$iface" ] && \
+ rt_name='main'
+
+ if [ -z "$target_iface" ] || [ "$target_iface" = "$iface" ]; then
case "$action" in
install)
output 2 "Setting up netifd extensions for $iface... "
- [ "$rt_name" = 'main' ] || sed -i "\#${rt_name}\$#d" "$rtTablesFile" >/dev/null 2>&1
- [ "$rt_name" = 'main' ] || echo "${tid} ${rt_name}" >> "$rtTablesFile"
- uci_set 'network' "${iface}" 'ip4table' "${rt_name}"
- uci_set 'network' "${iface}" 'ip6table' "${rt_name}"
- uci_add 'network' 'rule' "${rt_name}_ipv4"
- uci_set 'network' "${rt_name}_ipv4" 'priority' "${priority}"
- uci_set 'network' "${rt_name}_ipv4" 'lookup' "${rt_name}"
- uci_set 'network' "${rt_name}_ipv4" 'mark' "${mark}"
- uci_set 'network' "${rt_name}_ipv4" 'mask' "${fw_mask}"
- if [ -n "$ipv6_enabled" ]; then
+ if ! is_split_uplink || ! is_wan6 "$iface"; then
+ uci_set 'network' "${iface}" 'ip4table' "${rt_name}"
+ uci_add 'network' 'rule' "${rt_name}_ipv4"
+ uci_set 'network' "${rt_name}_ipv4" 'priority' "${_priority}"
+ uci_set 'network' "${rt_name}_ipv4" 'lookup' "${rt_name}"
+ uci_set 'network' "${rt_name}_ipv4" 'mark' "${_mark}"
+ uci_set 'network' "${rt_name}_ipv4" 'mask' "${fw_mask}"
+ fi
+ if [ -n "$ipv6_enabled" ] && { ! is_split_uplink || ! is_wan "$iface"; }; then
+ uci_set 'network' "${iface}" 'ip6table' "${rt_name}"
uci_add 'network' 'rule6' "${rt_name}_ipv6"
- uci_set 'network' "${rt_name}_ipv6" 'priority' "${priority}"
+ uci_set 'network' "${rt_name}_ipv6" 'priority' "${_priority}"
uci_set 'network' "${rt_name}_ipv6" 'lookup' "${rt_name}"
- uci_set 'network' "${rt_name}_ipv6" 'mark' "${mark}"
+ uci_set 'network' "${rt_name}_ipv6" 'mark' "${_mark}"
uci_set 'network' "${rt_name}_ipv6" 'mask' "${fw_mask}"
fi
- sed -i "\#${mark}#d" "$nftTempFile" >/dev/null 2>&1
- nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}"
- nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${mark}"
- nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return"
+ if ! is_split_uplink || ! is_wan6 "$iface"; then
+ [ "$rt_name" = 'main' ] || sed -i "\#${rt_name}\$#d" "$rtTablesFile" >/dev/null 2>&1
+ [ "$rt_name" = 'main' ] || echo "${_tid} ${rt_name}" >> "$rtTablesFile"
+ nft_file 'sed' 'temp' "\#${_mark}#d"
+ fi
+ if ! nft_file 'match' 'temp' "${nftPrefix}_mark_${_mark}"; then
+ nft add chain inet "$nftTable" "${nftPrefix}_mark_${_mark}"
+ nft add rule inet "$nftTable" "${nftPrefix}_mark_${_mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${_mark}"
+ nft add rule inet "$nftTable" "${nftPrefix}_mark_${_mark} return"
+ fi
+ local dscp="$(uci_get "$packageName" 'config' "${iface}_dscp")"
+ if [ "${dscp:-0}" -ge '1' ] && [ "${dscp:-0}" -le '63' ]; then
+ if ! is_split_uplink || ! is_wan6 "$iface"; then
+ nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${_mark}"
+ fi
+ if [ -n "$ipv6_enabled" ] && { ! is_split_uplink || ! is_wan "$iface"; }; then
+ nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${_mark}"
+ fi
+ fi
+ if [ "$iface" = "$icmp_interface" ]; then
+ if ! is_split_uplink || ! is_wan6 "$iface"; then
+ nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${_mark}"
+ fi
+ if [ -n "$ipv6_enabled" ] && { ! is_split_uplink || ! is_wan "$iface"; }; then
+ nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${_mark}"
+ fi
+ fi
+
output_okb
;;
remove|uninstall)
output 2 "Removing netifd extensions for $iface... "
[ "$rt_name" = 'main' ] || sed -i "\#${rt_name}\$#d" "$rtTablesFile" >/dev/null 2>&1
- sed -i "\#${mark}#d" "$nftNetifdPermFile" >/dev/null 2>&1
- uci_remove 'network' "${iface}" 'ip4table' "${rt_name}" 2>/dev/null
- uci_remove 'network' "${iface}" 'ip6table' "${rt_name}" 2>/dev/null
+ nft_file 'sed' 'netifd' "\#${_mark}#d"
output_okb
;;
esac
fi
- mark="$(printf '0x%06x' $((mark + uplink_mark)))"
- priority="$((priority - 1))"
- tid="$((tid + 1))"
+
+ if [ -z "$splitUplinkSecondIface" ]; then
+ mark="$(printf '0x%06x' $((_mark + uplink_mark)))"
+ priority="$((_priority - 1))"
+ tid="$((_tid + 1))"
+ fi
}
load_package_config
local action="${1:-install}"
local target_iface="$2"
+ local lan_priority="$((uplink_ip_rules_priority + 1000))"
local mark="$(printf '0x%06x' "$uplink_mark")"
local priority="$uplink_ip_rules_priority"
- local lan_priority="$((uplink_ip_rules_priority + 1000))"
local tid="$(get_rt_tables_non_pbr_next_id)"
+ local _uplinkMark _uplinkPriority _uplinkTableID
case "$action" in
check)
fi
if [ -z "$netifd_interface_local" ]; then
json add warning 'warningNetifdMissingInterfaceLocal' 'lan'
- output_error 'warningNetifdMissingInterfaceLocal' 'lan'
+ output_warning 'warningNetifdMissingInterfaceLocal' 'lan'
netifd_interface_local='lan'
fi
[ "$netifd_strict_enforcement" = '1' ] || unset netifd_strict_enforcement
;;
esac
- nft_file 'netifd_create'
+ nft_file 'create' 'netifd'
output 1 "Netifd extensions $action ${target_iface:+on $target_iface }"
+ uci_remove 'network' 'rule' "main_ipv4" 2>/dev/null
+ uci_remove 'network' 'rule6' "main_ipv6" 2>/dev/null
config_load 'network'
config_foreach _netifd_process_interface 'interface' "$action"
output_1_newline
case "$action" in
install)
- nft_file 'netifd_install'
+ nft_file 'install' 'netifd'
if [ -z "$target_iface" ]; then
uci_set "$packageName" 'config' 'netifd_enabled' '1'
fi
;;
remove)
if [ -z "$target_iface" ]; then
- nft_file 'netifd_remove'
+ nft_file 'delete' 'netifd'
uci_remove "$packageName" 'config' 'netifd_enabled' 2>/dev/null
fi
;;
uninstall)
- nft_file 'netifd_remove'
+ if [ -z "$target_iface" ]; then
+ nft_file 'delete' 'netifd'
+ fi
;;
esac
uci_commit "$packageName"
-# cat "$nftNetifdPermFile"
-# cat "$rtTablesFile"
-# uci changes
-# uci revert network
uci_commit 'network'
sync
output "Restarting network ${action:+(on_$action) }"
local negation value dest4 dest6 first_value
local inline_set_ipv4_empty_flag inline_set_ipv6_empty_flag
local name="$1" src_addr="$2" dest_dns="$3" uid="$4" dest_dns_port="$5"
+ local dest_dns_ipv4="$6" dest_dns_ipv6="$7"
local chain='dstnat' iface='dns'
if [ -z "${dest_dns_ipv4}${dest_dns_ipv6}" ]; then
- processPolicyError='true'
+ processDnsPolicyError='true'
json add error 'errorPolicyProcessNoInterfaceDns' "'$dest_dns'"
return 1
fi
if [ -z "$ipv6_enabled" ] && is_ipv6 "$(str_first_word "$src_addr")"; then
- processPolicyError='true'
+ processDnsPolicyError='true'
json add error 'errorPolicyProcessNoIpv6' "$name"
return 1
fi
if { is_ipv4 "$(str_first_word "$src_addr")" && [ -z "$dest_dns_ipv4" ]; } || \
{ is_ipv6 "$(str_first_word "$src_addr")" && [ -z "$dest_dns_ipv6" ]; }; then
- processPolicyError='true'
+ processDnsPolicyError='true'
json add error 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_dns':'$dest_dns_port'"
return 1
fi
fi
if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
- processPolicyError='true'
+ processDnsPolicyError='true'
json add error 'errorPolicyProcessInsertionFailed' "$name"
json add error 'errorPolicyProcessCMD' "nft $param4"
json add error 'errorPolicyProcessCMD' "nft $param6"
logger -t "$packageName" "ERROR: nft $param4"
logger -t "$packageName" "ERROR: nft $param6"
elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
- processPolicyError='true'
+ processDnsPolicyError='true'
json add error 'errorPolicyProcessInsertionFailedIpv4' "$name"
json add error 'errorPolicyProcessCMD' "nft $param4"
logger -t "$packageName" "ERROR: nft $param4"
param4="${param4:+$param4 }ether saddr ${negation:+$negation }{ $(inline_set "$value") }"
param6="${param6:+$param6 }ether saddr ${negation:+$negation }{ $(inline_set "$value") }"
elif is_domain "$first_value_src"; then
- local inline_set_ipv4='' inline_set_ipv6='' d=''
- unset src_inline_set_ipv4_empty_flag
- unset src_inline_set_ipv6_empty_flag
- for d in $value; do
- local resolved_ipv4 resolved_ipv6
- resolved_ipv4="$(resolveip_to_nftset4 "$d")"
- resolved_ipv6="$(resolveip_to_nftset6 "$d")"
- if [ -z "${resolved_ipv4}${resolved_ipv6}" ]; then
- json add error 'errorFailedToResolve' "$d"
- else
- [ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
- [ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
- fi
- done
- [ -n "$inline_set_ipv4" ] || src_inline_set_ipv4_empty_flag='true'
- [ -n "$inline_set_ipv6" ] || src_inline_set_ipv6_empty_flag='true'
- param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }{ $inline_set_ipv4 }"
- param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }{ $inline_set_ipv6 }"
+ local target='src' type='ip'
+ if resolver 'create_resolver_set' "$iface" "$target" "$type" "$uid" "$name" && \
+ resolver 'add_resolver_element' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}${nftset_suffix}"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}${nftset_suffix}"
+ else
+ local inline_set_ipv4='' inline_set_ipv6='' d=''
+ unset src_inline_set_ipv4_empty_flag
+ unset src_inline_set_ipv6_empty_flag
+ for d in $value; do
+ local resolved_ipv4 resolved_ipv6
+ resolved_ipv4="$(resolveip_to_nftset4 "$d")"
+ resolved_ipv6="$(resolveip_to_nftset6 "$d")"
+ if [ -z "${resolved_ipv4}${resolved_ipv6}" ]; then
+ json add error 'errorFailedToResolve' "$d"
+ else
+ [ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
+ [ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
+ fi
+ done
+ [ -n "$inline_set_ipv4" ] || src_inline_set_ipv4_empty_flag='true'
+ [ -n "$inline_set_ipv6" ] || src_inline_set_ipv6_empty_flag='true'
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }{ $inline_set_ipv4 }"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }{ $inline_set_ipv6 }"
+ fi
else
param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }{ $(inline_set "$value") }"
param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }{ $(inline_set "$value") }"
src_addr="$(str_extras_to_space "$src_addr")"
dest_dns="$(str_extras_to_space "$dest_dns")"
+ unset j
+ for i in $src_addr; do
+ if is_url "$i"; then
+ i="$(process_url "$i")"
+ fi
+ j="${j:+$j }$i"
+ done
+ src_addr="$j"
+
local dest_dns_interface dest_dns_ipv4 dest_dns_ipv6
dest_dns_interface="$(str_first_value_interface "$dest_dns")"
dest_dns_ipv4="$(str_first_value_ipv4 "$dest_dns")"
if is_supported_interface "$dest_dns_interface"; then
local d
for d in $(uci -q get network."$dest_dns_interface".dns); do
- if ! is_family_mismatch "$src_addr" "$d"; then
- if is_ipv4 "$d"; then
- dest_dns_ipv4="${dest_dns_ipv4:-$d}"
- elif is_ipv6 "$d"; then
- dest_dns_ipv6="${dest_dns_ipv6:-$d}"
+ if ! is_family_mismatch "$src_addr" "$d"; then
+ if is_ipv4 "$d"; then
+ dest_dns_ipv4="${dest_dns_ipv4:-$d}"
+ elif is_ipv6 "$d"; then
+ dest_dns_ipv6="${dest_dns_ipv6:-$d}"
+ fi
fi
- fi
- done
+ done
fi
unset processDnsPolicyError
if str_contains "$filter_group_src_addr" 'ipv6' && [ -z "$dest_dns_ipv6" ] ; then
continue
fi
- dns_policy_routing "$name" "$filtered_value_src_addr" "$dest_dns" "$uid" "$dest_dns_port"
+ dns_policy_routing "$name" "$filtered_value_src_addr" "$dest_dns" "$uid" "$dest_dns_port" "$dest_dns_ipv4" "$dest_dns_ipv6"
fi
done
}
interface_routing() {
- local action="$1" tid="$2" mark="$3" iface="$4" gw4="$5" dev="$6" gw6="$7" dev6="$8" priority="$9"
+ local action="$1" tid="$2" mark="$3" iface="$4" gw4="$5" dev4="$6" gw6="$7" dev6="$8" priority="$9"
local dscp s=0 i ipv4_error=1 ipv6_error=1
if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
json add error 'errorInterfaceRoutingEmptyValues'
case "$action" in
create)
is_netifd_interface "$iface" && return 0
- ifacesTriggers="${ifacesTriggers:+$ifacesTriggers }$iface"
- if ! grep -q "$tid ${ipTablePrefix}_${iface}" "$rtTablesFile"; then
- sed -i "/${ipTablePrefix}_${iface}/d" "$rtTablesFile"
- echo "$tid ${ipTablePrefix}_${iface}" >> "$rtTablesFile"
- sync
+ # Normalize table name for split uplink scenarios
+ local table_iface="$iface"
+ if is_split_uplink && [ "$iface" = "$uplink_interface6" ]; then
+ table_iface="$uplink_interface4"
fi
- # Ensure a clean slate for this table before adding routes/rules
- ip -4 rule flush table "$tid" >/dev/null 2>&1
- ip -4 route flush table "$tid" >/dev/null 2>&1
- if [ -n "$ipv6_enabled" ]; then
- ip -6 rule flush table "$tid" >/dev/null 2>&1
- ip -6 route flush table "$tid" >/dev/null 2>&1
+ if ! grep -q "$tid ${ipTablePrefix}_${table_iface}" "$rtTablesFile"; then
+ sed -i "/${ipTablePrefix}_${table_iface}/d" "$rtTablesFile"
+ echo "$tid ${ipTablePrefix}_${table_iface}" >> "$rtTablesFile"
+ sync
fi
- if [ -n "$gw4" ] || [ -n "$strict_enforcement" ]; then
+
+ if [ -n "$dev4" ]; then
ipv4_error=0
- if [ -z "$gw4" ]; then
- try ip -4 route replace unreachable default table "$tid" || ipv4_error=1
- else
- try ip -4 route replace default via "$gw4" dev "$dev" table "$tid" || ipv4_error=1
+ ip -4 rule flush table "$tid" >/dev/null 2>&1
+ ip -4 route flush table "$tid" >/dev/null 2>&1
+
+ if [ -n "$gw4" ] || [ -n "$strict_enforcement" ]; then
+ if [ -z "$gw4" ]; then
+ try ip -4 route replace unreachable default table "$tid" || ipv4_error=1
+ else
+ try ip -4 route replace default via "$gw4" dev "$dev4" table "$tid" || ipv4_error=1
+ fi
+ try ip -4 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
fi
- # try ip -4 rule replace fwmark "${mark}/${fw_mask}" lookup 'main' suppress_prefixlength 0 priority "$((priority - 1000))" || ipv4_error=1
- ip -4 rule del lookup 'main' suppress_prefixlength "$prefixlength" priority "$priority" >/dev/null 2>&1
- try ip -4 rule add lookup 'main' suppress_prefixlength "$prefixlength" priority "$((priority - 1))" || ipv4_error=1
- # {
- # for prio in $(ip -4 rule show | awk '/lookup main/ && /suppress_prefixlength 0/ {gsub(":", "", $1); print $1}'); do
- # rule="$(ip -4 rule show | awk -v p="$prio" '($1==p":"){ $1=""; sub(/^ /,""); print }')"
- # [ -n "$rule" ] || continue
- # rule="${rule/lookup main/lookup $tid}"
- # ip -4 rule replace priority "$prio" $rule >/dev/null 2>&1 || ipv4_error=1
- # done
- # }
- try ip -4 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
- fi
- try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1
- try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${mark}" || ipv4_error=1
- try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
- if [ -n "$ipv6_enabled" ]; then
+
+ if ! nft_file 'match' 'temp' "${nftPrefix}_mark_${mark}"; then
+ try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1
+ try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${mark}" || ipv4_error=1
+ try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
+ fi
+
+ dscp="$(uci_get "$packageName" 'config' "${iface}_dscp" '0')"
+ if [ "$dscp" -ge '1' ] && [ "$dscp" -le '63' ]; then
+ try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
+ fi
+ if [ "$iface" = "$icmp_interface" ]; then
+ try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
+ fi
+ fi
+
+ if [ -n "$ipv6_enabled" ] && [ -n "$dev6" ]; then
ipv6_error=0
+ ip -6 rule flush table "$tid" >/dev/null 2>&1
+ ip -6 route flush table "$tid" >/dev/null 2>&1
+
if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ -n "$strict_enforcement" ]; then
if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
try ip -6 route replace unreachable default table "$tid" || ipv6_error=1
try ip -6 route replace "$(ip -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" || ipv6_error=1
try ip -6 route replace default dev "$dev6" table "$tid" || ipv6_error=1
fi
- # try ip -6 rule replace fwmark "${mark}/${fw_mask}" lookup 'main' suppress_prefixlength 0 priority "$((priority - 1000))" || ipv6_error=1
- ip -6 rule del lookup 'main' suppress_prefixlength "$prefixlength" priority "$priority" >/dev/null 2>&1
- try ip -6 rule add lookup 'main' suppress_prefixlength "$prefixlength" priority "$((priority - 1))" || ipv6_error=1
- # {
- # for prio in $(ip -6 rule show | awk '/lookup main/ && /suppress_prefixlength 0/ {gsub(":", "", $1); print $1}'); do
- # rule="$(ip -6 rule show | awk -v p="$prio" '($1==p":"){ $1=""; sub(/^ /,""); print }')"
- # [ -n "$rule" ] || continue
- # rule="${rule/lookup main/lookup $tid}"
- # ip -6 rule replace priority "$prio" $rule >/dev/null 2>&1 || ipv6_error=1
- # done
- # }
try ip -6 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
fi
- fi
- if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
- dscp="$(uci_get "$packageName" 'config' "${iface}_dscp")"
- if [ "${dscp:-0}" -ge '1' ] && [ "${dscp:-0}" -le '63' ]; then
- try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
- if [ -n "$ipv6_enabled" ]; then
- try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
- fi
+
+ if ! nft_file 'match' 'temp' "${nftPrefix}_mark_${mark}"; then
+ try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv6_error=1
+ try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${mark}" || ipv6_error=1
+ try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv6_error=1
+ fi
+
+ dscp="$(uci_get "$packageName" 'config' "${iface}_dscp" '0')"
+ if [ "$dscp" -ge '1' ] && [ "$dscp" -le '63' ]; then
+ try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
fi
if [ "$iface" = "$icmp_interface" ]; then
- try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
- if [ -n "$ipv6_enabled" ]; then
- try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
- fi
+ try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
fi
+ fi
+
+ if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
+ s=0
else
s=1
fi
ip -4 route flush table "$tid" >/dev/null 2>&1
ip -6 rule flush table "$tid" >/dev/null 2>&1
ip -6 route flush table "$tid" >/dev/null 2>&1
- sed -i "/${ipTablePrefix}_${iface}\$/d" "$rtTablesFile"
+ # Normalize table name for split uplink scenarios
+ local table_iface="$iface"
+ if is_split_uplink && [ "$iface" = "$uplink_interface6" ]; then
+ table_iface="$uplink_interface4"
+ fi
+ sed -i "/${ipTablePrefix}_${table_iface}\$/d" "$rtTablesFile"
sync
return "$s"
;;
reload_interface)
is_netifd_interface "$iface" && return 0
- ipv4_error=0
- ip -4 rule flush table "$tid" >/dev/null 2>&1
- ip -4 route flush table "$tid" >/dev/null 2>&1
- if [ -n "$ipv6_enabled" ]; then
- ip -6 rule flush table "$tid" >/dev/null 2>&1
- ip -6 route flush table "$tid" >/dev/null 2>&1
- fi
- if [ -n "$gw4" ] || [ -n "$strict_enforcement" ]; then
- if [ -z "$gw4" ]; then
- try ip -4 route replace unreachable default table "$tid" || ipv4_error=1
- else
- try ip -4 route replace default via "$gw4" dev "$dev" table "$tid" || ipv4_error=1
+
+ if [ -n "$dev4" ]; then
+ ipv4_error=0
+ ip -4 rule flush fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
+ ip -4 route flush table "$tid" >/dev/null 2>&1
+ if [ -n "$gw4" ] || [ -n "$strict_enforcement" ]; then
+ if [ -z "$gw4" ]; then
+ try ip -4 route replace unreachable default table "$tid" || ipv4_error=1
+ else
+ try ip -4 route replace default via "$gw4" dev "$dev4" table "$tid" || ipv4_error=1
+ fi
+ try ip -4 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
fi
- # try ip -4 rule replace fwmark "${mark}/${fw_mask}" lookup 'main' suppress_prefixlength 0 priority "$((priority - 1000))" || ipv4_error=1
- ip -4 rule del lookup 'main' suppress_prefixlength "$prefixlength" priority "$priority" >/dev/null 2>&1
- try ip -4 rule add lookup 'main' suppress_prefixlength "$prefixlength" priority "$((priority - 1))" || ipv4_error=1
- # {
- # for prio in $(ip -4 rule show | awk '/lookup main/ && /suppress_prefixlength 0/ {gsub(":", "", $1); print $1}'); do
- # rule="$(ip -4 rule show | awk -v p="$prio" '($1==p":"){ $1=""; sub(/^ /,""); print }')"
- # [ -n "$rule" ] || continue
- # rule="${rule/lookup main/lookup $tid}"
- # ip -4 rule replace priority "$prio" $rule >/dev/null 2>&1 || ipv4_error=1
- # done
- # }
- try ip -4 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
- fi
- if [ -n "$ipv6_enabled" ]; then
+ fi
+
+ if [ -n "$ipv6_enabled" ] && [ -n "$dev6" ]; then
ipv6_error=0
+ ip -6 rule flush fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1
+ ip -6 route flush table "$tid" >/dev/null 2>&1
if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ -n "$strict_enforcement" ]; then
if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
try ip -6 route replace unreachable default table "$tid" || ipv6_error=1
try ip -6 route replace "$(ip -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" || ipv6_error=1
try ip -6 route replace default dev "$dev6" table "$tid" || ipv6_error=1
fi
- # try ip -6 rule replace fwmark "${mark}/${fw_mask}" lookup 'main' suppress_prefixlength 0 priority "$((priority - 1000))" || ipv6_error=1
- ip -6 rule del lookup 'main' suppress_prefixlength "$prefixlength" priority "$priority" >/dev/null 2>&1
- try ip -6 rule add lookup 'main' suppress_prefixlength "$prefixlength" priority "$((priority - 1))" || ipv6_error=1
- # {
- # for prio in $(ip -6 rule show | awk '/lookup main/ && /suppress_prefixlength 0/ {gsub(":", "", $1); print $1}'); do
- # rule="$(ip -6 rule show | awk -v p="$prio" '($1==p":"){ $1=""; sub(/^ /,""); print }')"
- # [ -n "$rule" ] || continue
- # rule="${rule/lookup main/lookup $tid}"
- # ip -6 rule replace priority "$prio" $rule >/dev/null 2>&1 || ipv6_error=1
- # done
- # }
try ip -6 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
fi
fi
+
if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
s=0
else
}
process_interface() {
- local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" reloadedIface="$3"
+ local gw4 gw6 dev4 dev6 s=0 dscp iface="$1" action="$2" reloadedIface="$3"
local displayText dispDev dispGw4 dispGw6 dispStatus
- if [ "$iface" = 'all' ] && [ "$action" = 'prepare' ]; then
- config_load 'network'
- ifaceMark="$(printf '0x%06x' "$uplink_mark")"
- ifacePriority="$uplink_ip_rules_priority"
- unset ifaceTableID
- return 0
+ if [ "$iface" = 'all' ]; then
+ case "$action" in
+ reset_globals)
+ config_load 'network'
+ ifaceMark="$(printf '0x%06x' "$uplink_mark")"
+ ifacePriority="$uplink_ip_rules_priority"
+ unset ifaceTableID
+ unset _uplinkMark _uplinkPriority _uplinkTableID
+ return 0
+ ;;
+ create_global_rules)
+ ip -4 rule del lookup 'main' suppress_prefixlength "$prefixlength" priority "$ifacePriority" >/dev/null 2>&1
+ try ip -4 rule add lookup 'main' suppress_prefixlength "$prefixlength" priority "$ifacePriority" || ipv4_error=1
+ if [ -n "$ipv6_enabled" ]; then
+ ip -6 rule del lookup 'main' suppress_prefixlength "$prefixlength" priority "$ifacePriority" >/dev/null 2>&1
+ try ip -6 rule add lookup 'main' suppress_prefixlength "$prefixlength" priority "$ifacePriority" || ipv6_error=1
+ fi
+ _wg_server() {
+ local iface="$1"
+ if is_wg_server "$iface" && ! is_ignored_interface "$iface"; then
+ local disabled listen_port
+ config_get disabled "$iface" 'disabled'
+ config_get listen_port "$iface" 'listen_port'
+ if [ "$disabled" != '1' ] && [ -n "$listen_port" ]; then
+ if [ -n "$uplink_interface4" ]; then
+ ip rule del sport "$listen_port" table "${ipTablePrefix}_${uplink_interface4}" >/dev/null 2>&1
+ ip rule add sport "$listen_port" table "${ipTablePrefix}_${uplink_interface4}" >/dev/null 2>&1
+ if [ -n "$ipv6_enabled" ]; then
+ ip -6 rule del sport "$listen_port" table "${ipTablePrefix}_${uplink_interface4}" >/dev/null 2>&1
+ ip -6 rule add sport "$listen_port" table "${ipTablePrefix}_${uplink_interface4}" >/dev/null 2>&1
+ fi
+ fi
+ fi
+ fi
+ }
+ config_foreach _wg_server 'interface'
+ return 0
+ ;;
+ esac
fi
if [ "$iface" = 'tor' ]; then
fi
if is_wg_server "$iface" && ! is_ignored_interface "$iface"; then
- local disabled listen_port
- disabled="$(uci_get 'network' "$iface" 'disabled')"
- listen_port="$(uci_get 'network' "$iface" 'listen_port')"
case "$action" in
- create|reload|reload_interface)
- if [ "$disabled" != '1' ] && [ -n "$listen_port" ]; then
- if [ -n "$wanIface4" ]; then
- ip rule del sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
- ip rule add sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
- fi
- if [ -n "$ipv6_enabled" ] && [ -n "$wanIface6" ]; then
- ip -6 rule del sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
- ip -6 rule add sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
- fi
- fi
- ;;
destroy)
+ local listen_port="$(uci_get 'network' "$iface" 'listen_port')"
if [ -n "$listen_port" ]; then
- ip rule del sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
- ip -6 rule del sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
+ ip rule del sport "$listen_port" table "pbr_${uplink_interface4}" >/dev/null 2>&1
+ ip -6 rule del sport "$listen_port" table "pbr_${uplink_interface4}" >/dev/null 2>&1
fi
;;
esac
fi
is_supported_interface "$iface" || return 0
- is_wan6 "$iface" && return 0
- [ "$((ifaceMark))" -gt "$((fw_mask))" ] && return 1
+ if [ "$((ifaceMark))" -gt "$((fw_mask))" ]; then
+ json add error 'errorInterfaceMarkOverflow' "$iface"
+ return 1
+ fi
if is_ovpn "$iface" && ! is_ovpn_valid "$iface"; then
- : || json add warning 'warningInvalidOVPNConfig' "$iface"
+ : # output_warning 'warningInvalidOVPNConfig' "$iface"
fi
- network_get_device dev "$iface"
- [ -z "$dev" ] && network_get_physdev dev "$iface"
- if is_wan "$iface" && [ -n "$wanIface6" ] && str_contains "$wanIface6" "$iface"; then
- network_get_device dev6 "$wanIface6"
- [ -z "$dev6" ] && network_get_physdev dev6 "$wanIface6"
+ network_get_device dev4 "$iface"
+ [ -z "$dev4" ] && network_get_physdev dev4 "$iface"
+ if is_wan "$iface" && [ -n "$uplink_interface6" ]; then
+ network_get_device dev6 "$uplink_interface6"
+ [ -z "$dev6" ] && network_get_physdev dev6 "$uplink_interface6"
fi
- [ -z "$dev6" ] && dev6="$dev"
+ [ -z "$dev6" ] && dev6="$dev4"
[ -z "$ifaceMark" ] && ifaceMark="$(printf '0x%06x' "$uplink_mark")"
[ -z "$ifacePriority" ] && ifacePriority="$uplink_ip_rules_priority"
+ local _mark="$ifaceMark" _priority="$ifacePriority" _tid="$ifaceTableID"
+ local splitUplinkSecondIface
+
+ if is_split_uplink; then
+ if is_wan "$iface" || is_wan6 "$iface"; then
+ if [ -n "$_uplinkMark" ] && [ -n "$_uplinkPriority" ] && [ -n "$_uplinkTableID" ]; then
+ _mark="$_uplinkMark"
+ _priority="$_uplinkPriority"
+ _tid="$_uplinkTableID"
+ splitUplinkSecondIface='true'
+ else
+ _uplinkMark="$ifaceMark"
+ _uplinkPriority="$ifacePriority"
+ fi
+ fi
+ fi
+
case "$action" in
- pre_init)
- [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_non_pbr_next_id)"
- eval "pre_init_mark_${iface//-/_}"='$ifaceMark'
- eval "pre_init_priority_${iface//-/_}"='$ifacePriority'
- eval "pre_init_tid_${iface//-/_}"='$ifaceTableID'
- ifaceMark="$(printf '0x%06x' $((ifaceMark + uplink_mark)))"
- ifacePriority="$((ifacePriority - 1))"
- ifaceTableID="$((ifaceTableID + 1))"
- return 0
+ enumerate_interface)
+ if [ -z "$splitUplinkSecondIface" ] && [ -z "$_tid" ]; then
+ _tid="$(get_rt_tables_non_pbr_next_id)"
+ ifaceTableID="$_tid"
+ fi
+
+ eval "enum_mark_${iface//-/_}"='$_mark'
+ eval "enum_priority_${iface//-/_}"='$_priority'
+ eval "enum_tid_${iface//-/_}"='$_tid'
+ ifacesTriggers="${ifacesTriggers:+$ifacesTriggers }$iface"
;;
create)
- ifaceTableID="$(get_rt_tables_id "$iface")"
- [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
- eval "mark_${iface//-/_}"='$ifaceMark'
- eval "tid_${iface//-/_}"='$ifaceTableID'
- pbr_get_gateway4 gw4 "$iface" "$dev"
+ if [ -z "$splitUplinkSecondIface" ]; then
+ _tid="$(get_rt_tables_id "$iface")"
+ [ -z "$_tid" ] && _tid="$(get_rt_tables_next_id)"
+ ifaceTableID="$_tid"
+ fi
+ eval "mark_${iface//-/_}"='$_mark'
+ eval "tid_${iface//-/_}"='$_tid'
+ pbr_get_gateway4 gw4 "$iface" "$dev4"
pbr_get_gateway6 gw6 "$iface" "$dev6"
dispGw4="${gw4:-0.0.0.0}"
dispGw6="${gw6:-::/0}"
- [ "$iface" != "$dev" ] && dispDev="$dev"
- if is_default_dev "$dev"; then
+ if is_split_uplink; then
+ if is_wan "$iface"; then
+ gw6=""; dev6=""
+ elif is_wan6 "$iface"; then
+ gw4=""; dev4=""
+ fi
+ fi
+ [ "$iface" != "$dev4" ] && dispDev="$dev4"
+ if is_default_dev "$dev4"; then
[ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
fi
if is_netifd_interface_default "$iface"; then
fi
displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
output 2 "Setting up routing for '$displayText' "
- if interface_routing 'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
- json_add_gateway 'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
+ if interface_routing 'create' "$_tid" "$_mark" "$iface" "$gw4" "$dev4" "$gw6" "$dev6" "$_priority"; then
+ json_add_gateway 'create' "$_tid" "$_mark" "$iface" "$gw4" "$dev4" "$gw6" "$dev6" "$_priority" "$dispStatus"
gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\n"
if is_netifd_interface "$iface"; then output_okb; else output_ok; fi
else
fi
;;
create_user_set)
- ifaceTableID="$(get_rt_tables_id "$iface")"
- [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
- eval "mark_${iface//-/_}"='$ifaceMark'
- eval "tid_${iface//-/_}"='$ifaceTableID'
- pbr_get_gateway4 gw4 "$iface" "$dev"
- pbr_get_gateway6 gw6 "$iface" "$dev6"
- dispGw4="${gw4:-0.0.0.0}"
- dispGw6="${gw6:-::/0}"
- [ "$iface" != "$dev" ] && dispDev="$dev"
- if is_default_dev "$dev"; then
+ if [ -z "$splitUplinkSecondIface" ]; then
+ _tid="$(get_rt_tables_id "$iface")"
+ [ -z "$_tid" ] && _tid="$(get_rt_tables_next_id)"
+ ifaceTableID="$_tid"
+ fi
+ eval "mark_${iface//-/_}"='$_mark'
+ eval "tid_${iface//-/_}"='$_tid'
+ if is_split_uplink; then
+ if is_wan "$iface"; then
+ dev6=""
+ elif is_wan6 "$iface"; then
+ dev4=""
+ fi
+ fi
+ [ "$iface" != "$dev4" ] && dispDev="$dev4"
+ if is_default_dev "$dev4"; then
[ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
fi
if is_netifd_interface_default "$iface"; then
[ "$verbosity" = '1' ] && dispStatus="$_OKB_" || dispStatus="$__OKB__"
fi
- displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
- interface_routing 'create_user_set' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"
+ displayText="${iface}/${dispDev:+$dispDev/}"
+ interface_routing 'create_user_set' "$_tid" "$_mark" "$iface" "" "$dev4" "" "$dev6" "$_priority"
;;
destroy)
- ifaceTableID="$(get_rt_tables_id "$iface")"
- [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
- eval "mark_${iface//-/_}"='$ifaceMark'
- eval "tid_${iface//-/_}"='$ifaceTableID'
- pbr_get_gateway4 gw4 "$iface" "$dev"
- pbr_get_gateway6 gw6 "$iface" "$dev6"
- dispGw4="${gw4:-0.0.0.0}"
- dispGw6="${gw6:-::/0}"
- [ "$iface" != "$dev" ] && dispDev="$dev"
- if is_default_dev "$dev"; then
+ if [ -z "$splitUplinkSecondIface" ]; then
+ _tid="$(get_rt_tables_id "$iface")"
+ [ -z "$_tid" ] && _tid="$(get_rt_tables_next_id)"
+ ifaceTableID="$_tid"
+ fi
+ eval "mark_${iface//-/_}"='$_mark'
+ eval "tid_${iface//-/_}"='$_tid'
+ if is_split_uplink; then
+ if is_wan "$iface"; then
+ dev6=""
+ elif is_wan6 "$iface"; then
+ dev4=""
+ fi
+ fi
+ [ "$iface" != "$dev4" ] && dispDev="$dev4"
+ if is_default_dev "$dev4"; then
[ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
fi
if is_netifd_interface_default "$iface"; then
[ "$verbosity" = '1' ] && dispStatus="$_OKB_" || dispStatus="$__OKB__"
fi
- displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
- displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
+ displayText="${iface}/${dispDev:+$dispDev}"
output 2 "Removing routing for '$displayText' "
- #interface_routing 'destroy' "${ifaceTableID}" "${ifaceMark}" "${iface}"
- interface_routing 'destroy' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"
+ interface_routing 'destroy' "$_tid" "$_mark" "$iface" "" "$dev4" "" "$dev6" "$_priority"
if is_netifd_interface "$iface"; then output_okb; else output_ok; fi
;;
reload)
- ifaceTableID="$(get_rt_tables_id "$iface")"
- [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
- eval "mark_${iface//-/_}"='$ifaceMark'
- eval "tid_${iface//-/_}"='$ifaceTableID'
- pbr_get_gateway4 gw4 "$iface" "$dev"
+ if [ -z "$splitUplinkSecondIface" ]; then
+ _tid="$(get_rt_tables_id "$iface")"
+ [ -z "$_tid" ] && _tid="$(get_rt_tables_next_id)"
+ ifaceTableID="$_tid"
+ fi
+ eval "mark_${iface//-/_}"='$_mark'
+ eval "tid_${iface//-/_}"='$_tid'
+ pbr_get_gateway4 gw4 "$iface" "$dev4"
pbr_get_gateway6 gw6 "$iface" "$dev6"
dispGw4="${gw4:-0.0.0.0}"
dispGw6="${gw6:-::/0}"
- [ "$iface" != "$dev" ] && dispDev="$dev"
- if is_default_dev "$dev"; then
+ if is_split_uplink; then
+ if is_wan "$iface"; then
+ gw6=""; dev6=""
+ elif is_wan6 "$iface"; then
+ gw4=""; dev4=""
+ fi
+ fi
+ [ "$iface" != "$dev4" ] && dispDev="$dev4"
+ if is_default_dev "$dev4"; then
[ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
fi
if is_netifd_interface_default "$iface"; then
gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\n"
;;
reload_interface)
- ifaceTableID="$(get_rt_tables_id "$iface")"
- [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
- eval "mark_${iface//-/_}"='$ifaceMark'
- eval "tid_${iface//-/_}"='$ifaceTableID'
- pbr_get_gateway4 gw4 "$iface" "$dev"
+ if [ -z "$splitUplinkSecondIface" ]; then
+ _tid="$(get_rt_tables_id "$iface")"
+ [ -z "$_tid" ] && _tid="$(get_rt_tables_next_id)"
+ ifaceTableID="$_tid"
+ fi
+ eval "mark_${iface//-/_}"='$_mark'
+ eval "tid_${iface//-/_}"='$_tid'
+ pbr_get_gateway4 gw4 "$iface" "$dev4"
pbr_get_gateway6 gw6 "$iface" "$dev6"
dispGw4="${gw4:-0.0.0.0}"
dispGw6="${gw6:-::/0}"
- [ "$iface" != "$dev" ] && dispDev="$dev"
- if is_default_dev "$dev"; then
+ if is_split_uplink; then
+ if is_wan "$iface"; then
+ gw6=""; dev6=""
+ elif is_wan6 "$iface"; then
+ gw4=""; dev4=""
+ fi
+ fi
+ [ "$iface" != "$dev4" ] && dispDev="$dev4"
+ if is_default_dev "$dev4"; then
[ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
fi
if is_netifd_interface_default "$iface"; then
displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
if [ "$iface" = "$reloadedIface" ]; then
output 2 "Reloading routing for '$displayText' "
- if interface_routing 'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
- json_add_gateway 'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
+ if interface_routing 'reload_interface' "$_tid" "$_mark" "$iface" "$gw4" "$dev4" "$gw6" "$dev6" "$_priority"; then
+ json_add_gateway 'reload_interface' "$_tid" "$_mark" "$iface" "$gw4" "$dev4" "$gw6" "$dev6" "$_priority" "$dispStatus"
gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\n"
if is_netifd_interface "$iface"; then output_okb; else output_ok; fi
else
output_fail
fi
else
- json_add_gateway 'skip_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
+ json_add_gateway 'skip_interface' "$_tid" "$_mark" "$iface" "$gw4" "$dev4" "$gw6" "$dev6" "$_priority" "$dispStatus"
gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\n"
fi
;;
esac
- ifaceMark="$(printf '0x%06x' $((ifaceMark + uplink_mark)))"
- ifacePriority="$((ifacePriority - 1))"
+
+ if is_split_uplink && [ -z "$splitUplinkSecondIface" ]; then
+ if is_wan "$iface" || is_wan6 "$iface"; then
+ _uplinkTableID="$_tid"
+ fi
+ fi
+
+ if [ -z "$splitUplinkSecondIface" ]; then
+ ifaceMark="$(printf '0x%06x' $((ifaceMark + uplink_mark)))"
+ ifacePriority="$((ifacePriority - 1))"
+ ifaceTableID="$((ifaceTableID + 1))"
+ fi
return $s
}
}
boot() {
- nft_file 'delete'
+ nft_file 'delete' 'main'
rc_procd start_service 'on_boot' && service_started 'on_boot'
}
load_environment "${param:-on_start}" "$(load_validate_config)" || return 1
output "Processing environment (${param:-on_start}) "
- is_wan_up "$param" || { output_error "$(get_text 'errorUplinkDown')"; return 1; }
+ if ! is_wan_up "$param"; then
+ output_failn
+ output_warning "$(get_text 'warningUplinkDown')"
+ pbrBootFlag=1
+ return 0
+ fi
- process_interface 'all' 'prepare'
- config_foreach process_interface 'interface' 'pre_init'
+ process_interface 'all' 'reset_globals'
+ config_foreach process_interface 'interface' 'enumerate_interface'
case "$param" in
on_boot)
;;
on_interface_reload)
reloadedIface="$2"
- local tid pre_init_tid
+ local tid enum_tid
tid="$(get_rt_tables_id "$reloadedIface")"
- pre_init_tid="$(eval echo "\$pre_init_tid_${reloadedIface//-/_}")"
- if [ "$tid" = "$pre_init_tid" ]; then
+ enum_tid="$(eval echo "\$enum_tid_${reloadedIface//-/_}")"
+ if [ "$tid" = "$enum_tid" ]; then
serviceStartTrigger='on_interface_reload'
else
serviceStartTrigger='on_start'
output_okn
output 1 "Reloading Interface: $reloadedIface "
json_add_array 'gateways'
- process_interface 'all' 'prepare'
+ process_interface 'all' 'reset_globals'
config_foreach process_interface 'interface' 'reload_interface' "$reloadedIface"
json_close_array
output_1_newline
on_reload|on_start|*)
resolver 'store_hash'
resolver 'configure'
-# cleanup_main_table
-# cleanup_main_chains
-# cleanup_sets
-# cleanup_marking_chains
- cleanup_rt_tables
- nft_file 'create'
+ cleanup 'main_table' 'rt_tables' 'main_chains' 'sets'
+ nft_file 'create' 'main'
output_okn
output 1 'Processing interfaces '
json_add_array 'gateways'
- process_interface 'all' 'prepare'
+ process_interface 'all' 'reset_globals'
config_foreach process_interface 'interface' 'create'
process_interface 'tor' 'destroy'
is_tor_running && process_interface 'tor' 'create'
+ process_interface 'all' 'create_global_rules'
json_close_array
ip route flush cache
output_1_newline
output_1_newline
fi
if is_config_enabled 'include' || [ -d "/etc/${packageName}.d/" ]; then
- process_interface 'all' 'prepare'
+ process_interface 'all' 'reset_globals'
config_foreach process_interface 'interface' 'create_user_set'
output 1 'Processing user file(s) '
config_load "$packageName"
fi
output_1_newline
fi
- nft_file 'install'
+ nft_file 'install' 'main'
resolver 'compare_hash' && resolver 'restart'
;;
esac
service_started() {
[ -n "$pbrBootFlag" ] && return 0
local error warning c
- if nft_file 'exists'; then
+ if nft_file 'exists' 'main'; then
procd_set_config_changed firewall
[ -n "$gatewaySummary" ] && output "$serviceName (fw4 nft file mode) started with gateways:\n${gatewaySummary}"
else
rm -f "$packageLockFile"
[ "$1" = 'quiet' ] && quiet_mode 'on'
load_environment 'on_stop'
- if nft_file 'exists'; then
+ if nft_file 'exists' 'main'; then
nft_file_mode=1
fi
- output 'Resetting chains and sets '
-# if nft_file 'delete' && cleanup_main_table && cleanup_main_chains && cleanup_sets && cleanup_marking_chains; then
- if nft_file 'delete'; then
+ output 'Resetting routing '
+ if nft_file 'delete' 'main' && \
+ cleanup 'main_table' 'rt_tables' 'main_chains' && \
+ ip route flush cache; then
output_okn
else
output_failn
fi
- output 1 'Resetting interfaces '
- config_load 'network'
- config_foreach process_interface 'interface' 'destroy'
- process_interface 'tor' 'destroy'
- cleanup_rt_tables
- output 1 "\n"
- ip route flush cache
unset ifaceMark
unset ifaceTableID
- resolver 'store_hash'
- resolver 'cleanup'
+ output 'Resetting resolver '
+ if resolver 'store_hash' && resolver 'cleanup'; then
+ output_okn
+ else
+ output_failn
+ fi
resolver 'compare_hash' && resolver 'restart'
+
if [ -n "$enabled" ]; then
if [ -n "$nft_file_mode" ]; then
output "$serviceName (fw4 nft file mode) stopped "; output_okn;
version() { echo "$PKG_VERSION"; }
status_service() {
- local i dev dev6 wanTID ipv6_enabled
+ local i dev4 dev6 wanTID ipv6_enabled
- [ "$(uci_get "$packageName" config ipv6_enabled)" = "1" ] && ipv6_enabled='true'
+ load_package_config 'status'
+ load_network 'status'
- json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
- if [ -n "$wanIface4" ]; then
- network_get_gateway wanGW4 "$wanIface4"
- network_get_device dev "$wanIface4"
- fi
- if [ -n "$wanIface6" ]; then
- network_get_device dev6 "$wanIface6"
- wanGW6="$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')"
- [ "$wanGW6" = "default" ] && wanGW6="$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')"
- fi
+ [ -f "/etc/os-release" ] && . /etc/os-release
while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
[ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
# shellcheck disable=SC2154
- status="$serviceName installed on $dist $vers."
- [ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
- [ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
+ status="$serviceName on $OPENWRT_RELEASE.\n"
+ if [ -n "$uplink_interface4" ]; then
+ network_get_device dev4 "$uplink_interface4"
+ [ -z "$dev4" ] && network_get_physdev dev4 "$uplink_interface4"
+ status="${status}Uplink (IPv4): ${uplink_interface4}${dev4:+/$dev4}/${uplinkGW4:-0.0.0.0}.\n"
+ fi
+ if [ -n "$uplink_interface6" ]; then
+ network_get_device dev6 "$uplink_interface6"
+ [ -z "$dev6" ] && network_get_physdev dev6 "$uplink_interface6"
+ [ -z "$dev6" ] && dev6="$dev4"
+ status="${status}Uplink (IPv6): ${uplink_interface6}${dev6:+/$dev6}/${uplinkGW6:-::/0}.\n"
+ fi
echo "$_SEPARATOR_"
echo "$packageName - environment"
- echo "$status"
+ echo -en "$status"
echo "$_SEPARATOR_"
dnsmasq --version 2>/dev/null | sed '/^$/,$d'
- if nft_file 'netifd_exists'; then
+ if nft_file 'exists' 'netifd'; then
echo "$_SEPARATOR_"
- echo "$packageName fw4 netifd nft file: $nftNetifdPermFile"
- sed '1d;2d;' "$nftNetifdPermFile"
+ nft_file 'show' 'netifd'
fi
- if nft_file 'exists'; then
+ if nft_file 'exists' 'main'; then
echo "$_SEPARATOR_"
- echo "$packageName fw4 nft file: $nftPermFile"
- sed '1d;2d;' "$nftPermFile"
+ nft_file 'show' 'main'
fi
echo "$_SEPARATOR_"
echo "$packageName chains - policies"
local src_addr
local dest_dns
local dest_dns_port
- uci_load_validate "$packageName" 'policy' "$1" "${2}${3:+ $3}" \
+ uci_load_validate "$packageName" 'dns_policy' "$1" "${2}${3:+ $3}" \
'name:string:Untitled' \
'enabled:bool:1' \
'src_addr:list(neg(or(host,network,macaddr,string)))' \