clean commit
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
#!/bin/sh
|
||||
|
||||
# TPROXY 透明代理脚本 for OpenWrt with NFTables
|
||||
# 功能: IPv4/v6, DNS劫持(可选), IP分流, 本机代理
|
||||
# 版本: 1.1.2 (2025-06-16)
|
||||
# 维护: su
|
||||
|
||||
# --- [ 用户配置区: 请根据您的环境修改此区域 ] ---
|
||||
|
||||
# --- 功能开关 ---
|
||||
# 是否开启DNS劫持功能. "true":开启, "false":关闭
|
||||
ENABLE_DNS_HIJACKING="false"
|
||||
|
||||
# --- 核心设置 ---
|
||||
# TPROXY 代理服务监听的 TCP/UDP 端口
|
||||
PROXY_PORT=7893
|
||||
# Adguard 或其他 DNS 软件的监听端口
|
||||
ADGUARDHOME_DNS_PORT=553
|
||||
# 代理软件的 DNS 监听端口 (仅在开启劫持时有效)
|
||||
PROXY_DNS_PORT=1053
|
||||
|
||||
# --- 系统集成设置 ---
|
||||
# LAN 接口名称
|
||||
LAN_IF="br-lan"
|
||||
# 运行代理软件的用户名 (用于豁免代理自身流量,防止死循环)
|
||||
PROXY_USER="root"
|
||||
# 存放 .nft 自定义规则文件的目录
|
||||
NFT_CUSTOM_PATH="/etc/nikki/nftables"
|
||||
# 您正在使用的 nftables 表名
|
||||
NFT_TABLE="nikki"
|
||||
|
||||
# --- IP 集合名称 (需与 .nft 文件中定义的名称完全一致) ---
|
||||
NFT_SET_RESERVED_V4="reserved_ip"
|
||||
NFT_SET_RESERVED_V6="reserved_ip6"
|
||||
NFT_SET_BYPASS_V4="china_ip"
|
||||
NFT_SET_BYPASS_V6="china_ip6"
|
||||
|
||||
# --- 高级设置 ---
|
||||
# 防火墙标记 (fwmark) 和策略路由表 ID
|
||||
PROXY_MARK=0xff
|
||||
ROUTE_TABLE=100
|
||||
|
||||
# 锁文件路径 (防止重复运行)
|
||||
LOCK_FILE="/var/run/tproxy.lock"
|
||||
|
||||
|
||||
# --- [ 核心逻辑区: 通常无需修改 ] ---
|
||||
|
||||
check_root() {
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
printf "错误: 此脚本需要以 root 权限运行。\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查是否已有实例运行
|
||||
check_lock() {
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
# 检查服务是否真正在运行(通过防火墙表存在性判断)
|
||||
if nft list table inet "$NFT_TABLE" >/dev/null 2>&1; then
|
||||
printf "错误: TPROXY 服务已在运行中\n" >&2
|
||||
printf "如需重新启动,请使用: %s restart\n" "$0" >&2
|
||||
printf "如需强制启动,请先停止服务: %s stop\n" "$0" >&2
|
||||
exit 1
|
||||
else
|
||||
# 清理失效的锁文件
|
||||
printf " -> 清理失效的锁文件...\n"
|
||||
rm -f "$LOCK_FILE"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建锁文件
|
||||
create_lock() {
|
||||
# 确保锁文件目录存在
|
||||
local lock_dir=$(dirname "$LOCK_FILE")
|
||||
[ ! -d "$lock_dir" ] && mkdir -p "$lock_dir" 2>/dev/null
|
||||
|
||||
# 写入启动时间戳而不是PID,因为脚本执行完后进程会结束
|
||||
date '+%Y-%m-%d %H:%M:%S' > "$LOCK_FILE"
|
||||
if [ $? -ne 0 ]; then
|
||||
printf "警告: 无法创建锁文件 %s\n" "$LOCK_FILE" >&2
|
||||
else
|
||||
printf " -> 锁文件已创建: %s\n" "$LOCK_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理锁文件
|
||||
remove_lock() {
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
rm -f "$LOCK_FILE"
|
||||
printf " -> 锁文件已清理: %s\n" "$LOCK_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
load_user_nft_files() {
|
||||
if [ ! -d "$NFT_CUSTOM_PATH" ]; then
|
||||
printf "错误: 找不到目录 %s\n" "$NFT_CUSTOM_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf " -> 正在从 %s 加载自定义规则...\n" "$NFT_CUSTOM_PATH"
|
||||
|
||||
# 检查是否存在 .nft 文件
|
||||
file_count=$(find "$NFT_CUSTOM_PATH" -name "*.nft" -type f | wc -l)
|
||||
if [ "$file_count" -eq 0 ]; then
|
||||
printf "警告: 目录 %s 中没有找到 .nft 文件\n" "$NFT_CUSTOM_PATH" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
for file in "$NFT_CUSTOM_PATH"/*.nft; do
|
||||
if [ -f "$file" ]; then
|
||||
printf " - 加载 %s\n" "$(basename "$file")"
|
||||
if ! nft -f "$file"; then
|
||||
printf "错误: 加载 %s 时发生错误!\n" "$file" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
do_start() {
|
||||
printf "▶️ 启动透明代理服务...\n"
|
||||
check_lock
|
||||
|
||||
# 先清理可能存在的残留规则,但不删除锁文件
|
||||
printf " -> 清理可能存在的残留规则...\n"
|
||||
ip -4 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true
|
||||
ip -6 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true
|
||||
ip -4 route flush table $ROUTE_TABLE 2>/dev/null
|
||||
ip -6 route flush table $ROUTE_TABLE 2>/dev/null
|
||||
nft delete table inet $NFT_TABLE 2>/dev/null || true
|
||||
|
||||
# 创建锁文件
|
||||
create_lock
|
||||
|
||||
# 设置异常退出时自动清理锁文件
|
||||
trap 'remove_lock; exit' INT TERM
|
||||
|
||||
# 1. 设置策略路由
|
||||
printf " -> 正在设置策略路由...\n"
|
||||
ip -4 rule add fwmark $PROXY_MARK lookup $ROUTE_TABLE
|
||||
ip -6 rule add fwmark $PROXY_MARK lookup $ROUTE_TABLE
|
||||
ip -4 route add local default dev lo table $ROUTE_TABLE
|
||||
ip -6 route add local default dev lo table $ROUTE_TABLE
|
||||
|
||||
# 2. 加载并配置 nftables
|
||||
printf " -> 正在加载并配置 nftables...\n"
|
||||
|
||||
nft add table inet $NFT_TABLE
|
||||
if [ $? -ne 0 ]; then
|
||||
printf "错误: 创建 nftables 表 '%s' 失败。请检查是否已有同名表或nftables服务是否正常。\n" "$NFT_TABLE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 加载 IP Set 文件
|
||||
load_user_nft_files
|
||||
|
||||
nft add chain inet $NFT_TABLE mangle_prerouting { type filter hook prerouting priority -150\; }; nft flush chain inet $NFT_TABLE mangle_prerouting
|
||||
nft add chain inet $NFT_TABLE mangle_output { type route hook output priority -150\; }; nft flush chain inet $NFT_TABLE mangle_output
|
||||
for chain in tproxy_lan_v4 tproxy_lan_v6 tproxy_loc_v4 tproxy_loc_v6; do
|
||||
nft add chain inet $NFT_TABLE "$chain"; nft flush chain inet $NFT_TABLE "$chain"
|
||||
done
|
||||
|
||||
# 3. 应用规则
|
||||
printf " -> 正在应用核心防火墙规则...\n"
|
||||
# 规则 A: DNS 劫持 (可选)
|
||||
if [ "$ENABLE_DNS_HIJACKING" = "true" ]; then
|
||||
printf " - DNS 劫持已开启。\n"
|
||||
nft add chain inet $NFT_TABLE dns_redirect { type nat hook prerouting priority -100\; }; nft flush chain inet $NFT_TABLE dns_redirect
|
||||
nft add rule inet $NFT_TABLE dns_redirect iifname $LAN_IF meta l4proto {tcp, udp} th dport 53 redirect to :$PROXY_DNS_PORT
|
||||
else
|
||||
printf " - DNS 劫持已关闭。\n"
|
||||
fi
|
||||
|
||||
# 规则 B: 处理本机发出流量 (在 OUTPUT 链中仅作标记)
|
||||
nft add rule inet $NFT_TABLE mangle_output meta nfproto ipv4 jump tproxy_loc_v4
|
||||
nft add rule inet $NFT_TABLE mangle_output meta nfproto ipv6 jump tproxy_loc_v6
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v4 skuid $PROXY_USER return # 关键: 豁免代理程序自身,防止死循环
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v4 ip daddr @$NFT_SET_RESERVED_V4 return
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v4 ip daddr @$NFT_SET_BYPASS_V4 return
|
||||
# 不处理 DNS 流量
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v4 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v4 meta l4proto {tcp, udp} meta mark set $PROXY_MARK
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v6 skuid $PROXY_USER return
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v6 ip6 daddr @$NFT_SET_RESERVED_V6 return
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v6 ip6 daddr @$NFT_SET_BYPASS_V6 return
|
||||
# 不处理 DNS 流量
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v6 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return
|
||||
nft add rule inet $NFT_TABLE tproxy_loc_v6 meta l4proto {tcp, udp} meta mark set $PROXY_MARK
|
||||
|
||||
# 规则 C: 处理局域网及本机回环流量 (在 PREROUTING 链中执行 TPROXY)
|
||||
nft add rule inet $NFT_TABLE mangle_prerouting iifname $LAN_IF meta nfproto ipv4 meta l4proto {tcp, udp} jump tproxy_lan_v4
|
||||
nft add rule inet $NFT_TABLE mangle_prerouting iifname $LAN_IF meta nfproto ipv6 meta l4proto {tcp, udp} jump tproxy_lan_v6
|
||||
nft add rule inet $NFT_TABLE mangle_prerouting iifname "lo" meta nfproto ipv4 meta l4proto {tcp, udp} meta mark $PROXY_MARK jump tproxy_lan_v4
|
||||
nft add rule inet $NFT_TABLE mangle_prerouting iifname "lo" meta nfproto ipv6 meta l4proto {tcp, udp} meta mark $PROXY_MARK jump tproxy_lan_v6
|
||||
|
||||
nft add rule inet $NFT_TABLE tproxy_lan_v4 ip daddr @$NFT_SET_RESERVED_V4 return
|
||||
nft add rule inet $NFT_TABLE tproxy_lan_v4 ip daddr @$NFT_SET_BYPASS_V4 return
|
||||
# 不处理 DNS 流量
|
||||
nft add rule inet $NFT_TABLE tproxy_lan_v4 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return
|
||||
nft add rule inet $NFT_TABLE tproxy_lan_v4 meta l4proto {tcp, udp} meta mark set $PROXY_MARK tproxy to :$PROXY_PORT
|
||||
nft add rule inet $NFT_TABLE tproxy_lan_v6 ip6 daddr @$NFT_SET_RESERVED_V6 return
|
||||
nft add rule inet $NFT_TABLE tproxy_lan_v6 ip6 daddr @$NFT_SET_BYPASS_V6 return
|
||||
# 不处理 DNS 流量
|
||||
nft add rule inet $NFT_TABLE tproxy_lan_v6 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return
|
||||
nft add rule inet $NFT_TABLE tproxy_lan_v6 meta l4proto {tcp, udp} meta mark set $PROXY_MARK tproxy to :$PROXY_PORT
|
||||
|
||||
printf "✅ 透明代理服务已成功启动。\n"
|
||||
|
||||
# 清除陷阱,正常启动时保持锁文件
|
||||
trap - INT TERM
|
||||
}
|
||||
|
||||
do_stop() {
|
||||
printf "⏹️ 停止透明代理服务...\n"
|
||||
|
||||
# 清理策略路由
|
||||
ip -4 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true
|
||||
ip -6 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true
|
||||
# 清理自定义路由表内容
|
||||
ip -4 route flush table $ROUTE_TABLE 2>/dev/null
|
||||
ip -6 route flush table $ROUTE_TABLE 2>/dev/null
|
||||
# 删除 nft 路由表
|
||||
printf " -> 正在清理防火墙表 '%s'...\n" "$NFT_TABLE"
|
||||
nft delete table inet $NFT_TABLE 2>/dev/null || true
|
||||
|
||||
# 清理锁文件
|
||||
remove_lock
|
||||
|
||||
printf "🛑 透明代理服务已完全停止。\n"
|
||||
}
|
||||
|
||||
do_status() {
|
||||
ICON_OK="✅"
|
||||
ICON_FAIL="❌"
|
||||
ICON_INFO="ℹ️"
|
||||
ICON_OFF="⚪️"
|
||||
|
||||
printf "--- TPROXY 服务状态检查 ---\n"
|
||||
|
||||
# 检查锁文件状态
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
local start_time=$(cat "$LOCK_FILE" 2>/dev/null)
|
||||
if [ -n "$start_time" ]; then
|
||||
printf "锁文件状态: %s 存在 (启动时间: %s)\n" "$ICON_OK" "$start_time"
|
||||
else
|
||||
printf "锁文件状态: %s 存在但无效\n" "$ICON_FAIL"
|
||||
fi
|
||||
else
|
||||
printf "锁文件状态: %s 不存在\n" "$ICON_OFF"
|
||||
fi
|
||||
|
||||
if ! nft list table inet "$NFT_TABLE" >/dev/null 2>&1; then
|
||||
printf "总状态: %s 未激活 (找不到防火墙表 '%s')\n" "$ICON_OFF" "$NFT_TABLE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ip rule show | grep -q "fwmark $PROXY_MARK lookup $ROUTE_TABLE"; then
|
||||
printf "总状态: %s 激活\n" "$ICON_OK"
|
||||
else
|
||||
printf "总状态: %s 部分激活 (防火墙规则存在,但策略路由缺失)\n" "$ICON_FAIL"
|
||||
fi
|
||||
|
||||
printf "\n[+] IP 路由与规则:\n"
|
||||
if ip rule show | grep -q "fwmark $PROXY_MARK"; then
|
||||
printf " %s 策略路由:\n" "$ICON_INFO"
|
||||
ip rule show | grep "fwmark $PROXY_MARK" | sed 's/^/ -> /'
|
||||
else
|
||||
printf " %s 策略路由: %s 缺失!\n" "$ICON_FAIL" "$ICON_FAIL"
|
||||
fi
|
||||
printf " %s 自定义路由表 (ID: %s):\n" "$ICON_INFO" "$ROUTE_TABLE"
|
||||
ip route show table "$ROUTE_TABLE" | sed 's/^/ -> /'
|
||||
|
||||
printf "\n[+] NFTables 规则状态 (主表: %s)\n" "$NFT_TABLE"
|
||||
|
||||
if [ "$ENABLE_DNS_HIJACKING" = "true" ]; then
|
||||
if nft list chain inet "$NFT_TABLE" "dns_redirect" >/dev/null 2>&1; then
|
||||
printf " %s DNS 劫持: 已激活 (转发至端口 %s)\n" "$ICON_OK" "$PROXY_DNS_PORT"
|
||||
else
|
||||
printf " %s DNS 劫持: 激活失败\n" "$ICON_FAIL"
|
||||
fi
|
||||
else
|
||||
printf " %s DNS 劫持: 已禁用\n" "$ICON_OFF"
|
||||
fi
|
||||
|
||||
if nft list chain inet "$NFT_TABLE" "tproxy_lan_v4" >/dev/null 2>&1; then
|
||||
printf " %s TPROXY 代理: 已激活 (监听端口 %s)\n" "$ICON_OK" "$PROXY_PORT"
|
||||
else
|
||||
printf " %s TPROXY 代理: 激活失败\n" "$ICON_FAIL"
|
||||
fi
|
||||
|
||||
printf " %s IP 列表加载状态:\n" "$ICON_INFO"
|
||||
if nft list set inet "$NFT_TABLE" "$NFT_SET_RESERVED_V4" >/dev/null 2>&1; then
|
||||
printf " - %s: %s\n" "$NFT_SET_RESERVED_V4" "$ICON_OK 本地 IPv4 已直连"
|
||||
else
|
||||
printf " - %s: %s\n" "$NFT_SET_RESERVED_V4" "$ICON_FAIL 未加载"
|
||||
fi
|
||||
if nft list set inet "$NFT_TABLE" "$NFT_SET_RESERVED_V6" >/dev/null 2>&1; then
|
||||
printf " - %s: %s\n" "$NFT_SET_RESERVED_V6" "$ICON_OK 本地 IPv6 已直连"
|
||||
else
|
||||
printf " - %s: %s\n" "$NFT_SET_RESERVED_V6" "$ICON_FAIL 未加载"
|
||||
fi
|
||||
if nft list set inet "$NFT_TABLE" "$NFT_SET_BYPASS_V4" >/dev/null 2>&1; then
|
||||
printf " - %s: %s\n" "$NFT_SET_BYPASS_V4" "$ICON_OK 国内 IPv4 已直连"
|
||||
else
|
||||
printf " - %s: %s\n" "$NFT_SET_BYPASS_V4" "$ICON_FAIL 未加载"
|
||||
fi
|
||||
if nft list set inet "$NFT_TABLE" "$NFT_SET_BYPASS_V6" >/dev/null 2>&1; then
|
||||
printf " - %s: %s\n" "$NFT_SET_BYPASS_V6" "$ICON_OK 国内 IPv6 已直连"
|
||||
else
|
||||
printf " - %s: %s\n" "$NFT_SET_BYPASS_V6" "$ICON_FAIL 未加载"
|
||||
fi
|
||||
|
||||
printf -- "--------------------------------\n"
|
||||
}
|
||||
|
||||
# --- 脚本主入口 ---
|
||||
check_root
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
do_start
|
||||
;;
|
||||
stop)
|
||||
do_stop
|
||||
;;
|
||||
restart)
|
||||
printf "🔄 重启透明代理服务...\n"
|
||||
do_stop
|
||||
sleep 1
|
||||
do_start
|
||||
;;
|
||||
status)
|
||||
do_status
|
||||
;;
|
||||
*)
|
||||
printf "用法: %s {start|stop|restart|status}\n" "$0"
|
||||
printf " start - 启动透明代理服务\n"
|
||||
printf " stop - 停止透明代理服务\n"
|
||||
printf " restart - 重启透明代理服务\n"
|
||||
printf " status - 查看服务状态\n"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user