#!/bin/bash
# ============================================================================
#  4W 线路自测脚本  v1  (https://ping.4w.ee/tools/line-check.sh)
# ============================================================================
#  在**你自己的服务器**上跑，测这台机器到中国大陆方向的线路质量。
#
#  它做什么：向几个公开的国内节点发 ICMP 包，统计延迟/抖动/丢包，并跑一次路由。
#  它不做什么：不改任何配置、不装任何东西、不上传任何数据到我们这儿。
#
#  用法：
#     bash line-check.sh              # 全部测试
#     bash line-check.sh --quick      # 只跑延迟（约 20 秒）
#
#  为什么选这些目标：
#     · 223.5.5.5 / 119.29.29.29 —— 阿里 / 腾讯的公共 DNS，全国都有，最稳
#     · 三大运营商的公开 DNS —— 能大致区分你的机器到哪家更近
#     注意：DNS 服务器可能对 ICMP 限速，数字只作**横向参考**，别当绝对值。
# ============================================================================
set -uo pipefail

QUICK=0
[ "${1:-}" = "--quick" ] && QUICK=1

# 目标清单：显示名|地址
TARGETS=(
  "阿里 DNS (全国通用)|223.5.5.5"
  "腾讯 DNS (全国通用)|119.29.29.29"
  "电信 上海|202.96.209.133"
  "联通 北京|123.125.99.1"
  "移动 广州|211.136.192.6"
)

# 站点节点（用于对比"商业线路"大概什么水平）
NODES=(
  "香港节点 ping.4w.ee|179.236.105.113"
  "日本节点 ping.4w.ee|193.111.30.119"
  "美国节点 ping.4w.ee|179.253.242.67"
)

ok()   { printf '  \033[32m✓\033[0m %s\n' "$*"; }
warn() { printf '  \033[33m!\033[0m %s\n' "$*"; }
bad()  { printf '  \033[31m✗\033[0m %s\n' "$*"; }
hdr()  { printf '\n\033[1m%s\033[0m\n' "$*"; }

# ---------------------------------------------------------------------------
hdr "0. 环境检查"
OS=$( (cat /etc/os-release 2>/dev/null | grep -m1 PRETTY_NAME | cut -d= -f2 | tr -d '"') || echo "未知")
printf '  系统      : %s\n' "$OS"
printf '  内核      : %s (%s)\n' "$(uname -r)" "$(uname -m)"
printf '  主机名    : %s\n' "$(hostname)"
if command -v systemd-detect-virt >/dev/null 2>&1; then
  printf '  虚拟化    : %s\n' "$(systemd-detect-virt 2>/dev/null || echo unknown)"
fi
printf '  时间      : %s\n' "$(date '+%F %T %Z')"

for t in ping; do
  command -v $t >/dev/null 2>&1 || { bad "缺少 $t —— 请先安装（Debian/Ubuntu: apt install iputils-ping）"; exit 1; }
done
ok "ping 可用"
command -v mtr >/dev/null 2>&1 && ok "mtr 可用（会多测路由）" || warn "没有 mtr（跳过一次路由测试，可选）"
command -v curl >/dev/null 2>&1 && ok "curl 可用" || warn "没有 curl（跳过 HTTP 测试，可选）"

# ---------------------------------------------------------------------------
hdr "1. 到国内方向的延迟与丢包（每目标 20 包）"
printf '  %-22s %8s %8s %8s %8s\n' "目标" "平均" "最小" "最大" "丢包"
printf '  %s\n' "------------------------------------------------------------------"

declare -a RESULTS
for item in "${TARGETS[@]}"; do
  name="${item%%|*}"; ip="${item##*|}"
  out=$(ping -c 20 -i 0.2 -W 2 "$ip" 2>/dev/null)
  if [ -z "$out" ]; then
    printf '  %-22s %8s %8s %8s %8s\n' "$name" "—" "—" "—" "100%"
    RESULTS+=("$name|fail")
    continue
  fi
  loss=$(echo "$out" | grep -oE '[0-9]+(\.[0-9]+)?% packet loss' | grep -oE '^[0-9.]+')
  # ⚠️ 解析要小心：ping 的汇总行是  min/avg/max/mdev = a/b/c/d ms
  #    第一版只 grep 到 `= a/b/c` 三段就去 cut，结果**列串位**（最大列显示 mdev）✗
  #    ⇒ 取 `=` 之后到 `ms` 之前的**整串**，再按 / 切四段 ✓
  vals=$(echo "$out" | grep -oE '= [0-9.]+/[0-9.]+/[0-9.]+/[0-9.]+' | sed 's/^= //')
  if [ -n "$vals" ]; then
    min=$(echo "$vals" | cut -d/ -f1)
    avg=$(echo "$vals" | cut -d/ -f2)
    max=$(echo "$vals" | cut -d/ -f3)
    mdev=$(echo "$vals" | cut -d/ -f4)
    printf '  %-22s %7sms %7sms %7sms %7s%%   (抖动 %sms)\n' \
      "$name" "$avg" "$min" "$max" "${loss:-?}" "$mdev"
    RESULTS+=("$name|$avg|${loss:-0}")
  else
    printf '  %-22s %8s %8s %8s %7s%%\n' "$name" "—" "—" "—" "${loss:-?}"
    RESULTS+=("$name|fail")
  fi
done

if [ "$QUICK" = "0" ]; then
  hdr "2. 到本站各区域节点的延迟（看你的机器离哪边近）"
  printf '  %-26s %8s %8s\n' "节点" "平均" "丢包"
  printf '  %s\n' "------------------------------------------------"
  for item in "${NODES[@]}"; do
    name="${item%%|*}"; ip="${item##*|}"
    out=$(ping -c 10 -i 0.2 -W 2 "$ip" 2>/dev/null)
    loss=$(echo "$out" | grep -oE '[0-9]+(\.[0-9]+)?% packet loss' | grep -oE '^[0-9.]+')
    avg=$(echo "$out" | tail -1 | grep -oE '= [0-9.]+/[0-9.]+' | awk -F'[=/]' '{print $3}')
    printf '  %-26s %7sms %7s%%\n' "$name" "${avg:-—}" "${loss:-—}"
  done
fi

if [ "$QUICK" = "0" ] && command -v mtr >/dev/null 2>&1; then
  hdr "3. 到香港节点的路由（看有没有绕路）"
  printf '  （只显示前 12 跳；关注"从第几跳开始离开你的机房"和总跳数）\n\n'
  mtr -n -c 3 -r -m 12 179.236.105.113 2>/dev/null | sed 's/^/  /' \
    || warn "mtr 执行失败（有些容器环境不允许原始套接字）"
fi

hdr "4. 怎么读这些数字"
cat <<'EOF'
  延迟：
    到国内 DNS 在 200ms 以内、且四个数（最小/平均/最大）彼此接近  → 线路稳
    平均值比最小值高出很多（比如最小 40、平均 120）              → 抖动大，可能有绕路或拥塞
  丢包：
    0%      → 这条方向是干净的
    0-1%    → 正常，DNS 服务器本身可能限速，这个量级可忽略
    1-3%    → 值得注意，换个时段再测一次
    >3%     → 明显有问题；如果多个目标都这样，基本是线路问题而不是目标问题
  重要：ICMP 只反映"网络层通不通、快不快"，不等于 TCP/应用层的实际速度。
        而且**高峰期（晚 19:00-24:00）和白天差别可能很大** —— 建议两个时段各测一次。

  想看持续的、每小时更新的对比数据（含晚高峰），到 https://ping.4w.ee/
EOF

hdr "5. 小结"
fail=0; badloss=0
for r in "${RESULTS[@]}"; do
  IFS='|' read -r nm avg loss <<< "$r"
  [ "$avg" = "fail" ] && { fail=$((fail+1)); continue; }
  awk -v l="$loss" 'BEGIN{exit !(l+0 > 3)}' && badloss=$((badloss+1))
done
if [ "$fail" -gt 0 ]; then
  warn "$fail 个目标完全不通（可能是 ICMP 被防火墙挡了，不一定是坏消息）"
fi
if [ "$badloss" -gt 0 ]; then
  bad "$badloss 个目标丢包 >3% —— 建议换个时段复测，或同时用 TCP 方式（tcping）复核"
else
  ok "没有目标出现 >3% 的丢包"
fi

cat <<'EOF'

  ────────────────────────────────────────────────────────
  实测数据来自你自己的机器、你自己的网络 —— 这是最有参考价值的。
  如果想把它和商业线路横向比，去 https://ping.4w.ee/ 看 12 台机房的
  每小时实测（同一套目标、同一套参数、含晚高峰）。
  ────────────────────────────────────────────────────────
EOF
