#!/bin/sh
# ============================================================================
# 切换到「全灭」模式后的效果验证（只读，不改设备设置）
#
# 用法（在路由上执行）：
#     sh /tmp/ogcheck.sh
#
# 采样那 20 秒里请用客户端连一次你关心的协议（例如打开 Clash 上一个网页）。
# 脚本会回答三个问题：
#   ① 队列规则是不是真的"全量"了（不带 appmark 限制）
#   ② 引擎有没有在判定（decided_streams 有没有涨）
#   ③ 有没有真的命中屏蔽规则（引擎日志里的「命中规则 panel:shield:...」）
# ============================================================================
J=/usr/sbin/jq
[ -x "$J" ] || J=$(command -v jq 2>/dev/null)
WIN=20
hr() { echo "──────────────────────────────────────────────"; }

APPDIR=""
for d in /etc/log/apps/opengfw-*; do [ -d "$d" ] && APPDIR="$d"; done
[ -n "$APPDIR" ] || { echo "❌ 没找到已安装的 opengfw"; exit 0; }
VPID=""
for p in /proc/[0-9]*; do
  [ -r "$p/cmdline" ] || continue
  tr '\0' ' ' < "$p/cmdline" 2>/dev/null | grep -q opengfwd && { VPID=${p#/proc/}; break; }
done
argof() { [ -n "$VPID" ] || return; tr '\0' '\n' < "/proc/$VPID/cmdline" 2>/dev/null | awk -v f="$1" 'p==f{print;exit}{p=$0}'; }
envof() { [ -r "$1" ] && sed -n "s/^$2=//p" "$1" | head -1; }
ENVF="$APPDIR/.pkg/app/.env"
LISTEN=$(argof -listen); WUSER=$(argof -web-user); WPASS=$(argof -web-password)
[ -n "$WUSER" ] || WUSER=$(envof "$ENVF" OPENGFW_WEB_USER)
[ -n "$WPASS" ] || WPASS=$(envof "$ENVF" OPENGFW_WEB_PASSWORD)
[ -n "${1:-}" ] && WPASS="$1"
HOST="${LISTEN%:*}"; PORT="${LISTEN##*:}"
case "$HOST" in ""|0.0.0.0|"::"|"[::]") HOST=127.0.0.1 ;; esac
[ -n "$PORT" ] || PORT=9170
BASE="https://$HOST:$PORT"
api() { curl -sk -m 15 -u "$WUSER:$WPASS" "$BASE$1"; }
LOG="$APPDIR/.pkg/log/engine.log"

echo "opengfw 效果验证   $(date '+%Y-%m-%d %H:%M:%S')"
echo "面板: $BASE   引擎 pid: ${VPID:-未运行}"
hr

echo "① 队列规则（全量接管应为：只有噪声条件，没有 appmark/ikverdict）"
for c in FORWARD; do
  iptables -w -S $c 2>/dev/null | grep NFQUEUE | sed 's/^/  /'
done
if iptables -w -S FORWARD 2>/dev/null | grep NFQUEUE | grep -q appmark; then
  echo "  ❗ 规则里仍带 appmark → 还是「辅助增强」，插件看不到流的载荷"
  echo "     改法：面板 → 原生配置预览 → config.yaml → ikuai.scope 改成 full → 保存"
fi
echo "  当前: $(api /api/status | $J -r '"识别范围=" + .settings.scope + "  流量筛选=" + .settings.queue_only + "  拦截=" + (.fw_active|tostring)')"
hr

echo "② 采样 ${WIN} 秒 —— 现在请用客户端连一次要测的协议"
A=$(api /api/status | $J -c '.stats')
q() { for c in INPUT OUTPUT FORWARD; do iptables -w -L $c -v -n -x 2>/dev/null | grep NFQUEUE; done | awk '{s+=$1} END {print s+0}'; }
QA=$(q); LA=$(wc -c < "$LOG" 2>/dev/null || echo 0)
sleep "$WIN"
QB=$(q); B=$(api /api/status | $J -c '.stats')
hr
echo "③ 结果"
# 设备上的 jq 是老版本（不支持 --slurpfile/--argfile），所以逐字段取值再用 shell 算差值。
val() { echo "$1" | $J -r "$2 // 0"; }
QA_INC=$((QB-QA))
TP=$(( $(val "$B" .total_packets) - $(val "$A" .total_packets) ))
DS=$(( $(val "$B" .decided_streams) - $(val "$A" .decided_streams) ))
BP=$(( $(val "$B" .blocked_packets) - $(val "$A" .blocked_packets) ))
FE=$(( $(val "$B" .shadowsocks_catches) - $(val "$A" .shadowsocks_catches) ))
TR=$(( $(val "$B" .trojan_catches) - $(val "$A" .trojan_catches) ))
SS=$(( $(val "$B" .ssh_catches) - $(val "$A" .ssh_catches) ))
echo "  队列包数增量: $QA_INC"
echo "  引擎处理报文: $TP"
echo "  给出判定的流: $DS"
echo "  被拦截报文:   $BP"
echo "  各协议命中:   fet(SS/VMess)= $FE  trojan= $TR  ssh= $SS"
echo
echo "  引擎日志里这几秒的判定（关键看有没有 fet 与「命中规则」）:"
if [ -s "$LOG" ]; then
  tail -c +$((LA+1)) "$LOG" 2>/dev/null | grep -aE 'fet|命中规则|流识别' | tail -8 | cut -c1-170 | sed 's/^/    /'
  echo "    命中规则统计: $(tail -c +$((LA+1)) "$LOG" 2>/dev/null | grep -ac '命中规则')"
else
  echo "    （引擎日志为空 —— 重启后还没有任何流经过）"
fi
hr
echo "怎么读这份结果："
echo "  · 引擎处理报文有增量 + 给出判定的流有增量 → 插件确实在看这些流量"
echo "  · 日志里出现 map[fet:… yes:true] 且紧跟「命中规则 panel:shield:shadowsocks」→ 拦住并记录了"
echo "  · 有判定流入但 fet 命中为 0 → 这条流的首包不像全加密（可能是 TLS 伪装或 UDP）"
echo "本轮结束，把整段输出发回。"
