#!/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