clean commit
This commit is contained in:
@@ -0,0 +1,89 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# 🚀 脚本工具集
|
||||||
|
|
||||||
|
常用 Linux/路由器/服务器 Bash 工具脚本,适合自动化、性能优化、代理配置等场景。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ 工具与一键命令
|
||||||
|
|
||||||
|
### 1. tcp.sh —— TCP 网络优化
|
||||||
|
优化:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/tcp.sh") 1
|
||||||
|
```
|
||||||
|
|
||||||
|
代理加速:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/tcp.sh") 1
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. tproxy.sh —— OpenWrt/路由透明代理自动配置
|
||||||
|
一键运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/tproxy.sh")
|
||||||
|
```
|
||||||
|
|
||||||
|
代理加速:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/tproxy.sh")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. mihomo-install.sh —— Mihomo 加速核心一键安装
|
||||||
|
一键安装:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/mihomo-install.sh")
|
||||||
|
```
|
||||||
|
|
||||||
|
代理加速:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/mihomo-install.sh")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. mssh.sh —— 多主机 SSH 管理与端口转发
|
||||||
|
一键运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/mssh.sh")
|
||||||
|
```
|
||||||
|
|
||||||
|
代理加速:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/mssh.sh")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. curl-cc.sh —— 模拟浏览器 UA 自动访问签到
|
||||||
|
一键运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/curl-cc.sh")
|
||||||
|
```
|
||||||
|
|
||||||
|
代理加速:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/curl-cc.sh")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 建议所有脚本以 root 权限运行,详细参数和说明请阅读各脚本头部注释。
|
||||||
|
|
||||||
|
> 建议所有脚本以 root 权限运行,详细参数和说明请阅读各脚本头部注释。
|
||||||
+345
@@ -0,0 +1,345 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# curl-cc.sh
|
||||||
|
# 自动访问指定网站,模拟真实浏览器行为
|
||||||
|
# 用法: ./curl-cc.sh [-v|--verbose] [-h|--help]
|
||||||
|
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# 配置选项
|
||||||
|
VERBOSE=0
|
||||||
|
SCRIPT_NAME=$(basename "$0")
|
||||||
|
|
||||||
|
# 显示帮助信息
|
||||||
|
show_help() {
|
||||||
|
cat << EOF
|
||||||
|
用法: $SCRIPT_NAME [选项]
|
||||||
|
|
||||||
|
描述:
|
||||||
|
自动访问指定网站,模拟真实浏览器行为进行签到或访问
|
||||||
|
|
||||||
|
选项:
|
||||||
|
-v, --verbose 显示详细输出信息
|
||||||
|
-h, --help 显示此帮助信息
|
||||||
|
|
||||||
|
示例:
|
||||||
|
$SCRIPT_NAME # 静默模式运行
|
||||||
|
$SCRIPT_NAME -v # 详细模式运行
|
||||||
|
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
if [ "$VERBOSE" -eq 1 ]; then
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# 解析命令行参数
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-v|--verbose)
|
||||||
|
VERBOSE=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "未知选项: $1"
|
||||||
|
echo "使用 -h 或 --help 查看帮助"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
VISIT_PATH="/short_code/visit_short_code_proc_ajax.php?ref="
|
||||||
|
CLICK_PATH="/short_code/click_short_code_proc_ajax.php?c=c"
|
||||||
|
|
||||||
|
# Referer 来源列表(最常用的社交媒体和平台)
|
||||||
|
REFERERS=(
|
||||||
|
# 主流社交媒体
|
||||||
|
"https://www.facebook.com/"
|
||||||
|
"https://twitter.com/"
|
||||||
|
"https://www.instagram.com/"
|
||||||
|
"https://www.tiktok.com/"
|
||||||
|
"https://www.reddit.com/"
|
||||||
|
|
||||||
|
# 视频平台
|
||||||
|
"https://www.youtube.com/"
|
||||||
|
|
||||||
|
# 搜索引擎
|
||||||
|
"https://www.google.com/"
|
||||||
|
|
||||||
|
# 通讯工具
|
||||||
|
"https://t.me/"
|
||||||
|
)
|
||||||
|
|
||||||
|
# User-Agent 列表(2025年最新版本,最常用设备)
|
||||||
|
UAS=(
|
||||||
|
# === Android 设备 ===
|
||||||
|
# Samsung Galaxy (市场占有率最高)
|
||||||
|
"Mozilla/5.0 (Linux; Android 14; SM-S24 Ultra) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36"
|
||||||
|
"Mozilla/5.0 (Linux; Android 14; SM-S23) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36"
|
||||||
|
|
||||||
|
# Google Pixel
|
||||||
|
"Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.58 Mobile Safari/537.36"
|
||||||
|
|
||||||
|
# === iOS 设备 ===
|
||||||
|
# iPhone - Safari (最常用)
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Mobile/15E148 Safari/604.1"
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.7 Mobile/15E148 Safari/604.1"
|
||||||
|
|
||||||
|
# iPhone - Chrome
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/130.0.6723.90 Mobile/15E148 Safari/604.1"
|
||||||
|
|
||||||
|
# === Windows 桌面 ===
|
||||||
|
# Chrome (最常用)
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
|
||||||
|
|
||||||
|
# Edge
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.2849.68"
|
||||||
|
|
||||||
|
# === macOS 桌面 ===
|
||||||
|
# Safari
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15"
|
||||||
|
|
||||||
|
# Chrome on Mac
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
|
||||||
|
)
|
||||||
|
|
||||||
|
hosts=( "org.cc.cc" "sky.cc.cc" )
|
||||||
|
|
||||||
|
# 生成随机 ref 参数(30% 概率为空,70% 概率为 6-12 位随机字符串)
|
||||||
|
rand_ref() {
|
||||||
|
if [ $((RANDOM % 10)) -lt 3 ]; then
|
||||||
|
printf ""
|
||||||
|
else
|
||||||
|
local len=$((6 + RANDOM % 7))
|
||||||
|
# 使用更便携的随机字符串生成方式
|
||||||
|
if [ -r /dev/urandom ]; then
|
||||||
|
head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c "$len"
|
||||||
|
else
|
||||||
|
# 备用方案(如果 /dev/urandom 不可用)
|
||||||
|
< /dev/urandom tr -dc 'a-zA-Z0-9' | head -c "$len"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 随机选择一个 User-Agent
|
||||||
|
choose_ua() {
|
||||||
|
echo "${UAS[$((RANDOM % ${#UAS[@]}))]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 随机选择一个 Referer
|
||||||
|
choose_referer() {
|
||||||
|
echo "${REFERERS[$((RANDOM % ${#REFERERS[@]}))]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 根据地理位置获取 Accept-Language
|
||||||
|
get_accept_language() {
|
||||||
|
local country_code=""
|
||||||
|
|
||||||
|
# 尝试从 ipinfo.io 获取国家代码(设置超时 3 秒)
|
||||||
|
if command -v curl &> /dev/null; then
|
||||||
|
country_code=$(curl -s --max-time 3 https://ipinfo.io/country 2>/dev/null | tr -d '[:space:]')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 根据国家代码返回对应的 Accept-Language
|
||||||
|
case "$country_code" in
|
||||||
|
CN|HK|TW|MO) # 中国、香港、台湾、澳门
|
||||||
|
echo "zh-CN,zh;q=0.9,en;q=0.8"
|
||||||
|
;;
|
||||||
|
JP) # 日本
|
||||||
|
echo "ja,en;q=0.9"
|
||||||
|
;;
|
||||||
|
KR) # 韩国
|
||||||
|
echo "ko,en;q=0.9"
|
||||||
|
;;
|
||||||
|
ES|MX|AR|CO|CL|PE|VE) # 西班牙语国家
|
||||||
|
echo "es,en;q=0.9"
|
||||||
|
;;
|
||||||
|
FR|BE|CH) # 法语国家
|
||||||
|
echo "fr,en;q=0.9"
|
||||||
|
;;
|
||||||
|
DE|AT) # 德语国家
|
||||||
|
echo "de,en;q=0.9"
|
||||||
|
;;
|
||||||
|
RU|BY|KZ|UA) # 俄语国家
|
||||||
|
echo "ru,en;q=0.9"
|
||||||
|
;;
|
||||||
|
PT|BR) # 葡萄牙语国家
|
||||||
|
echo "pt,en;q=0.9"
|
||||||
|
;;
|
||||||
|
IT) # 意大利
|
||||||
|
echo "it,en;q=0.9"
|
||||||
|
;;
|
||||||
|
TR) # 土耳其
|
||||||
|
echo "tr,en;q=0.9"
|
||||||
|
;;
|
||||||
|
SA|AE|EG|IQ|JO) # 阿拉伯语国家
|
||||||
|
echo "ar,en;q=0.9"
|
||||||
|
;;
|
||||||
|
IN) # 印度
|
||||||
|
echo "en-IN,hi;q=0.9,en;q=0.8"
|
||||||
|
;;
|
||||||
|
*) # 默认英文(包括 US, GB, CA, AU, SG 等)
|
||||||
|
echo "en-US,en;q=0.9"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行访问请求
|
||||||
|
make_visit_request() {
|
||||||
|
local host="$1"
|
||||||
|
local ref_url="$2" # URL 参数中的 ref(referral site URL)
|
||||||
|
local ua="$3"
|
||||||
|
local referer="https://${host}/" # HTTP Header 中的 Referer 固定为自身
|
||||||
|
|
||||||
|
# 构建完整 URL,ref 参数为 referral site 的完整 URL
|
||||||
|
local url="https://${host}${VISIT_PATH}${ref_url}"
|
||||||
|
|
||||||
|
log_info "正在访问: ${host}"
|
||||||
|
log_info "URL: ${url}"
|
||||||
|
log_info "Referer Header: ${referer}"
|
||||||
|
log_info "User-Agent: ${ua}"
|
||||||
|
|
||||||
|
# 使用 || true 确保即使 curl 失败也不会导致脚本退出
|
||||||
|
local curl_exit_code=0
|
||||||
|
curl -s -S -X GET "$url" \
|
||||||
|
-H "Host: ${host}" \
|
||||||
|
-H "Connection: keep-alive" \
|
||||||
|
-H "Accept: */*" \
|
||||||
|
-H "X-Requested-With: XMLHttpRequest" \
|
||||||
|
-H "User-Agent: ${ua}" \
|
||||||
|
-H "Content-Type: charset=utf-8" \
|
||||||
|
-H "Referer: ${referer}" \
|
||||||
|
-H "Accept-Encoding: gzip, deflate" \
|
||||||
|
-H "Accept-Language: ${ACCEPT_LANG}" \
|
||||||
|
--compressed -o /dev/null --max-time ${MAX_TIME} || curl_exit_code=$?
|
||||||
|
|
||||||
|
if [ $curl_exit_code -eq 0 ]; then
|
||||||
|
log_info "✓ ${host} 访问成功"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "✗ ${host} 访问失败 (curl 退出码: ${curl_exit_code})"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行电话点击请求
|
||||||
|
make_click_request() {
|
||||||
|
local host="$1"
|
||||||
|
local ua="$2"
|
||||||
|
local referer="https://${host}/" # HTTP Header 中的 Referer 固定为自身
|
||||||
|
|
||||||
|
# 构建完整 URL
|
||||||
|
local url="https://${host}${CLICK_PATH}"
|
||||||
|
|
||||||
|
log_info " ├─ 电话点击统计"
|
||||||
|
log_info " ├─ URL: ${url}"
|
||||||
|
|
||||||
|
# 使用 || true 确保即使 curl 失败也不会导致脚本退出
|
||||||
|
local curl_exit_code=0
|
||||||
|
curl -s -S -X GET "$url" \
|
||||||
|
-H "Host: ${host}" \
|
||||||
|
-H "Connection: keep-alive" \
|
||||||
|
-H "Accept: */*" \
|
||||||
|
-H "X-Requested-With: XMLHttpRequest" \
|
||||||
|
-H "User-Agent: ${ua}" \
|
||||||
|
-H "Content-Type: charset=utf-8" \
|
||||||
|
-H "Referer: ${referer}" \
|
||||||
|
-H "Accept-Encoding: gzip, deflate" \
|
||||||
|
-H "Accept-Language: ${ACCEPT_LANG}" \
|
||||||
|
--compressed -o /dev/null --max-time ${MAX_TIME} || curl_exit_code=$?
|
||||||
|
|
||||||
|
if [ $curl_exit_code -eq 0 ]; then
|
||||||
|
log_info " └─ ✓ 电话点击记录成功"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error " └─ ✗ 电话点击记录失败 (curl 退出码: ${curl_exit_code})"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
MAX_TIME=15
|
||||||
|
SLEEP_MIN=3
|
||||||
|
SLEEP_MAX=6
|
||||||
|
|
||||||
|
# 主执行流程
|
||||||
|
main() {
|
||||||
|
log_info "==================== 开始执行 ===================="
|
||||||
|
log_info "目标主机数量: ${#hosts[@]}"
|
||||||
|
|
||||||
|
local success_count=0
|
||||||
|
local fail_count=0
|
||||||
|
|
||||||
|
# 获取地理位置对应的语言
|
||||||
|
ACCEPT_LANG="$(get_accept_language)"
|
||||||
|
log_info "Accept-Language: ${ACCEPT_LANG}"
|
||||||
|
|
||||||
|
# 为本次执行生成统一的 UA(所有请求使用同一个 UA)
|
||||||
|
local ua
|
||||||
|
ua="$(choose_ua)"
|
||||||
|
|
||||||
|
for host in "${hosts[@]}"; do
|
||||||
|
local ref_url=""
|
||||||
|
|
||||||
|
# 33% 概率直接访问(unknown),67% 概率从社交媒体跳转
|
||||||
|
if [ $((RANDOM % 3)) -eq 0 ]; then
|
||||||
|
# 直接访问,ref 参数为空
|
||||||
|
ref_url=""
|
||||||
|
log_info "直接访问(referral site: unknown)"
|
||||||
|
else
|
||||||
|
# 从随机社交媒体跳转
|
||||||
|
ref_url="$(choose_referer)"
|
||||||
|
log_info "referral site: ${ref_url}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 执行访问请求,不管成功失败都继续
|
||||||
|
if make_visit_request "$host" "$ref_url" "$ua"; then
|
||||||
|
((success_count++))
|
||||||
|
|
||||||
|
# 20% 概率点击电话统计
|
||||||
|
if [ $((RANDOM % 5)) -eq 0 ]; then
|
||||||
|
log_info " ↳ 触发电话点击(20% 概率)"
|
||||||
|
make_click_request "$host" "$ua" || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 在请求之间随机等待(最后一个请求除外)
|
||||||
|
if [ "$host" != "${hosts[-1]}" ]; then
|
||||||
|
local sleep_sec=$((SLEEP_MIN + RANDOM % (SLEEP_MAX - SLEEP_MIN + 1)))
|
||||||
|
local ms=$((RANDOM % 1000))
|
||||||
|
local sleep_time
|
||||||
|
|
||||||
|
# 检查 awk 是否可用
|
||||||
|
if command -v awk &> /dev/null; then
|
||||||
|
sleep_time=$(awk "BEGIN{printf \"%.3f\", ${sleep_sec} + ${ms}/1000}")
|
||||||
|
else
|
||||||
|
sleep_time="${sleep_sec}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "等待 ${sleep_time} 秒..."
|
||||||
|
sleep "$sleep_time"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_info "==================== 执行完成 ===================="
|
||||||
|
log_info "成功: ${success_count}, 失败: ${fail_count}"
|
||||||
|
|
||||||
|
# 如果有失败的请求,返回非零退出码
|
||||||
|
if [ "$fail_count" -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main
|
||||||
@@ -0,0 +1,581 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# mihomo 安装脚本
|
||||||
|
# 作者: su
|
||||||
|
# 版本: v1.0.0
|
||||||
|
# 日期: 2024年11月14日
|
||||||
|
|
||||||
|
set -e -o pipefail
|
||||||
|
|
||||||
|
# 定义颜色
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # 无颜色
|
||||||
|
|
||||||
|
# 检测系统类型和架构
|
||||||
|
detect_system() {
|
||||||
|
if [ -f /etc/alpine-release ]; then
|
||||||
|
SYSTEM_TYPE="Alpine"
|
||||||
|
elif command -v systemctl > /dev/null; then
|
||||||
|
SYSTEM_TYPE="Debian"
|
||||||
|
elif [ -f /etc/openwrt_release ]; then
|
||||||
|
SYSTEM_TYPE="OpenWrt"
|
||||||
|
elif [ -d /etc/init.d ]; then
|
||||||
|
SYSTEM_TYPE="Init.d"
|
||||||
|
else
|
||||||
|
echo "不支持的初始化系统"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARCH_RAW=$(uname -m)
|
||||||
|
case "${ARCH_RAW}" in
|
||||||
|
'x86_64') ARCH='amd64';;
|
||||||
|
'x86' | 'i686' | 'i386') ARCH='386';;
|
||||||
|
'aarch64' | 'arm64') ARCH='arm64';;
|
||||||
|
'armv7l') ARCH='armv7';;
|
||||||
|
's390x') ARCH='s390x';;
|
||||||
|
*) echo "Unsupported architecture: ${ARCH_RAW}"; exit 1;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$SYSTEM_TYPE" == "Debian" ]; then
|
||||||
|
SYSTEM_VERSION=$(cat /etc/os-release | grep VERSION_ID | cut -d'=' -f2 | tr -d '"')
|
||||||
|
elif [ "$SYSTEM_TYPE" == "Alpine" ]; then
|
||||||
|
SYSTEM_VERSION=$(cat /etc/alpine-release)
|
||||||
|
elif [ "$SYSTEM_TYPE" == "OpenWrt" ]; then
|
||||||
|
SYSTEM_VERSION=$(cat /etc/openwrt_release | grep DISTRIB_RELEASE | cut -d'=' -f2)
|
||||||
|
else
|
||||||
|
SYSTEM_VERSION="Unknown"
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}当前系统为 ${SYSTEM_TYPE}-${SYSTEM_VERSION}_${ARCH_RAW}${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 设置时区
|
||||||
|
set_timezone() {
|
||||||
|
if [ "$SYSTEM_TYPE" == "Alpine" ]; then
|
||||||
|
echo -e "${YELLOW}检测到 Alpine 系统,使用 cp 和 echo 设置时区${NC}"
|
||||||
|
apk add --no-cache tzdata
|
||||||
|
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
echo "Asia/Shanghai" > /etc/timezone
|
||||||
|
apk del tzdata
|
||||||
|
else
|
||||||
|
CURRENT_TIMEZONE=$(timedatectl show --property=Timezone --value)
|
||||||
|
if [ "$CURRENT_TIMEZONE" != "Asia/Shanghai" ]; then
|
||||||
|
echo -e "${YELLOW}设置时区为Asia/Shanghai${NC}"
|
||||||
|
timedatectl set-timezone Asia/Shanghai
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 安装必要软件
|
||||||
|
install_software() {
|
||||||
|
if [ "$SYSTEM_TYPE" == "Alpine" ]; then
|
||||||
|
echo -e "${YELLOW}正在安装必要软件${NC}"
|
||||||
|
apk add --no-cache curl nano grep gzip
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}正在安装必要软件${NC}"
|
||||||
|
if command -v apt-get > /dev/null; then
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y curl nano gzip
|
||||||
|
elif command -v yum > /dev/null; then
|
||||||
|
yum install -y curl nano gzip
|
||||||
|
else
|
||||||
|
echo -e "${RED}不支持的包管理器${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检测并下载 mihomo 版本
|
||||||
|
download_mihomo() {
|
||||||
|
VERSION=$(curl -sL "https://gh.llkk.cc/https://api.github.com/repos/MetaCubeX/mihomo/releases/latest" | grep -oP '"tag_name": "\K(.*)(?=")')
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
echo -e "${RED}无法获取最新版本号${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}获取到的版本: ${VERSION}${NC}"
|
||||||
|
|
||||||
|
DOWNLOAD_URL="https://gh.llkk.cc/https://github.com/MetaCubeX/mihomo/releases/download/${VERSION}/mihomo-linux-${ARCH}-${VERSION}.gz"
|
||||||
|
echo "从 ${DOWNLOAD_URL} 下载 mihomo"
|
||||||
|
curl -Lo mihomo.gz "${DOWNLOAD_URL}"
|
||||||
|
echo -e "${YELLOW}Mihomo ${VERSION} 下载完成, 开始安装${NC}"
|
||||||
|
gzip -d mihomo.gz
|
||||||
|
chmod +x mihomo
|
||||||
|
mv mihomo /usr/local/bin/
|
||||||
|
}
|
||||||
|
|
||||||
|
# 删除已有服务
|
||||||
|
remove_existing_service() {
|
||||||
|
if [ -f /etc/systemd/system/mihomo.service ] || [ -f /etc/init.d/mihomo ] || [ -f /etc/rc.d/mihomo ]; then
|
||||||
|
read -p "检测到已有的 mihomo 服务,是否删除?(y/n): " choice
|
||||||
|
case "$choice" in
|
||||||
|
y|Y )
|
||||||
|
if [ -f /etc/systemd/system/mihomo.service ]; then
|
||||||
|
systemctl stop mihomo
|
||||||
|
systemctl disable mihomo
|
||||||
|
rm -f /etc/systemd/system/mihomo.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/init.d/mihomo ]; then
|
||||||
|
/etc/init.d/mihomo stop
|
||||||
|
rm -f /etc/init.d/mihomo
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/rc.d/mihomo ]; then
|
||||||
|
/etc/rc.d/mihomo stop
|
||||||
|
rm -f /etc/rc.d/mihomo
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
n|N )
|
||||||
|
echo -e "${YELLOW}保留已有的 mihomo 服务,退出安装。${NC}"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
echo -e "${RED}无效的选择,退出安装。${NC}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 配置 OpenRC 服务,Alpine 专用
|
||||||
|
configure_openrc() {
|
||||||
|
echo "创建 OpenRC 服务文件"
|
||||||
|
cat <<EOF > /etc/init.d/mihomo
|
||||||
|
#!/sbin/openrc-run
|
||||||
|
command="/usr/local/bin/mihomo"
|
||||||
|
command_args="-d /etc/mihomo"
|
||||||
|
command_background=true
|
||||||
|
pidfile="/var/run/mihomo.pid"
|
||||||
|
name="Mihomo Service"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need net
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pre() {
|
||||||
|
ebegin "等待 1 秒"
|
||||||
|
sleep 1
|
||||||
|
eend \$?
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
chmod +x /etc/init.d/mihomo
|
||||||
|
rc-update add mihomo default
|
||||||
|
rc-service mihomo start
|
||||||
|
}
|
||||||
|
|
||||||
|
# 配置 systemd 服务,Debian系 专用
|
||||||
|
configure_systemd() {
|
||||||
|
echo "创建 systemd 服务文件"
|
||||||
|
cat <<EOF > /etc/systemd/system/mihomo.service
|
||||||
|
[Unit]
|
||||||
|
Description=mihomo Daemon, Another Clash Kernel.
|
||||||
|
After=network.target NetworkManager.service systemd-networkd.service iwd.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
LimitNPROC=500
|
||||||
|
LimitNOFILE=1000000
|
||||||
|
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
|
||||||
|
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
|
||||||
|
Restart=always
|
||||||
|
ExecStartPre=/usr/bin/sleep 1s
|
||||||
|
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
|
||||||
|
ExecReload=/bin/kill -HUP \$MAINPID
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl start mihomo
|
||||||
|
systemctl enable mihomo
|
||||||
|
}
|
||||||
|
|
||||||
|
# 配置 init.d 服务,OpenWrt 和 Init.d 专用
|
||||||
|
configure_initd() {
|
||||||
|
echo "创建 init.d 服务文件"
|
||||||
|
cat <<EOF > /etc/init.d/mihomo
|
||||||
|
#!/bin/sh
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: mihomo
|
||||||
|
# Required-Start: \$network
|
||||||
|
# Required-Stop: \$network
|
||||||
|
# Default-Start: 2 3 4 5
|
||||||
|
# Default-Stop: 0 1 6
|
||||||
|
# Short-Description: Mihomo Service
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
start() {
|
||||||
|
echo "Starting mihomo"
|
||||||
|
/usr/bin/sleep 1
|
||||||
|
/usr/local/bin/mihomo -d /etc/mihomo &
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
echo "Stopping mihomo"
|
||||||
|
pkill -f /usr/local/bin/mihomo
|
||||||
|
}
|
||||||
|
|
||||||
|
restart() {
|
||||||
|
stop
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
case "\$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: /etc/init.d/mihomo {start|stop|restart}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
chmod +x /etc/init.d/mihomo
|
||||||
|
update-rc.d mihomo defaults
|
||||||
|
/etc/init.d/mihomo start
|
||||||
|
}
|
||||||
|
|
||||||
|
# 内置配置
|
||||||
|
BUILTIN_CONFIG=$(cat <<EOF
|
||||||
|
port: 7890
|
||||||
|
socks-port: 7891
|
||||||
|
redir-port: 7892
|
||||||
|
tproxy-port: 7893
|
||||||
|
mixed-port: 1080
|
||||||
|
mode: rule
|
||||||
|
log-level: info
|
||||||
|
allow-lan: true # 允许局域网连接
|
||||||
|
bind-address: "*" # 绑定所有地址
|
||||||
|
ipv6: true # IPv6 总开关
|
||||||
|
tcp-concurrent: true # tcp 并发
|
||||||
|
unified-delay: true # 统一延迟
|
||||||
|
find-process-mode: strict # 进程匹配
|
||||||
|
global-client-fingerprint: random # 随机指纹
|
||||||
|
global-ua: clash-verge/v1.8.10 # 下载资源 UA
|
||||||
|
|
||||||
|
|
||||||
|
proxies:
|
||||||
|
# clashMeta配置参考可以看看这个wiki
|
||||||
|
# https://wiki.metacubex.one
|
||||||
|
|
||||||
|
### 锚点
|
||||||
|
p: &p # 订阅
|
||||||
|
type: http
|
||||||
|
interval: 1800
|
||||||
|
health-check:
|
||||||
|
enable: true
|
||||||
|
url: https://www.gstatic.com/generate_204
|
||||||
|
interval: 300
|
||||||
|
t : &t # 节点
|
||||||
|
type: url-test
|
||||||
|
include-all: true
|
||||||
|
url: https://www.gstatic.com/generate_204
|
||||||
|
interval: 300
|
||||||
|
skip-cert-verify: true # 跳过 TLS 证书验证
|
||||||
|
tolerance: 15 # 存在比当前节点延迟低15时切换
|
||||||
|
lazy: true # 未选择此策略组时,不会进行检测
|
||||||
|
sort:
|
||||||
|
delay: asc
|
||||||
|
speed: desc
|
||||||
|
r: &r # 规则
|
||||||
|
type: http
|
||||||
|
format: yaml
|
||||||
|
behavior: classical
|
||||||
|
interval: 86400
|
||||||
|
|
||||||
|
# 代理提供(订阅)组
|
||||||
|
proxy-providers:
|
||||||
|
1.机场1: # 机场名
|
||||||
|
url: ""
|
||||||
|
path: ./proxy_providers/机场1.yaml
|
||||||
|
#指定保存路径 非必填
|
||||||
|
<<: *p
|
||||||
|
|
||||||
|
2.机场2: # 机场名
|
||||||
|
url: ""
|
||||||
|
path: ./proxy_providers/机场2.yaml
|
||||||
|
proxy: DIRECT
|
||||||
|
<<: *p
|
||||||
|
|
||||||
|
|
||||||
|
# 代理组
|
||||||
|
proxy-groups:
|
||||||
|
- name: 🎯 总模式
|
||||||
|
type: select
|
||||||
|
proxies:
|
||||||
|
- ♻️ 自动选择
|
||||||
|
- 🚀 手动选择
|
||||||
|
- 🇭🇰 香港节点
|
||||||
|
- 🇸🇬 狮城节点
|
||||||
|
- 🇹🇼 台湾节点
|
||||||
|
- 🇯🇵 日本节点
|
||||||
|
- 🇺🇸 美国节点
|
||||||
|
- 🌎 其它地区
|
||||||
|
- 🔃 负载均衡-轮询
|
||||||
|
- 🔃 负载均衡-散列
|
||||||
|
- 直连
|
||||||
|
|
||||||
|
# 策略组
|
||||||
|
- name: ♻️ 自动选择
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 🚀 手动选择
|
||||||
|
type: select
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 🇭🇰 香港节点
|
||||||
|
filter: "(?i)🇭🇰|港|hk|hongkong|hong kong"
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 🇸🇬 狮城节点
|
||||||
|
filter: "(?i)🇸🇬|新|sg|singapore"
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 🇹🇼 台湾节点
|
||||||
|
filter: "(?i)🇹🇼|台|tw|taiwan"
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 🇯🇵 日本节点
|
||||||
|
filter: "(?i)🇯🇵|日|jp|japan"
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 🇺🇸 美国节点
|
||||||
|
filter: "(?i)🇺🇸|美|us|unitedstates|united states"
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 🌎 其它地区
|
||||||
|
type: select
|
||||||
|
filter: "(?i)^(?!.*(?:🇭🇰|🇯🇵|🇺🇸|🇸🇬|🇨🇳|港|hk|hongkong|台|tw|taiwan|日|jp|japan|新|sg|singapore|美|us|unitedstates)).*"
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 🔃 负载均衡-轮询
|
||||||
|
type: load-balance
|
||||||
|
strategy: round-robin
|
||||||
|
filter: "🇭🇰|🇯🇵|🇸🇬|香港|hk|HK|hongkong|台|台湾|TW|taiwan|日本|jp|JP|新加坡|sg|韩国|中转|狮城|菲律宾"
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 🔃 负载均衡-散列
|
||||||
|
type: load-balance
|
||||||
|
strategy: consistent-hashing
|
||||||
|
filter: "🇭🇰|🇯🇵|🇺🇸|🇸🇬|香港|hk|HK|hongkong|台|台湾|TW|taiwan|日本|jp|JP|新加坡|sg|美国|US"
|
||||||
|
<<: *t
|
||||||
|
|
||||||
|
- name: 直连
|
||||||
|
type: select
|
||||||
|
proxies:
|
||||||
|
- DIRECT
|
||||||
|
|
||||||
|
|
||||||
|
# 分流规则提供(订阅)组
|
||||||
|
rule-providers:
|
||||||
|
AWAvenue: # 秋风去广告
|
||||||
|
behavior: domain
|
||||||
|
path: ./rule_providers/AWAvenue-Ads-Rule-Clash.yaml
|
||||||
|
url: "https://raw.githubusercontent.com/TG-Twilight/AWAvenue-Ads-Rule/main/Filters/AWAvenue-Ads-Rule-Clash.yaml"
|
||||||
|
<<: *r
|
||||||
|
# OpenAi: # AI 服务
|
||||||
|
# path: ./rule_providers/OpenAi.yaml
|
||||||
|
# url: "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/Providers/Ruleset/OpenAi.yaml"
|
||||||
|
# <<: *r
|
||||||
|
直连: # 自定义直连
|
||||||
|
type: file
|
||||||
|
behavior: domain
|
||||||
|
format: text
|
||||||
|
path: ./rule_providers/直连.yaml
|
||||||
|
|
||||||
|
代理: # 自定义代理
|
||||||
|
type: file
|
||||||
|
behavior: domain
|
||||||
|
format: text
|
||||||
|
path: ./rule_providers/代理.yaml
|
||||||
|
|
||||||
|
# 其他参数
|
||||||
|
sniffer:
|
||||||
|
enable: true
|
||||||
|
sniff:
|
||||||
|
HTTP:
|
||||||
|
ports: [80, 8080-8880]
|
||||||
|
TLS:
|
||||||
|
ports: [443, 8443]
|
||||||
|
QUIC:
|
||||||
|
ports: [443, 8443]
|
||||||
|
skip-domain:
|
||||||
|
- Mijia Cloud
|
||||||
|
|
||||||
|
profile:
|
||||||
|
store-selected: true # 存储 select 选择记录
|
||||||
|
store-fake-ip: true # 持久化 fake-ip
|
||||||
|
|
||||||
|
# webui
|
||||||
|
external-controller: 0.0.0.0:9090
|
||||||
|
external-ui: webui
|
||||||
|
external-ui-name: xd
|
||||||
|
external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip"
|
||||||
|
|
||||||
|
# geo
|
||||||
|
geox-url:
|
||||||
|
geoip: "https://gcore.jsdelivr.net/gh/Loyalsoldier/geoip@release/geoip.dat"
|
||||||
|
geosite: "https://gcore.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat"
|
||||||
|
mmdb: "https://gcore.jsdelivr.net/gh/Loyalsoldier/geoip@release/Country.mmdb"
|
||||||
|
geodata-mode: true
|
||||||
|
geodata-loader: standard
|
||||||
|
geo-auto-update: true
|
||||||
|
geo-update-interval: 24
|
||||||
|
|
||||||
|
tun:
|
||||||
|
enable: true
|
||||||
|
device: tun
|
||||||
|
stack: mixed
|
||||||
|
dns-hijack:
|
||||||
|
- any:53
|
||||||
|
- tcp://any:53
|
||||||
|
auto-route: true
|
||||||
|
auto-redirect: true
|
||||||
|
auto-detect-interface: true
|
||||||
|
|
||||||
|
dns:
|
||||||
|
enable: true
|
||||||
|
cache-algorithm: arc
|
||||||
|
use-system-hosts: true
|
||||||
|
ipv6: true
|
||||||
|
listen: 0.0.0.0:1053
|
||||||
|
enhanced-mode: redir-host # fake-ip
|
||||||
|
fake-ip-range: 198.18.0.1/16
|
||||||
|
fake-ip-filter:
|
||||||
|
- '+.lan'
|
||||||
|
- '+.invalid.*'
|
||||||
|
- '+.localhost'
|
||||||
|
- '+.local.*'
|
||||||
|
- '+.time.*'
|
||||||
|
- '+.ntp.*'
|
||||||
|
- '+.time.edu.cn'
|
||||||
|
- '+.ntp.org.cn'
|
||||||
|
- '+.pool.ntp.org'
|
||||||
|
- '+.qpic.cn'
|
||||||
|
- "+.stun.*"
|
||||||
|
- "+.stun.*.*"
|
||||||
|
- "+.stun.*.*.*"
|
||||||
|
- '+.sushen.tk'
|
||||||
|
- localhost.ptlogin2.qq.com
|
||||||
|
- dns.msftncsi.com
|
||||||
|
- www.msftncsi.com
|
||||||
|
- www.msftconnecttest.com
|
||||||
|
- time1.cloud.tencent.com
|
||||||
|
|
||||||
|
nameserver:
|
||||||
|
- https://1.0.0.1/dns-query
|
||||||
|
- https://8.8.8.8/dns-query
|
||||||
|
- https://208.67.222.222/dns-query
|
||||||
|
proxy-server-nameserver:
|
||||||
|
- https://119.29.29.29/dns-query
|
||||||
|
- https://223.5.5.5/dns-query
|
||||||
|
nameserver-policy:
|
||||||
|
"geosite:category-ads-all":
|
||||||
|
- rcode://success
|
||||||
|
direct-nameserver:
|
||||||
|
- system
|
||||||
|
- 119.29.29.29
|
||||||
|
- 223.5.5.5
|
||||||
|
direct-nameserver-follow-policy: true
|
||||||
|
|
||||||
|
|
||||||
|
# 分流规则
|
||||||
|
rules:
|
||||||
|
- AND,(AND,(DST-PORT,443),(NETWORK,UDP)),(NOT,((GEOSITE,CN))),REJECT-DROP # 禁用quic(不包括国内)
|
||||||
|
- RULE-SET,AWAvenue,REJECT-DROP
|
||||||
|
# - RULE-SET,OpenAi,🇸🇬 狮城节点
|
||||||
|
- RULE-SET,直连,DIRECT
|
||||||
|
- RULE-SET,代理,🎯 总模式
|
||||||
|
# - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
|
||||||
|
|
||||||
|
- GEOSITE,openai,🇸🇬 狮城节点
|
||||||
|
|
||||||
|
- GEOSITE,ehentai,🎯 总模式
|
||||||
|
- GEOSITE,github,🎯 总模式
|
||||||
|
- GEOSITE,twitter,🎯 总模式
|
||||||
|
- GEOSITE,youtube,🎯 总模式
|
||||||
|
- GEOSITE,google,🎯 总模式
|
||||||
|
- GEOSITE,telegram,🎯 总模式
|
||||||
|
- GEOSITE,netflix,🎯 总模式
|
||||||
|
- GEOSITE,bahamut,🎯 总模式
|
||||||
|
- GEOSITE,spotify,🎯 总模式
|
||||||
|
- GEOSITE,bilibili,DIRECT
|
||||||
|
- GEOSITE,CN,DIRECT
|
||||||
|
- GEOSITE,private,DIRECT
|
||||||
|
- GEOSITE,category-ads-all,REJECT-DROP
|
||||||
|
|
||||||
|
- GEOIP,google,🎯 总模式
|
||||||
|
- GEOIP,netflix,🎯 总模式
|
||||||
|
- GEOIP,telegram,🎯 总模式
|
||||||
|
- GEOIP,twitter,🎯 总模式
|
||||||
|
- GEOIP,CN,DIRECT,no-resolve
|
||||||
|
- GEOIP,private,DIRECT,no-resolve
|
||||||
|
|
||||||
|
- MATCH,🎯 总模式
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
# 询问是否使用内置配置
|
||||||
|
ask_builtin_config() {
|
||||||
|
read -p "是否使用内置配置?(y/n): " choice
|
||||||
|
case "$choice" in
|
||||||
|
y|Y )
|
||||||
|
echo -e "${YELLOW}使用内置配置,请在配置中添加订阅${NC}"
|
||||||
|
mkdir /etc/mihomo/
|
||||||
|
echo "$BUILTIN_CONFIG" > /etc/mihomo/config.yaml
|
||||||
|
;;
|
||||||
|
n|N )
|
||||||
|
echo -e "${YELLOW}不使用内置配置,请上传配置到/etc/mihomo/config.yaml${NC}"
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
echo -e "${RED}无效的选择,退出安装。${NC}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# 启用系统转发
|
||||||
|
enable_ip_forwarding() {
|
||||||
|
echo 'net.ipv4.ip_forward = 1' | tee -a /etc/sysctl.conf
|
||||||
|
echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.conf
|
||||||
|
sysctl -p
|
||||||
|
if [ "$SYSTEM_TYPE" == "Alpine" ]; then
|
||||||
|
rc-update add sysctl
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主函数
|
||||||
|
main() {
|
||||||
|
detect_system
|
||||||
|
set_timezone
|
||||||
|
install_software
|
||||||
|
remove_existing_service
|
||||||
|
download_mihomo
|
||||||
|
ask_builtin_config
|
||||||
|
# enable_ip_forwarding
|
||||||
|
|
||||||
|
case "$SYSTEM_TYPE" in
|
||||||
|
"Alpine")
|
||||||
|
configure_openrc
|
||||||
|
;;
|
||||||
|
"Debian")
|
||||||
|
configure_systemd
|
||||||
|
;;
|
||||||
|
"OpenWrt" | "Init.d")
|
||||||
|
configure_initd
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo -e "${GREEN}脚本执行完毕。${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
main
|
||||||
@@ -0,0 +1,726 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# mssh - SSH 管理工具
|
||||||
|
|
||||||
|
# 配置文件路径
|
||||||
|
CONFIG_FILE="$HOME/.mssh.conf"
|
||||||
|
|
||||||
|
# 颜色定义
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
ORANGE='\033[0;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
PURPLE='\033[0;35m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# Emoji 定义
|
||||||
|
SERVER_EMOJI="🌐"
|
||||||
|
KEY_EMOJI="🔑"
|
||||||
|
PASSWORD_EMOJI="🔒"
|
||||||
|
CONNECT_EMOJI="🔌"
|
||||||
|
SUCCESS_EMOJI="✅"
|
||||||
|
ERROR_EMOJI="❌"
|
||||||
|
WARNING_EMOJI="⚠️"
|
||||||
|
INFO_EMOJI="𝒊"
|
||||||
|
FORWARD_EMOJI="📡"
|
||||||
|
|
||||||
|
# 信号处理
|
||||||
|
graceful_exit() {
|
||||||
|
echo -e "\n${INFO_EMOJI} ${CYAN} 正在退出 mssh...${NC}"
|
||||||
|
|
||||||
|
# 检查是否有活跃的端口转发
|
||||||
|
local active_forwards
|
||||||
|
active_forwards=$(get_section "active_forwards")
|
||||||
|
|
||||||
|
if [ -n "$active_forwards" ]; then
|
||||||
|
echo -e "${WARNING_EMOJI} ${YELLOW}检测到活跃的端口转发进程${NC}"
|
||||||
|
echo -e "${INFO_EMOJI} ${CYAN}这些进程将继续在后台运行${NC}"
|
||||||
|
echo -e "${CYAN}您可以随时重新启动 mssh 来管理它们${NC}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# 显示活跃的转发
|
||||||
|
local i=1
|
||||||
|
while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do
|
||||||
|
[ -z "$rule_name" ] && continue
|
||||||
|
printf " ${GREEN}%2d.${NC} ${FORWARD_EMOJI} ${YELLOW}%s${NC} (PID:%s)\n" "$i" "$rule_name" "$pid"
|
||||||
|
((i++))
|
||||||
|
done <<< "$active_forwards"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\n🔚 脚本已退出"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 设置信号处理
|
||||||
|
trap graceful_exit SIGINT SIGTERM SIGQUIT
|
||||||
|
|
||||||
|
# 密码加密函数
|
||||||
|
encrypt_password() {
|
||||||
|
local password="$1"
|
||||||
|
[ -z "$password" ] && return
|
||||||
|
|
||||||
|
# 简单的字符位移 + base64
|
||||||
|
local encrypted
|
||||||
|
encrypted=$(echo "$password" | tr 'A-Za-z0-9' 'N-ZA-Mn-za-m5-90-4' | base64 -w 0)
|
||||||
|
echo "ENC:$encrypted"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 密码解密函数
|
||||||
|
decrypt_password() {
|
||||||
|
local encrypted="$1"
|
||||||
|
[ -z "$encrypted" ] && return
|
||||||
|
|
||||||
|
# 解密格式:ENC:base64data
|
||||||
|
if [[ "$encrypted" =~ ^ENC: ]]; then
|
||||||
|
local encrypted_data="${encrypted#ENC:}"
|
||||||
|
echo "$encrypted_data" | base64 -d | tr 'N-ZA-Mn-za-m5-90-4' 'A-Za-z0-9'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 安全读取函数
|
||||||
|
safe_read() {
|
||||||
|
local prompt="$1"
|
||||||
|
local var_name="$2"
|
||||||
|
local response
|
||||||
|
|
||||||
|
if ! read -r -p "$prompt" response; then
|
||||||
|
graceful_exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$var_name" ]; then
|
||||||
|
eval "$var_name=\"$response\""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查 sshpass
|
||||||
|
check_sshpass() {
|
||||||
|
if ! command -v sshpass >/dev/null 2>&1; then
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}未找到 sshpass。某些功能可能不可用。${NC}"
|
||||||
|
echo -e "${INFO_EMOJI} ${YELLOW}可以运行以下命令安装:${NC}"
|
||||||
|
echo -e " ${CYAN}Ubuntu/Debian: sudo apt install sshpass${NC}"
|
||||||
|
echo -e " ${CYAN}CentOS/RHEL: sudo yum install sshpass${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 初始化配置文件
|
||||||
|
init_config() {
|
||||||
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
|
cat > "$CONFIG_FILE" << 'EOF'
|
||||||
|
# mssh 配置文件
|
||||||
|
# 格式说明:
|
||||||
|
# [servers] 部分:alias|user@host|port|keypath|encrypted_password
|
||||||
|
# [port_forwards] 部分:server_alias|rule_name|local_port|remote_host|remote_port|user@host|port|keypath|encrypted_password
|
||||||
|
# [active_forwards] 部分:rule_name|local_port|pid|user@host|port|server_alias|remote_host|remote_port
|
||||||
|
|
||||||
|
[servers]
|
||||||
|
|
||||||
|
[port_forwards]
|
||||||
|
|
||||||
|
[active_forwards]
|
||||||
|
|
||||||
|
EOF
|
||||||
|
echo -e "${SUCCESS_EMOJI} ${GREEN}配置文件已创建: $CONFIG_FILE${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 获取配置文件中的特定部分
|
||||||
|
get_section() {
|
||||||
|
local section="$1"
|
||||||
|
local config_file="${2:-$CONFIG_FILE}"
|
||||||
|
|
||||||
|
awk -v section="[$section]" '
|
||||||
|
$0 == section { in_section = 1; next }
|
||||||
|
/^\[.*\]$/ { in_section = 0; next }
|
||||||
|
in_section && NF > 0 && !/^#/ { print }
|
||||||
|
' "$config_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 格式化端口转发显示
|
||||||
|
format_forward_display() {
|
||||||
|
local num="$1"
|
||||||
|
local rule_name="$2"
|
||||||
|
local local_port="$3"
|
||||||
|
local server_alias="$4"
|
||||||
|
local remote_host="$5"
|
||||||
|
local remote_port="$6"
|
||||||
|
local pid="$7"
|
||||||
|
|
||||||
|
local pid_text=""
|
||||||
|
[ -n "$pid" ] && pid_text=" PID:$pid"
|
||||||
|
|
||||||
|
if [ "$remote_host" = "127.0.0.1" ]; then
|
||||||
|
printf " ${GREEN}%2d.${NC} ${FORWARD_EMOJI} ${YELLOW}%s${NC} (本地:%s -> %s:%s)%s\n" \
|
||||||
|
"$num" "$rule_name" "$local_port" "$server_alias" "$remote_port" "$pid_text"
|
||||||
|
else
|
||||||
|
printf " ${GREEN}%2d.${NC} ${FORWARD_EMOJI} ${YELLOW}%s${NC} (本地:%s -> %s -> %s:%s)%s\n" \
|
||||||
|
"$num" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" "$pid_text"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 显示服务器列表
|
||||||
|
show_servers() {
|
||||||
|
echo -e "${CYAN}已保存的服务器:${NC}"
|
||||||
|
echo -e "${CYAN}---------------${NC}"
|
||||||
|
|
||||||
|
local servers
|
||||||
|
servers=$(get_section "servers")
|
||||||
|
if [ -z "$servers" ]; then
|
||||||
|
echo -e " ${YELLOW}暂无服务器${NC}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local i=1
|
||||||
|
while IFS='|' read -r alias_name user_host port key_path password; do
|
||||||
|
[ -z "$alias_name" ] && continue
|
||||||
|
|
||||||
|
# 显示密码和密钥状态
|
||||||
|
local has_password=""
|
||||||
|
local has_key=""
|
||||||
|
[ -n "$password" ] && has_password="${PASSWORD_EMOJI}"
|
||||||
|
[ -n "$key_path" ] && [ -f "$key_path" ] && has_key="${KEY_EMOJI}"
|
||||||
|
|
||||||
|
printf " ${GREEN}%2d.${NC} ${SERVER_EMOJI} ${YELLOW}%s${NC} (%s:%s) %s%s\n" \
|
||||||
|
"$i" "$alias_name" "$user_host" "$port" "$has_password" "$has_key"
|
||||||
|
((i++))
|
||||||
|
done <<< "$servers"
|
||||||
|
|
||||||
|
return $((i-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
# 显示活跃的端口转发
|
||||||
|
show_active_forwards() {
|
||||||
|
local active_forwards
|
||||||
|
active_forwards=$(get_section "active_forwards")
|
||||||
|
if [ -n "$active_forwards" ]; then
|
||||||
|
echo
|
||||||
|
echo -e "${CYAN}已启用的端口转发:${NC}"
|
||||||
|
echo -e "${CYAN}------------------${NC}"
|
||||||
|
|
||||||
|
local i=1
|
||||||
|
while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do
|
||||||
|
[ -z "$rule_name" ] && continue
|
||||||
|
format_forward_display "$i" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" "$pid"
|
||||||
|
((i++))
|
||||||
|
done <<< "$active_forwards"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查并清理无效的端口转发进程
|
||||||
|
check_and_cleanup_forwards() {
|
||||||
|
local active_forwards
|
||||||
|
active_forwards=$(get_section "active_forwards")
|
||||||
|
|
||||||
|
if [ -z "$active_forwards" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp)
|
||||||
|
local cleanup_needed=false
|
||||||
|
local cleaned_count=0
|
||||||
|
|
||||||
|
# 复制配置文件到临时文件
|
||||||
|
cp "$CONFIG_FILE" "$temp_file"
|
||||||
|
|
||||||
|
while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do
|
||||||
|
[ -z "$rule_name" ] && continue
|
||||||
|
|
||||||
|
# 检查进程是否存在
|
||||||
|
if ! kill -0 "$pid" 2>/dev/null; then
|
||||||
|
# 进程不存在,从配置中移除
|
||||||
|
local rule_line="${rule_name}|${local_port}|${pid}|${user_host}|${port}|${server_alias}|${remote_host}|${remote_port}"
|
||||||
|
|
||||||
|
awk -v target_line="$rule_line" '
|
||||||
|
$0 == target_line && in_active { next }
|
||||||
|
/^\[active_forwards\]$/ { in_active=1 }
|
||||||
|
/^\[.*\]$/ && !/^\[active_forwards\]$/ { in_active=0 }
|
||||||
|
{ print }
|
||||||
|
' "$temp_file" > "${temp_file}.new" && mv "${temp_file}.new" "$temp_file"
|
||||||
|
|
||||||
|
cleanup_needed=true
|
||||||
|
((cleaned_count++))
|
||||||
|
fi
|
||||||
|
done <<< "$active_forwards"
|
||||||
|
|
||||||
|
# 如果有清理,更新配置文件并显示信息
|
||||||
|
if [ "$cleanup_needed" = true ]; then
|
||||||
|
mv "$temp_file" "$CONFIG_FILE"
|
||||||
|
if [ "$cleaned_count" -gt 0 ]; then
|
||||||
|
echo -e "${INFO_EMOJI} ${YELLOW}已清理 $cleaned_count 个失效的端口转发进程${NC}"
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
rm -f "$temp_file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主菜单
|
||||||
|
main_menu() {
|
||||||
|
# 在显示菜单前检查并清理无效的端口转发进程
|
||||||
|
check_and_cleanup_forwards
|
||||||
|
|
||||||
|
clear
|
||||||
|
echo -e "${PURPLE}==========================================${NC}"
|
||||||
|
echo -e "${PURPLE}:: mssh - SSH 管理工具 ::${NC}"
|
||||||
|
echo -e "${PURPLE}==========================================${NC}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
show_servers
|
||||||
|
show_active_forwards
|
||||||
|
local has_active_forwards=$?
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo -e "${BLUE}选项分区:${NC}"
|
||||||
|
echo -e "${BLUE}---------${NC}"
|
||||||
|
echo -e " ${GREEN}[a] 增加服务器${NC}"
|
||||||
|
echo -e " ${RED}[d] 删除服务器${NC}"
|
||||||
|
echo -e " ${BLUE}[p] 端口转发${NC}"
|
||||||
|
echo -e " ${CYAN}[q] 退出${NC}"
|
||||||
|
|
||||||
|
# 显示端口转发持久化提示
|
||||||
|
if [ $has_active_forwards -eq 0 ]; then
|
||||||
|
echo
|
||||||
|
echo -e "${INFO_EMOJI} ${YELLOW}提示:端口转发将在退出程序后继续运行${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo -ne "${GREEN}请选择操作: ${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加服务器
|
||||||
|
add_server() {
|
||||||
|
while true; do
|
||||||
|
clear
|
||||||
|
echo -e "${PURPLE}============ 添加服务器 ============${NC}"
|
||||||
|
echo
|
||||||
|
echo -e "${INFO_EMOJI} ${CYAN}添加新服务器 (0或回车返回)${NC}"
|
||||||
|
echo
|
||||||
|
safe_read "$(echo -e "${CYAN}输入别名: ${NC}")" alias_name; [ "$alias_name" = "0" ] || [ -z "$alias_name" ] && return
|
||||||
|
safe_read "$(echo -e "${CYAN}输入用户名@主机: ${NC}")" user_host; [ "$user_host" = "0" ] || [ -z "$user_host" ] && return
|
||||||
|
safe_read "$(echo -e "${CYAN}输入端口 (默认22): ${NC}")" port; [ "$port" = "0" ] && return; port=${port:-22}
|
||||||
|
echo -ne "${CYAN}输入密码 (可选): ${NC}"
|
||||||
|
read -r -s password
|
||||||
|
echo
|
||||||
|
safe_read "$(echo -e "${CYAN}输入私钥路径 (可选): ${NC}")" key_path; [ "$key_path" = "0" ] && return
|
||||||
|
|
||||||
|
if [ -n "$key_path" ] && [ ! -f "$key_path" ]; then
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}密钥文件不存在${NC}"
|
||||||
|
safe_read "$(echo -e "${YELLOW}是否继续? (y/N): ${NC}")" continue_without_key
|
||||||
|
if [[ ! "${continue_without_key:-n}" =~ [yY] ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
key_path=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 加密密码
|
||||||
|
[ -n "$password" ] && password=$(encrypt_password "$password")
|
||||||
|
|
||||||
|
# 添加到配置文件
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp)
|
||||||
|
awk -v new_server="${alias_name}|${user_host}|${port}|${key_path}|${password}" '
|
||||||
|
/^\[servers\]$/ { print; in_servers=1; next }
|
||||||
|
/^\[.*\]$/ { in_servers=0 }
|
||||||
|
in_servers && /^$/ { print new_server; added=1 }
|
||||||
|
{ print }
|
||||||
|
END { if (!added && in_servers) print new_server }
|
||||||
|
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
|
||||||
|
|
||||||
|
echo -e "${SUCCESS_EMOJI} ${GREEN}服务器已添加: $alias_name${NC}"
|
||||||
|
echo
|
||||||
|
safe_read "$(echo -e "${YELLOW}是否继续添加? (y/N): ${NC}")" continue_add
|
||||||
|
if [[ ! "${continue_add:-n}" =~ [yY] ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 删除服务器
|
||||||
|
delete_server() {
|
||||||
|
while true; do
|
||||||
|
clear
|
||||||
|
echo -e "${PURPLE}============ 删除服务器 ============${NC}"
|
||||||
|
echo
|
||||||
|
echo -e "${WARNING_EMOJI} ${YELLOW}删除服务器${NC}"
|
||||||
|
echo
|
||||||
|
show_servers
|
||||||
|
echo
|
||||||
|
safe_read "$(echo -e "${CYAN}选择要删除的服务器编号 (0或回车返回): ${NC}")" choice
|
||||||
|
[ "$choice" = "0" ] || [ -z "$choice" ] && return
|
||||||
|
|
||||||
|
local servers
|
||||||
|
servers=$(get_section "servers")
|
||||||
|
local server_line
|
||||||
|
server_line=$(echo "$servers" | sed -n "${choice}p")
|
||||||
|
|
||||||
|
if [ -n "$server_line" ]; then
|
||||||
|
local alias_name
|
||||||
|
alias_name=$(echo "$server_line" | cut -d'|' -f1)
|
||||||
|
echo -e "${WARNING_EMOJI} ${YELLOW}确认删除服务器: $alias_name${NC}"
|
||||||
|
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp)
|
||||||
|
awk -v target_line="$server_line" '
|
||||||
|
$0 == target_line && in_servers { next }
|
||||||
|
/^\[servers\]$/ { in_servers=1 }
|
||||||
|
/^\[.*\]$/ && !/^\[servers\]$/ { in_servers=0 }
|
||||||
|
{ print }
|
||||||
|
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
|
||||||
|
|
||||||
|
echo -e "${SUCCESS_EMOJI} ${GREEN}服务器已删除: $alias_name${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
safe_read "$(echo -e "${YELLOW}是否继续删除? (y/N): ${NC}")" continue_del
|
||||||
|
if [[ ! "${continue_del:-n}" =~ [yY] ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加端口转发规则
|
||||||
|
add_port_forward_rule() {
|
||||||
|
echo -e "${INFO_EMOJI} ${CYAN}添加新端口转发规则 (0或回车返回):${NC}"
|
||||||
|
|
||||||
|
safe_read "$(echo -e "${CYAN}输入规则名称: ${NC}")" rule_name
|
||||||
|
if [ "$rule_name" = "0" ] || [ -z "$rule_name" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
safe_read "$(echo -e "${CYAN}输入本地端口: ${NC}")" local_port
|
||||||
|
if [ "$local_port" = "0" ] || [ -z "$local_port" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
safe_read "$(echo -e "${CYAN}输入远程主机 (默认127.0.0.1): ${NC}")" remote_host
|
||||||
|
if [ "$remote_host" = "0" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
remote_host=${remote_host:-127.0.0.1}
|
||||||
|
|
||||||
|
safe_read "$(echo -e "${CYAN}输入远程端口 (默认${local_port}): ${NC}")" remote_port
|
||||||
|
if [ "$remote_port" = "0" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
remote_port=${remote_port:-$local_port}
|
||||||
|
|
||||||
|
echo -e "\n${CYAN}请选择要使用的服务器:${NC}"
|
||||||
|
show_servers
|
||||||
|
echo
|
||||||
|
safe_read "$(echo -e "${CYAN}选择服务器编号: ${NC}")" server_choice
|
||||||
|
if [ "$server_choice" = "0" ] || [ -z "$server_choice" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local servers
|
||||||
|
servers=$(get_section "servers")
|
||||||
|
local selected_server
|
||||||
|
selected_server=$(echo "$servers" | sed -n "${server_choice}p")
|
||||||
|
if [ -n "$selected_server" ]; then
|
||||||
|
IFS='|' read -r server_alias user_host port key_path password <<< "$selected_server"
|
||||||
|
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp)
|
||||||
|
awk -v new_rule="${server_alias}|${rule_name}|${local_port}|${remote_host}|${remote_port}|${user_host}|${port}|${key_path}|${password}" '
|
||||||
|
/^\[port_forwards\]$/ { print; in_forwards=1; next }
|
||||||
|
/^\[.*\]$/ { in_forwards=0 }
|
||||||
|
in_forwards && /^$/ { print new_rule; added=1 }
|
||||||
|
{ print }
|
||||||
|
END { if (!added && in_forwards) print new_rule }
|
||||||
|
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
|
||||||
|
|
||||||
|
echo -e "${SUCCESS_EMOJI} ${GREEN}端口转发规则已添加: $rule_name${NC}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 删除端口转发规则
|
||||||
|
delete_port_forward() {
|
||||||
|
local rule_number="$1"
|
||||||
|
|
||||||
|
local forwards
|
||||||
|
forwards=$(get_section "port_forwards")
|
||||||
|
local rule_line
|
||||||
|
rule_line=$(echo "$forwards" | sed -n "${rule_number}p")
|
||||||
|
|
||||||
|
if [ -n "$rule_line" ]; then
|
||||||
|
local rule_name
|
||||||
|
rule_name=$(echo "$rule_line" | cut -d'|' -f2)
|
||||||
|
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp)
|
||||||
|
awk -v target_line="$rule_line" '
|
||||||
|
$0 == target_line && in_forwards { next }
|
||||||
|
/^\[port_forwards\]$/ { in_forwards=1 }
|
||||||
|
/^\[.*\]$/ && !/^\[port_forwards\]$/ { in_forwards=0 }
|
||||||
|
{ print }
|
||||||
|
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
|
||||||
|
|
||||||
|
echo -e "${SUCCESS_EMOJI} ${GREEN}规则已删除: $rule_name${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}无效的规则编号!${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 停止后台端口转发
|
||||||
|
stop_background_forward() {
|
||||||
|
local rule_number="$1"
|
||||||
|
|
||||||
|
local active_forwards
|
||||||
|
active_forwards=$(get_section "active_forwards")
|
||||||
|
local active_line
|
||||||
|
active_line=$(echo "$active_forwards" | sed -n "${rule_number}p")
|
||||||
|
|
||||||
|
if [ -n "$active_line" ]; then
|
||||||
|
IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port <<< "$active_line"
|
||||||
|
|
||||||
|
if kill "$pid" 2>/dev/null; then
|
||||||
|
echo -e "${SUCCESS_EMOJI} ${GREEN}端口转发已停止: $rule_name (PID: $pid)${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${WARNING_EMOJI} ${YELLOW}进程可能已经停止: $rule_name (PID: $pid)${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 从活跃转发列表中移除
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp)
|
||||||
|
awk -v target_line="$active_line" '
|
||||||
|
$0 == target_line && in_active { next }
|
||||||
|
/^\[active_forwards\]$/ { in_active=1 }
|
||||||
|
/^\[.*\]$/ && !/^\[active_forwards\]$/ { in_active=0 }
|
||||||
|
{ print }
|
||||||
|
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
|
||||||
|
else
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}无效的活跃转发编号!${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 启动后台端口转发
|
||||||
|
start_background_forward() {
|
||||||
|
local rule_number="$1"
|
||||||
|
|
||||||
|
local forwards
|
||||||
|
forwards=$(get_section "port_forwards")
|
||||||
|
local rule_line
|
||||||
|
rule_line=$(echo "$forwards" | sed -n "${rule_number}p")
|
||||||
|
|
||||||
|
if [ -n "$rule_line" ]; then
|
||||||
|
IFS='|' read -r server_alias rule_name local_port remote_host remote_port user_host port key_path password <<< "$rule_line"
|
||||||
|
|
||||||
|
# 解密密码
|
||||||
|
[ -n "$password" ] && password=$(decrypt_password "$password")
|
||||||
|
|
||||||
|
# 构建 SSH 命令
|
||||||
|
local ssh_args=("-p" "$port" "-L" "${local_port}:${remote_host}:${remote_port}" "-N" "$user_host")
|
||||||
|
[ -n "$key_path" ] && ssh_args+=("-i" "$key_path")
|
||||||
|
|
||||||
|
# 启动后台进程 (使用密码或密钥认证)
|
||||||
|
local ssh_pid
|
||||||
|
if [ -n "$password" ] && command -v sshpass >/dev/null 2>&1; then
|
||||||
|
echo -e "${INFO_EMOJI} ${YELLOW}使用保存的密码启动端口转发...${NC}"
|
||||||
|
nohup sshpass -p "$password" ssh "${ssh_args[@]}" >/dev/null 2>&1 &
|
||||||
|
ssh_pid=$!
|
||||||
|
disown $ssh_pid 2>/dev/null
|
||||||
|
|
||||||
|
# 等待SSH连接建立
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# 检查进程是否仍在运行
|
||||||
|
if ! kill -0 "$ssh_pid" 2>/dev/null; then
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}端口转发启动失败,请检查网络连接和认证信息${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${INFO_EMOJI} ${YELLOW}启动端口转发 (需要手动输入密码)...${NC}"
|
||||||
|
nohup ssh "${ssh_args[@]}" </dev/tty >/dev/null 2>&1 &
|
||||||
|
ssh_pid=$!
|
||||||
|
disown $ssh_pid 2>/dev/null
|
||||||
|
|
||||||
|
# 给用户一些时间输入密码
|
||||||
|
echo -e "${INFO_EMOJI} ${CYAN}请在上方输入SSH密码...${NC}"
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 检查进程是否仍在运行
|
||||||
|
if ! kill -0 "$ssh_pid" 2>/dev/null; then
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}端口转发启动失败,请检查网络连接和认证信息${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp)
|
||||||
|
awk -v new_active="${rule_name}|${local_port}|${ssh_pid}|${user_host}|${port}|${server_alias}|${remote_host}|${remote_port}" '
|
||||||
|
/^\[active_forwards\]$/ { print; in_active=1; next }
|
||||||
|
/^\[.*\]$/ { in_active=0 }
|
||||||
|
in_active && /^$/ { print new_active; added=1 }
|
||||||
|
{ print }
|
||||||
|
END { if (!added && in_active) print new_active }
|
||||||
|
' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE"
|
||||||
|
|
||||||
|
echo -e "${SUCCESS_EMOJI} ${GREEN}端口转发已启动: $rule_name (PID: $ssh_pid)${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}无效的规则编号!${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 端口转发管理
|
||||||
|
list_port_forwards() {
|
||||||
|
while true; do
|
||||||
|
clear
|
||||||
|
echo -e "${PURPLE}=========== 端口转发管理 ===========${NC}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
local forwards
|
||||||
|
forwards=$(get_section "port_forwards")
|
||||||
|
if [ -z "$forwards" ]; then
|
||||||
|
echo -e "${CYAN}已配置的端口转发规则:${NC}"
|
||||||
|
echo -e "${CYAN}------------------------${NC}"
|
||||||
|
echo -e " ${YELLOW}暂无端口转发规则${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${CYAN}已配置的端口转发规则:${NC}"
|
||||||
|
echo -e "${CYAN}------------------------${NC}"
|
||||||
|
local i=1
|
||||||
|
while IFS='|' read -r server_alias rule_name local_port remote_host remote_port _ _ _ _ || [ -n "$rule_name" ]; do
|
||||||
|
[ -z "$rule_name" ] && continue
|
||||||
|
format_forward_display "$i" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port"
|
||||||
|
((i++))
|
||||||
|
done <<< "$forwards"
|
||||||
|
fi
|
||||||
|
|
||||||
|
show_active_forwards
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo -e "${BLUE}操作选项:${NC}"
|
||||||
|
echo -e "${BLUE}---------${NC}"
|
||||||
|
echo -e " ${GREEN}[1] 添加端口转发${NC}"
|
||||||
|
echo -e " ${YELLOW}[2] 启动端口转发${NC}"
|
||||||
|
echo -e " ${ORANGE}[3] 停止端口转发${NC}"
|
||||||
|
echo -e " ${RED}[4] 删除转发规则${NC}"
|
||||||
|
echo
|
||||||
|
safe_read "$(echo -e "${CYAN}请选择操作 (0或回车返回): ${NC}")" choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
0|"") return ;;
|
||||||
|
1)
|
||||||
|
echo
|
||||||
|
if add_port_forward_rule; then
|
||||||
|
safe_read "按回车键继续..." pause_key
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
if [ -z "$forwards" ]; then
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}暂无端口转发规则可启动!${NC}"
|
||||||
|
safe_read "按回车键继续..." pause_key
|
||||||
|
else
|
||||||
|
safe_read "$(echo -e "${CYAN}输入要启动的规则编号: ${NC}")" start_choice
|
||||||
|
if [ -n "$start_choice" ]; then
|
||||||
|
start_background_forward "$start_choice"
|
||||||
|
fi
|
||||||
|
safe_read "按回车键继续..." pause_key
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
local active_forwards
|
||||||
|
active_forwards=$(get_section "active_forwards")
|
||||||
|
if [ -z "$active_forwards" ]; then
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}暂无活跃的端口转发可停止!${NC}"
|
||||||
|
safe_read "按回车键继续..." pause_key
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo -e "${CYAN}当前活跃的端口转发:${NC}"
|
||||||
|
local i=1
|
||||||
|
while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do
|
||||||
|
[ -z "$rule_name" ] && continue
|
||||||
|
format_forward_display "$i" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" "$pid"
|
||||||
|
((i++))
|
||||||
|
done <<< "$active_forwards"
|
||||||
|
echo
|
||||||
|
safe_read "$(echo -e "${CYAN}输入要停止的端口转发编号: ${NC}")" stop_choice
|
||||||
|
if [ -n "$stop_choice" ]; then
|
||||||
|
stop_background_forward "$stop_choice"
|
||||||
|
fi
|
||||||
|
safe_read "按回车键继续..." pause_key
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
if [ -z "$forwards" ]; then
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}暂无端口转发规则可删除!${NC}"
|
||||||
|
safe_read "按回车键继续..." pause_key
|
||||||
|
else
|
||||||
|
safe_read "$(echo -e "${CYAN}输入要删除的规则编号: ${NC}")" del_choice
|
||||||
|
if [ -n "$del_choice" ]; then
|
||||||
|
delete_port_forward "$del_choice"
|
||||||
|
fi
|
||||||
|
safe_read "按回车键继续..." pause_key
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*) echo -e "${ERROR_EMOJI} ${RED}无效选项!${NC}"; sleep 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 连接服务器
|
||||||
|
connect_server() {
|
||||||
|
local choice=$1
|
||||||
|
|
||||||
|
local servers
|
||||||
|
servers=$(get_section "servers")
|
||||||
|
local selected_server
|
||||||
|
selected_server=$(echo "$servers" | sed -n "${choice}p")
|
||||||
|
|
||||||
|
if [ -n "$selected_server" ]; then
|
||||||
|
IFS='|' read -r alias_name user_host port key_path password <<< "$selected_server"
|
||||||
|
|
||||||
|
# 解密密码
|
||||||
|
[ -n "$password" ] && password=$(decrypt_password "$password")
|
||||||
|
|
||||||
|
echo -e "${CONNECT_EMOJI} ${GREEN}连接到: $alias_name ($user_host:$port)${NC}"
|
||||||
|
|
||||||
|
local ssh_args=("-p" "$port" "$user_host")
|
||||||
|
[ -n "$key_path" ] && ssh_args+=("-i" "$key_path")
|
||||||
|
|
||||||
|
if [ -n "$password" ] && command -v sshpass >/dev/null 2>&1; then
|
||||||
|
echo -e "${WARNING_EMOJI} ${YELLOW}使用保存的密码连接...${NC}"
|
||||||
|
sshpass -p "$password" ssh "${ssh_args[@]}"
|
||||||
|
else
|
||||||
|
ssh "${ssh_args[@]}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}"
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主程序
|
||||||
|
check_sshpass
|
||||||
|
init_config
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
main_menu
|
||||||
|
safe_read "" choice
|
||||||
|
|
||||||
|
case $choice in
|
||||||
|
[0-9]*)
|
||||||
|
if [[ "$choice" =~ ^[0-9]+$ ]]; then
|
||||||
|
connect_server "$choice"
|
||||||
|
else
|
||||||
|
echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}"
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
a|A) add_server ;;
|
||||||
|
d|D) delete_server ;;
|
||||||
|
p|P) list_port_forwards ;;
|
||||||
|
q|Q) graceful_exit ;;
|
||||||
|
*) echo -e "${ERROR_EMOJI} ${RED}无效选项!${NC}"; sleep 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# tcp调优脚本 v25.10.11
|
||||||
|
# 用法:
|
||||||
|
# ./tcp.sh 1 启用调优
|
||||||
|
# ./tcp.sh 0 还原配置
|
||||||
|
# ./tcp.sh 2 对比优化前后的参数
|
||||||
|
|
||||||
|
BACKUP_FILE="/var/backups/tcp_backup_adaptive.conf"
|
||||||
|
TUNE_FILE="/etc/sysctl.d/tcp_bbr_adaptive.conf"
|
||||||
|
|
||||||
|
# 检查是否以 root 权限运行
|
||||||
|
check_root() {
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "✗ 错误: 此脚本需要 root 权限运行"
|
||||||
|
echo " 请使用: sudo $0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 确保备份目录存在
|
||||||
|
ensure_backup_dir() {
|
||||||
|
local backup_dir
|
||||||
|
backup_dir=$(dirname "$BACKUP_FILE")
|
||||||
|
if [ ! -d "$backup_dir" ]; then
|
||||||
|
mkdir -p "$backup_dir" 2>/dev/null || {
|
||||||
|
echo "✗ 无法创建备份目录: $backup_dir"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
PARAMS=(
|
||||||
|
"net.core.default_qdisc"
|
||||||
|
"net.ipv4.tcp_congestion_control"
|
||||||
|
"net.core.rmem_max"
|
||||||
|
"net.core.wmem_max"
|
||||||
|
"net.ipv4.tcp_rmem"
|
||||||
|
"net.ipv4.tcp_wmem"
|
||||||
|
)
|
||||||
|
|
||||||
|
get_sys_info() {
|
||||||
|
MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
||||||
|
MEM_BYTES=$((MEM_KB * 1024))
|
||||||
|
echo "内存: $((MEM_BYTES/1024/1024)) MB, CPU: $(nproc) 核"
|
||||||
|
}
|
||||||
|
|
||||||
|
ask_network_info() {
|
||||||
|
# 读取并验证带宽输入
|
||||||
|
while true; do
|
||||||
|
read -p "最大带宽 (Mbps): " BW
|
||||||
|
if [[ "$BW" =~ ^[0-9]+(\.[0-9]+)?$ ]] && (( $(echo "$BW > 0" | bc -l) )); then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "✗ 无效输入,请输入正数 (例如: 100 或 1000)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 读取并验证 RTT 输入
|
||||||
|
while true; do
|
||||||
|
read -p "平均延迟 RTT (ms): " RTT
|
||||||
|
if [[ "$RTT" =~ ^[0-9]+(\.[0-9]+)?$ ]] && (( $(echo "$RTT > 0" | bc -l) )); then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "✗ 无效输入,请输入正数 (例如: 10 或 50)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
BW_BITS=$((BW * 1000000))
|
||||||
|
RTT_SEC=$(awk "BEGIN {print $RTT/1000}")
|
||||||
|
BDP_BITS=$(awk "BEGIN {print $BW_BITS * $RTT_SEC}")
|
||||||
|
BDP_BYTES=$(awk "BEGIN {print int($BDP_BITS/8)}")
|
||||||
|
MIN_BUF=$((4*1024*1024))
|
||||||
|
MAX_BUF_LIMIT=$((MEM_BYTES/8))
|
||||||
|
if [ $BDP_BYTES -lt $MIN_BUF ]; then
|
||||||
|
BUF=$MIN_BUF
|
||||||
|
elif [ $BDP_BYTES -gt $MAX_BUF_LIMIT ]; then
|
||||||
|
BUF=$MAX_BUF_LIMIT
|
||||||
|
else
|
||||||
|
BUF=$BDP_BYTES
|
||||||
|
fi
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "计算结果:"
|
||||||
|
echo " 带宽延迟积 (BDP): $(numfmt --to=iec-i --suffix=B $BDP_BYTES)"
|
||||||
|
echo " 缓冲区上限: $(numfmt --to=iec-i --suffix=B $BUF)"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_bbr() {
|
||||||
|
if ! sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -qw bbr; then
|
||||||
|
echo "⚠️ 未检测到 BBR 支持,尝试加载 tcp_bbr 模块..."
|
||||||
|
if modprobe tcp_bbr 2>/dev/null; then
|
||||||
|
echo "✓ tcp_bbr 模块加载成功"
|
||||||
|
# 添加到开机自动加载
|
||||||
|
if [ ! -f /etc/modules-load.d/tcp_bbr.conf ]; then
|
||||||
|
echo "tcp_bbr" > /etc/modules-load.d/tcp_bbr.conf
|
||||||
|
echo "✓ 已设置 BBR 模块开机自动加载"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✗ 内核不支持 BBR,无法加载模块"
|
||||||
|
echo " 请确认内核版本 >= 4.9 或更新内核"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✓ BBR 支持已启用"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_tune() {
|
||||||
|
check_root
|
||||||
|
ensure_backup_dir
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " 系统信息检测"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
get_sys_info
|
||||||
|
echo ""
|
||||||
|
check_bbr
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " 网络参数配置"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
ask_network_info
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ ! -f "$BACKUP_FILE" ]; then
|
||||||
|
echo "📋 备份当前参数到 $BACKUP_FILE"
|
||||||
|
for p in "${PARAMS[@]}"; do
|
||||||
|
echo "$p = $(sysctl -n $p)" >> "$BACKUP_FILE"
|
||||||
|
done
|
||||||
|
echo "✓ 备份完成"
|
||||||
|
else
|
||||||
|
echo "ℹ️ 检测到已存在备份文件,跳过备份"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📝 生成配置文件: $TUNE_FILE"
|
||||||
|
cat > "$TUNE_FILE" <<EOF
|
||||||
|
net.core.default_qdisc = fq
|
||||||
|
net.ipv4.tcp_congestion_control = bbr
|
||||||
|
net.core.rmem_max = $BUF
|
||||||
|
net.core.wmem_max = $BUF
|
||||||
|
net.ipv4.tcp_rmem = 4096 87380 $BUF
|
||||||
|
net.ipv4.tcp_wmem = 4096 65536 $BUF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "⚙️ 应用配置..."
|
||||||
|
if sysctl -p "$TUNE_FILE" >/dev/null 2>&1; then
|
||||||
|
echo "✓ 配置应用成功"
|
||||||
|
else
|
||||||
|
echo "✗ 配置应用失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " 当前参数"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
for p in "${PARAMS[@]}"; do
|
||||||
|
printf "%-35s %s\n" "$p" "$(sysctl -n $p)"
|
||||||
|
done
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_tune() {
|
||||||
|
check_root
|
||||||
|
|
||||||
|
if [ ! -f "$BACKUP_FILE" ]; then
|
||||||
|
echo "✗ 未找到备份文件: $BACKUP_FILE"
|
||||||
|
echo " 无法还原配置"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " 还原配置"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
[ -f "$TUNE_FILE" ] && rm -f "$TUNE_FILE" && echo "✓ 已删除调优配置文件"
|
||||||
|
|
||||||
|
echo "⚙️ 还原系统参数..."
|
||||||
|
while IFS= read -r line; do
|
||||||
|
key=$(echo "$line" | awk -F= '{print $1}' | xargs)
|
||||||
|
value=$(echo "$line" | awk -F= '{print $2}' | xargs)
|
||||||
|
if [ -n "$key" ] && [ -n "$value" ]; then
|
||||||
|
if sysctl -w "$key=$value" >/dev/null 2>&1; then
|
||||||
|
echo " ✓ $key"
|
||||||
|
else
|
||||||
|
echo " ✗ $key (失败)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < "$BACKUP_FILE"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " 当前参数"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
for p in "${PARAMS[@]}"; do
|
||||||
|
printf "%-35s %s\n" "$p" "$(sysctl -n $p)"
|
||||||
|
done
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
read -p "是否删除备份文件? (y/N): " del_backup
|
||||||
|
if [[ "$del_backup" =~ ^[Yy]$ ]]; then
|
||||||
|
rm -f "$BACKUP_FILE"
|
||||||
|
echo "✓ 已删除备份文件"
|
||||||
|
else
|
||||||
|
echo "ℹ️ 保留备份文件: $BACKUP_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
compare_params() {
|
||||||
|
if [ ! -f "$BACKUP_FILE" ]; then
|
||||||
|
echo "✗ 未找到备份文件: $BACKUP_FILE"
|
||||||
|
echo " 无法对比参数"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
printf "%-35s %-25s %-25s\n" "参数" "优化前" "优化后"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
for p in "${PARAMS[@]}"; do
|
||||||
|
old=$(grep "^$p" "$BACKUP_FILE" | awk -F= '{print $2}' | xargs)
|
||||||
|
new=$(sysctl -n $p 2>/dev/null || echo "N/A")
|
||||||
|
printf "%-35s %-25s %-25s\n" "$p" "$old" "$new"
|
||||||
|
done
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " TCP 调优脚本 v25.10.11"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "请选择操作:"
|
||||||
|
echo " 1 - 启用 BBR 调优"
|
||||||
|
echo " 0 - 还原默认配置"
|
||||||
|
echo " 2 - 对比优化前后参数"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
read -p "请输入选项 (1/0/2): " choice
|
||||||
|
case "$choice" in
|
||||||
|
1) apply_tune ;;
|
||||||
|
0) restore_tune ;;
|
||||||
|
2) compare_params ;;
|
||||||
|
*) echo "✗ 无效选项" ; exit 1 ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
case "$1" in
|
||||||
|
1) apply_tune ;;
|
||||||
|
0) restore_tune ;;
|
||||||
|
2) compare_params ;;
|
||||||
|
*) echo "用法: $0 {1|0|2}
|
||||||
|
1 = 启用调优
|
||||||
|
0 = 还原配置
|
||||||
|
2 = 对比优化前后的参数" ; exit 1 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
@@ -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