Browse Source

Merge pull request #1838 from zxlhhyccc/tuic

luci-app-ssr-plus: optimize nftables fw4 rules and Add nftables rule persistence.
zxl hhyccc 1 week ago
parent
commit
08dd79a0ca

+ 4 - 0
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua

@@ -73,6 +73,10 @@ if Process_list:find("tcp.udp.ssr.retcp") then
 	reudp_run = 1
 end
 
+if Process_list:find("nft.ssr.retcp") then
+	redir_run = 1
+end
+
 if Process_list:find("local.ssr.retcp") then
 	redir_run = 1
 	sock5_run = 1

+ 40 - 6
luci-app-ssr-plus/root/etc/init.d/shadowsocksr

@@ -211,8 +211,10 @@ start_dns() {
 	local run_mode="$(uci_get_by_type global run_mode)"
 
 	if [ "$ssrplus_dns" != "0" ]; then
-		if [ -n "$dnsserver" ]; then
-			add_dns_into_ipset $run_mode $dnsserver
+		if command -v iptables-legacy >/dev/null 2>&1; then
+			if [ -n "$dnsserver" ]; then
+				add_dns_into_ipset $run_mode $dnsserver
+			fi
 		fi
 		case "$ssrplus_dns" in
 		1)
@@ -236,7 +238,9 @@ start_dns() {
 			output=$(for i in $(echo $mosdns_dnsserver | sed "s/,/ /g"); do
 				dnsserver=${i%:*}
 				dnsserver=${i##*/}
-				add_dns_into_ipset $run_mode $dnsserver
+				if command -v iptables-legacy >/dev/null 2>&1; then
+					add_dns_into_ipset $run_mode $dnsserver
+				fi
 				echo "            - addr: $i"
 				echo "              enable_pipeline: true"
 			done)
@@ -1172,7 +1176,17 @@ load_config() {
 	tcp_config_file=$TMP_PATH/tcp-only-ssr-retcp.json
 	case "$UDP_RELAY_SERVER" in
 	nil)
-		mode="tcp"
+		if command -v nft >/dev/null 2>&1; then
+			# nftables / fw4
+			mode="tcp,udp"
+			ARG_UDP=""
+			udp_config_file=""
+			UDP_RELAY_SERVER="nil"
+			tcp_config_file=$TMP_PATH/nft-ssr-retcp.json
+		else
+			# iptables / fw3
+			mode="tcp"
+		fi
 		;;
 	$GLOBAL_SERVER | same)
 		mode="tcp,udp"
@@ -1185,7 +1199,13 @@ load_config() {
 		udp_config_file=$TMP_PATH/udp-only-ssr-reudp.json
 		ARG_UDP="-U"
 		start_udp
-		mode="tcp"
+		if command -v nft >/dev/null 2>&1; then
+			# nftables / fw4
+			mode="tcp,udp"
+		else
+			# iptables / fw3
+			mode="tcp"
+		fi
 		;;
 	esac
 	case "$LOCAL_SERVER" in
@@ -1419,6 +1439,11 @@ start_rules() {
 		2) echo "-O" ;;
 		esac
 	}
+	if command -v nft >/dev/null 2>&1; then
+		ARG_A="-A"
+	else
+		ARG_A=""
+	fi
 	/usr/share/shadowsocksr/gfw2ipset.sh
 	/usr/bin/ssr-rules \
 		-s "$server" \
@@ -1438,7 +1463,8 @@ start_rules() {
 		-N "$shunt_ip" \
 		-M "$(uci_get_by_type global netflix_proxy 0)" \
 		-I "/etc/ssrplus/netflixip.list" \
-		$(get_arg_out) $(gfwmode) $ARG_UDP
+		$(get_arg_out) $(gfwmode) $ARG_UDP $ARG_A
+		
 	return $?
 }
 
@@ -1486,6 +1512,13 @@ boot() {
 stop() {
 	unlock
 	set_lock
+	if command -v nft >/dev/null 2>&1; then
+		/usr/bin/ssr-rules -K
+		local CLEANUP_PERSISTENCE="$(uci_get_by_type global global_server nil)"
+		if [ "$CLEANUP_PERSISTENCE" == "nil" ]; then
+			/usr/bin/ssr-rules -X
+		fi
+	fi
 	/usr/bin/ssr-rules -f
 	local srulecount=0
 	if command -v nft >/dev/null 2>&1; then
@@ -1527,6 +1560,7 @@ stop() {
 		killall -q -9 kcptun-client
 	fi
 	$PS -w | grep -v "grep" | grep ssr-monitor | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
+	$PS -w | grep -v "grep" | grep ssr-rules | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
 	$PS -w | grep -v "grep" | grep "sleep 0000" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
 	( \
 		# Graceful kill first, so programs have the chance to stop its subprocesses

+ 855 - 178
luci-app-ssr-plus/root/usr/bin/ssr-rules

@@ -28,6 +28,24 @@ detect_firewall
 
 TAG="_SS_SPEC_RULE_"                                  # comment tag
 
+# 这些变量将在后续的 getopts 参数解析中被赋值
+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  # 自动更新检查间隔(秒),0表示禁用自动更新
+fi
+
+# 修改 usage 函数
 usage() {
 	cat <<-EOF
 		Usage: ssr-rules [options]
@@ -63,6 +81,15 @@ usage() {
 		    -r                      router mode
 		    -c                      oversea mode
 		    -z                      all mode
+		    
+		    # 新增持久化管理选项 (使用不同的字母避免冲突)
+		    -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
@@ -73,6 +100,40 @@ loger() {
 	logger -st ssr-rules[$$] -p$1 $2
 }
 
+# 清理持久化和运行模块文件等
+cleanup_persistence_files() {
+	if [ "$USE_NFT" != "1" ]; then
+		return 0
+	fi
+
+	# 删除持久化规则文件
+	if [ -f "$NFTABLES_RULES_FILE" ]; then
+		rm -f "$NFTABLES_RULES_FILE" 2>/dev/null
+		loger 5 "Removed persistence file: $NFTABLES_RULES_FILE"
+	fi
+
+	# 删除运行模块文件
+	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
+
+	# 删除 TPROXY 文件
+	if [ -f "/tmp/.last_tproxy" ]; then
+		rm -f "/tmp/.last_tproxy" 2>/dev/null
+		loger 5 "Removed run mode file: /tmp/.last_tproxy"
+	fi
+
+	# 删除 PROXY_PORTS 文件
+	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
+
+	loger 5 "Persistence cleanup completed"
+	return 0
+}
+
 flush_r() {
 	if [ "$USE_NFT" = "1" ]; then
 		flush_nftables
@@ -136,6 +197,13 @@ flush_nftables() {
 	# 重置防火墙 include 文件
 	[ -n "$FWI" ] && echo '#!/bin/sh' >"$FWI"
 
+	# 清理持久化和运行模块文件
+	if [ "$CLEANUP_PERSISTENCE" = "1" ]; then
+		cleanup_persistence_files
+	fi
+
+	loger 6 "Memory rules flushed successfully"
+
 	return 0
 }
 
@@ -182,7 +250,7 @@ ipset_nft() {
 	fi
 
 	# Create necessary collections
-	for setname in china gmlan fplan bplan whitelist blacklist netflix; do
+	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
@@ -190,7 +258,7 @@ ipset_nft() {
 		fi
 	done
 
-	# 批量导入中国IP列表
+	# 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
@@ -212,109 +280,111 @@ ipset_nft() {
 		[ -n "$ip" ] && $NFT add element inet ss_spec blacklist "{ $ip }" 2>/dev/null
 	done
 
-	# Create main chain for WAN access control
-	if ! $NFT list chain inet ss_spec ss_spec_wan_ac >/dev/null 2>&1; then
-		$NFT add chain inet ss_spec ss_spec_wan_ac 2>/dev/null
-	fi
-	$NFT flush chain inet ss_spec ss_spec_wan_ac 2>/dev/null
-
-	# Create forward chain with better error handling
-	if ! $NFT list chain inet ss_spec ss_spec_wan_fw >/dev/null 2>&1; then
-		$NFT add chain inet ss_spec ss_spec_wan_fw 2>/dev/null || {
-		loger 3 "Failed to create forward chain"
-		return 1
-		}
-	fi
-	# Clear existing rules
-	$NFT flush chain inet ss_spec ss_spec_wan_fw 2>/dev/null
-
-	EXT_ARGS=""
-	if [ -n "$PROXY_PORTS" ]; then
-		PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
-		if [ -n "$PORTS_ARGS" ]; then
-			EXT_ARGS="th dport { $PORTS_ARGS }"
+	# Create main chains for WAN access control
+	for chain in ss_spec_wan_fw_tcp ss_spec_wan_fw_udp ss_spec_wan_ac_tcp ss_spec_wan_ac_udp; do
+		if ! $NFT list chain inet ss_spec $chain >/dev/null 2>&1; then
+			$NFT add chain inet ss_spec $chain
 		fi
-	fi
+		$NFT flush chain inet ss_spec $chain
+	done
 
 	# Add basic rules
-	# ========== 按照正确顺序添加规则 ==========
-
-	# 1. 基础例外规则(最高优先级)
-	$NFT add rule inet ss_spec ss_spec_wan_ac tcp dport 53 ip daddr 127.0.0.0/8 return
-	[ -n "$server" ] && $NFT add rule inet ss_spec ss_spec_wan_ac tcp dport != 53 ip daddr "$server" return
-
-	# 2. 强制访问控制
-	$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
+	# BASIC RULES (exceptions first) — TCP
+	$NFT add rule inet ss_spec ss_spec_wan_ac_tcp 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_tcp 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_tcp ip daddr @blacklist jump ss_spec_wan_fw_tcp
+	$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip daddr @whitelist return
+	$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip saddr @fplan jump ss_spec_wan_fw_tcp
+	$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip saddr @bplan return
+
+	# BASIC RULES (exceptions first) — UDP
+	$NFT add rule inet ss_spec ss_spec_wan_ac_udp meta l4proto udp udp dport 53 ip daddr 127.0.0.0/8 return
+	[ -n "$server" ] && $NFT add rule inet ss_spec ss_spec_wan_ac_udp meta l4proto udp udp dport != 53 ip daddr "$server" return
+
+	# Access control: blacklist -> whitelist -> fplan/bplan — UDP
+	$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip daddr @blacklist jump ss_spec_wan_fw_udp
+	$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip daddr @whitelist return
+	$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip saddr @fplan jump ss_spec_wan_fw_udp
+	$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip saddr @bplan return
 
-	# 3. 特殊功能规则
 	# 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 ip daddr @music return
+		$NFT add rule inet ss_spec ss_spec_wan_ac_tcp meta l4proto tcp ip daddr @music return
+		$NFT add rule inet ss_spec ss_spec_wan_ac_udp meta l4proto udp ip daddr @music return
 	fi
 
 	# Shunt/Netflix rules
-	if [ "$SHUNT_PORT" != "0" ] && [ -f "$SHUNT_LIST" ]; then
+	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
-		case "$SHUNT_PORT" in
-		1)
-			$NFT add rule inet ss_spec ss_spec_wan_ac meta l4proto tcp $EXT_ARGS ip daddr @netflix counter redirect to :$local_port
-			;;
-		*)
-			$NFT add rule inet ss_spec ss_spec_wan_ac meta l4proto 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 meta l4proto 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
 
-	# 4. 模式特定规则
 	# 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; }'
+		if ! $NFT list set inet ss_spec ss_spec_wan_ac_tcp >/dev/null 2>&1; then
+			$NFT add set inet ss_spec ss_spec_wan_ac_tcp '{ type ipv4_addr; flags interval; auto-merge; }'
 		else
-			$NFT flush set inet ss_spec ss_spec_wan_ac 2>/dev/null
+			$NFT flush set inet ss_spec ss_spec_wan_ac_tcp 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
+			[ -n "$ip" ] && $NFT add element inet ss_spec ss_spec_wan_ac_tcp "{ $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_fw >/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
+		$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip daddr @ss_spec_wan_ac_tcp return
+		$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip daddr @china return
+		if $NFT list chain inet ss_spec ss_spec_wan_ac_tcp >/dev/null 2>&1; then
+			$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip saddr @gmlan ip daddr != @china jump ss_spec_wan_fw_tcp
+			$NFT add rule inet ss_spec ss_spec_wan_ac_tcp jump ss_spec_wan_fw_tcp
+		fi
+		if ! $NFT list set inet ss_spec ss_spec_wan_ac_udp >/dev/null 2>&1; then
+			$NFT add set inet ss_spec ss_spec_wan_ac_udp '{ type ipv4_addr; flags interval; auto-merge; }'
+		else
+			$NFT flush set inet ss_spec ss_spec_wan_ac_udp 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_udp "{ $ip }" 2>/dev/null
+		done
+		$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip daddr @ss_spec_wan_ac_udp return
+		$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip daddr @china return
+		if $NFT list chain inet ss_spec ss_spec_wan_fw_udp >/dev/null 2>&1; then
+			$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip saddr @gmlan ip daddr != @china jump ss_spec_wan_fw_udp
+			$NFT add rule inet ss_spec ss_spec_wan_ac_udp jump ss_spec_wan_fw_udp
 		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
+		$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip daddr @china return
+		$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip daddr @gfwlist jump ss_spec_wan_fw_tcp
+		$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip saddr @gmlan ip daddr != @china jump ss_spec_wan_fw_tcp
+		$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip daddr @china return
+		$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip daddr @gfwlist jump ss_spec_wan_fw_udp
+		$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip saddr @gmlan ip daddr != @china jump ss_spec_wan_fw_udp
 		;;
 	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
+		$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip daddr @oversea jump ss_spec_wan_fw_tcp
+		$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip saddr @gmlan jump ss_spec_wan_fw_tcp
+		$NFT add rule inet ss_spec ss_spec_wan_ac_tcp ip daddr @china jump ss_spec_wan_fw_tcp
+		$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip daddr @oversea jump ss_spec_wan_fw_udp
+		$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip saddr @gmlan jump ss_spec_wan_fw_udp
+		$NFT add rule inet ss_spec ss_spec_wan_ac_udp ip daddr @china jump ss_spec_wan_fw_udp
 		;;
 	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
+		if $NFT list chain inet ss_spec ss_spec_wan_fw_tcp >/dev/null 2>&1; then
+			$NFT add rule inet ss_spec ss_spec_wan_ac_tcp jump ss_spec_wan_fw_tcp
+		fi
+		if $NFT list chain inet ss_spec ss_spec_wan_fw_udp >/dev/null 2>&1; then
+			$NFT add rule inet ss_spec ss_spec_wan_ac_udp jump ss_spec_wan_fw_udp
 		fi
 		;;
 	esac
@@ -402,28 +472,68 @@ fw_rule() {
 }
 
 fw_rule_nft() {
-	# Exclude special local addresses
-	if $NFT list chain inet ss_spec ss_spec_wan_fw >/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_wan_fw ip daddr $net return 2>/dev/null
-		done
+	# 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=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
-		RULE="tcp dport { $PORTS } counter redirect to :"$local_port""
+		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 }"
+			UDP_EXT_ARGS="meta l4proto udp udp dport { $PORTS_ARGS }"
+
+			TCP_RULE="meta l4proto tcp tcp dport { $PORTS_ARGS } counter redirect to :$local_port"
+			UDP_RULE="meta l4proto udp udp dport { $PORTS_ARGS } counter tproxy ip to :$local_port meta mark set 0x01"
+		fi
 	else
+		TCP_EXT_ARGS="meta l4proto tcp"
+		UDP_EXT_ARGS="meta l4proto udp"
+
 		# default: redirect everything except ssh(22)
-		RULE="tcp dport != 22 counter redirect to :"$local_port""
+		TCP_RULE="meta l4proto tcp tcp dport != 22 counter redirect to :$local_port"
+		# default: when PROXY_PORTS present, redirect those udp ports to local_port
+		UDP_RULE="meta l4proto udp counter tproxy ip to :$local_port meta mark set 0x01"
 	fi
-	if ! $NFT list chain inet ss_spec ss_spec_wan_fw 2>/dev/null | grep -q "$RULE"; then
-		if ! $NFT add rule inet ss_spec ss_spec_wan_fw $RULE 2>/dev/null; then
-			loger 3 "Can't redirect, please check nftables."
+	# add TCP rule to fw chain if not exists (use -F exact match)
+	if ! $NFT list chain inet ss_spec ss_spec_wan_fw_tcp 2>/dev/null | grep -F -- "$TCP_RULE" >/dev/null 2>&1; then
+		if ! $NFT add rule inet ss_spec ss_spec_wan_fw_tcp $TCP_RULE 2>/dev/null; then
+			loger 3 "Can't redirect TCP, please check nftables."
 			return 1
 		fi
 	fi
 
+	if ! $NFT list chain inet ss_spec ss_spec_wan_fw_udp 2>/dev/null | grep -F -- "$UDP_RULE" >/dev/null 2>&1; then
+		if ! $NFT add rule inet ss_spec ss_spec_wan_fw_udp $UDP_RULE 2>/dev/null; then
+			loger 3 "Can't tproxy UDP, 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 $TCP_EXT_ARGS ip daddr @netflix counter redirect to :$local_port
+			$NFT add rule inet ss_spec ss_spec_wan_ac_udp $UDP_EXT_ARGS ip daddr @netflix counter tproxy ip to :$local_port meta mark set 0x01
+			;;
+		*)
+			$NFT add rule inet ss_spec ss_spec_wan_ac_tcp $TCP_EXT_ARGS ip daddr @netflix counter redirect to :$SHUNT_PORT
+			$NFT add rule inet ss_spec ss_spec_wan_ac_udp $UDP_EXT_ARGS ip daddr @netflix counter tproxy ip to :$SHUNT_PORT meta mark set 0x01
+			if [ "$SHUNT_PROXY" = "1" ]; then
+				$NFT add rule inet ss_spec ss_spec_wan_ac_tcp $TCP_EXT_ARGS ip daddr $SHUNT_IP counter redirect to :$local_port
+				$NFT add rule inet ss_spec ss_spec_wan_ac_udp $UDP_EXT_ARGS ip daddr $SHUNT_IP counter tproxy ip to :$local_port meta mark set 0x01
+			else
+                [ -n "$SHUNT_IP" ] && $NFT add element inet ss_spec whitelist "{ $SHUNT_IP }" 2>/dev/null
+			fi
+            ;;
+		esac
+	fi
+
 	return $?
 }
 
@@ -482,34 +592,67 @@ ac_rule_nft() {
 		esac
 	fi
 
-	# 创建ss_spec_prerouting链
-	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; }'
+	# Create ss_spec_prerouting tcp chain
+	if ! $NFT list chain inet ss_spec ss_spec_prerouting_tcp >/dev/null 2>&1; then
+		$NFT add chain inet ss_spec ss_spec_prerouting_tcp '{ type nat hook prerouting priority 0; policy accept; }'
+	fi
+	$NFT flush chain inet ss_spec ss_spec_prerouting_tcp 2>/dev/null
+
+	# Exclude special local addresses
+	if $NFT list chain inet ss_spec ss_spec_prerouting_tcp >/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_tcp ip daddr $net return 2>/dev/null
+		done
 	fi
-	$NFT flush chain inet ss_spec ss_spec_prerouting 2>/dev/null
 
-	# 创建ss_spec_output链
-	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; }'
+	# 暂注释 IPV6 用于后续开启 IPV6
+	#if $NFT list chain inet ss_spec ss_spec_prerouting_tcp >/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_tcp ip6 daddr $net return 2>/dev/null
+	#	done
+	#fi
+
+	# Create ss_spec_prerouting udp chain
+	if ! $NFT list chain inet ss_spec ss_spec_prerouting_udp >/dev/null 2>&1; then
+		$NFT add chain inet ss_spec ss_spec_prerouting_udp '{ type filter hook prerouting priority -150; policy accept; }'
 	fi
-	$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
+	$NFT flush chain inet ss_spec ss_spec_prerouting_udp 2>/dev/null
+
+	# Exclude special local addresses
+	if $NFT list chain inet ss_spec ss_spec_prerouting_udp >/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_udp ip daddr $net return 2>/dev/null
+		done
+	fi
+
+	# 暂注释 IPV6 用于后续开启 IPV6
+	#if $NFT list chain inet ss_spec ss_spec_prerouting_udp >/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_udp ip6 daddr $net return 2>/dev/null
+	#	done
+	#fi
 
 	# Build a rule in the prerouting hook chain that jumps to business chain with conditions
-	EXT_ARGS=""
 	if [ -n "$PROXY_PORTS" ]; then
 		PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
 		if [ -n "$PORTS_ARGS" ]; then
-			EXT_ARGS="th dport { $PORTS_ARGS }"
+			TCP_EXT_ARGS="meta l4proto tcp tcp dport { $PORTS_ARGS }"
+			UDP_EXT_ARGS="meta l4proto udp udp dport { $PORTS_ARGS }"
 		fi
+	else
+		TCP_EXT_ARGS="meta l4proto tcp"
+		UDP_EXT_ARGS="meta l4proto udp"
 	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 insert rule inet ss_spec ss_spec_prerouting meta l4proto tcp $EXT_ARGS $MATCH_SET jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
+			$NFT add rule inet ss_spec ss_spec_prerouting_tcp $TCP_EXT_ARGS $MATCH_SET jump ss_spec_wan_ac_tcp comment "\"$TAG\"" 2>/dev/null
+			$NFT add rule inet ss_spec ss_spec_prerouting_udp $UDP_EXT_ARGS $MATCH_SET jump ss_spec_wan_ac_udp comment "\"$TAG\"" 2>/dev/null
 		else
-			$NFT insert rule inet ss_spec ss_spec_prerouting meta l4proto tcp $EXT_ARGS jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
+			$NFT add rule inet ss_spec ss_spec_prerouting_tcp $TCP_EXT_ARGS jump ss_spec_wan_ac_tcp comment "\"$TAG\"" 2>/dev/null
+			$NFT add rule inet ss_spec ss_spec_prerouting_udp $UDP_EXT_ARGS jump ss_spec_wan_ac_udp comment "\"$TAG\"" 2>/dev/null
 		fi
 	else
 		# For each Interface, find its actual ifname and add an iifname-limited prerouting rule
@@ -518,9 +661,11 @@ ac_rule_nft() {
 			[ -z "$IFNAME" ] && IFNAME=$(uci -P /var/state get network."$name".device 2>/dev/null)
 			if [ -n "$IFNAME" ]; then
 				if [ -n "$MATCH_SET" ]; then
-					$NFT insert rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" meta l4proto tcp $EXT_ARGS $MATCH_SET jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
+					$NFT add rule inet ss_spec ss_spec_prerouting_tcp meta iifname "$IFNAME" $TCP_EXT_ARGS $MATCH_SET jump ss_spec_wan_ac_tcp comment "\"$TAG\"" 2>/dev/null
+					$NFT add rule inet ss_spec ss_spec_prerouting_udp meta iifname "$IFNAME" $UDP_EXT_ARGS $MATCH_SET jump ss_spec_wan_ac_udp comment "\"$TAG\"" 2>/dev/null
 				else
-					$NFT insert rule inet ss_spec ss_spec_prerouting meta iifname "$IFNAME" meta l4proto tcp $EXT_ARGS jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
+					$NFT add rule inet ss_spec ss_spec_prerouting_tcp meta iifname "$IFNAME" $TCP_EXT_ARGS jump ss_spec_wan_ac_tcp comment "\"$TAG\"" 2>/dev/null
+					$NFT add rule inet ss_spec ss_spec_prerouting_udp meta iifname "$IFNAME" $UDP_EXT_ARGS jump ss_spec_wan_ac_udp comment "\"$TAG\"" 2>/dev/null
 				fi
 			fi
 		done
@@ -528,8 +673,51 @@ ac_rule_nft() {
 
 	case "$OUTPUT" in
 	1)
+		# Create ss_spec_output tcp chain
+		if ! $NFT list chain inet ss_spec ss_spec_output_tcp >/dev/null 2>&1; then
+			$NFT add chain inet ss_spec ss_spec_output_tcp '{ type nat hook output priority 0; policy accept; }'
+		fi
+		$NFT flush chain inet ss_spec ss_spec_output_tcp 2>/dev/null
+		
+		# Exclude special local addresses
+		if $NFT list chain inet ss_spec ss_spec_output_tcp >/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_tcp ip daddr $net return 2>/dev/null
+			done
+		fi
+
+		# 暂注释 IPV6 用于后续开启 IPV6
+		#if $NFT list chain inet ss_spec ss_spec_output_tcp >/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_tcp ip6 daddr $net return 2>/dev/null
+		#	done
+		#fi
+
 		# create output hook chain & route output traffic into router chain
-		$NFT insert rule inet ss_spec ss_spec_output meta l4proto tcp $EXT_ARGS jump ss_spec_wan_ac comment "\"$TAG\"" 2>/dev/null
+		$NFT add rule inet ss_spec ss_spec_output_tcp $TCP_EXT_ARGS jump ss_spec_wan_ac_tcp comment "\"$TAG\"" 2>/dev/null
+
+		# Create ss_spec_output udp chain
+		if ! $NFT list chain inet ss_spec ss_spec_output_udp >/dev/null 2>&1; then
+			$NFT add chain inet ss_spec ss_spec_output_udp '{ type filter hook output priority -150; policy accept; }'
+		fi
+		$NFT flush chain inet ss_spec ss_spec_output_udp 2>/dev/null
+		
+		# Exclude special local addresses
+		if $NFT list chain inet ss_spec ss_spec_output_udp >/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_udp ip daddr $net return 2>/dev/null
+			done
+		fi
+
+		# 暂注释 IPV6 用于后续开启 IPV6
+		#if $NFT list chain inet ss_spec ss_spec_output_udp >/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_udp 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_udp $UDP_EXT_ARGS meta mark set 0x01 comment "\"$TAG\"" 2>/dev/null
 		;;
 	2)
 		# router mode output chain: create ssr_gen_router set & router chain
@@ -539,8 +727,10 @@ ac_rule_nft() {
 		done
 		$NFT add 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 meta l4proto tcp $EXT_ARGS jump ss_spec_router comment "\"$TAG\"" 2>/dev/null
+		$NFT add rule inet ss_spec ss_spec_router jump ss_spec_wan_fw_tcp 2>/dev/null
+		$NFT add rule inet ss_spec ss_spec_output $TCP_EXT_ARGS jump ss_spec_router comment "\"$TAG\"" 2>/dev/null
+		$NFT add rule inet ss_spec ss_spec_router jump ss_spec_wan_fw_udp 2>/dev/null
+		$NFT add rule inet ss_spec ss_spec_output $UDP_EXT_ARGS jump ss_spec_router comment "\"$TAG\"" 2>/dev/null
 		;;
 	esac
 	return 0
@@ -605,8 +795,13 @@ tp_rule() {
 
 tp_rule_nft() {
 	# set up routing table for tproxy
-	ip rule add fwmark 0x01/0x01 table 100 2>/dev/null
-	ip route add local 0.0.0.0/0 dev lo table 100 2>/dev/null
+	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
@@ -615,20 +810,15 @@ tp_rule_nft() {
 
 	local MATCH_SET=""
 
-	EXT_ARGS=""
 	if [ -n "$PROXY_PORTS" ]; then
 		PORTS_ARGS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
 		if [ -n "$PORTS_ARGS" ]; then
-			EXT_ARGS="th dport { $PORTS_ARGS }"
+			EXT_ARGS="udp dport { $PORTS_ARGS }"
 		else
 			EXT_ARGS=""
 		fi
 	fi
 
-	# 有端口 => 1,无端口 => 0
-	HAS_PORTS=0
-	[ -n "$EXT_ARGS" ] && HAS_PORTS=1
-
 	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
@@ -664,7 +854,7 @@ tp_rule_nft() {
 		fi
 	done
 
-	# 批量导入中国IP列表
+	# 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
@@ -682,12 +872,19 @@ tp_rule_nft() {
 		done
 	fi
 
+	# 暂注释 IPV6 用于后续开启 IPV6
+	#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 udp dport 53 return 2>/dev/null
+	$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 udp dport != 53 ip daddr "$server" return 2>/dev/null
+		$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)
@@ -700,12 +897,7 @@ tp_rule_nft() {
 
 	# 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
-
-	if [ $HAS_PORTS -eq 1 ]; then
-		$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
-	else
-		$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip saddr @fplan counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
-	fi
+	$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
@@ -722,50 +914,38 @@ tp_rule_nft() {
 
 		$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 dport 80 drop 2>/dev/null
+		$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp udp dport 80 drop 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
-		if [ $HAS_PORTS -eq 1 ]; then
-			$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
-		else
-			$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp ip daddr != @ss_spec_wan_ac counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
-		fi
+		$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)
-		$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 dport 80 drop 2>/dev/null
-		if [ $HAS_PORTS -eq 1 ]; then
-			$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
+		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 drop 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
-		if ! $NFT list set ip ss_spec_mangle china >/dev/null 2>&1; then
-			$NFT add set ip ss_spec_mangle china '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
-		fi
-		if [ $HAS_PORTS -eq 1 ]; then
-			$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
-		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)
-		if [ $HAS_PORTS -eq 1 ]; then
-			$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
-		else
-			$NFT add rule ip ss_spec_mangle ss_spec_tproxy meta l4proto udp counter tproxy ip to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
-		fi
+		$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
 
-	# 创建 prerouting 链(hook prerouting)
+	# 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
 
-	# 添加规则到 prerouting 链
+	# add prerouting jump (idempotent)
 	if [ -z "$Interface" ]; then
 		# 全局规则
 		if [ -n "$MATCH_SET" ]; then
@@ -884,17 +1064,23 @@ gen_include() {
 	return $?
 }
 
+# 修改gen_include_nft,调用持久化功能
 gen_include_nft() {
 	# Generate nftables include file for firewall4
 	[ -n "$FWI" ] && echo '#!/bin/sh' >"$FWI"
-	cat <<-'EOF' >>"$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
-		nft list ruleset | awk '/^table (inet|ip) ss_spec/{flag=1} /^table / && !/^table (inet|ip) ss_spec/{flag=0} flag'
+		# 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"
 }
@@ -914,7 +1100,273 @@ gen_include_iptables() {
 	EOF
 }
 
-while getopts ":m:s:l:S:L:i:e:a:B:b:w:p:G:D:F:N:M:I:oOuUfgrczh" arg; do
+# 检查 nftables 规则状态
+check_nftables_status() {
+	if [ "$USE_NFT" != "1" ]; then
+		echo "NFTables not in use"
+		return 0
+	fi
+
+	# 检查ss_spec表是否存在
+	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
+
+	# 检查是否有基本规则
+	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_rules() {
+	if [ "$USE_NFT" != "1" ]; then
+		return 1  # NFTables未使用,需要更新
+	fi
+
+	# 如果没有持久化文件,更新持久化文件
+	if [ ! -f "$NFTABLES_RULES_FILE" ]; then
+		loger 6 "No persistence file found, update needed"
+		return 1  # 需要更新持久化文件
+	fi
+
+	# 检查ss_spec表是否存在
+	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  # 需要更新ss_spec表
+	fi
+
+	# 生成当前规则的临时文件
+	local temp_file=$(mktemp)
+	local rules_file=$(mktemp)
+	loger 7 "DEBUG: Temporary file path: $current_rules_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
+
+	# 检查是否成功导出了当前规则
+	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  # 导出失败,需要更新
+	fi
+
+	# 比较当前规则与持久化文件中的规则
+	if ! cmp -s "$rules_file" "$NFTABLES_RULES_FILE"; then
+		loger 6 "Rules differ, update needed"
+		rm -f "$temp_file" "$rules_file"
+		return 1  # 需要更新
+	fi
+
+	rm -f "$temp_file" "$rules_file"
+	loger 6 "Rules unchanged, no update needed"
+	return 0  # 无需更新
+}
+
+# 自动更新持久化规则
+persist_nftables_rules() {
+	if [ "$USE_NFT" != "1" ]; then
+		return 0
+	fi
+
+	# 如果模式未改变且存在持久化文件,跳过更新
+	if [ "$MODE_CHANGED" = "0" ] && [ -f "$NFTABLES_RULES_FILE" ]; then
+		loger 6 "Mode unchanged and persistence file exists, skipping update"
+		return 0
+	fi
+
+	# 强制更新时,跳过比较检查并删除旧文件
+	if [ "$FORCE_UPDATE" = "1" ]; then
+		loger 6 "Force update requested, removing old persistence file"
+		rm -f "$NFTABLES_RULES_FILE" 2>/dev/null
+	# 非强制更新时,进行规则比较
+	elif [ -f "$NFTABLES_RULES_FILE" ]; then
+		if compare_rules; then
+			loger 6 "Rules unchanged, skipping persistence update"
+			return 0
+		fi
+	fi
+
+	# 确保目录存在
+	mkdir -p "$NFTABLES_RULES_DIR" 2>/dev/null
+
+	# 生成nftables规则文件
+	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 "" >> "$NFTABLES_RULES_FILE"
+
+	local HAS_RULES=0
+
+	# 分别导出每个表
+	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
+
+	# 检查是否成功导出了规则
+	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
+
+	# 设置文件权限
+	chmod 644 "$NFTABLES_RULES_FILE" 2>/dev/null
+
+	# 记录成功信息
+	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
+}
+
+# 自动更新守护进程
+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_auto_update_daemon
+
+	# 直接在后台启动守护进程
+	(
+		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() {
+	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() {
+	if [ "$USE_NFT" != "1" ]; then
+		echo "NFTables not in use"
+		return 0
+	fi
+
+	# 移除现有规则文件确保重新创建
+	rm -f "$NFTABLES_RULES_FILE" 2>/dev/null
+
+	# 调用持久化函数
+	if persist_nftables_rules; then
+		loger 5 "Persistence update completed successfully"
+		return 0
+	else
+		loger 3 "Persistence update failed"
+		return 1
+	fi
+}
+
+# 从持久化文件恢复规则
+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"
+
+	# 清理现有规则
+	flush_r
+
+	# 从文件恢复规则
+	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
@@ -994,58 +1446,283 @@ while getopts ":m:s:l:S:L:i:e:a:B:b:w:p:G:D:F:N:M:I:oOuUfgrczh" arg; do
 	z)
 		RUNMODE=all
 		;;
+	# 新增持久化管理选项
+	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_r
-		exit 0
+		FLUSH_RULES=1
+		;;
+	h) 
+		usage 0 
 		;;
-	h) usage 0 ;;
 	esac
 done
 
-if [ -z "$server" ] || [ -z "$local_port" ]; then
-	usage 2
+# 首先处理需要立即退出的选项
+if [ "$CHECK_STATUS" = "1" ]; then
+	check_nftables_status
+	exit $?
 fi
 
-if ! echo "$local_port" | grep -qE '^[0-9]+$'; then
-	loger 3 "Invalid local port: $local_port"
-	exit 1
+if [ "$STOP_AUTO_UPDATE" = "1" ]; then
+	stop_auto_update_daemon
+	exit 0
 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
-
-# First check whether nftables is working properly
-if [ "$USE_NFT" = "1" ]; then
-	if ! $NFT list tables 2>/dev/null; then
-		loger 3 "nftables is not working properly, check if nftables is installed and running"
-		exit 1
+# 只有-X选项,执行清理后退出
+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
+
+# 检查是否有持久化管理选项单独处理
+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
 
-if [ "$USE_NFT" = "1" ]; then
-	# NFTables
-	if flush_r && ipset_r && fw_rule && ac_rule && tp_rule && gen_include; then
-		loger 5 "NFTables rules applied successfully"
+# 处理持久化管理选项的情况
+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
+
+# 强制刷新规则
+if [ "$FLUSH_RULES" = "1" ]; then
+	flush_r
+	# 如果只有 -f 选项,则退出
+	if [ -z "$server" ] && [ -z "$local_port" ] && [ "$FORCE_UPDATE" != "1" ] && \
+		[ "$RESTORE_RULES" != "1" ] && [ "$ENABLE_AUTO_UPDATE" != "1" ] && \
+		[ "$CLEANUP_PERSISTENCE" != "1" ]; then
 		exit 0
+	fi
+fi
+
+# 从持久化文件恢复规则(在规则应用之前)
+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
+
+# 运行模式更改
+runmode_change() {
+	local mode_file="/tmp/.ssr_run_mode"
+	local new_mode=""
+	local old_mode=""
+
+	# 从参数获取模式
+	if [ -n "$1" ]; then
+		new_mode="$1"
+	fi
+
+	# 从文件中读取上一次的运行模式
+	if [ -f "$mode_file" ]; then
+		old_mode=$(cat "$mode_file" 2>/dev/null)
+	fi
+
+	# 比较模式是否改变
+	if [ "$old_mode" = "$new_mode" ] && [ -n "$old_mode" ]; then
+		# 模式未改变
+		echo "$new_mode" > "$mode_file"  # 更新文件时间戳
+		loger 6 "Runmode unchanged: $new_mode"
+		return 1  # 返回1表示未改变
 	else
-		loger 3 "NFTables setup failed!"
+		# 模式已改变或首次运行
+		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  # 返回0表示已改变
+	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
-else
-	# iptables
-	if flush_r && fw_rule && ipset_r && ac_rule && tp_rule && gen_include; then
-		loger 5 "iptables rules applied successfully"
-		exit 0
+
+	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
+		# 保存上一次 TPROXY 状态文件
+		TPROXY_STATE_FILE="/tmp/.last_tproxy"
+		if [ -f "$TPROXY_STATE_FILE" ]; then
+			LAST_TPROXY=$(cat "$TPROXY_STATE_FILE")
+		else
+			LAST_TPROXY=""
+		fi
+
+		# 保存上一次 PROXY_PORTS 状态
+		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
+
+		# STEP 1: 判断 TPROXY 是否有值(1 或 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: 判断 PROXY_PORTS 是否有值(非空字符串)
+		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 3: 判断是否需要强制重建
+		FORCE_RECREATE=0
+		PERSISTENCE_EXISTS=0
+
+		# 触发条件:
+		# 1. TPROXY 从空 ↔ 有值变化
+		# 2. PROXY_PORTS 从空 ↔ 有值变化
+		if [ "$TPROXY_HAS_VALUE" != "$LAST_HAS_VALUE" ] || [ "$PROXY_HAS_VALUE" != "$LAST_PROXY_HAS_VALUE" ]; then
+			FORCE_RECREATE=1
+			loger 6 "TPROXY or PROXY_PORTS changed → force rebuild rules"
+			rm -f "$NFTABLES_RULES_FILE" 2>/dev/null
+		else
+			# 未触发 FORCE_RECREATE → 检查持久化文件
+			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: 保存当前状态
+		echo "$TPROXY" > "$TPROXY_STATE_FILE"
+		echo "$PROXY_PORTS" > "$PROXY_PORTS_STATE_FILE"
+
+		# STEP 5: 判断运行模式是否改变
+		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: 模式改变且持久化存在 → 删除一次
+		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 优先 → 必须重建规则
+		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: 持久化存在 → 尝试 restore
+		if [ "$PERSISTENCE_EXISTS" = "1" ]; then
+			# 恢复规则
+			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: 持久化不存在或 restore 失败 → 生成新规则
+		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
-		loger 3 "iptables setup failed!"
-		exit 1
+		# 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

+ 3 - 1
luci-app-ssr-plus/root/usr/share/shadowsocksr/update.lua

@@ -213,7 +213,9 @@ local function update(url, file, type, file2)
 			if type == "gfw_data" or type == "ad_data" then
 				luci.sys.call("/usr/share/shadowsocksr/gfw2ipset.sh")
 			else
-				luci.sys.call("/usr/share/shadowsocksr/chinaipset.sh " .. TMP_PATH .. "/china_ssr.txt")
+				if luci.sys.call("command -v ipset >/dev/null 2>&1") == 0 then
+					luci.sys.call("/usr/share/shadowsocksr/chinaipset.sh " .. TMP_PATH .. "/china_ssr.txt")
+				end
 			end
 			if args then
 				log(0, tonumber(icount) / Num)