Browse Source

Merge pull request #1820 from zxlhhyccc/tuic

luci-app-ssr-plus: Fix nftables fw4 support.
zxl hhyccc 3 weeks ago
parent
commit
25292fbb3a

+ 2 - 3
luci-app-ssr-plus/Makefile

@@ -42,7 +42,7 @@ LUCI_PKGARCH:=all
 LUCI_DEPENDS:= \
 	+coreutils +coreutils-base64 +dns2tcp +dnsmasq-full \
 	+jq +ip-full +lua +lua-neturl +libuci-lua +microsocks \
-	+tcping +resolveip +curl +nping \
+	+tcping +resolveip +shadowsocksr-libev-ssr-check +curl +nping \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray:curl \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_V2ray:v2ray-core \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_Xray:curl \
@@ -68,8 +68,7 @@ LUCI_DEPENDS:= \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_V2ray_Plugin:v2ray-plugin \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-local \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-redir \
-	+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-redir \
-	+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server:shadowsocksr-libev-ssr-check \
+	+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server:shadowsocksr-libev-ssr-server \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_Trojan:trojan
 
 

+ 42 - 27
luci-app-ssr-plus/root/etc/init.d/shadowsocksr

@@ -1260,9 +1260,14 @@ start_server() {
 		if [ "$server_count" == "1" ]; then
 			if command -v nft >/dev/null 2>&1; then
 				# nftables / fw4
-				if ! nft list chain inet fw4 SSR-SERVER-RULE >/dev/null 2>&1; then
-					nft add chain inet fw4 SSR-SERVER-RULE
-					nft insert rule inet fw4 input jump SSR-SERVER-RULE
+				if nft list table inet fw4 >/dev/null 2>&1; then
+					if ! nft list chain inet fw4 SSR-SERVER-RULE >/dev/null 2>&1; then
+						nft add chain inet fw4 SSR-SERVER-RULE 2>/dev/null
+					fi
+					if ! nft list chain inet fw4 input 2>/dev/null | grep -q "jump SSR-SERVER-RULE"; then
+						nft insert rule inet fw4 input jump SSR-SERVER-RULE comment \"SSR Server Input Hook\" 2>/dev/null
+					fi
+					nft flush chain inet fw4 SSR-SERVER-RULE 2>/dev/null
 				fi
 			else
 				# iptables / fw3
@@ -1317,20 +1322,19 @@ start_server() {
 		fi
 		if command -v nft >/dev/null 2>&1; then
 			# nftables / fw4
-			extract_rules() {
-				nft list chain inet fw4 SSR-SERVER-RULE 2>/dev/null | \
-					grep -v 'chain SSR-SERVER-RULE' | grep -v '^\s*{' | grep -v '^\s*}' | sed 's/ counter//g'
-			}
-			cat <<-EOF >>$FWI
-				nft flush chain inet fw4 SSR-SERVER-RULE 2>/dev/null || true
-				nft -f - <<-EOT
-				table inet fw4 {
-					chain SSR-SERVER-RULE {
-						type filter hook input priority 0; policy accept;
-						$(extract_rules)
-					}
-				}
-				EOT
+			cat <<-'EOF' >>"$FWI"
+				# 确保表存在
+				if nft list table inet fw4 >/dev/null 2>&1; then
+					# 如果不存在 SSR-SERVER-RULE 链,则创建
+					if ! nft list chain inet fw4 SSR-SERVER-RULE >/dev/null 2>&1; then
+						nft add chain inet fw4 SSR-SERVER-RULE 2>/dev/null
+					# 从 input 链跳转到 SSR-SERVER-RULE(如果未添加)
+					if ! nft list chain inet fw4 input | grep -q 'jump SSR-SERVER-RULE'; then
+						nft insert rule inet fw4 input jump SSR-SERVER-RULE comment \"SSR Server Input Hook\" 2>/dev/null
+					fi
+					# 已存在则清空链
+					nft flush chain inet fw4 SSR-SERVER-RULE 2>/dev/null
+				fi
 			EOF
 		else
 			# iptables / fw3
@@ -1483,26 +1487,38 @@ stop() {
 	unlock
 	set_lock
 	/usr/bin/ssr-rules -f
+	local srulecount=0
 	if command -v nft >/dev/null 2>&1; then
 		# nftables / fw4
 		#local srulecount=$(nft list ruleset 2>/dev/null | grep -c 'SSR-SERVER-RULE')
-		#local srulecount=$(nft list chain inet fw4 SSR-SERVER-RULE 2>/dev/null | grep -c 'dport')
-		local srulecount=$(nft list chain inet fw4 SSR-SERVER-RULE | grep -vE '^\s*(chain|{|})' | wc -l)
+		if nft list chain inet fw4 SSR-SERVER-RULE >/dev/null 2>&1; then
+			srulecount=$(nft list chain inet fw4 SSR-SERVER-RULE | grep SSR-SERVER-RULE | wc -l)
+		fi
 	else
 		# iptables / fw3
-		local srulecount=$(iptables -L | grep SSR-SERVER-RULE | wc -l)
+		srulecount=$(iptables -L | grep SSR-SERVER-RULE | wc -l)
 	fi
 	if [ $srulecount -gt 0 ]; then
 		if command -v nft >/dev/null 2>&1; then
 			# nftables / fw4
-			nft flush chain inet fw4 SSR-SERVER-RULE 2>/dev/null || true
-			nft delete rule inet fw4 input jump SSR-SERVER-RULE 2>/dev/null || true
-			nft delete chain inet fw4 SSR-SERVER-RULE 2>/dev/null || true
+			if nft list table inet fw4 >/dev/null 2>&1; then
+				if nft list chain inet fw4 SSR-SERVER-RULE >/dev/null 2>&1; then
+					for handle in $(nft --handle list chain inet fw4 input 2>/dev/null | \
+						grep 'jump SSR-SERVER-RULE' | awk '{for(i=1;i<=NF;i++) if($i=="handle") print $(i+1)}'); do
+						nft delete rule inet fw4 input handle $handle 2>/dev/null || true
+					done
+					nft flush chain inet fw4 SSR-SERVER-RULE 2>/dev/null || true
+					nft delete chain inet fw4 SSR-SERVER-RULE 2>/dev/null || true
+				fi
+			fi
 		else
 			# iptables / fw3
-			iptables -F SSR-SERVER-RULE
-			iptables -t filter -D INPUT -j SSR-SERVER-RULE
-			iptables -X SSR-SERVER-RULE 2>/dev/null
+			if iptables-save -t filter | grep -q "SSR-SERVER-RULE"; then
+				logger -t ssr-rules "Flushing and deleting SSR-SERVER-RULE chain (iptables)"
+				iptables -F SSR-SERVER-RULE 2>/dev/null || true
+				iptables -t filter -D INPUT -j SSR-SERVER-RULE 2>/dev/null || true
+				iptables -X SSR-SERVER-RULE 2>/dev/null || true
+			fi
 		fi
 	fi
 	if [ -z "$switch_server" ]; then
@@ -1550,4 +1566,3 @@ reset() {
 	cp /usr/share/shadowsocksr/shadowsocksr.config /etc/config/shadowsocksr
 	unset_lock
 }
-

+ 210 - 138
luci-app-ssr-plus/root/usr/bin/ssr-rules

@@ -82,7 +82,7 @@ flush_r() {
 }
 
 flush_nftables() {
-	# Remove nftables rules and sets
+	# Remove nftables rules and sets more carefully
 	$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
@@ -91,6 +91,21 @@ flush_nftables() {
 	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
 
+	# 删除 nftables 集合
+	$NFT delete set inet ss_spec ss_spec_lan_ac 2>/dev/null
+	$NFT delete set inet ss_spec ss_spec_wan_ac 2>/dev/null
+	$NFT delete set inet ss_spec ssr_gen_router 2>/dev/null
+	$NFT delete set inet ss_spec fplan 2>/dev/null
+	$NFT delete set inet ss_spec bplan 2>/dev/null
+	$NFT delete set inet ss_spec gmlan 2>/dev/null
+	$NFT delete set inet ss_spec oversea 2>/dev/null
+	$NFT delete set inet ss_spec whitelist 2>/dev/null
+	$NFT delete set inet ss_spec blacklist 2>/dev/null
+	$NFT delete set inet ss_spec netflix 2>/dev/null
+	$NFT delete set inet ss_spec gfwlist 2>/dev/null
+	$NFT delete set inet ss_spec china 2>/dev/null
+	$NFT delete set inet ss_spec music 2>/dev/null
+
 	[ -n "$FWI" ] && echo '#!/bin/sh' >"$FWI"
 	
 	return 0
@@ -134,107 +149,128 @@ ipset_r() {
 
 ipset_nft() {
 	[ -f "$IGNORE_LIST" ] && /usr/share/shadowsocksr/chinaipset.sh "$IGNORE_LIST"
-	
+
 	# Create nftables table and sets
-	$NFT add table inet ss_spec 2>/dev/null
-	$NFT add set inet ss_spec ss_spec_wan_ac '{ type ipv4_addr; flags interval; }' 2>/dev/null
-	$NFT add set inet ss_spec gmlan '{ type ipv4_addr; flags interval; }' 2>/dev/null
-	$NFT add set inet ss_spec fplan '{ type ipv4_addr; flags interval; }' 2>/dev/null  
-	$NFT add set inet ss_spec bplan '{ type ipv4_addr; flags interval; }' 2>/dev/null
-	$NFT add set inet ss_spec whitelist '{ type ipv4_addr; flags interval; }' 2>/dev/null
-	$NFT add set inet ss_spec blacklist '{ type ipv4_addr; flags interval; }' 2>/dev/null
-	$NFT add set inet ss_spec netflix '{ type ipv4_addr; flags interval; }' 2>/dev/null
-	
-	# Add IP addresses to sets
-	for ip in $LAN_GM_IP; do 
-		$NFT add element inet ss_spec gmlan "{ $ip }"
-	done
-	for ip in $LAN_FP_IP; do 
-		$NFT add element inet ss_spec fplan "{ $ip }"
-	done
-	for ip in $LAN_BP_IP; do 
-		$NFT add element inet ss_spec bplan "{ $ip }"
-	done
-	for ip in $WAN_BP_IP; do 
-		$NFT add element inet ss_spec whitelist "{ $ip }"
-	done
-	for ip in $WAN_FW_IP; do 
-		$NFT add element inet ss_spec blacklist "{ $ip }"
-	done
-	
-	# Create main chain for WAN access control
-	$NFT add chain inet ss_spec ss_spec_wan_ac '{ type nat hook prerouting priority dstnat; }' 2>/dev/null
-	$NFT add rule inet ss_spec ss_spec_wan_ac tcp dport 53 ip daddr 127.0.0.0/8 return
-	$NFT add rule inet ss_spec ss_spec_wan_ac tcp dport != 53 ip daddr "$server" return
-	
-	# Add special IP ranges to WAN AC set
-	for ip in $(gen_spec_iplist); do
-		$NFT add element inet ss_spec ss_spec_wan_ac "{ $ip }"
-	done
-	
-	# Set up mode-specific rules
-	case "$RUNMODE" in
-	router)
-		if command -v ipset >/dev/null 2>&1 && ipset list china -name -quiet >/dev/null 2>&1; then
-			$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  
-			$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan ip daddr != "@china" goto ss_spec_wan_fw
-			$NFT add rule inet ss_spec ss_spec_wan_ac goto ss_spec_wan_fw
+	$NFT list table inet ss_spec >/dev/null 2>&1 || $NFT add table inet ss_spec
+
+	# Create necessary collections
+	for setname in ss_spec_wan_ac gmlan fplan bplan whitelist blacklist netflix; 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 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 goto ss_spec_wan_fw
+        	$NFT flush set inet ss_spec $setname 2>/dev/null
+		fi
+    done
+
+    # 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 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 '{ type nat hook prerouting priority dstnat - 1; policy accept; }' 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
+    
+    # Add basic rules
+    $NFT add rule inet ss_spec ss_spec_wan_ac tcp dport 53 ip daddr 127.0.0.0/8 return
+    $NFT add rule inet ss_spec ss_spec_wan_ac udp dport 53 ip daddr 127.0.0.0/8 return
+    $NFT add rule inet ss_spec ss_spec_wan_ac tcp dport != 53 ip daddr "$server" return
+    $NFT add rule inet ss_spec ss_spec_wan_ac udp dport != 53 ip daddr "$server" return
+
+    # 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
+
+    # Set up mode-specific rules
+    case "$RUNMODE" in
+    router)
+		$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 2>/dev/null
+		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
 		fi
 		;;
-	gfw)
-		if command -v ipset >/dev/null 2>&1 && ipset list china -name -quiet >/dev/null 2>&1; then
-			$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" goto ss_spec_wan_fw
-			$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan ip daddr != "@china" goto ss_spec_wan_fw
+    gfw)
+		$NFT add set inet ss_spec gfwlist '{ type ipv4_addr; flags interval; }' 2>/dev/null
+		$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @china return 2>/dev/null
+		$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @gfwlist jump ss_spec_wan_fw 2>/dev/null
+		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
 		fi
 		;;
-	oversea)
-		$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @oversea goto ss_spec_wan_fw
-		$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan goto ss_spec_wan_fw
-		if command -v ipset >/dev/null 2>&1 && ipset list china -name -quiet >/dev/null 2>&1; then
-			$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr "@china" goto ss_spec_wan_fw
+    oversea)
+		$NFT add set inet ss_spec oversea '{ type ipv4_addr; flags interval; }' 2>/dev/null
+		if $NFT list chain inet ss_spec ss_spec_wan_fw >/dev/null 2>&1; then
+			$NFT insert rule inet ss_spec ss_spec_wan_ac ip daddr @oversea jump SS_SPEC_WAN_FW 2>/dev/null
+			$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @gmlan jump ss_spec_wan_fw 2>/dev/null
+			$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @china jump ss_spec_wan_fw 2>/dev/null
 		fi
 		;;
-	all)
-		$NFT add rule inet ss_spec ss_spec_wan_ac goto 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
-	
-	# Access control rules
-	$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @fplan goto ss_spec_wan_fw
-	$NFT add rule inet ss_spec ss_spec_wan_ac ip saddr @bplan return
-	$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @blacklist goto ss_spec_wan_fw  
-	$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr @whitelist return
-	
-	# Music unlocking support
-	if command -v ipset >/dev/null 2>&1 && ipset list music -name -quiet >/dev/null 2>&1; then
-		$NFT add rule inet ss_spec ss_spec_wan_ac ip daddr "@music" return 2>/dev/null
+    esac
+
+    # Access control rules
+    $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
+    $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
+
+    # Music unlocking support
+    if $NFT list set inet ss_spec music >/dev/null 2>&1; then
+		$NFT insert rule inet ss_spec ss_spec_wan_ac ip daddr @music return 2>/dev/null
 	fi
-	
-	# Shunt/Netflix rules
-	if [ "$SHUNT_PORT" != "0" ]; then
-		for ip in $(cat "${SHUNT_LIST:=/dev/null}" 2>/dev/null); do 
-			$NFT add element inet ss_spec netflix "{ $ip }"
+
+    # Shunt/Netflix rules
+    if [ "$SHUNT_PORT" != "0" ] && [ -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
+		PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
 		case "$SHUNT_PORT" in
 		1)
-			$NFT add rule inet ss_spec ss_spec_wan_ac tcp dport "$PROXY_PORTS" ip daddr @netflix redirect to :"$local_port"
-			;;
+			$NFT insert rule inet ss_spec ss_spec_wan_ac tcp dport { $PORTS } ip daddr @netflix redirect to :"$local_port"
+        	;;
 		*)
-			$NFT add rule inet ss_spec ss_spec_wan_ac tcp dport "$PROXY_PORTS" ip daddr @netflix redirect to :"$SHUNT_PORT"
+			$NFT insert rule inet ss_spec ss_spec_wan_ac tcp dport { $PORTS } ip daddr @netflix redirect to :"$SHUNT_PORT"
 			if [ "$SHUNT_PROXY" = "1" ]; then
-				$NFT add rule inet ss_spec ss_spec_wan_ac tcp dport "$PROXY_PORTS" ip daddr "$SHUNT_IP" redirect to :"$local_port"
+				$NFT insert rule inet ss_spec ss_spec_wan_ac tcp dport { $PORTS } ip daddr "$SHUNT_IP" redirect to :"$local_port"
 			else
-				$NFT add element inet ss_spec whitelist "{ $SHUNT_IP }"
+				[ -n "$SHUNT_IP" ] && $NFT add element inet ss_spec whitelist "{ $SHUNT_IP }" 2>/dev/null
 			fi
 			;;
 		esac
-	fi
-	return 0
+    fi
+    return $?
 }
 
 ipset_iptables() {
@@ -281,7 +317,7 @@ ipset_iptables() {
 	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 >/dev/null 2>&1; then
+	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
@@ -317,39 +353,36 @@ fw_rule() {
 }
 
 fw_rule_nft() {
-	# 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
-
 	# Exclude special local addresses
-	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 0.0.0.0/8 return 2>/dev/null
-	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 10.0.0.0/8 return 2>/dev/null
-	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 127.0.0.0/8 return 2>/dev/null
-	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 169.254.0.0/16 return 2>/dev/null
-	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 172.16.0.0/12 return 2>/dev/null
-	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 192.168.0.0/16 return 2>/dev/null
-	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 224.0.0.0/4 return 2>/dev/null
-	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 240.0.0.0/4 return 2>/dev/null
+	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 0.0.0.0/8 return
+	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 10.0.0.0/8 return
+	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 127.0.0.0/8 return
+	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 169.254.0.0/16 return
+	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 172.16.0.0/12 return
+	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 192.168.0.0/16 return
+	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 224.0.0.0/4 return
+	$NFT add rule inet ss_spec ss_spec_wan_fw ip daddr 240.0.0.0/4 return
 
 	# redirect/translation: when PROXY_PORTS present, redirect those tcp ports to local_port
 	if [ -n "$PROXY_PORTS" ]; then
-		$NFT add rule inet ss_spec ss_spec_wan_fw tcp dport "$PROXY_PORTS" redirect to :"$local_port" 2>/dev/null || {
-			loger 3 "Can't redirect, please check nftables."
-			exit 1
-		}
+		PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
+		if ! $NFT list chain inet ss_spec ss_spec_wan_fw 2>/dev/null | grep -q "tcp dport { $PORTS } redirect to :$local_port"; then
+			if ! $NFT add rule inet ss_spec ss_spec_wan_fw tcp dport { $PORTS } redirect to :"$local_port" 2>/dev/null; then
+				loger 3 "Can't redirect, please check nftables."
+				return 1
+			fi
+		fi
 	else
 		# default: redirect everything except ssh(22)
-		$NFT add rule inet ss_spec ss_spec_wan_fw tcp dport != 22 redirect to :"$local_port" 2>/dev/null || {
-			loger 3 "Can't redirect, please check nftables."
-			exit 1
-		}
+		if ! $NFT list chain inet ss_spec ss_spec_wan_fw 2>/dev/null | grep -q "tcp dport != 22 redirect to :$local_port"; then
+			if ! $NFT add rule inet ss_spec ss_spec_wan_fw tcp dport != 22 redirect to :$local_port 2>/dev/null; then
+				loger 3 "Can't redirect, please check nftables."
+				return 1
+			fi
+		fi
 	fi
 
-	return 0
+	return $?
 }
 
 fw_rule_iptables() {
@@ -379,7 +412,8 @@ ac_rule() {
 }
 
 ac_rule_nft() {
-	local MATCH_SET_CONDITION=""
+	local MATCH_SET=""
+
 	if [ -n "$LAN_AC_IP" ]; then
 		# Create LAN access control set if needed
 		$NFT add set inet ss_spec ss_spec_lan_ac '{ type ipv4_addr; flags interval; }' 2>/dev/null
@@ -389,10 +423,10 @@ ac_rule_nft() {
 
 		case "${LAN_AC_IP%${LAN_AC_IP#?}}" in
 		w | W)
-			MATCH_SET_CONDITION="ip saddr @ss_spec_lan_ac"
+			MATCH_SET="ip saddr @ss_spec_lan_ac"
 			;;
 		b | B)
-			MATCH_SET_CONDITION="ip saddr != @ss_spec_lan_ac"
+			MATCH_SET="ip saddr != @ss_spec_lan_ac"
 			;;
 		*)
 			loger 3 "Bad argument \`-a $LAN_AC_IP\`."
@@ -401,12 +435,26 @@ ac_rule_nft() {
 		esac
 	fi
 
+	# 创建ss_spec_prerouting链
+	if ! $NFT list chain inet ss_spec_prerouting >/dev/null 2>&1; then
+		$NFT add chain inet ss_spec ss_spec_prerouting '{ type filter hook prerouting priority -1; policy accept; }'
+	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 -1; policy accept; }'
+	fi
+	$NFT flush chain inet ss_spec ss_spec_output 2>/dev/null
+
 	# Build a rule in the prerouting hook chain that jumps to business chain with conditions
 	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_CONDITION" ]; then
+		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 tcp $MATCH_SET_CONDITION comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
+			$NFT insert rule inet ss_spec ss_spec_prerouting tcp dport $EXT_ARGS $MATCH_SET comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
+		else
+			$NFT insert rule inet ss_spec ss_spec_prerouting tcp dport $EXT_ARGS comment "\"$TAG\"" jump ss_spec_wan_ac
 		fi
 	else
 		# For each Interface, find its actual ifname and add an iifname-limited prerouting rule
@@ -414,10 +462,10 @@ ac_rule_nft() {
 			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_CONDITION" ]; then
-					$NFT insert rule inet ss_spec ss_spec_prerouting iifname "$IFNAME" tcp $MATCH_SET_CONDITION comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
+				if [ -n "$MATCH_SET" ]; then
+					$NFT insert rule inet ss_spec ss_spec_prerouting iifname "$IFNAME" tcp dport $EXT_ARGS $MATCH_SET comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
 				else
-					$NFT insert rule inet ss_spec ss_spec_prerouting iifname "$IFNAME" tcp comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
+					$NFT insert rule inet ss_spec ss_spec_prerouting iifname "$IFNAME" tcp dport $EXT_ARGS comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
 				fi
 			fi
 		done
@@ -426,8 +474,7 @@ ac_rule_nft() {
 	case "$OUTPUT" in
 	1)
 		# create output hook chain & route output traffic into router chain
-		$NFT add chain inet ss_spec ss_spec_output '{ type nat hook output priority dstnat; }' 2>/dev/null
-		$NFT add rule inet ss_spec ss_spec_output tcp comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
+		$NFT add rule inet ss_spec ss_spec_output tcp dport $EXT_ARGS comment "\"$TAG\"" jump ss_spec_wan_ac 2>/dev/null
 		;;
 	2)
 		# router mode output chain: create ssr_gen_router set & router chain
@@ -438,8 +485,7 @@ ac_rule_nft() {
 		$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 chain inet ss_spec ss_spec_output '{ type nat hook output priority dstnat; }' 2>/dev/null
-		$NFT add rule inet ss_spec ss_spec_output tcp comment "\"$TAG\"" jump ss_spec_router 2>/dev/null
+		$NFT add rule inet ss_spec ss_spec_output tcp dport $EXT_ARGS comment "\"$TAG\"" jump ss_spec_router 2>/dev/null
 		;;
 	esac
 	return 0
@@ -525,7 +571,7 @@ tp_rule_nft() {
 	$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 224.0.0.0/4 return 2>/dev/null
 	$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr 240.0.0.0/4 return 2>/dev/null
 
-	# avoid redirecting to udp server address - 修正变量名
+	# avoid redirecting to udp server address
 	$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport != 53 ip daddr "$server" return 2>/dev/null
 
 	# if server != SERVER add SERVER to whitelist set (so tproxy won't touch it)
@@ -536,7 +582,8 @@ tp_rule_nft() {
 	# access control and tproxy rules
 	$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @bplan return 2>/dev/null
 	if [ -n "$PROXY_PORTS" ]; then
-		$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport "$PROXY_PORTS" ip saddr @fplan tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+		PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
+		$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip saddr @fplan tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 	else
 		$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @fplan tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 	fi
@@ -549,7 +596,8 @@ tp_rule_nft() {
 		$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport 80 drop 2>/dev/null
 		$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @gmlan ip daddr != @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 		if [ -n "$PROXY_PORTS" ]; then
-			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport "$PROXY_PORTS" ip daddr != @ss_spec_wan_ac tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+			PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
+			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip daddr != @ss_spec_wan_ac tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 		else
 			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr != @ss_spec_wan_ac tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 		fi
@@ -558,20 +606,23 @@ tp_rule_nft() {
 		$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip daddr @china return 2>/dev/null
 		$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport 80 drop 2>/dev/null
 		if [ -n "$PROXY_PORTS" ]; then
-			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport "$PROXY_PORTS" ip daddr @gfwlist tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+			PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
+			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip daddr @gfwlist tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 		fi
 		$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @gmlan ip daddr != @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 		;;
 	oversea)
 		if [ -n "$PROXY_PORTS" ]; then
-			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport "$PROXY_PORTS" ip saddr @oversea tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
-			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport "$PROXY_PORTS" ip daddr @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+			PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
+			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip saddr @oversea tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } ip daddr @china tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 		fi
 		$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp ip saddr @gmlan tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 		;;
 	all)
 		if [ -n "$PROXY_PORTS" ]; then
-			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport "$PROXY_PORTS" tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
+			PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
+			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp dport { $PORTS } tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 		else
 			$NFT add rule ip ss_spec_mangle ss_spec_tproxy udp tproxy to :"$LOCAL_PORT" meta mark set 0x01 2>/dev/null
 		fi
@@ -579,9 +630,10 @@ tp_rule_nft() {
 	esac
 
 	# insert jump from ip prerouting to our tproxy chain
-	$NFT add rule ip ss_spec_mangle prerouting udp comment "\"$TAG\"" jump ss_spec_tproxy 2>/dev/null
+	PORTS=$(echo "$PROXY_PORTS" | sed 's/-m multiport --dports //')
+	$NFT add rule ip ss_spec_mangle prerouting udp dport { $PORTS } comment "\"$TAG\"" jump ss_spec_tproxy 2>/dev/null
 
-	return 0
+	return $?
 }
 
 tp_rule_iptables() {
@@ -682,9 +734,9 @@ gen_include_nft() {
 	[ -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
+		nft add table inet ss_spec 2>/dev/null
+		nft add table ip ss_spec 2>/dev/null
+		nft add table ip ss_spec_mangle 2>/dev/null
 
 		# Restore shadowsocks nftables rules
 		nft list ruleset | awk '/table (inet|ip) ss_spec/{flag=1} flag'
@@ -815,10 +867,30 @@ case "$TPROXY" in
 	;;
 esac
 
-if flush_r && fw_rule && ipset_r && ac_rule && tp_rule && gen_include; then
-	loger 5 "Rules applied successfully"
-	exit 0
+# 首先检查nftables是否正常工作
+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
+	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"
+		exit 0
+	else
+		loger 3 "NFTables setup failed!"
+		exit 1
+	fi
 else
-	loger 3 "Start 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

+ 19 - 4
luci-app-ssr-plus/root/usr/share/shadowsocksr/chinaipset.sh

@@ -1,7 +1,22 @@
 #!/bin/sh
 [ -f "$1" ] && china_ip=$1
-ipset -! flush china 2>/dev/null
-ipset -! -R <<-EOF || exit 1
-	create china hash:net
-	$(cat ${china_ip:=/etc/ssrplus/china_ssr.txt} | sed -e "s/^/add china /")
+
+if command -v nft >/dev/null 2>&1; then
+    # 确保表和集合存在
+    nft add table inet ss_spec 2>/dev/null
+    nft add set inet ss_spec china '{ type ipv4_addr; flags interval; auto-merge; }' 2>/dev/null
+    nft flush set inet ss_spec china 2>/dev/null
+
+    # 批量导入
+    if [ -f "${china_ip:=/etc/ssrplus/china_ssr.txt}" ]; then
+        echo "批量导入中国IP列表..."
+        nft add element inet ss_spec china { $(tr '\n' ',' < "${china_ip}" | sed 's/,$//') } 2>/dev/null
+        echo "中国IP集合导入完成"
+    fi
+else
+    ipset -! flush china 2>/dev/null
+    ipset -! -R <<-EOF || exit 1
+        create china hash:net
+        $(cat ${china_ip:=/etc/ssrplus/china_ssr.txt} | sed -e "s/^/add china /")
 EOF
+fi

+ 26 - 4
luci-app-ssr-plus/root/usr/share/shadowsocksr/gfw2ipset.sh

@@ -7,7 +7,12 @@ netflix() {
 		for line in $(cat /etc/ssrplus/netflix.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done
 		for line in $(cat /etc/ssrplus/netflix.list); do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done
 	fi
-	cat /etc/ssrplus/netflix.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$1\nipset=\/&\/netflix/" >$TMP_DNSMASQ_PATH/netflix_forward.conf
+	if command -v nft >/dev/null 2>&1; then
+		# 移除 ipset
+		cat /etc/ssrplus/netflix.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$1/" >$TMP_DNSMASQ_PATH/netflix_forward.conf
+	else
+		cat /etc/ssrplus/netflix.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$1\nipset=\/&\/netflix/" >$TMP_DNSMASQ_PATH/netflix_forward.conf
+	fi
 }
 mkdir -p $TMP_DNSMASQ_PATH
 if [ "$(uci_get_by_type global run_mode router)" == "oversea" ]; then
@@ -16,6 +21,16 @@ else
 	cp -rf /etc/ssrplus/gfw_list.conf $TMP_DNSMASQ_PATH/
 	cp -rf /etc/ssrplus/gfw_base.conf $TMP_DNSMASQ_PATH/
 fi
+
+if command -v nft >/dev/null 2>&1; then
+    # 移除 ipset 指令
+    for conf_file in gfw_base.conf gfw_list.conf; do
+        if [ -f "$TMP_DNSMASQ_PATH/$conf_file" ]; then
+            sed -i '/ipset=/d' "$TMP_DNSMASQ_PATH/$conf_file"
+        fi
+    done
+fi
+
 if [ "$(uci_get_by_type global netflix_enable 0)" == "1" ]; then
 	# 只有开启 NetFlix分流 才需要取值
 	SHUNT_SERVER=$(uci_get_by_type global netflix_server nil)
@@ -34,6 +49,7 @@ $(uci_get_by_type global global_server nil) | $switch_server | same)
 	netflix $tmp_shunt_dns_port
 	;;
 esac
+
 # 此处使用while方式读取 防止 /etc/ssrplus/ 目录下的 black.list white.list deny.list 等2个或多个文件一行中存在空格 比如:# abc.com 而丢失:server
 while read line; do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done < /etc/ssrplus/black.list
 while read line; do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done < /etc/ssrplus/black.list
@@ -41,10 +57,17 @@ while read line; do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done < /e
 while read line; do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done < /etc/ssrplus/white.list
 while read line; do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_list.conf; done < /etc/ssrplus/deny.list
 while read line; do sed -i "/$line/d" $TMP_DNSMASQ_PATH/gfw_base.conf; done < /etc/ssrplus/deny.list
+
 # 此处直接使用 cat 因为有 sed '/#/d' 删除了 数据
-cat /etc/ssrplus/black.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$dns_port\nipset=\/&\/blacklist/" >$TMP_DNSMASQ_PATH/blacklist_forward.conf
-cat /etc/ssrplus/white.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1\nipset=\/&\/whitelist/" >$TMP_DNSMASQ_PATH/whitelist_forward.conf
+if command -v nft >/dev/null 2>&1; then
+	cat /etc/ssrplus/black.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$dns_port/" >$TMP_DNSMASQ_PATH/blacklist_forward.conf
+	cat /etc/ssrplus/white.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1/" >$TMP_DNSMASQ_PATH/whitelist_forward.conf
+else
+	cat /etc/ssrplus/black.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1#$dns_port\nipset=\/&\/blacklist/" >$TMP_DNSMASQ_PATH/blacklist_forward.conf
+	cat /etc/ssrplus/white.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/server=\/&\/127.0.0.1\nipset=\/&\/whitelist/" >$TMP_DNSMASQ_PATH/whitelist_forward.conf
+fi
 cat /etc/ssrplus/deny.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/address=\/&\//" >$TMP_DNSMASQ_PATH/denylist.conf
+
 if [ "$(uci_get_by_type global adblock 0)" == "1" ]; then
 	cp -f /etc/ssrplus/ad.conf $TMP_DNSMASQ_PATH/
 	if [ -f "$TMP_DNSMASQ_PATH/ad.conf" ]; then
@@ -56,4 +79,3 @@ if [ "$(uci_get_by_type global adblock 0)" == "1" ]; then
 else
 	rm -f $TMP_DNSMASQ_PATH/ad.conf
 fi
-