AC2100/route_test.sh

483 lines
19 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# ============================================================
# 三网路由检测脚本 (电信 / 联通 / 移动)
# 适用系统: Debian / Ubuntu
# 功能: 自动安装依赖,测试去程+回程路由
# ============================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
RESET='\033[0m'
# ── 测试目标 IP ──────────────────────────────────────────────
# 去程从本地到这些国内IP
TELECOM_IPS=("202.96.209.5" "61.139.2.69" "211.137.58.20") # 电信各省
UNICOM_IPS=("211.95.46.3" "218.104.111.114" "61.135.169.121") # 联通各省
MOBILE_IPS=("221.179.131.11" "223.5.5.5" "120.196.165.1") # 移动各省
# 回程测试目标
TELECOM_RETURN="180.97.33.108" # 电信上海
UNICOM_RETURN="218.104.111.114" # 联通武汉
MOBILE_RETURN="221.179.131.11" # 移动北京
# ── 辅助函数 ─────────────────────────────────────────────────
print_banner() {
echo -e "${CYAN}"
echo "╔══════════════════════════════════════════════════╗"
echo "║ 三网路由检测脚本 by route_test ║"
echo "║ 电信 / 联通 / 移动 去程 + 回程 ║"
echo "╚══════════════════════════════════════════════════╝"
echo -e "${RESET}"
}
print_section() {
echo ""
echo -e "${BOLD}${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BOLD}${YELLOW} $1${RESET}"
echo -e "${BOLD}${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
}
check_root() {
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}[错误] 请使用 root 用户运行此脚本${RESET}"
echo " sudo bash route_test.sh"
exit 1
fi
}
get_vps_info() {
print_section "📋 VPS 基本信息"
local ip=$(curl -s --max-time 5 ip.sb 2>/dev/null || echo "获取失败")
local info=$(curl -s --max-time 5 ipinfo.io 2>/dev/null)
local org=$(echo "$info" | grep '"org"' | cut -d'"' -f4)
local city=$(echo "$info" | grep '"city"' | cut -d'"' -f4)
local country=$(echo "$info" | grep '"country"' | cut -d'"' -f4)
echo -e " IP地址 : ${GREEN}${ip}${RESET}"
echo -e " 归属地 : ${GREEN}${country} ${city}${RESET}"
echo -e " ASN/运营: ${GREEN}${org}${RESET}"
echo -e " 系统 : ${GREEN}$(lsb_release -d 2>/dev/null | cut -f2)${RESET}"
echo -e " 内核 : ${GREEN}$(uname -r)${RESET}"
echo -e " 时间 : ${GREEN}$(date '+%Y-%m-%d %H:%M:%S %Z')${RESET}"
}
install_deps() {
print_section "🔧 检查并安装依赖"
# ── 检测系统架构 ──────────────────────────────────────────
local arch_raw
arch_raw=$(uname -m)
local nt_arch=""
case "$arch_raw" in
x86_64) nt_arch="amd64" ;;
aarch64|arm64) nt_arch="arm64" ;;
armv7l|armv6l) nt_arch="armv7" ;;
mips) nt_arch="mips" ;;
mips64) nt_arch="mips64";;
mipsle|mipsel) nt_arch="mipsle";;
*) nt_arch="" ;;
esac
echo -e " 系统架构: ${GREEN}${arch_raw}${RESET} → nexttrace 目标包: ${GREEN}${nt_arch:-不支持}${RESET}"
apt-get update -qq 2>/dev/null
# mtr
if ! command -v mtr &>/dev/null; then
echo -e " ${YELLOW}安装 mtr...${RESET}"
apt-get install -y -qq mtr-tiny 2>/dev/null && \
echo -e " ${GREEN}✔ mtr 安装完成${RESET}" || \
echo -e " ${RED}✘ mtr 安装失败${RESET}"
else
echo -e " ${GREEN}✔ mtr 已安装${RESET}"
fi
# traceroute
if ! command -v traceroute &>/dev/null; then
echo -e " ${YELLOW}安装 traceroute...${RESET}"
apt-get install -y -qq traceroute 2>/dev/null && \
echo -e " ${GREEN}✔ traceroute 安装完成${RESET}" || \
echo -e " ${RED}✘ traceroute 安装失败${RESET}"
else
echo -e " ${GREEN}✔ traceroute 已安装${RESET}"
fi
# nexttrace
if command -v nexttrace &>/dev/null; then
echo -e " ${GREEN}✔ nexttrace 已安装 $(nexttrace --version 2>/dev/null | head -1)${RESET}"
return
fi
if [[ -z "$nt_arch" ]]; then
echo -e " ${RED}✘ 不支持的架构 (${arch_raw}),跳过 nexttrace 安装${RESET}"
return
fi
echo -e " ${YELLOW}安装 nexttrace (${nt_arch})...${RESET}"
local nt_bin="/usr/local/bin/nexttrace"
local nt_file="nexttrace_linux_${nt_arch}"
local gh_url="https://github.com/nxtrace/NTrace-core/releases/latest/download/${nt_file}"
local mirror_url="https://ghfast.top/${gh_url}"
# 依次尝试: 官方安装脚本 → GitHub 直链 → ghfast 镜像
local installed=0
# 方法1: 官方安装脚本
echo -e " ${CYAN}[1/3] 尝试官方安装脚本...${RESET}"
if curl -sf --max-time 15 https://raw.githubusercontent.com/nxtrace/NTrace-core/main/nt_install.sh \
| bash -s -- --yes &>/dev/null; then
command -v nexttrace &>/dev/null && installed=1
fi
# 方法2: GitHub Releases 直链
if [[ $installed -eq 0 ]]; then
echo -e " ${CYAN}[2/3] 尝试 GitHub 直链下载...${RESET}"
if curl -fL --max-time 30 -o "$nt_bin" "$gh_url" 2>/dev/null; then
chmod +x "$nt_bin"
command -v nexttrace &>/dev/null && installed=1
fi
fi
# 方法3: ghfast 镜像
if [[ $installed -eq 0 ]]; then
echo -e " ${CYAN}[3/3] 尝试 ghfast 镜像下载...${RESET}"
if curl -fL --max-time 30 -o "$nt_bin" "$mirror_url" 2>/dev/null; then
chmod +x "$nt_bin"
command -v nexttrace &>/dev/null && installed=1
fi
fi
if [[ $installed -eq 1 ]]; then
echo -e " ${GREEN}✔ nexttrace 安装完成 $(nexttrace --version 2>/dev/null | head -1)${RESET}"
else
echo -e " ${RED}✘ nexttrace 全部安装方式失败,回程追踪将降级为 traceroute${RESET}"
echo -e " ${YELLOW} 手动安装: curl -Lo ${nt_bin} ${mirror_url} && chmod +x ${nt_bin}${RESET}"
fi
}
# ── 全局结果收集变量 ─────────────────────────────────────────
# 延迟(ms): -1 = 超时
TELECOM_AVG_LATENCY=-1
UNICOM_AVG_LATENCY=-1
MOBILE_AVG_LATENCY=-1
# 回程线路识别: unknown / cn2 / 163 / 9929 / 4837 / cmin2 / cmnet / mixed
TELECOM_RETURN_LINE="unknown"
UNICOM_RETURN_LINE="unknown"
MOBILE_RETURN_LINE="unknown"
# MTR 最终跳丢包率(%)
TELECOM_LOSS=-1
UNICOM_LOSS=-1
MOBILE_LOSS=-1
# ── 去程测试ping延迟────────────────────────────────────────
ping_test() {
local ip=$1
local result
result=$(ping -c 3 -W 3 "$ip" 2>/dev/null | tail -1 | awk -F'/' '{print $5}')
if [[ -n "$result" ]]; then
echo -e " ${ip}${GREEN}${result} ms${RESET}"
echo "$result" # 供调用方捕获
else
echo -e " ${ip}${RED}超时/不可达${RESET}"
echo "-1"
fi
}
# 计算一组 IP 的平均延迟(忽略超时)
avg_latency_for() {
local -n _ips=$1
local total=0 count=0
for ip in "${_ips[@]}"; do
local val
val=$(ping -c 3 -W 3 "$ip" 2>/dev/null | tail -1 | awk -F'/' '{print $5}')
if [[ -n "$val" && "$val" != "-1" ]]; then
total=$(awk "BEGIN{print $total + $val}")
((count++))
fi
done
if [[ $count -gt 0 ]]; then
awk "BEGIN{printf \"%.1f\", $total / $count}"
else
echo "-1"
fi
}
outbound_test() {
print_section "📤 去程延迟测试VPS → 国内)"
echo -e "\n ${BOLD}▶ 电信${RESET}"
for ip in "${TELECOM_IPS[@]}"; do ping_test "$ip" > /dev/null; done
# 单独计算平均值供评估用(不重复输出)
for ip in "${TELECOM_IPS[@]}"; do
local r; r=$(ping -c 3 -W 3 "$ip" 2>/dev/null | tail -1 | awk -F'/' '{print $5}')
[[ -n "$r" ]] && echo -e " ${ip}${GREEN}${r} ms${RESET}" || echo -e " ${ip}${RED}超时/不可达${RESET}"
done
TELECOM_AVG_LATENCY=$(avg_latency_for TELECOM_IPS)
echo -e "\n ${BOLD}▶ 联通${RESET}"
for ip in "${UNICOM_IPS[@]}"; do
local r; r=$(ping -c 3 -W 3 "$ip" 2>/dev/null | tail -1 | awk -F'/' '{print $5}')
[[ -n "$r" ]] && echo -e " ${ip}${GREEN}${r} ms${RESET}" || echo -e " ${ip}${RED}超时/不可达${RESET}"
done
UNICOM_AVG_LATENCY=$(avg_latency_for UNICOM_IPS)
echo -e "\n ${BOLD}▶ 移动${RESET}"
for ip in "${MOBILE_IPS[@]}"; do
local r; r=$(ping -c 3 -W 3 "$ip" 2>/dev/null | tail -1 | awk -F'/' '{print $5}')
[[ -n "$r" ]] && echo -e " ${ip}${GREEN}${r} ms${RESET}" || echo -e " ${ip}${RED}超时/不可达${RESET}"
done
MOBILE_AVG_LATENCY=$(avg_latency_for MOBILE_IPS)
}
# ── 回程路由追踪 + 线路识别 ───────────────────────────────────
# 返回识别到的线路标签,写入全局变量 _DETECTED_LINE
_DETECTED_LINE="unknown"
detect_line_from_trace() {
local trace_output="$1"
local lower
lower=$(echo "$trace_output" | tr '[:upper:]' '[:lower:]')
if echo "$lower" | grep -qE 'as4809|cn2|cnc-gia'; then
_DETECTED_LINE="cn2"
elif echo "$lower" | grep -qE 'as58807|cmin2'; then
_DETECTED_LINE="cmin2"
elif echo "$lower" | grep -qE 'as9929|cnc-backbone|cuii'; then
_DETECTED_LINE="9929"
elif echo "$lower" | grep -qE 'as4837|cu169|169骨干'; then
_DETECTED_LINE="4837"
elif echo "$lower" | grep -qE 'as9808|cmnet'; then
_DETECTED_LINE="cmnet"
elif echo "$lower" | grep -qE 'as4134|chinanet|163骨干'; then
_DETECTED_LINE="163"
else
_DETECTED_LINE="unknown"
fi
}
return_route_nexttrace() {
local label=$1
local ip=$2
local color=$3
local result_var=$4 # 写回哪个全局变量
echo -e "\n ${color}${BOLD}${label} 回程路由 → ${ip}${RESET}"
echo -e " ${CYAN}$(printf '─%.0s' {1..48})${RESET}"
local trace_output=""
if command -v nexttrace &>/dev/null; then
trace_output=$(nexttrace --no-rdns "$ip" 2>/dev/null)
echo "$trace_output" | grep -v "^$" | sed 's/^/ /'
else
echo -e " ${RED}nexttrace 未安装,使用 traceroute 替代无ASN信息${RESET}"
trace_output=$(traceroute -n -m 20 "$ip" 2>/dev/null)
echo "$trace_output" | sed 's/^/ /'
fi
detect_line_from_trace "$trace_output"
eval "$result_var=\"$_DETECTED_LINE\""
}
return_route_test() {
print_section "📥 回程路由追踪VPS → 国内三网)"
echo -e " ${YELLOW}提示: 关注 AS 号码${RESET}"
echo -e " ${YELLOW} 电信CN2: as4809 普通163: as4134${RESET}"
echo -e " ${YELLOW} 联通9929: as9929 普通169: as4837${RESET}"
echo -e " ${YELLOW} 移动CMIN2: as58807 普通CMNet: as9808${RESET}"
return_route_nexttrace "电信" "$TELECOM_RETURN" "$RED" "TELECOM_RETURN_LINE"
return_route_nexttrace "联通" "$UNICOM_RETURN" "$BLUE" "UNICOM_RETURN_LINE"
return_route_nexttrace "移动" "$MOBILE_RETURN" "$GREEN" "MOBILE_RETURN_LINE"
}
# ── MTR 质量测试 ──────────────────────────────────────────────
mtr_test() {
print_section "📊 MTR 丢包率测试各发送10个包"
_run_mtr() {
local label=$1 ip=$2 loss_var=$3
echo -e "\n ${BOLD}${label} (${ip})${RESET}"
if command -v mtr &>/dev/null; then
local mtr_out
mtr_out=$(mtr --report --report-cycles 10 --no-dns "$ip" 2>/dev/null)
echo "$mtr_out" | awk 'NR>1 {printf " %-5s %-18s Loss:%5s Avg:%7s ms\n", $1, $2, $3, $8}' | head -20
# 取最后一个有效跳的丢包率
local loss
loss=$(echo "$mtr_out" | awk 'NR>1 && $2!="???" {last=$3} END{print last}' | tr -d '%')
eval "$loss_var=\"${loss:--1}\""
else
echo -e " ${RED}mtr 未安装${RESET}"
fi
}
_run_mtr "电信上海" "$TELECOM_RETURN" "TELECOM_LOSS"
_run_mtr "联通武汉" "$UNICOM_RETURN" "UNICOM_LOSS"
_run_mtr "移动北京" "$MOBILE_RETURN" "MOBILE_LOSS"
}
# ── 线路评级 ─────────────────────────────────────────────────
# 入参: 线路标签字符串,返回评分 0~5 和颜色
rate_line() {
local line=$1
case "$line" in
cn2) echo "5|${GREEN}|CN2/GIA ⭐⭐⭐⭐⭐ 顶级优化线路" ;;
9929) echo "4|${GREEN}|AS9929 ⭐⭐⭐⭐ 联通精品线路" ;;
cmin2) echo "4|${GREEN}|CMIN2 ⭐⭐⭐⭐ 移动精品线路" ;;
4837) echo "2|${YELLOW}|AS4837 ⭐⭐ 联通普通169骨干" ;;
cmnet) echo "2|${YELLOW}|CMNet ⭐⭐ 移动普通骨干" ;;
163) echo "1|${YELLOW}|AS4134 ⭐ 电信普通163骨干" ;;
unknown) echo "0|${RED}|未识别 ❓ 无法判断线路质量" ;;
*) echo "0|${RED}|未知 ❓ 无法判断线路质量" ;;
esac
}
# 延迟评级
rate_latency() {
local ms=$1
if [[ "$ms" == "-1" ]]; then
echo "${RED}超时/全部不可达"
elif awk "BEGIN{exit !($ms < 150)}"; then
echo "${GREEN}${ms} ms 极佳 (<150ms)"
elif awk "BEGIN{exit !($ms < 200)}"; then
echo "${GREEN}${ms} ms 良好 (<200ms)"
elif awk "BEGIN{exit !($ms < 250)}"; then
echo "${YELLOW}${ms} ms 一般 (<250ms)"
else
echo "${RED}${ms} ms 较差 (>250ms)"
fi
}
# 丢包评级
rate_loss() {
local loss=$1
if [[ "$loss" == "-1" ]]; then
echo "${YELLOW}未测试"
elif awk "BEGIN{exit !($loss == 0)}"; then
echo "${GREEN}0% 无丢包"
elif awk "BEGIN{exit !($loss <= 2)}"; then
echo "${GREEN}${loss}% 正常"
elif awk "BEGIN{exit !($loss <= 5)}"; then
echo "${YELLOW}${loss}% 轻微丢包"
elif awk "BEGIN{exit !($loss <= 15)}"; then
echo "${RED}${loss}% 丢包明显"
else
echo "${RED}${loss}% 丢包严重"
fi
}
# ── 综合评估 ─────────────────────────────────────────────────
evaluate() {
print_section "📊 综合评估报告"
local isp_names=("电信" "联通" "移动")
local return_lines=("$TELECOM_RETURN_LINE" "$UNICOM_RETURN_LINE" "$MOBILE_RETURN_LINE")
local latencies=("$TELECOM_AVG_LATENCY" "$UNICOM_AVG_LATENCY" "$MOBILE_AVG_LATENCY")
local losses=("$TELECOM_LOSS" "$UNICOM_LOSS" "$MOBILE_LOSS")
local colors=("$RED" "$BLUE" "$GREEN")
local total_score=0
local scored_count=0
printf "\n %-6s %-28s %-22s %-14s\n" "运营商" "回程线路" "去程延迟" "丢包率"
echo -e " ${BLUE}$(printf '─%.0s' {1..75})${RESET}"
for i in 0 1 2; do
local isp="${isp_names[$i]}"
local line="${return_lines[$i]}"
local lat="${latencies[$i]}"
local loss="${losses[$i]}"
local color="${colors[$i]}"
local rate_info; rate_info=$(rate_line "$line")
local score="${rate_info%%|*}"
local line_color; line_color=$(echo "$rate_info" | cut -d'|' -f2)
local line_label; line_label=$(echo "$rate_info" | cut -d'|' -f3)
local lat_str; lat_str=$(rate_latency "$lat")
local loss_str; loss_str=$(rate_loss "$loss")
printf " ${color}${BOLD}%-6s${RESET} ${line_color}%-28s${RESET} ${lat_str}%-0s${RESET} ${loss_str}%-0s${RESET}\n" \
"$isp" "$line_label" "" ""
if [[ "$score" -gt 0 ]]; then
total_score=$((total_score + score))
((scored_count++))
fi
done
echo -e " ${BLUE}$(printf '─%.0s' {1..75})${RESET}"
# ── 总体评语 ──────────────────────────────────────────────
echo ""
local avg_score=0
[[ $scored_count -gt 0 ]] && avg_score=$((total_score / scored_count))
local verdict=""
local verdict_color=""
if [[ $avg_score -ge 4 ]]; then
verdict_color="${GREEN}"
verdict="优质线路,三网均走精品通道,适合对延迟敏感的业务(游戏/直播/企业)"
elif [[ $avg_score -eq 3 ]]; then
verdict_color="${GREEN}"
verdict="良好线路,部分运营商走优化通道,日常使用体验较好"
elif [[ $avg_score -eq 2 ]]; then
verdict_color="${YELLOW}"
verdict="普通线路,走普通骨干网,延迟和稳定性一般,适合轻度使用"
elif [[ $avg_score -eq 1 ]]; then
verdict_color="${YELLOW}"
verdict="较差线路,高峰期可能拥堵,建议评估是否符合使用需求"
else
verdict_color="${RED}"
verdict="线路质量未知或较差,建议进一步测试或联系商家确认"
fi
echo -e " ${BOLD}总体评价: ${verdict_color}${verdict}${RESET}"
# ── 分运营商建议 ──────────────────────────────────────────
echo ""
echo -e " ${BOLD}分运营商建议:${RESET}"
for i in 0 1 2; do
local isp="${isp_names[$i]}"
local line="${return_lines[$i]}"
local lat="${latencies[$i]}"
local tip=""
case "$line" in
cn2|9929|cmin2)
tip="✅ 线路优质,推荐使用"
[[ $(awk "BEGIN{print ($lat > 0 && $lat > 220)}") == "1" ]] && \
tip="${tip}(延迟略高,属正常范围)"
;;
4837|cmnet|163)
tip="⚠️ 普通骨干线路,高峰期可能拥堵,敏感业务慎用"
;;
unknown)
tip="❓ 线路未能识别,建议手动查看回程路由中的 AS 号"
;;
esac
echo -e " ${colors[$i]}${isp}${RESET}: ${tip}"
done
echo ""
echo -e " ${CYAN}如需可视化路由图,访问 nexttrace 输出的 MapTrace URL${RESET}"
echo -e " ${CYAN}如需重新测试: bash route_test.sh${RESET}"
echo ""
}
# ── 主流程 ────────────────────────────────────────────────────
main() {
check_root
print_banner
get_vps_info
install_deps
outbound_test
return_route_test
mtr_test
evaluate
}
main