All checks were successful
openwrt_helloworld / Update openwrt_helloworld (openwrt-25.12) (push) Successful in 27s
1986 lines
61 KiB
Bash
Executable File
1986 lines
61 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# Copyright (C) 2017 openwrt-ssr
|
|
# Copyright (C) 2017 yushi studio <ywb94@qq.com>
|
|
#
|
|
# This is free software, licensed under the GNU General Public License v3.
|
|
# See /LICENSE for more information.
|
|
#
|
|
|
|
. $IPKG_INSTROOT/etc/init.d/shadowsocksr
|
|
|
|
# Detect firewall version and set appropriate tools
|
|
detect_firewall() {
|
|
check_run_environment
|
|
case "$USE_TABLES" in
|
|
nftables)
|
|
USE_NFT=1
|
|
NFT="nft"
|
|
echolog "ssr-rules: Using nftables"
|
|
;;
|
|
iptables)
|
|
USE_NFT=0
|
|
IPT="iptables -t nat" # alias of iptables TCP
|
|
ipt="iptables -t mangle" # alias of iptables UDP
|
|
echolog "ssr-rules: Using iptables"
|
|
;;
|
|
*)
|
|
echolog "ERROR: No supported firewall backend"
|
|
return 1
|
|
;;
|
|
esac
|
|
FWI=$(uci get firewall.shadowsocksr.path 2>/dev/null) # firewall include file
|
|
}
|
|
|
|
# Initialize firewall detection
|
|
detect_firewall
|
|
|
|
TAG="_SS_SPEC_RULE_" # comment tag
|
|
|
|
# Initialize all global switch variables (for both NFT and IPT)
|
|
# These variables will be set in getopts parameter parsing
|
|
ENABLE_AUTO_UPDATE=0
|
|
STOP_AUTO_UPDATE=0
|
|
FORCE_UPDATE=0
|
|
CHECK_STATUS=0
|
|
RESTORE_RULES=0
|
|
FLUSH_RULES=0
|
|
CLEANUP_PERSISTENCE=0
|
|
|
|
if [ "$USE_NFT" = "1" ]; then
|
|
# NFTables persistence directory
|
|
NFTABLES_RULES_DIR="/usr/share/nftables.d/ruleset-post"
|
|
NFTABLES_RULES_FILE="$NFTABLES_RULES_DIR/99-shadowsocksr.nft"
|
|
# Auto-update configuration
|
|
AUTO_UPDATE_INTERVAL=300 # auto-update check interval (seconds), 0 means disable
|
|
fi
|
|
|
|
# Modified usage function
|
|
usage() {
|
|
cat <<-EOF
|
|
Usage: ssr-rules [options]
|
|
|
|
Valid options are:
|
|
|
|
-s <server_ip> ip address of shadowsocksr remote server
|
|
-l <local_port> port number of shadowsocksr local server
|
|
-S <server_ip> ip address of shadowsocksr remote UDP server
|
|
-L <local_port> port number of shadowsocksr local UDP server
|
|
-i <ip_list_file> a file content is bypassed ip list
|
|
-a <lan_ips> lan ip of access control, need a prefix to
|
|
define access control mode
|
|
-b <wan_ips> wan ip of will be bypassed
|
|
-w <wan_ips> wan ip of will be forwarded
|
|
-B <bp_lan_ips> lan ip of will be bypassed proxy
|
|
-p <fp_lan_ips> lan ip of will be global proxy
|
|
-G <gm_lan_ips> lan ip of will be game mode proxy
|
|
-D <proxy_ports> proxy ports
|
|
-F shunt mode
|
|
-N shunt server IP
|
|
-M shunt proxy mode
|
|
-m <Interface> Interface name
|
|
-I <ip_list_file> a file content is bypassed shunt ip list
|
|
-e <extra_options> extra options for iptables
|
|
-o apply the rules to the OUTPUT chain
|
|
-O apply the global rules to the OUTPUT chain
|
|
-u enable udprelay mode, TPROXY is required
|
|
-U enable udprelay mode, using different IP
|
|
and ports for TCP and UDP
|
|
-f flush the rules
|
|
-g gfwlist mode
|
|
-r router mode
|
|
-c oversea mode
|
|
-z all mode
|
|
|
|
# New persistence management options (use different letters to avoid conflicts)
|
|
-A enable auto-update daemon
|
|
-K stop auto-update daemon
|
|
-P force update persistence
|
|
-C check rules status
|
|
-R restore rules from persistence file
|
|
-X cleanup persistence files on stop
|
|
|
|
-h show this help message and exit
|
|
EOF
|
|
exit $1
|
|
}
|
|
|
|
loger() {
|
|
# 1.alert 2.crit 3.err 4.warn 5.notice 6.info 7.debug
|
|
logger -st ssr-rules[$$] -p$1 $2
|
|
}
|
|
|
|
# IP list normalization function (for comparison)
|
|
normalize_ip_list() {
|
|
echo "$1" | tr ' ' '\n' | sort | tr '\n' ' ' | sed 's/ $//'
|
|
}
|
|
|
|
# Check if IP list has changed
|
|
check_ip_list_changed() {
|
|
local current_list="$1"
|
|
local last_list="$2"
|
|
local list_name="$3"
|
|
|
|
local current_norm=$(normalize_ip_list "$current_list")
|
|
local last_norm=$(normalize_ip_list "$last_list")
|
|
|
|
if [ "$current_norm" != "$last_norm" ]; then
|
|
loger 6 "$list_name changed: '$last_norm' -> '$current_norm'"
|
|
return 1 # changed
|
|
else
|
|
loger 6 "$list_name unchanged: '$current_norm'"
|
|
return 0 # unchanged
|
|
fi
|
|
}
|
|
|
|
# Cleanup persistence and runtime module files
|
|
cleanup_persistence_files() {
|
|
if [ "$USE_NFT" != "1" ]; then
|
|
return 0
|
|
fi
|
|
|
|
# Remove persistence rule file
|
|
if [ -f "$NFTABLES_RULES_FILE" ]; then
|
|
rm -f "$NFTABLES_RULES_FILE" 2>/dev/null
|
|
loger 5 "Removed persistence file: $NFTABLES_RULES_FILE"
|
|
fi
|
|
|
|
# Remove run mode file
|
|
if [ -f "/tmp/.ssr_run_mode" ]; then
|
|
rm -f "/tmp/.ssr_run_mode" 2>/dev/null
|
|
loger 5 "Removed run mode file: /tmp/.ssr_run_mode"
|
|
fi
|
|
|
|
# Remove TPROXY file
|
|
if [ -f "/tmp/.last_tproxy" ]; then
|
|
rm -f "/tmp/.last_tproxy" 2>/dev/null
|
|
loger 5 "Removed run mode file: /tmp/.last_tproxy"
|
|
fi
|
|
|
|
# Remove PROXY_PORTS file
|
|
if [ -f "/tmp/.last_proxy_ports" ]; then
|
|
rm -f "/tmp/.last_proxy_ports" 2>/dev/null
|
|
loger 5 "Removed run mode file: /tmp/.last_proxy_ports"
|
|
fi
|
|
|
|
# Remove WAN_BP_IP file
|
|
if [ -f "/tmp/.last_wan_bp_ip" ]; then
|
|
rm -f "/tmp/.last_wan_bp_ip" 2>/dev/null
|
|
loger 5 "Removed run mode file: /tmp/.last_wan_bp_ip"
|
|
fi
|
|
|
|
# Remove LAN_AC_IP file
|
|
if [ -f "/tmp/.last_lan_ac_ip" ]; then
|
|
rm -f "/tmp/.last_lan_ac_ip" 2>/dev/null
|
|
loger 5 "Removed run mode file: /tmp/.last_lan_ac_ip"
|
|
fi
|
|
|
|
# Remove LAN_BP_IP file
|
|
if [ -f "/tmp/.last_lan_bp_ip" ]; then
|
|
rm -f "/tmp/.last_lan_bp_ip" 2>/dev/null
|
|
loger 5 "Removed run mode file: /tmp/.last_lan_bp_ip"
|
|
fi
|
|
|
|
# Remove WAN_FW_IP file
|
|
if [ -f "/tmp/.last_wan_fw_ip" ]; then
|
|
rm -f "/tmp/.last_wan_fw_ip" 2>/dev/null
|
|
loger 5 "Removed run mode file: /tmp/.last_wan_fw_ip"
|
|
fi
|
|
|
|
# Remove LAN_FP_IP file
|
|
if [ -f "/tmp/.last_lan_fp_ip" ]; then
|
|
rm -f "/tmp/.last_lan_fp_ip" 2>/dev/null
|
|
loger 5 "Removed run mode file: /tmp/.last_lan_fp_ip"
|
|
fi
|
|
|
|
# Remove LAN_GM_IP file
|
|
if [ -f "/tmp/.last_lan_gm_ip" ]; then
|
|
rm -f "/tmp/.last_lan_gm_ip" 2>/dev/null
|
|
loger 5 "Removed run mode file: /tmp/.last_lan_gm_ip"
|
|
fi
|
|
|
|
# Remove xhttp file state and hash files
|
|
if [ -f "/tmp/.last_xhttp_file" ]; then
|
|
rm -f "/tmp/.last_xhttp_file" 2>/dev/null
|
|
loger 5 "Removed xhttp file state: /tmp/.last_xhttp_file"
|
|
fi
|
|
if [ -f "/tmp/.last_xhttp_hash" ]; then
|
|
rm -f "/tmp/.last_xhttp_hash" 2>/dev/null
|
|
loger 5 "Removed xhttp hash file: /tmp/.last_xhttp_hash"
|
|
fi
|
|
|
|
loger 5 "Persistence cleanup completed"
|
|
return 0
|
|
}
|
|
|
|
flush_r() {
|
|
if [ "$USE_NFT" = "1" ]; then
|
|
flush_nftables
|
|
else
|
|
flush_iptables_legacy
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
flush_nftables() {
|
|
# Delete inet ss_spec table
|
|
if $NFT list table inet ss_spec >/dev/null 2>&1; then
|
|
# Delete all chains
|
|
local CHAINS=$($NFT list table inet ss_spec | awk '/chain [a-zA-Z0-9_-]+/ {print $2}' | sort -u)
|
|
for chain in $CHAINS; do
|
|
$NFT flush chain inet ss_spec $chain 2>/dev/null
|
|
$NFT delete chain inet ss_spec $chain 2>/dev/null
|
|
done
|
|
|
|
# Delete all sets
|
|
local SETS=$($NFT list table inet ss_spec | awk '/set [a-zA-Z0-9_-]+/ {print $2}' | sort -u)
|
|
for setname in $SETS; do
|
|
$NFT flush set inet ss_spec $setname 2>/dev/null
|
|
$NFT delete set inet ss_spec $setname 2>/dev/null
|
|
done
|
|
|
|
# Delete entire table
|
|
$NFT delete table inet ss_spec 2>/dev/null
|
|
fi
|
|
|
|
# Delete ip ss_spec_mangle table (if exists)
|
|
if $NFT list table ip ss_spec_mangle >/dev/null 2>&1; then
|
|
# Delete all chains
|
|
local CHAINS=$($NFT list table ip ss_spec_mangle | awk '/chain [a-zA-Z0-9_-]+/ {print $2}' | sort -u)
|
|
for chain in $CHAINS; do
|
|
$NFT flush chain ip ss_spec_mangle $chain 2>/dev/null
|
|
$NFT delete chain ip ss_spec_mangle $chain 2>/dev/null
|
|
done
|
|
|
|
# Delete all sets
|
|
local SETS=$($NFT list table ip ss_spec_mangle | awk '/set [a-zA-Z0-9_-]+/ {print $2}' | sort -u)
|
|
for setname in $SETS; do
|
|
$NFT flush set ip ss_spec_mangle $setname 2>/dev/null
|
|
$NFT delete set ip ss_spec_mangle $setname 2>/dev/null
|
|
done
|
|
# Delete entire table
|
|
$NFT delete table ip ss_spec_mangle 2>/dev/null
|
|
fi
|
|
|
|
# Delete policy routing mark rules
|
|
ip rule del fwmark 0x01/0x01 table 100 2>/dev/null
|
|
ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null
|
|
|
|
# Optional: force delete all ss_spec related sets (even if table was accidentally deleted)
|
|
for setname in ss_spec_lan_ac ss_spec_wan_ac ssr_gen_router \
|
|
china fplan bplan gmlan oversea whitelist blacklist netflix gfwlist music; do
|
|
$NFT delete set inet ss_spec $setname 2>/dev/null
|
|
$NFT delete set ip ss_spec_mangle $setname 2>/dev/null
|
|
done
|
|
|
|
# Reset firewall include file
|
|
[ -n "$FWI" ] && echo '#!/bin/sh' >"$FWI"
|
|
|
|
# Cleanup persistence and runtime module files
|
|
if [ "$CLEANUP_PERSISTENCE" = "1" ]; then
|
|
cleanup_persistence_files
|
|
fi
|
|
|
|
loger 6 "Memory rules flushed successfully"
|
|
|
|
return 0
|
|
}
|
|
|
|
flush_iptables_legacy() {
|
|
flush_iptables() {
|
|
local ipt="iptables -t $1"
|
|
local DAT=$(iptables-save -t $1)
|
|
eval $(echo "$DAT" | grep "$TAG" | sed -e 's/^-A/$ipt -D/' -e 's/$/;/')
|
|
for chain in $(echo "$DAT" | awk '/^:SS_SPEC/{print $1}'); do
|
|
$ipt -F ${chain:1} 2>/dev/null && $ipt -X ${chain:1}
|
|
done
|
|
}
|
|
flush_iptables nat
|
|
flush_iptables mangle
|
|
ip rule del fwmark 0x01/0x01 table 100 2>/dev/null
|
|
ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null
|
|
for setname in ss_spec_lan_ac ss_spec_wan_ac ssr_gen_router \
|
|
china fplan bplan gmlan oversea whitelist blacklist netflix gfwlist music; do
|
|
ipset -X $setname 2>/dev/null
|
|
done
|
|
[ -n "$FWI" ] && echo '#!/bin/sh' >$FWI
|
|
return 0
|
|
}
|
|
|
|
ipset_r() {
|
|
if [ "$USE_NFT" = "1" ]; then
|
|
ipset_nft
|
|
else
|
|
ipset_iptables
|
|
fi
|
|
return $?
|
|
}
|
|
|
|
ipset_nft() {
|
|
# Create nftables table and sets
|
|
if ! $NFT list table inet ss_spec >/dev/null 2>&1; then
|
|
$NFT add table inet ss_spec 2>/dev/null
|
|
fi
|
|
|
|
# Create necessary collections
|
|
for setname in china gmlan fplan bplan whitelist blacklist netflix music; do
|
|
if ! $NFT list set inet ss_spec $setname >/dev/null 2>&1; then
|
|
$NFT add set inet ss_spec $setname '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
|
|
else
|
|
$NFT flush set inet ss_spec $setname 2>/dev/null
|
|
fi
|
|
done
|
|
|
|
# Bulk import china ip list safely (avoid huge single element limitation)
|
|
if [ -f "${china_ip:=/etc/ssrplus/china_ssr.txt}" ]; then
|
|
$NFT add element inet ss_spec china "{ $(tr '\n' ',' < "${china_ip}" | sed 's/,$//') }" 2>/dev/null
|
|
fi
|
|
|
|
# Bulk import xhttp ip list into nft whitelist (server + shunt)
|
|
if [ -f "${xhttp_ip:=/etc/ssrplus/xhttp_address.txt}" ]; then
|
|
$NFT add element inet ss_spec whitelist "{ $(tr '\n' ',' < "${xhttp_ip}" | sed 's/,$//') }" 2>/dev/null
|
|
fi
|
|
|
|
# Add IP addresses to sets
|
|
for ip in $LAN_GM_IP; do
|
|
[ -n "$ip" ] && $NFT add element inet ss_spec gmlan "{ $ip }" 2>/dev/null
|
|
done
|
|
for ip in $LAN_FP_IP; do
|
|
[ -n "$ip" ] && $NFT add element inet ss_spec fplan "{ $ip }" 2>/dev/null
|
|
done
|
|
for ip in $LAN_BP_IP; do
|
|
[ -n "$ip" ] && $NFT add element inet ss_spec bplan "{ $ip }" 2>/dev/null
|
|
done
|
|
for ip in $WAN_BP_IP; do
|
|
[ -n "$ip" ] && $NFT add element inet ss_spec whitelist "{ $ip }" 2>/dev/null
|
|
done
|
|
for ip in $WAN_FW_IP; do
|
|
[ -n "$ip" ] && $NFT add element inet ss_spec blacklist "{ $ip }" 2>/dev/null
|
|
done
|
|
|
|
# Create main chains for WAN access control
|
|
for chain in ss_spec_wan_fw ss_spec_wan_ac; do
|
|
if ! $NFT list chain inet ss_spec $chain >/dev/null 2>&1; then
|
|
$NFT add chain inet ss_spec $chain
|
|
fi
|
|
$NFT flush chain inet ss_spec $chain
|
|
done
|
|
|
|
# Add basic rules
|
|
# BASIC RULES (exceptions first) — TCP
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac meta l4proto tcp tcp dport 53 ip daddr 127.0.0.0/8 return
|
|
[ -n "$server" ] && $NFT add rule inet ss_spec ss_spec_wan_ac meta l4proto tcp tcp dport != 53 ip daddr "$server" return
|
|
|
|
# Access control: blacklist -> whitelist -> fplan/bplan — TCP
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @blacklist jump ss_spec_wan_fw
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @whitelist return
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @fplan jump ss_spec_wan_fw
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @bplan return
|
|
|
|
# Music unlocking support
|
|
if $NFT list set inet ss_spec music >/dev/null 2>&1; then
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac meta l4proto tcp ip daddr @music return
|
|
fi
|
|
|
|
# Shunt/Netflix rules
|
|
if [ -f "$SHUNT_LIST" ]; then
|
|
for ip in $(cat "$SHUNT_LIST" 2>/dev/null); do
|
|
[ -n "$ip" ] && $NFT add element inet ss_spec netflix "{ $ip }" 2>/dev/null
|
|
done
|
|
fi
|
|
|
|
# Set up mode-specific rules
|
|
case "$RUNMODE" in
|
|
router)
|
|
if ! $NFT list set inet ss_spec ss_spec_wan_ac >/dev/null 2>&1; then
|
|
$NFT add set inet ss_spec ss_spec_wan_ac '{ type ipv4_addr; flags interval; auto-merge; }'
|
|
else
|
|
$NFT flush set inet ss_spec ss_spec_wan_ac 2>/dev/null
|
|
fi
|
|
# Add special IP ranges to WAN AC set
|
|
for ip in $(gen_spec_iplist); do
|
|
[ -n "$ip" ] && $NFT add element inet ss_spec ss_spec_wan_ac "{ $ip }" 2>/dev/null
|
|
done
|
|
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @ss_spec_wan_ac return
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @china return
|
|
if $NFT list chain inet ss_spec ss_spec_wan_ac >/dev/null 2>&1; then
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan ip daddr != @china jump ss_spec_wan_fw
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac jump ss_spec_wan_fw
|
|
fi
|
|
;;
|
|
gfw)
|
|
if ! $NFT list set inet ss_spec gfwlist >/dev/null 2>&1; then
|
|
$NFT add set inet ss_spec gfwlist '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
|
|
fi
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @china return
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @gfwlist jump ss_spec_wan_fw
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan ip daddr != @china jump ss_spec_wan_fw
|
|
;;
|
|
oversea)
|
|
if ! $NFT list set inet ss_spec oversea >/dev/null 2>&1; then
|
|
$NFT add set inet ss_spec oversea '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
|
|
fi
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @oversea jump ss_spec_wan_fw
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan jump ss_spec_wan_fw
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @china jump ss_spec_wan_fw
|
|
;;
|
|
all)
|
|
if $NFT list chain inet ss_spec ss_spec_wan_fw >/dev/null 2>&1; then
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac jump ss_spec_wan_fw
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
return $?
|
|
}
|
|
|
|
ipset_iptables() {
|
|
[ -f "$IGNORE_LIST" ] && /usr/share/shadowsocksr/chinaipset.sh "$IGNORE_LIST"
|
|
|
|
$IPT -N SS_SPEC_WAN_AC 2>/dev/null
|
|
$IPT -F SS_SPEC_WAN_AC
|
|
|
|
$IPT -I SS_SPEC_WAN_AC -p tcp --dport 53 -d 127.0.0.0/8 -j RETURN
|
|
$IPT -I SS_SPEC_WAN_AC -p tcp ! --dport 53 -d "$server" -j RETURN
|
|
|
|
ipset -N gmlan hash:net 2>/dev/null
|
|
for ip in $LAN_GM_IP; do ipset -! add gmlan "$ip"; done
|
|
|
|
case "$RUNMODE" in
|
|
router)
|
|
ipset -! -R <<-EOF || return 1
|
|
create ss_spec_wan_ac hash:net
|
|
$(gen_spec_iplist | sed -e "s/^/add ss_spec_wan_ac /")
|
|
EOF
|
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set ss_spec_wan_ac dst -j RETURN
|
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j RETURN
|
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set ! --match-set china dst -j SS_SPEC_WAN_FW
|
|
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
|
|
;;
|
|
gfw)
|
|
ipset -N gfwlist hash:net 2>/dev/null
|
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j RETURN
|
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set gfwlist dst -j SS_SPEC_WAN_FW
|
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -m set ! --match-set china dst -j SS_SPEC_WAN_FW
|
|
;;
|
|
oversea)
|
|
ipset -N oversea hash:net 2>/dev/null
|
|
$IPT -I SS_SPEC_WAN_AC -m set --match-set oversea dst -j SS_SPEC_WAN_FW
|
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set gmlan src -j SS_SPEC_WAN_FW
|
|
$IPT -A SS_SPEC_WAN_AC -m set --match-set china dst -j SS_SPEC_WAN_FW
|
|
;;
|
|
all)
|
|
$IPT -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW
|
|
;;
|
|
esac
|
|
|
|
ipset -N fplan hash:net 2>/dev/null
|
|
for ip in $LAN_FP_IP; do ipset -! add fplan "$ip"; done
|
|
$IPT -I SS_SPEC_WAN_AC -m set --match-set fplan src -j SS_SPEC_WAN_FW
|
|
|
|
ipset -N bplan hash:net 2>/dev/null
|
|
for ip in $LAN_BP_IP; do ipset -! add bplan "$ip"; done
|
|
$IPT -I SS_SPEC_WAN_AC -m set --match-set bplan src -j RETURN
|
|
|
|
ipset -N whitelist hash:net 2>/dev/null
|
|
if [ -f "${xhttp_ip:=/etc/ssrplus/xhttp_address.txt}" ]; then
|
|
while IFS= read -r ip; do
|
|
[ -n "$ip" ] && ipset add whitelist "$ip" -exist
|
|
done < "$xhttp_ip"
|
|
fi
|
|
|
|
ipset -N blacklist hash:net 2>/dev/null
|
|
$IPT -I SS_SPEC_WAN_AC -m set --match-set blacklist dst -j SS_SPEC_WAN_FW
|
|
$IPT -I SS_SPEC_WAN_AC -m set --match-set whitelist dst -j RETURN
|
|
|
|
if [ $(ipset list music -name -quiet | grep music) ]; then
|
|
$IPT -I SS_SPEC_WAN_AC -m set --match-set music dst -j RETURN 2>/dev/null
|
|
fi
|
|
|
|
for ip in $WAN_BP_IP; do ipset -! add whitelist "$ip"; done
|
|
for ip in $WAN_FW_IP; do ipset -! add blacklist "$ip"; done
|
|
|
|
if [ "$SHUNT_PORT" != "0" ]; then
|
|
ipset -N netflix hash:net 2>/dev/null
|
|
for ip in $(cat "${SHUNT_LIST:=/dev/null}" 2>/dev/null); do ipset -! add netflix "$ip"; done
|
|
case "$SHUNT_PORT" in
|
|
0) ;;
|
|
1)
|
|
$IPT -I SS_SPEC_WAN_AC -p tcp -m set --match-set netflix dst -j REDIRECT --to-ports "$local_port"
|
|
;;
|
|
*)
|
|
$IPT -I SS_SPEC_WAN_AC -p tcp -m set --match-set netflix dst -j REDIRECT --to-ports "$SHUNT_PORT"
|
|
if [ "$SHUNT_PROXY" = "1" ]; then
|
|
$IPT -I SS_SPEC_WAN_AC -p tcp -d "$SHUNT_IP" -j REDIRECT --to-ports "$local_port"
|
|
else
|
|
ipset -! add whitelist "$SHUNT_IP"
|
|
fi
|
|
;;
|
|
esac
|
|
fi
|
|
return $?
|
|
}
|
|
|
|
fw_rule() {
|
|
if [ "$USE_NFT" = "1" ]; then
|
|
fw_rule_nft
|
|
else
|
|
fw_rule_iptables
|
|
fi
|
|
return $?
|
|
}
|
|
|
|
fw_rule_nft() {
|
|
# set up routing table for tproxy
|
|
if ! ip rule show | grep -Eq "fwmark 0x0*1.*lookup 100"; then
|
|
ip rule add fwmark 0x01/0x01 table 100 2>/dev/null
|
|
fi
|
|
|
|
if ! ip route show table 100 | grep -q "^local.*dev lo"; then
|
|
ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null
|
|
fi
|
|
|
|
# redirect/translation: when PROXY_PORTS present, redirect those tcp ports to local_port
|
|
if [ -n "$PROXY_PORTS" ]; then
|
|
PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
|
|
if [ -n "$PORTS_ARGS" ]; then
|
|
TCP_EXT_ARGS="meta l4proto tcp tcp dport { $PORTS_ARGS }"
|
|
TCP_RULE="meta l4proto tcp tcp dport { $PORTS_ARGS } counter redirect to :$local_port"
|
|
fi
|
|
else
|
|
TCP_EXT_ARGS="meta l4proto tcp"
|
|
# default: redirect everything except ssh(22)
|
|
TCP_RULE="meta l4proto tcp tcp dport != 22 counter redirect to :$local_port"
|
|
fi
|
|
# add TCP rule to fw chain if not exists (use -F exact match)
|
|
if ! $NFT list chain inet ss_spec ss_spec_wan_fw 2>/dev/null | grep -F -- "$TCP_RULE" >/dev/null 2>&1; then
|
|
if ! $NFT add rule inet ss_spec ss_spec_wan_fw $TCP_RULE 2>/dev/null; then
|
|
loger 3 "Can't redirect TCP, please check nftables."
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
if [ "$SHUNT_PORT" != "0" ] && [ -f "$SHUNT_LIST" ]; then
|
|
case "$SHUNT_PORT" in
|
|
1)
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac $TCP_EXT_ARGS ip daddr @netflix counter redirect to :$local_port
|
|
;;
|
|
*)
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac $TCP_EXT_ARGS ip daddr @netflix counter redirect to :$SHUNT_PORT
|
|
if [ "$SHUNT_PROXY" = "1" ]; then
|
|
$NFT add rule inet ss_spec ss_spec_wan_ac $TCP_EXT_ARGS ip daddr $SHUNT_IP counter redirect to :$local_port
|
|
else
|
|
[ -n "$SHUNT_IP" ] && $NFT add element inet ss_spec whitelist "{ $SHUNT_IP }" 2>/dev/null
|
|
fi
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
return $?
|
|
}
|
|
|
|
fw_rule_iptables() {
|
|
# set up routing table for tproxy
|
|
if ! ip rule show | grep -Eq "fwmark 0x0*1.*lookup 100"; then
|
|
ip rule add fwmark 0x01/0x01 table 100 2>/dev/null
|
|
fi
|
|
|
|
if ! ip route show table 100 | grep -q "^local.*dev lo"; then
|
|
ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null
|
|
fi
|
|
|
|
# Create TCP chain in NAT table
|
|
$IPT -N SS_SPEC_WAN_FW 2>/dev/null
|
|
$IPT -F SS_SPEC_WAN_FW
|
|
|
|
for net in \
|
|
0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 \
|
|
172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4
|
|
do
|
|
$IPT -A SS_SPEC_WAN_FW -d "$net" -j RETURN
|
|
done
|
|
|
|
$IPT -A SS_SPEC_WAN_FW -p tcp $PROXY_PORTS -j REDIRECT --to-ports "$local_port" 2>/dev/null || {
|
|
loger 3 "Can't redirect TCP, please check the iptables."
|
|
exit 1
|
|
}
|
|
|
|
return $?
|
|
}
|
|
|
|
ac_rule() {
|
|
if [ "$USE_NFT" = "1" ]; then
|
|
ac_rule_nft
|
|
else
|
|
ac_rule_iptables
|
|
fi
|
|
return $?
|
|
}
|
|
|
|
ac_rule_nft() {
|
|
local MATCH_SET=""
|
|
|
|
if [ -n "$LAN_AC_IP" ]; then
|
|
# Create LAN access control set if needed
|
|
if ! $NFT list set inet ss_spec ss_spec_lan_ac >/dev/null 2>&1; then
|
|
$NFT add set inet ss_spec ss_spec_lan_ac '{ type ipv4_addr; flags interval; }' 2>/dev/null
|
|
else
|
|
$NFT flush set inet ss_spec ss_spec_lan_ac 2>/dev/null
|
|
fi
|
|
|
|
for ip in ${LAN_AC_IP#?}; do
|
|
[ -n "$ip" ] && $NFT add element inet ss_spec ss_spec_lan_ac "{ $ip }" 2>/dev/null
|
|
done
|
|
|
|
case "${LAN_AC_IP%${LAN_AC_IP#?}}" in
|
|
w | W)
|
|
MATCH_SET="ip saddr @ss_spec_lan_ac"
|
|
;;
|
|
b | B)
|
|
MATCH_SET="ip saddr != @ss_spec_lan_ac"
|
|
;;
|
|
*)
|
|
loger 3 "Bad argument \`-a $LAN_AC_IP\`."
|
|
return 2
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# Create ss_spec_prerouting tcp chain
|
|
if ! $NFT list chain inet ss_spec ss_spec_prerouting >/dev/null 2>&1; then
|
|
$NFT add chain inet ss_spec ss_spec_prerouting '{ type nat hook prerouting priority 0; policy accept; }'
|
|
fi
|
|
$NFT flush chain inet ss_spec ss_spec_prerouting 2>/dev/null
|
|
|
|
# Exclude special local addresses
|
|
if $NFT list chain inet ss_spec ss_spec_prerouting >/dev/null 2>&1; then
|
|
for net in 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4; do
|
|
$NFT add rule inet ss_spec ss_spec_prerouting ip daddr $net return 2>/dev/null
|
|
done
|
|
fi
|
|
|
|
# Temporarily comment IPV6 for future enablement
|
|
#if $NFT list chain inet ss_spec ss_spec_prerouting >/dev/null 2>&1; then
|
|
# for net in ::1/128 fe80::/10 fc00::/7 ff00::/8 ::/128 ::ffff:0:0/96; do
|
|
# $NFT add rule inet ss_spec ss_spec_prerouting ip6 daddr $net return 2>/dev/null
|
|
# done
|
|
#fi
|
|
|
|
# Build a rule in the prerouting hook chain that jumps to business chain with conditions
|
|
if [ -n "$PROXY_PORTS" ]; then
|
|
PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
|
|
if [ -n "$PORTS_ARGS" ]; then
|
|
TCP_EXT_ARGS="meta l4proto tcp tcp dport { $PORTS_ARGS }"
|
|
fi
|
|
else
|
|
TCP_EXT_ARGS="meta l4proto tcp"
|
|
fi
|
|
|
|
# Block UDP port 443 when TPROXY not Enable
|
|
if [ -z "$TPROXY" ]; then
|
|
# Add UDP 443 block rule
|
|
if [ -z "$Interface" ]; then
|
|
if [ -n "$MATCH_SET" ]; then
|
|
$NFT add rule inet ss_spec ss_spec_prerouting meta l4proto udp $MATCH_SET udp dport 443 drop comment "\"$TAG\"" 2>/dev/null
|
|
else
|
|
$NFT add rule inet ss_spec ss_spec_prerouting meta l4proto udp udp dport 443 drop comment "\"$TAG\"" 2>/dev/null
|
|
fi
|
|
else
|
|
for name in $Interface; do
|
|
local IFNAME=$(uci -P /var/state get network."$name".ifname 2>/dev/null)
|
|
[ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null)
|
|
if [ -n "$IFNAME" ]; then
|
|
if [ -n "$MATCH_SET" ]; then
|
|
$NFT add rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" meta l4proto udp $MATCH_SET udp dport 443 drop comment "\"$TAG\"" 2>/dev/null
|
|
else
|
|
$NFT add rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" meta l4proto udp udp dport 443 drop comment "\"$TAG\"" 2>/dev/null
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
if [ -z "$Interface" ]; then
|
|
# generic prerouting jump already exists (see ipset_nft), but if we have MATCH_SET_CONDITION we add a more specific rule
|
|
if [ -n "$MATCH_SET" ]; then
|
|
# add a more specific rule at the top of ss_spec_prerouting
|
|
$NFT add rule inet ss_spec ss_spec_prerouting $TCP_EXT_ARGS $MATCH_SET jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
|
|
else
|
|
$NFT add rule inet ss_spec ss_spec_prerouting $TCP_EXT_ARGS jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
|
|
fi
|
|
else
|
|
# For each Interface, find its actual ifname and add an iifname-limited prerouting rule
|
|
for name in $Interface; do
|
|
local IFNAME=$(uci -P /var/state get network."$name".ifname 2>/dev/null)
|
|
[ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null)
|
|
if [ -n "$IFNAME" ]; then
|
|
if [ -n "$MATCH_SET" ]; then
|
|
$NFT add rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" $TCP_EXT_ARGS $MATCH_SET jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
|
|
else
|
|
$NFT add rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" $TCP_EXT_ARGS jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
case "$OUTPUT" in
|
|
1)
|
|
# Create ss_spec_output tcp chain
|
|
if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
|
$NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }'
|
|
fi
|
|
$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
|
|
|
|
# Exclude special local addresses
|
|
if $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
|
for net in 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4; do
|
|
$NFT add rule inet ss_spec ss_spec_output ip daddr $net return 2>/dev/null
|
|
done
|
|
fi
|
|
|
|
# Temporarily comment IPV6 for future enablement
|
|
#if $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
|
# for net in ::1/128 fe80::/10 fc00::/7 ff00::/8 ::/128 ::ffff:0:0/96; do
|
|
# $NFT add rule inet ss_spec ss_spec_output ip6 daddr $net return 2>/dev/null
|
|
# done
|
|
#fi
|
|
|
|
# create output hook chain & route output traffic into router chain
|
|
$NFT add rule inet ss_spec ss_spec_output $TCP_EXT_ARGS jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
|
|
;;
|
|
2)
|
|
# Create ss_spec_output tcp chain
|
|
if ! $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
|
$NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority 0; policy accept; }'
|
|
fi
|
|
$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
|
|
|
|
# Exclude special local addresses
|
|
if $NFT list chain inet ss_spec ss_spec_output >/dev/null 2>&1; then
|
|
for net in 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4; do
|
|
$NFT add rule inet ss_spec ss_spec_output ip daddr $net return 2>/dev/null
|
|
done
|
|
fi
|
|
|
|
# router mode output chain: create ssr_gen_router set & router chain
|
|
$NFT add set inet ss_spec ssr_gen_router '{ type ipv4_addr; flags interval; }' 2>/dev/null
|
|
for ip in $(gen_spec_iplist); do
|
|
[ -n "$ip" ] && $NFT add element inet ss_spec ssr_gen_router "{ $ip }" 2>/dev/null
|
|
done
|
|
if ! $NFT list chain inet ss_spec ss_spec_router >/dev/null 2>&1; then
|
|
$NFT add chain inet ss_spec ss_spec_router 2>/dev/null
|
|
fi
|
|
$NFT flush chain inet ss_spec ss_spec_router 2>/dev/null
|
|
$NFT add rule inet ss_spec ss_spec_router ip daddr @ssr_gen_router return 2>/dev/null
|
|
$NFT add rule inet ss_spec ss_spec_router jump ss_spec_wan_fw 2>/dev/null
|
|
$NFT add rule inet ss_spec ss_spec_output $TCP_EXT_ARGS jump ss_spec_router comment "\"$TAG\"" 2>/dev/null
|
|
;;
|
|
esac
|
|
return 0
|
|
}
|
|
|
|
ac_rule_iptables() {
|
|
local MATCH_SET=""
|
|
if [ -n "$LAN_AC_IP" ]; then
|
|
case "${LAN_AC_IP%${LAN_AC_IP#?}}" in
|
|
w | W)
|
|
MATCH_SET="-m set --match-set ss_spec_lan_ac src"
|
|
;;
|
|
b | B)
|
|
MATCH_SET="-m set ! --match-set ss_spec_lan_ac src"
|
|
;;
|
|
*)
|
|
loger 3 "Bad argument \`-a $LAN_AC_IP\`."
|
|
return 2
|
|
;;
|
|
esac
|
|
fi
|
|
ipset -! -R <<-EOF || return 1
|
|
create ss_spec_lan_ac hash:net
|
|
$(for ip in ${LAN_AC_IP#?}; do echo "add ss_spec_lan_ac $ip"; done)
|
|
EOF
|
|
|
|
# Block UDP port 443 when TPROXY not Enable
|
|
if [ -z "$TPROXY" ]; then
|
|
# Add UDP 443 block rule
|
|
if [ -z "$Interface" ]; then
|
|
$ipt -I PREROUTING 1 -p udp $EXT_ARGS $MATCH_SET --dport 443 -j DROP -m comment --comment "$TAG"
|
|
else
|
|
for name in $Interface; do
|
|
local IFNAME=$(uci -P /var/state get network."$name".ifname 2>/dev/null)
|
|
[ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null)
|
|
if [ -n "$IFNAME" ]; then
|
|
$ipt -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p udp $EXT_ARGS $MATCH_SET --dport 443 -j DROP -m comment --comment "$TAG"
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
if [ -z "$Interface" ]; then
|
|
$IPT -I PREROUTING 1 -p tcp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_WAN_AC
|
|
else
|
|
for name in $Interface; do
|
|
local IFNAME=$(uci -P /var/state get network."$name".ifname 2>/dev/null)
|
|
[ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null)
|
|
if [ -n "$IFNAME" ]; then
|
|
$IPT -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p tcp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_WAN_AC
|
|
fi
|
|
done
|
|
fi
|
|
|
|
case "$OUTPUT" in
|
|
1)
|
|
$IPT -I OUTPUT 1 -p tcp $EXT_ARGS -m comment --comment "$TAG" -j SS_SPEC_WAN_AC
|
|
;;
|
|
2)
|
|
ipset -! -R <<-EOF || return 1
|
|
create ssr_gen_router hash:net
|
|
$(gen_spec_iplist | sed -e "s/^/add ssr_gen_router /")
|
|
EOF
|
|
$IPT -N SS_SPEC_ROUTER 2>/dev/null
|
|
$IPT -F SS_SPEC_ROUTER 2>/dev/null
|
|
$IPT -A SS_SPEC_ROUTER -m set --match-set ssr_gen_router dst -j RETURN && \
|
|
$IPT -A SS_SPEC_ROUTER -j SS_SPEC_WAN_FW
|
|
$IPT -I OUTPUT 1 -p tcp -m comment --comment "$TAG" -j SS_SPEC_ROUTER
|
|
;;
|
|
esac
|
|
return $?
|
|
}
|
|
|
|
tp_rule() {
|
|
[ -n "$TPROXY" ] || return 0
|
|
if [ "$USE_NFT" = "1" ]; then
|
|
tp_rule_nft
|
|
else
|
|
tp_rule_iptables
|
|
fi
|
|
return $?
|
|
}
|
|
|
|
tp_rule_nft() {
|
|
# set up routing table for tproxy
|
|
if ! ip rule show | grep -Eq "fwmark 0x0*1.*lookup 100"; then
|
|
ip rule add fwmark 0x01/0x01 table 100 2>/dev/null
|
|
fi
|
|
|
|
if ! ip route show table 100 | grep -q "^local.*dev lo"; then
|
|
ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null
|
|
fi
|
|
|
|
# create mangle table and tproxy chain
|
|
if ! $NFT list table ip ss_spec_mangle >/dev/null 2>&1; then
|
|
$NFT add table ip ss_spec_mangle 2>/dev/null
|
|
fi
|
|
|
|
local MATCH_SET=""
|
|
|
|
if [ -n "$PROXY_PORTS" ]; then
|
|
PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
|
|
if [ -n "$PORTS_ARGS" ]; then
|
|
EXT_ARGS="udp dport { $PORTS_ARGS }"
|
|
else
|
|
EXT_ARGS=""
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$LAN_AC_IP" ]; then
|
|
# Create LAN access control set if needed
|
|
if ! $NFT list set ip ss_spec_mangle ss_spec_lan_ac >/dev/null 2>&1; then
|
|
$NFT add set ip ss_spec_mangle ss_spec_lan_ac '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
|
|
else
|
|
$NFT flush set ip ss_spec_mangle ss_spec_lan_ac 2>/dev/null
|
|
fi
|
|
|
|
for ip in ${LAN_AC_IP#?}; do
|
|
[ -n "$ip" ] && $NFT add element ip ss_spec_mangle ss_spec_lan_ac "{ $ip }" 2>/dev/null
|
|
done
|
|
|
|
case "${LAN_AC_IP%${LAN_AC_IP#?}}" in
|
|
w | W)
|
|
MATCH_SET="ip saddr @ss_spec_lan_ac"
|
|
;;
|
|
b | B)
|
|
MATCH_SET="ip saddr != @ss_spec_lan_ac"
|
|
;;
|
|
*)
|
|
loger 3 "Bad argument \`-a $LAN_AC_IP\`."
|
|
return 2
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# Create necessary collections
|
|
for setname in china gmlan fplan bplan whitelist; do
|
|
if ! $NFT list set ip ss_spec_mangle $setname >/dev/null 2>&1; then
|
|
$NFT add set ip ss_spec_mangle $setname '{ type ipv4_addr; flags interval; auto-merge; }'
|
|
else
|
|
$NFT flush set ip ss_spec_mangle $setname 2>/dev/null
|
|
fi
|
|
done
|
|
|
|
# Bulk import china ip list safely (avoid huge single element limitation)
|
|
if [ -f "${china_ip:=/etc/ssrplus/china_ssr.txt}" ]; then
|
|
$NFT add element ip ss_spec_mangle china "{ $(tr '\n' ',' < "${china_ip}" | sed 's/,$//') }" 2>/dev/null
|
|
fi
|
|
|
|
# Bulk import xhttp ip list into nft whitelist (server + shunt)
|
|
if [ -f "${xhttp_ip:=/etc/ssrplus/xhttp_address.txt}" ]; then
|
|
$NFT add element ip ss_spec_mangle whitelist "{ $(tr '\n' ',' < "${xhttp_ip}" | sed 's/,$//') }" 2>/dev/null
|
|
fi
|
|
|
|
# use priority mangle for compatibility with other rules
|
|
if ! $NFT list chain ip ss_spec_mangle ss_spec_tproxy >/dev/null 2>&1; then
|
|
$NFT add chain ip ss_spec_mangle ss_spec_tproxy 2>/dev/null
|
|
else
|
|
$NFT flush chain ip ss_spec_mangle ss_spec_tproxy 2>/dev/null
|
|
fi
|
|
|
|
if $NFT list chain ip ss_spec_mangle ss_spec_tproxy >/dev/null 2>&1; then
|
|
for net in 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4; do
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy ip daddr $net return 2>/dev/null
|
|
done
|
|
fi
|
|
|
|
# Temporarily comment IPV6 for future enablement
|
|
#if $NFT list chain ip ss_spec_mangle ss_spec_tproxy >/dev/null 2>&1; then
|
|
# for net in ::1/128 fe80::/10 fc00::/7 ff00::/8 fe80::/10 ::/128 ::ffff:0:0/96; do
|
|
# $NFT add rule ip ss_spec_mangle ss_spec_tproxy ip6 daddr $net return 2>/dev/null
|
|
# done
|
|
#fi
|
|
|
|
# basic return rules in tproxy chain
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp dport 53 return 2>/dev/null
|
|
|
|
# avoid redirecting to udp server address
|
|
if [ -n "$server" ]; then
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp dport != 53 ip daddr "$server" return 2>/dev/null
|
|
fi
|
|
|
|
# if server != SERVER add SERVER to whitelist set (so tproxy won't touch it)
|
|
if [ -n "$server" ]; then
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy ip daddr "$server" return 2>/dev/null
|
|
fi
|
|
if [ -n "$SERVER" ] && [ "$server" != "$SERVER" ]; then
|
|
$NFT add element ip ss_spec_mangle whitelist "{ $SERVER }" 2>/dev/null
|
|
fi
|
|
|
|
# access control and tproxy rules
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @bplan return 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip saddr @fplan counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
|
|
|
|
# Handle different run modes for nftables
|
|
case "$RUNMODE" in
|
|
router)
|
|
if ! $NFT list set ip ss_spec_mangle ss_spec_wan_ac >/dev/null 2>&1; then
|
|
$NFT add set ip ss_spec_mangle ss_spec_wan_ac '{ type ipv4_addr; flags interval; auto-merge; }'
|
|
else
|
|
$NFT flush set ip ss_spec_mangle ss_spec_wan_ac 2>/dev/null
|
|
fi
|
|
# Add special IP ranges to WAN AC set
|
|
for ip in $(gen_spec_iplist); do
|
|
[ -n "$ip" ] && $NFT add element ip ss_spec_mangle ss_spec_wan_ac "{ $ip }" 2>/dev/null
|
|
done
|
|
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr @ss_spec_wan_ac return 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr @china return 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp udp dport 80 counter drop comment "\"$TAG\"" 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp udp dport 443 counter drop comment "\"$TAG\"" 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan ip daddr != @china counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip daddr != @ss_spec_wan_ac counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
|
|
;;
|
|
gfw)
|
|
if ! $NFT list set ip ss_spec_mangle gfwlist >/dev/null 2>&1; then
|
|
$NFT add set ip ss_spec_mangle gfwlist '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
|
|
fi
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr @china return 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp udp dport 80 counter drop comment "\"$TAG\"" 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp udp dport 443 counter drop comment "\"$TAG\"" 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip daddr @gfwlist counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan ip daddr != @china counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
|
|
;;
|
|
oversea)
|
|
if ! $NFT list set ip ss_spec_mangle oversea >/dev/null 2>&1; then
|
|
$NFT add set ip ss_spec_mangle oversea '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
|
|
fi
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip saddr @oversea counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS ip daddr @china counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @gmlan counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
|
|
;;
|
|
all)
|
|
$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp $EXT_ARGS counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
|
|
;;
|
|
esac
|
|
|
|
# finally, ensure prerouting hook entry to jump to tproxy chain
|
|
if ! $NFT list chain ip ss_spec_mangle prerouting >/dev/null 2>&1; then
|
|
$NFT add chain ip ss_spec_mangle prerouting '{ type filter hook prerouting priority mangle; policy accept; }'
|
|
fi
|
|
|
|
# add prerouting jump (idempotent)
|
|
if [ -z "$Interface" ]; then
|
|
# Global rules
|
|
if [ -n "$MATCH_SET" ]; then
|
|
$NFT add rule ip ss_spec_mangle prerouting meta l4proto udp $EXT_ARGS $MATCH_SET jump ss_spec_tproxy comment "\"$TAG\"" 2>/dev/null
|
|
else
|
|
$NFT add rule ip ss_spec_mangle prerouting meta l4proto udp $EXT_ARGS jump ss_spec_tproxy comment "\"$TAG\"" 2>/dev/null
|
|
fi
|
|
else
|
|
# Specific interface
|
|
for name in $Interface; do
|
|
IFNAME=$(uci -P /var/state get network."$name".ifname 2>/dev/null)
|
|
[ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null)
|
|
if [ -n "$IFNAME" ]; then
|
|
if [ -n "$MATCH_SET" ]; then
|
|
$NFT add rule ip ss_spec_mangle prerouting meta iifname "$IFNAME" meta l4proto udp $EXT_ARGS $MATCH_SET jump ss_spec_tproxy comment "\"$TAG\"" 2>/dev/null
|
|
else
|
|
$NFT add rule ip ss_spec_mangle prerouting meta iifname "$IFNAME" meta l4proto udp $EXT_ARGS jump ss_spec_tproxy comment "\"$TAG\"" 2>/dev/null
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
return $?
|
|
}
|
|
|
|
tp_rule_iptables() {
|
|
# set up routing table for tproxy
|
|
if ! ip rule show | grep -Eq "fwmark 0x0*1.*lookup 100"; then
|
|
ip rule add fwmark 0x01/0x01 table 100 2>/dev/null
|
|
fi
|
|
|
|
if ! ip route show table 100 | grep -q "^local.*dev lo"; then
|
|
ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null
|
|
fi
|
|
$ipt -N SS_SPEC_TPROXY 2>/dev/null
|
|
$ipt -F SS_SPEC_TPROXY
|
|
|
|
$ipt -A SS_SPEC_TPROXY -p udp --dport 53 -j RETURN
|
|
|
|
local MATCH_SET=""
|
|
if [ -n "$LAN_AC_IP" ]; then
|
|
case "${LAN_AC_IP%${LAN_AC_IP#?}}" in
|
|
w | W)
|
|
MATCH_SET_UDP="-m set --match-set ss_spec_lan_ac src"
|
|
;;
|
|
b | B)
|
|
MATCH_SET_UDP="-m set ! --match-set ss_spec_lan_ac src"
|
|
;;
|
|
*)
|
|
loger 3 "Bad argument \`-a $LAN_AC_IP\`."
|
|
return 2
|
|
;;
|
|
esac
|
|
fi
|
|
ipset -! -R <<-EOF || return 1
|
|
create ss_spec_lan_ac hash:net
|
|
$(for ip in ${LAN_AC_IP#?}; do echo "add ss_spec_lan_ac $ip"; done)
|
|
EOF
|
|
|
|
for net in \
|
|
0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 \
|
|
172.16.0.0/12 192.168.0.0/16 224.0.0.0/4 240.0.0.0/4
|
|
do
|
|
$ipt -A SS_SPEC_TPROXY -p udp -d "$net" -j RETURN
|
|
done
|
|
$ipt -A SS_SPEC_TPROXY -p udp ! --dport 53 -d "$SERVER" -j RETURN
|
|
[ "$server" != "$SERVER" ] && ipset -! add whitelist "$SERVER"
|
|
if [ -f "${xhttp_ip:=/etc/ssrplus/xhttp_address.txt}" ]; then
|
|
while IFS= read -r ip; do
|
|
[ -n "$ip" ] && ipset add whitelist "$ip" -exist
|
|
done < "$xhttp_ip"
|
|
fi
|
|
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set bplan src -j RETURN
|
|
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set fplan src -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
|
case "$RUNMODE" in
|
|
router)
|
|
ipset -! -R <<-EOF || return 1
|
|
create ss_spec_wan_ac hash:net
|
|
$(gen_spec_iplist | sed -e "s/^/add ss_spec_wan_ac /")
|
|
EOF
|
|
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set ss_spec_wan_ac dst -j RETURN
|
|
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set china dst -j RETURN
|
|
$ipt -A SS_SPEC_TPROXY -p udp --dport 80 -j DROP
|
|
$ipt -A SS_SPEC_TPROXY -p udp --dport 443 -j DROP
|
|
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
|
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set ! --match-set ss_spec_wan_ac dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
|
;;
|
|
gfw)
|
|
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set china dst -j RETURN
|
|
$ipt -A SS_SPEC_TPROXY -p udp --dport 80 -j DROP
|
|
$ipt -A SS_SPEC_TPROXY -p udp --dport 443 -j DROP
|
|
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set gfwlist dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
|
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set ! --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
|
;;
|
|
oversea)
|
|
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set oversea src -m dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
|
$ipt -A SS_SPEC_TPROXY -p udp -m set --match-set gmlan src -m set -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
|
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -m set --match-set china dst -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
|
;;
|
|
all)
|
|
$ipt -A SS_SPEC_TPROXY -p udp $PROXY_PORTS -j TPROXY --on-port "$LOCAL_PORT" --tproxy-mark 0x01/0x01
|
|
;;
|
|
esac
|
|
if [ -z "$Interface" ]; then
|
|
$ipt -I PREROUTING 1 -p udp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_TPROXY
|
|
else
|
|
for name in $Interface; do
|
|
local IFNAME=$(uci -P /var/state get network."$name".ifname 2>/dev/null)
|
|
[ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null)
|
|
[ -n "$IFNAME" ] && $ipt -I PREROUTING 1 ${IFNAME:+-i $IFNAME} -p udp $EXT_ARGS $MATCH_SET -m comment --comment "$TAG" -j SS_SPEC_TPROXY
|
|
done
|
|
fi
|
|
return $?
|
|
}
|
|
|
|
get_wan_ip() {
|
|
cat <<-EOF | grep -E "^([0-9]{1,3}\.){3}[0-9]{1,3}"
|
|
$server
|
|
$SERVER
|
|
$WAN_BP_IP
|
|
EOF
|
|
}
|
|
|
|
gen_spec_iplist() {
|
|
cat <<-EOF
|
|
0.0.0.0/8
|
|
10.0.0.0/8
|
|
100.64.0.0/10
|
|
127.0.0.0/8
|
|
169.254.0.0/16
|
|
172.16.0.0/12
|
|
192.0.0.0/24
|
|
192.0.2.0/24
|
|
192.88.99.0/24
|
|
192.168.0.0/16
|
|
198.18.0.0/15
|
|
198.51.100.0/24
|
|
203.0.113.0/24
|
|
224.0.0.0/4
|
|
240.0.0.0/4
|
|
255.255.255.255
|
|
$(get_wan_ip)
|
|
EOF
|
|
}
|
|
|
|
gen_include() {
|
|
[ -n "$FWI" ] || return 0
|
|
if [ "$USE_NFT" = "1" ]; then
|
|
gen_include_nft
|
|
else
|
|
gen_include_iptables
|
|
fi
|
|
return $?
|
|
}
|
|
|
|
# Modified gen_include_nft to call persistence function
|
|
gen_include_nft() {
|
|
# Generate nftables include file for firewall4
|
|
[ -n "$FWI" ] && echo '#!/bin/sh' >"$FWI"
|
|
cat <<-EOF >>"$FWI"
|
|
# Clear existing ss_spec tables
|
|
nft delete table inet ss_spec 2>/dev/null
|
|
nft delete table ip ss_spec 2>/dev/null
|
|
nft delete table ip ss_spec_mangle 2>/dev/null
|
|
|
|
# Restore shadowsocks nftables rules from persistent file
|
|
if [ -f "/usr/share/nftables.d/ruleset-post/99-shadowsocksr.nft" ]; then
|
|
nft -f /usr/share/nftables.d/ruleset-post/99-shadowsocksr.nft
|
|
else
|
|
# Fallback: restore from current ruleset (filtered)
|
|
nft list ruleset | awk '/^table (inet|ip) ss_spec/{flag=1} /^table / && !/^table (inet|ip) ss_spec/{flag=0} flag' | nft -f -
|
|
fi
|
|
EOF
|
|
chmod +x "$FWI"
|
|
}
|
|
|
|
gen_include_iptables() {
|
|
extract_rules() {
|
|
echo "*$1"
|
|
iptables-save -t $1 | grep SS_SPEC_ | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
|
|
echo 'COMMIT'
|
|
}
|
|
cat <<-EOF >>$FWI
|
|
iptables-save -c | grep -v "SS_SPEC" | iptables-restore -c
|
|
iptables-restore -n <<-EOT
|
|
$(extract_rules nat)
|
|
$(extract_rules mangle)
|
|
EOT
|
|
EOF
|
|
}
|
|
|
|
# Check nftables rules status
|
|
check_nftables_status() {
|
|
if [ "$USE_NFT" != "1" ]; then
|
|
echo "NFTables not in use"
|
|
return 0
|
|
fi
|
|
|
|
# Check if ss_spec tables exist
|
|
if ! $NFT list table inet ss_spec >/dev/null 2>&1 && \
|
|
! $NFT list table ip ss_spec_mangle >/dev/null 2>&1; then
|
|
echo "ss_spec tables missing in nftables"
|
|
return 1
|
|
fi
|
|
|
|
# Check if basic rules exist
|
|
if ! $NFT list table inet ss_spec 2>/dev/null | grep -q "chain.*ss_spec_wan_ac" || \
|
|
! $NFT list table inet ss_spec 2>/dev/null | grep -q "jump.*ss_spec_wan_fw"; then
|
|
echo "Basic SSR rules missing"
|
|
return 1
|
|
fi
|
|
|
|
echo "NFTables rules status: OK"
|
|
return 0
|
|
}
|
|
|
|
# Compare current rules with persistence rules
|
|
compare_rules() {
|
|
if [ "$USE_NFT" != "1" ]; then
|
|
return 1 # NFTables not used, need update
|
|
fi
|
|
|
|
# If no persistence file, update persistence file
|
|
if [ ! -f "$NFTABLES_RULES_FILE" ]; then
|
|
loger 6 "No persistence file found, update needed"
|
|
return 1 # Need to update persistence file
|
|
fi
|
|
|
|
# Check if ss_spec tables exist
|
|
if ! $NFT list table inet ss_spec >/dev/null 2>&1 && \
|
|
! $NFT list table ip ss_spec_mangle >/dev/null 2>&1; then
|
|
loger 6 "ss_spec tables missing, update needed"
|
|
return 1 # Need to update ss_spec table
|
|
fi
|
|
|
|
# Generate temporary file for current rules
|
|
local temp_file=$(mktemp)
|
|
local rules_file=$(mktemp)
|
|
loger 7 "DEBUG: Temporary file path: $rules_file"
|
|
|
|
# Export current rules to temporary file
|
|
$NFT list ruleset | awk '
|
|
/^table (inet ss_spec|ip ss_spec_mangle)/ {flag=1}
|
|
/^table / && !/^table (inet ss_spec|ip ss_spec_mangle)/ {flag=0}
|
|
flag
|
|
' > "$rules_file" 2>/dev/null
|
|
|
|
# Check if current rules were exported successfully
|
|
if [ ! -s "$rules_file" ] || ! grep -q "table" "$rules_file" 2>/dev/null; then
|
|
loger 4 "Failed to export current rules"
|
|
rm -f "$temp_file" "$rules_file"
|
|
return 1 # Export failed, need update
|
|
fi
|
|
|
|
# Compare current rules with rules in persistence file
|
|
if ! cmp -s "$rules_file" "$NFTABLES_RULES_FILE"; then
|
|
loger 6 "Rules differ, update needed"
|
|
rm -f "$temp_file" "$rules_file"
|
|
return 1 # Need update
|
|
fi
|
|
|
|
rm -f "$temp_file" "$rules_file"
|
|
loger 6 "Rules unchanged, no update needed"
|
|
return 0 # No update needed
|
|
}
|
|
|
|
# Auto-update persistence rules
|
|
persist_nftables_rules() {
|
|
if [ "$USE_NFT" != "1" ]; then
|
|
return 0
|
|
fi
|
|
|
|
# If mode unchanged and persistence file exists, skip update
|
|
if [ "$MODE_CHANGED" = "0" ] && [ -f "$NFTABLES_RULES_FILE" ]; then
|
|
loger 6 "Mode unchanged and persistence file exists, skipping update"
|
|
return 0
|
|
fi
|
|
|
|
# Force update: skip comparison check and delete old file
|
|
if [ "$FORCE_UPDATE" = "1" ]; then
|
|
loger 6 "Force update requested, removing old persistence file"
|
|
rm -f "$NFTABLES_RULES_FILE" 2>/dev/null
|
|
# Non-force update: compare rules
|
|
elif [ -f "$NFTABLES_RULES_FILE" ]; then
|
|
if compare_rules; then
|
|
loger 6 "Rules unchanged, skipping persistence update"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Ensure directory exists
|
|
mkdir -p "$NFTABLES_RULES_DIR" 2>/dev/null
|
|
|
|
# Generate nftables rule file
|
|
cat <<-'EOF' >>$NFTABLES_RULES_FILE
|
|
#!/usr/sbin/nft -f
|
|
|
|
# ShadowsocksR nftables rules
|
|
# Generated by ssr-rules script
|
|
EOF
|
|
|
|
echo "# Auto-updated: $(date)" >> "$NFTABLES_RULES_FILE"
|
|
echo "# Runmode: ${RUNMODE:-router}" >> "$NFTABLES_RULES_FILE"
|
|
echo "# Server: $server, Port: $local_port" >> "$NFTABLES_RULES_FILE"
|
|
echo "# WAN_BP_IP: $WAN_BP_IP" >> "$NFTABLES_RULES_FILE"
|
|
echo "# LAN_AC_IP: $LAN_AC_IP" >> "$NFTABLES_RULES_FILE"
|
|
echo "# LAN_BP_IP: $LAN_BP_IP" >> "$NFTABLES_RULES_FILE"
|
|
echo "# WAN_FW_IP: $WAN_FW_IP" >> "$NFTABLES_RULES_FILE"
|
|
echo "# LAN_FP_IP: $LAN_FP_IP" >> "$NFTABLES_RULES_FILE"
|
|
echo "# LAN_GM_IP: $LAN_GM_IP" >> "$NFTABLES_RULES_FILE"
|
|
echo "" >> "$NFTABLES_RULES_FILE"
|
|
|
|
local HAS_RULES=0
|
|
|
|
# Export each table separately
|
|
if $NFT list table inet ss_spec >/dev/null 2>&1; then
|
|
loger 6 "Exporting table inet ss_spec"
|
|
{
|
|
echo ""
|
|
echo "# inet ss_spec table for main rules"
|
|
$NFT list table inet ss_spec 2>/dev/null
|
|
} >> "$NFTABLES_RULES_FILE"
|
|
HAS_RULES=1
|
|
fi
|
|
|
|
if $NFT list table ip ss_spec_mangle >/dev/null 2>&1; then
|
|
loger 6 "Exporting table ip ss_spec_mangle"
|
|
{
|
|
echo ""
|
|
echo "# ip ss_spec_mangle table for TPROXY rules"
|
|
$NFT list table ip ss_spec_mangle 2>/dev/null
|
|
} >> "$NFTABLES_RULES_FILE"
|
|
HAS_RULES=1
|
|
fi
|
|
|
|
# Check if rules were exported successfully
|
|
if [ $HAS_RULES -eq 0 ] || [ ! -s "$NFTABLES_RULES_FILE" ] || ! grep -q "table" "$NFTABLES_RULES_FILE" 2>/dev/null; then
|
|
loger 4 "No ss_spec nftables rules found to persist"
|
|
rm -f "$NFTABLES_RULES_FILE" 2>/dev/null
|
|
return 1
|
|
fi
|
|
|
|
# Set file permissions
|
|
chmod 644 "$NFTABLES_RULES_FILE" 2>/dev/null
|
|
|
|
# Log success information
|
|
local TABLES=$(grep "^table" "$NFTABLES_RULES_FILE" | awk '{print $2 " " $3}' | tr '\n' ',' | sed 's/,$//')
|
|
loger 5 "NFTables rules persisted to $NFTABLES_RULES_FILE (Tables: $TABLES)"
|
|
|
|
return 0
|
|
}
|
|
|
|
# Auto-update daemon
|
|
start_auto_update_daemon() {
|
|
if [ "$USE_NFT" != "1" ] || [ "$AUTO_UPDATE_INTERVAL" = "0" ]; then
|
|
return 0
|
|
fi
|
|
|
|
loger 6 "Starting nftables rules auto-update daemon"
|
|
|
|
# Stop already running daemon
|
|
stop_auto_update_daemon
|
|
|
|
# Start daemon directly in background
|
|
(
|
|
logger -t ssr-rules[daemon] "Auto-update daemon started - PID: $$"
|
|
echo $$ > "/var/run/ssr-rules-daemon.pid"
|
|
|
|
while true; do
|
|
sleep 300
|
|
if [ -x "/usr/bin/ssr-rules" ]; then
|
|
if /usr/bin/ssr-rules -C >/dev/null 2>&1; then
|
|
logger -t ssr-rules[daemon] "Rules changed or missing, updating persistence"
|
|
if /usr/bin/ssr-rules -P >/dev/null 2>&1; then
|
|
logger -t ssr-rules[daemon] "Persistence rules updated successfully"
|
|
else
|
|
logger -t ssr-rules[daemon] "Failed to update persistence"
|
|
fi
|
|
else
|
|
logger -t ssr-rules[daemon] "Rules status OK, no update needed"
|
|
fi
|
|
else
|
|
logger -t ssr-rules[daemon] "Script not found, exiting daemon"
|
|
exit 1
|
|
fi
|
|
done
|
|
) &
|
|
|
|
local DAEMON_PID=$!
|
|
sleep 2
|
|
|
|
if kill -0 "$DAEMON_PID" 2>/dev/null; then
|
|
loger 6 "Auto-update daemon started with PID: $DAEMON_PID"
|
|
return 0
|
|
else
|
|
loger 3 "Auto-update daemon failed to start"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Stop auto-update daemon function
|
|
stop_auto_update_daemon() {
|
|
local PID_FILE="/var/run/ssr-rules-daemon.pid"
|
|
|
|
if [ -f "$PID_FILE" ]; then
|
|
local DAEMON_PID=$(cat "$PID_FILE" 2>/dev/null)
|
|
if [ -n "$DAEMON_PID" ] && kill -0 "$DAEMON_PID" 2>/dev/null; then
|
|
kill "$DAEMON_PID" 2>/dev/null
|
|
loger 6 "Stopped auto-update daemon (PID: $DAEMON_PID)"
|
|
fi
|
|
rm -f "$PID_FILE" 2>/dev/null
|
|
fi
|
|
|
|
loger 6 "Auto-update daemon stopped"
|
|
}
|
|
|
|
# Force update persistence rules function
|
|
force_update_persistence() {
|
|
if [ "$USE_NFT" != "1" ]; then
|
|
echo "NFTables not in use"
|
|
return 0
|
|
fi
|
|
|
|
# Remove existing rule file to ensure recreation
|
|
rm -f "$NFTABLES_RULES_FILE" 2>/dev/null
|
|
|
|
# Call persistence function
|
|
if persist_nftables_rules; then
|
|
loger 5 "Persistence update completed successfully"
|
|
return 0
|
|
else
|
|
loger 3 "Persistence update failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Restore rules from persistence file
|
|
restore_from_persistence() {
|
|
if [ "$USE_NFT" != "1" ]; then
|
|
loger 3 "NFTables not in use, cannot restore rules"
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -f "$NFTABLES_RULES_FILE" ]; then
|
|
loger 4 "Persistence file not found: $NFTABLES_RULES_FILE"
|
|
return 1
|
|
fi
|
|
|
|
loger 6 "Restoring rules from persistence file"
|
|
|
|
# Cleanup existing rules
|
|
flush_r
|
|
|
|
# Restore rules from file
|
|
if $NFT -f "$NFTABLES_RULES_FILE" 2>/dev/null; then
|
|
loger 5 "Rules restored successfully from persistence file"
|
|
return 0
|
|
else
|
|
loger 4 "Failed to restore rules from persistence file"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
while getopts ":m:s:l:S:L:i:e:a:B:b:w:p:G:D:F:N:M:I:oOuUfgrczAKPCRXh" arg; do
|
|
case "$arg" in
|
|
m)
|
|
Interface=$OPTARG
|
|
;;
|
|
s)
|
|
server=$OPTARG
|
|
;;
|
|
l)
|
|
local_port=$OPTARG
|
|
;;
|
|
S)
|
|
SERVER=$OPTARG
|
|
;;
|
|
L)
|
|
LOCAL_PORT=$OPTARG
|
|
;;
|
|
i)
|
|
IGNORE_LIST=$OPTARG
|
|
;;
|
|
e)
|
|
EXT_ARGS=$OPTARG
|
|
;;
|
|
a)
|
|
LAN_AC_IP=$OPTARG
|
|
;;
|
|
B)
|
|
LAN_BP_IP=$OPTARG
|
|
;;
|
|
b)
|
|
WAN_BP_IP=$(for ip in $OPTARG; do echo "$ip"; done)
|
|
;;
|
|
w)
|
|
WAN_FW_IP=$OPTARG
|
|
;;
|
|
p)
|
|
LAN_FP_IP=$OPTARG
|
|
;;
|
|
G)
|
|
LAN_GM_IP=$OPTARG
|
|
;;
|
|
D)
|
|
PROXY_PORTS=$OPTARG
|
|
;;
|
|
F)
|
|
SHUNT_PORT=$OPTARG
|
|
;;
|
|
N)
|
|
SHUNT_IP=$OPTARG
|
|
;;
|
|
M)
|
|
SHUNT_PROXY=$OPTARG
|
|
;;
|
|
I)
|
|
SHUNT_LIST=$OPTARG
|
|
;;
|
|
o)
|
|
OUTPUT=1
|
|
;;
|
|
O)
|
|
OUTPUT=2
|
|
;;
|
|
u)
|
|
TPROXY=1
|
|
;;
|
|
U)
|
|
TPROXY=2
|
|
;;
|
|
g)
|
|
RUNMODE=gfw
|
|
;;
|
|
r)
|
|
RUNMODE=router
|
|
;;
|
|
c)
|
|
RUNMODE=oversea
|
|
;;
|
|
z)
|
|
RUNMODE=all
|
|
;;
|
|
# New persistence management options
|
|
A)
|
|
ENABLE_AUTO_UPDATE=1
|
|
;;
|
|
K)
|
|
STOP_AUTO_UPDATE=1
|
|
;;
|
|
P)
|
|
FORCE_UPDATE=1
|
|
;;
|
|
C)
|
|
CHECK_STATUS=1
|
|
;;
|
|
R)
|
|
RESTORE_RULES=1
|
|
;;
|
|
X)
|
|
CLEANUP_PERSISTENCE=1
|
|
;;
|
|
f)
|
|
FLUSH_RULES=1
|
|
;;
|
|
h)
|
|
usage 0
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# First process options that need immediate exit
|
|
if [ "$CHECK_STATUS" = "1" ]; then
|
|
check_nftables_status
|
|
exit $?
|
|
fi
|
|
|
|
if [ "$STOP_AUTO_UPDATE" = "1" ]; then
|
|
stop_auto_update_daemon
|
|
exit 0
|
|
fi
|
|
|
|
# Only -X option, cleanup and exit
|
|
if [ "$CLEANUP_PERSISTENCE" = "1" ] && [ "$FLUSH_RULES" != "1" ] && [ -z "$server" ] && [ -z "$local_port" ] && \
|
|
[ "$FORCE_UPDATE" != "1" ] && [ "$RESTORE_RULES" != "1" ] && [ "$ENABLE_AUTO_UPDATE" != "1" ]; then
|
|
cleanup_persistence_files
|
|
exit $?
|
|
fi
|
|
|
|
# Check if there are persistence management options only
|
|
PERSISTENCE_ONLY=0
|
|
if [ -z "$server" ] && [ -z "$local_port" ] && [ "$FLUSH_RULES" != "1" ]; then
|
|
if [ "$FORCE_UPDATE" = "1" ] || [ "$RESTORE_RULES" = "1" ] || [ "$ENABLE_AUTO_UPDATE" = "1" ] || [ "$CLEANUP_PERSISTENCE" = "1" ]; then
|
|
PERSISTENCE_ONLY=1
|
|
else
|
|
usage 2
|
|
fi
|
|
fi
|
|
|
|
# Handle persistence management options
|
|
if [ "$PERSISTENCE_ONLY" = "1" ]; then
|
|
if [ "$FORCE_UPDATE" = "1" ]; then
|
|
force_update_persistence
|
|
exit $?
|
|
fi
|
|
|
|
if [ "$RESTORE_RULES" = "1" ]; then
|
|
restore_from_persistence
|
|
exit $?
|
|
fi
|
|
|
|
if [ "$ENABLE_AUTO_UPDATE" = "1" ]; then
|
|
start_auto_update_daemon
|
|
exit $?
|
|
fi
|
|
fi
|
|
|
|
# Force flush rules
|
|
if [ "$FLUSH_RULES" = "1" ]; then
|
|
flush_r
|
|
# If only -f option, then exit
|
|
if [ -z "$server" ] && [ -z "$local_port" ] && [ "$FORCE_UPDATE" != "1" ] && \
|
|
[ "$RESTORE_RULES" != "1" ] && [ "$ENABLE_AUTO_UPDATE" != "1" ] && \
|
|
[ "$CLEANUP_PERSISTENCE" != "1" ]; then
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# Restore rules from persistence file (before rule application)
|
|
if [ "$RESTORE_RULES" = "1" ]; then
|
|
restore_from_persistence
|
|
if [ $? -ne 0 ]; then
|
|
loger 3 "Failed to restore from persistence, continuing with rule application"
|
|
fi
|
|
fi
|
|
|
|
# Run mode change
|
|
runmode_change() {
|
|
local mode_file="/tmp/.ssr_run_mode"
|
|
local new_mode=""
|
|
local old_mode=""
|
|
|
|
# Get mode from parameters
|
|
if [ -n "$1" ]; then
|
|
new_mode="$1"
|
|
fi
|
|
|
|
# Read previous run mode from file
|
|
if [ -f "$mode_file" ]; then
|
|
old_mode=$(cat "$mode_file" 2>/dev/null)
|
|
fi
|
|
|
|
# Compare if mode changed
|
|
if [ "$old_mode" = "$new_mode" ] && [ -n "$old_mode" ]; then
|
|
# Mode unchanged
|
|
echo "$new_mode" > "$mode_file" # Update file timestamp
|
|
loger 6 "Runmode unchanged: $new_mode"
|
|
return 1 # Return 1 means unchanged
|
|
else
|
|
# Mode changed or first run
|
|
echo "$new_mode" > "$mode_file"
|
|
if [ -n "$old_mode" ]; then
|
|
loger 6 "Runmode changed from '$old_mode' to '$new_mode'"
|
|
else
|
|
loger 6 "Runmode set to '$new_mode'"
|
|
fi
|
|
return 0 # Return 0 means changed
|
|
fi
|
|
}
|
|
|
|
# Main process
|
|
if [ -n "$server" ] && [ -n "$local_port" ]; then
|
|
if ! echo "$local_port" | grep -qE '^[0-9]+$'; then
|
|
loger 3 "Invalid local port: $local_port"
|
|
exit 1
|
|
fi
|
|
|
|
case "$TPROXY" in
|
|
1)
|
|
SERVER=$server
|
|
LOCAL_PORT=$local_port
|
|
;;
|
|
2)
|
|
: ${SERVER:?"You must assign an ip for the udp relay server."}
|
|
: ${LOCAL_PORT:?"You must assign a port for the udp relay server."}
|
|
;;
|
|
esac
|
|
|
|
if [ "$USE_NFT" = "1" ]; then
|
|
# NFTables
|
|
# Save previous TPROXY state file
|
|
TPROXY_STATE_FILE="/tmp/.last_tproxy"
|
|
if [ -f "$TPROXY_STATE_FILE" ]; then
|
|
LAST_TPROXY=$(cat "$TPROXY_STATE_FILE")
|
|
else
|
|
LAST_TPROXY=""
|
|
fi
|
|
|
|
# Save previous PROXY_PORTS state
|
|
PROXY_PORTS_STATE_FILE="/tmp/.last_proxy_ports"
|
|
if [ -f "$PROXY_PORTS_STATE_FILE" ]; then
|
|
LAST_PROXY_PORTS=$(cat "$PROXY_PORTS_STATE_FILE")
|
|
else
|
|
LAST_PROXY_PORTS=""
|
|
fi
|
|
|
|
# Save previous WAN_BP_IP state
|
|
WAN_BP_IP_STATE_FILE="/tmp/.last_wan_bp_ip"
|
|
if [ -f "$WAN_BP_IP_STATE_FILE" ]; then
|
|
LAST_WAN_BP_IP=$(cat "$WAN_BP_IP_STATE_FILE")
|
|
else
|
|
LAST_WAN_BP_IP=""
|
|
fi
|
|
|
|
# Save previous LAN_AC_IP state
|
|
LAN_AC_IP_STATE_FILE="/tmp/.last_lan_ac_ip"
|
|
if [ -f "$LAN_AC_IP_STATE_FILE" ]; then
|
|
LAST_LAN_AC_IP=$(cat "$LAN_AC_IP_STATE_FILE")
|
|
else
|
|
LAST_LAN_AC_IP=""
|
|
fi
|
|
|
|
# Save previous LAN_BP_IP state
|
|
LAN_BP_IP_STATE_FILE="/tmp/.last_lan_bp_ip"
|
|
if [ -f "$LAN_BP_IP_STATE_FILE" ]; then
|
|
LAST_LAN_BP_IP=$(cat "$LAN_BP_IP_STATE_FILE")
|
|
else
|
|
LAST_LAN_BP_IP=""
|
|
fi
|
|
|
|
# Save previous WAN_FW_IP state
|
|
WAN_FW_IP_STATE_FILE="/tmp/.last_wan_fw_ip"
|
|
if [ -f "$WAN_FW_IP_STATE_FILE" ]; then
|
|
LAST_WAN_FW_IP=$(cat "$WAN_FW_IP_STATE_FILE")
|
|
else
|
|
LAST_WAN_FW_IP=""
|
|
fi
|
|
|
|
# Save previous LAN_FP_IP state
|
|
LAN_FP_IP_STATE_FILE="/tmp/.last_lan_fp_ip"
|
|
if [ -f "$LAN_FP_IP_STATE_FILE" ]; then
|
|
LAST_LAN_FP_IP=$(cat "$LAN_FP_IP_STATE_FILE")
|
|
else
|
|
LAST_LAN_FP_IP=""
|
|
fi
|
|
|
|
# Save previous LAN_GM_IP state
|
|
LAN_GM_IP_STATE_FILE="/tmp/.last_lan_gm_ip"
|
|
if [ -f "$LAN_GM_IP_STATE_FILE" ]; then
|
|
LAST_LAN_GM_IP=$(cat "$LAN_GM_IP_STATE_FILE")
|
|
else
|
|
LAST_LAN_GM_IP=""
|
|
fi
|
|
|
|
# Check for changes in the existence and content of the server XHTTP address file
|
|
XHTTP_FILE_STATE_FILE="/tmp/.last_xhttp_file"
|
|
XHTTP_FILE_HASH_FILE="/tmp/.last_xhttp_hash"
|
|
|
|
# Get the current server XHTTP file status
|
|
XHTTP_FILE_EXISTS=0
|
|
XHTTP_FILE_HASH=""
|
|
if [ -f "/etc/ssrplus/xhttp_address.txt" ]; then
|
|
XHTTP_FILE_EXISTS=1
|
|
XHTTP_FILE_HASH=$(md5sum /etc/ssrplus/xhttp_address.txt 2>/dev/null | awk '{print $1}')
|
|
fi
|
|
|
|
# Read the previous server XHTTP file status and hash
|
|
if [ -f "$XHTTP_FILE_STATE_FILE" ]; then
|
|
LAST_XHTTP_FILE_EXISTS=$(cat "$XHTTP_FILE_STATE_FILE")
|
|
else
|
|
LAST_XHTTP_FILE_EXISTS=""
|
|
fi
|
|
|
|
if [ -f "$XHTTP_FILE_HASH_FILE" ]; then
|
|
LAST_XHTTP_FILE_HASH=$(cat "$XHTTP_FILE_HASH_FILE")
|
|
else
|
|
LAST_XHTTP_FILE_HASH=""
|
|
fi
|
|
|
|
# STEP 1: Check if TPROXY has value (1 or 2)
|
|
if [ "$TPROXY" = "1" ] || [ "$TPROXY" = "2" ]; then
|
|
TPROXY_HAS_VALUE=1
|
|
else
|
|
TPROXY_HAS_VALUE=0
|
|
fi
|
|
|
|
if [ "$LAST_TPROXY" = "1" ] || [ "$LAST_TPROXY" = "2" ]; then
|
|
LAST_HAS_VALUE=1
|
|
else
|
|
LAST_HAS_VALUE=0
|
|
fi
|
|
|
|
# STEP 2: Check if PROXY_PORTS has value (non-empty string)
|
|
if [ -n "${PROXY_PORTS// }" ]; then
|
|
PROXY_HAS_VALUE=1
|
|
else
|
|
PROXY_HAS_VALUE=0
|
|
fi
|
|
|
|
if [ -n "${LAST_PROXY_PORTS// }" ]; then
|
|
LAST_PROXY_HAS_VALUE=1
|
|
else
|
|
LAST_PROXY_HAS_VALUE=0
|
|
fi
|
|
|
|
# STEP 2.5: Check if any IP list has changed
|
|
ANY_IP_LIST_CHANGED=0
|
|
|
|
# Check WAN_BP_IP
|
|
check_ip_list_changed "$WAN_BP_IP" "$LAST_WAN_BP_IP" "WAN_BP_IP"
|
|
if [ $? -eq 1 ]; then
|
|
ANY_IP_LIST_CHANGED=1
|
|
fi
|
|
|
|
# Check LAN_AC_IP
|
|
check_ip_list_changed "$LAN_AC_IP" "$LAST_LAN_AC_IP" "LAN_AC_IP"
|
|
if [ $? -eq 1 ]; then
|
|
ANY_IP_LIST_CHANGED=1
|
|
fi
|
|
|
|
# Check LAN_BP_IP
|
|
check_ip_list_changed "$LAN_BP_IP" "$LAST_LAN_BP_IP" "LAN_BP_IP"
|
|
if [ $? -eq 1 ]; then
|
|
ANY_IP_LIST_CHANGED=1
|
|
fi
|
|
|
|
# Check WAN_FW_IP
|
|
check_ip_list_changed "$WAN_FW_IP" "$LAST_WAN_FW_IP" "WAN_FW_IP"
|
|
if [ $? -eq 1 ]; then
|
|
ANY_IP_LIST_CHANGED=1
|
|
fi
|
|
|
|
# Check LAN_FP_IP
|
|
check_ip_list_changed "$LAN_FP_IP" "$LAST_LAN_FP_IP" "LAN_FP_IP"
|
|
if [ $? -eq 1 ]; then
|
|
ANY_IP_LIST_CHANGED=1
|
|
fi
|
|
|
|
# Check LAN_GM_IP
|
|
check_ip_list_changed "$LAN_GM_IP" "$LAST_LAN_GM_IP" "LAN_GM_IP"
|
|
if [ $? -eq 1 ]; then
|
|
ANY_IP_LIST_CHANGED=1
|
|
fi
|
|
|
|
# Check for changes in the existence of the server XHTTP address file.
|
|
if [ "$XHTTP_FILE_EXISTS" != "$LAST_XHTTP_FILE_EXISTS" ]; then
|
|
loger 6 "xhttp address file existence changed: '$LAST_XHTTP_FILE_EXISTS' -> '$XHTTP_FILE_EXISTS'"
|
|
ANY_IP_LIST_CHANGED=1
|
|
fi
|
|
|
|
# Check for XHTTP file content changes (compare hashes only when the file exists in both checks)
|
|
if [ "$XHTTP_FILE_EXISTS" = "1" ] && [ "$LAST_XHTTP_FILE_EXISTS" = "1" ]; then
|
|
if [ "$XHTTP_FILE_HASH" != "$LAST_XHTTP_FILE_HASH" ]; then
|
|
loger 6 "xhttp address file content changed (hash: '$LAST_XHTTP_FILE_HASH' -> '$XHTTP_FILE_HASH')"
|
|
ANY_IP_LIST_CHANGED=1
|
|
else
|
|
loger 6 "xhttp address file content unchanged"
|
|
fi
|
|
elif [ "$XHTTP_FILE_EXISTS" = "0" ] && [ "$LAST_XHTTP_FILE_EXISTS" = "1" ]; then
|
|
loger 6 "xhttp address file deleted"
|
|
ANY_IP_LIST_CHANGED=1
|
|
elif [ "$XHTTP_FILE_EXISTS" = "1" ] && [ "$LAST_XHTTP_FILE_EXISTS" = "0" ]; then
|
|
loger 6 "xhttp address file created"
|
|
ANY_IP_LIST_CHANGED=1
|
|
fi
|
|
|
|
# STEP 3: Determine if forced rebuild is needed
|
|
FORCE_RECREATE=0
|
|
PERSISTENCE_EXISTS=0
|
|
|
|
# Trigger conditions:
|
|
# 1. TPROXY changes from empty ↔ has value
|
|
# 2. PROXY_PORTS changes from empty ↔ has value
|
|
# 3. Any IP list has changed
|
|
if [ "$TPROXY_HAS_VALUE" != "$LAST_HAS_VALUE" ] || \
|
|
[ "$PROXY_HAS_VALUE" != "$LAST_PROXY_HAS_VALUE" ] || \
|
|
[ "$ANY_IP_LIST_CHANGED" = "1" ]; then
|
|
FORCE_RECREATE=1
|
|
loger 6 "TPROXY, PROXY_PORTS or any IP list changed → force rebuild rules"
|
|
rm -f "$NFTABLES_RULES_FILE" 2>/dev/null
|
|
else
|
|
# No FORCE_RECREATE triggered → check persistence file
|
|
if [ -f "$NFTABLES_RULES_FILE" ] && [ -s "$NFTABLES_RULES_FILE" ]; then
|
|
PERSISTENCE_EXISTS=1
|
|
loger 6 "Persistence file exists: $NFTABLES_RULES_FILE"
|
|
else
|
|
PERSISTENCE_EXISTS=0
|
|
loger 6 "Persistence file does not exist or empty"
|
|
fi
|
|
fi
|
|
|
|
# STEP 4: Save current state
|
|
echo "$TPROXY" > "$TPROXY_STATE_FILE"
|
|
echo "$PROXY_PORTS" > "$PROXY_PORTS_STATE_FILE"
|
|
echo "$(normalize_ip_list "$WAN_BP_IP")" > "$WAN_BP_IP_STATE_FILE"
|
|
echo "$(normalize_ip_list "$LAN_AC_IP")" > "$LAN_AC_IP_STATE_FILE"
|
|
echo "$(normalize_ip_list "$LAN_BP_IP")" > "$LAN_BP_IP_STATE_FILE"
|
|
echo "$(normalize_ip_list "$WAN_FW_IP")" > "$WAN_FW_IP_STATE_FILE"
|
|
echo "$(normalize_ip_list "$LAN_FP_IP")" > "$LAN_FP_IP_STATE_FILE"
|
|
echo "$(normalize_ip_list "$LAN_GM_IP")" > "$LAN_GM_IP_STATE_FILE"
|
|
echo "$XHTTP_FILE_EXISTS" > "$XHTTP_FILE_STATE_FILE"
|
|
echo "$XHTTP_FILE_HASH" > "$XHTTP_FILE_HASH_FILE"
|
|
|
|
# STEP 5: Check if run mode changed
|
|
if runmode_change "$RUNMODE"; then
|
|
MODE_CHANGED=1
|
|
loger 6 "Runmode changed: MODE_CHANGED=1"
|
|
else
|
|
MODE_CHANGED=0
|
|
loger 6 "Runmode unchanged: MODE_CHANGED=0"
|
|
fi
|
|
|
|
# STEP 6: Mode changed and persistence exists → delete once
|
|
if [ "$MODE_CHANGED" = "1" ] && [ "$PERSISTENCE_EXISTS" = "1" ]; then
|
|
loger 6 "Mode changed → removing persistence file"
|
|
rm -f "$NFTABLES_RULES_FILE"
|
|
PERSISTENCE_EXISTS=0
|
|
fi
|
|
|
|
# STEP 7: FORCE_RECREATE priority → must rebuild rules
|
|
if [ "$FORCE_RECREATE" = "1" ]; then
|
|
loger 5 "Forced regeneration of NFTables rules"
|
|
if flush_r && ipset_r && fw_rule && ac_rule && tp_rule && gen_include; then
|
|
loger 5 "NFT rules applied successfully (forced rebuild)"
|
|
persist_nftables_rules
|
|
[ "$ENABLE_AUTO_UPDATE" = "1" ] && start_auto_update_daemon
|
|
exit 0
|
|
else
|
|
loger 3 "NFT forced rebuild failed!"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# STEP 8: Persistence exists → try restore
|
|
if [ "$PERSISTENCE_EXISTS" = "1" ]; then
|
|
# Restore rules
|
|
if restore_from_persistence; then
|
|
loger 5 "NFT rules restored from persistence"
|
|
gen_include
|
|
[ "$ENABLE_AUTO_UPDATE" = "1" ] && start_auto_update_daemon
|
|
exit 0
|
|
else
|
|
loger 3 "Restore failed → fallback to full setup"
|
|
PERSISTENCE_EXISTS=0
|
|
fi
|
|
fi
|
|
|
|
# STEP 9: Persistence doesn't exist or restore failed → generate new rules
|
|
if flush_r && ipset_r && fw_rule && ac_rule && tp_rule && gen_include; then
|
|
loger 5 "NFTables rules applied successfully"
|
|
persist_nftables_rules
|
|
[ "$ENABLE_AUTO_UPDATE" = "1" ] && start_auto_update_daemon
|
|
exit 0
|
|
else
|
|
loger 3 "NFTables setup failed!"
|
|
exit 1
|
|
fi
|
|
else
|
|
# iptables
|
|
if flush_r && fw_rule && ipset_r && ac_rule && tp_rule && gen_include; then
|
|
loger 5 "iptables rules applied successfully"
|
|
exit 0
|
|
else
|
|
loger 3 "iptables setup failed!"
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|