Files
ykxmz/_build/test_deploy_device.sh
T
xmz7891 aac43e559d 修正 MTU 测试的过时断言(1500 -> 1400)
test_net_tune.py 原来断言 net-tune.sh 里写着 mtu 1500,
依据是更早一次实测「接口 MTU 降到 1400 会掉到 50%」。

那个结论后来被推翻了 —— 当时漏了一件事:
**MSS 钳制只管 TCP,QUIC 走 UDP 完全不受保护。**
路径 MTU 实测 1472(1500 的包不通),QUIC 默认发 ~1452 的包会被
中间设备静默丢掉,而 UDP 丢了不回 ICMP,只能等超时。
把接口 MTU 压到 1400 之后,QUIC 建齐 10 条连接从 28 秒缩到 12 秒。

所以现在是**故意**设 1400。断言跟着改,
不然它会永久假失败,还会诱导后人把真正的改进改回 1500。

顺带记录:全量回归 38 个阶段全部通过。
新增的三个阶段(局域网优化 / 手表局域网切换 / 编码与换行 /
管理页设备控制)都是这轮加的。
2026-10-07 18:10:30 +08:00

301 lines
14 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/aarch64 设备**上跑 deploy.sh,
# 但把所有写入关进 /root/_dtest/sandbox,并且用桩命令挡住
# systemd / iptables / ip / sysctl —— 绝不能碰正在服务的线上环境。
# ════════════════════════════════════════════════════════════════
#
# 为什么要单独在真机上测:
# 开发机是 Windows/Git Bash。它能验证语法、参数、幂等、
# 生成的 unit 内容,但验不了:
# · 真实 sed/awk/grep 的行为(GNU vs BSD 有差异)
# · 真实的 /proc 读取、python3 -m py_compile
# · 真实文件权限、路径长度、换行处理
# 这一轮把这些补上。
#
# 为什么必须用桩挡 systemd/iptables/ip/sysctl:
# --prefix 只隔离**文件写入**,不隔离**命令副作用**。
# 如果不挡,`systemctl restart lanremote-relay` 会真的把
# 线上中转重启一遍,`iptables` 会真的改线上规则。
#
# 用法:把这个脚本和 relay/ 目录一起放在 /root/_dtest/ 下,
# 然后 bash /root/_dtest/test_deploy_device.sh
# ════════════════════════════════════════════════════════════════
set -u
BASE=/root/_dtest
SRC="$BASE/relay"
BIN="$BASE/bin"
SANDBOX="$BASE/sandbox"
LOG="$BASE/stub.log"
PASS=0
FAIL=0
ok() { PASS=$((PASS + 1)); printf " [PASS] %s\n" "$1"; }
bad() { FAIL=$((FAIL + 1)); printf " [FAIL] %s\n" "$1"; }
check(){ if [ "$2" = "$3" ]; then ok "$1"; else bad "$1(期望 $3,实际 $2)"; fi; }
echo "============================================================"
echo " 真机沙箱测试 · $(uname -m) · $(uname -r)"
echo "============================================================"
echo " 系统: $(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME")"
echo " bash: $(bash --version | head -1)"
[ -f "$SRC/deploy.sh" ] || { echo " 找不到 $SRC/deploy.sh"; exit 1; }
# ────────────────────────────────────────────────────────────
echo
echo "── 0. 先记录线上现状(测完要确认没被碰过)──"
# ⚠ 必须用**绝对路径**抓真实命令。
# 后面会把桩目录插到 PATH 最前面,那时 `systemctl` / `iptables`
# 就都变成桩了;如果第 8 节还按名字调用,比的就是
# "真实系统 vs 桩",必然对不上(真踩过)。
REAL_SYSTEMCTL=$(command -v systemctl 2>/dev/null || echo systemctl)
REAL_IPTABLES=$(command -v iptables 2>/dev/null || echo iptables)
echo " 真实 systemctl: $REAL_SYSTEMCTL"
echo " 真实 iptables: $REAL_IPTABLES"
LIVE_RELAY_ACTIVE=$("$REAL_SYSTEMCTL" is-active lanremote-relay 2>/dev/null | head -1 || echo "?")
LIVE_TUNNEL_ACTIVE=$("$REAL_SYSTEMCTL" is-active lanremote-tunnel 2>/dev/null | head -1 || echo "?")
LIVE_TUNNEL2_ACTIVE=$("$REAL_SYSTEMCTL" is-active lanremote-tunnel-h2 2>/dev/null | head -1 || echo "?")
LIVE_MTU=$(cat /sys/class/net/wlan0/mtu 2>/dev/null || echo "?")
LIVE_MSS=$(("$REAL_IPTABLES" -t mangle -S OUTPUT 2>/dev/null || true) | grep -c TCPMSS | head -1)
LIVE_MSS=${LIVE_MSS:-0}
LIVE_KEEPALIVE=$(cat /proc/sys/net/ipv4/tcp_keepalive_time 2>/dev/null || echo "?")
echo " lanremote-relay = $LIVE_RELAY_ACTIVE"
echo " lanremote-tunnel = $LIVE_TUNNEL_ACTIVE"
echo " lanremote-tunnel-h2 = $LIVE_TUNNEL2_ACTIVE"
echo " wlan0 MTU = $LIVE_MTU"
echo " MSS 规则数 = $LIVE_MSS"
echo " keepalive_time = $LIVE_KEEPALIVE"
# ────────────────────────────────────────────────────────────
echo
echo "── 1. 搭桩命令 ──"
mkdir -p "$BIN" "$SANDBOX"
: > "$LOG"
export STUB_LOG="$LOG"
cat > "$BIN/systemctl" <<'EOF'
#!/bin/bash
echo "systemctl $*" >> "$STUB_LOG"
STATE="$STUB_LOG.state"
touch "$STATE"
SVC=""
for a in "$@"; do
case "$a" in
-*) continue ;;
is-active|is-enabled|start|stop|restart|reload|enable|disable|status|daemon-reload|mask|unmask) continue ;;
*) SVC="$a" ;;
esac
done
case "$1" in
is-active)
if [ -n "$SVC" ] && grep -qxF "$SVC" "$STATE" 2>/dev/null; then
echo "active"; exit 0
fi
echo "inactive"; exit 3 ;;
is-enabled) echo "enabled"; exit 0 ;;
start|restart)
[ -n "$SVC" ] && { grep -qxF "$SVC" "$STATE" 2>/dev/null || echo "$SVC" >> "$STATE"; }
exit 0 ;;
*) exit 0 ;;
esac
EOF
cat > "$BIN/iptables" <<'EOF'
#!/bin/bash
echo "iptables $*" >> "$STUB_LOG"
RULES="$STUB_LOG.rules"; touch "$RULES"
if [ "${1:-}" = "-t" ] && [ "${3:-}" = "-L" ]; then
while read -r r; do [ -n "$r" ] && echo "TCPMSS tcp -- $r"; done < "$RULES"
exit 0
fi
chain=""; mss=""; action=""; prev=""
for a in "$@"; do
case "$prev" in -C|-D|-A) chain="$a";; --set-mss) mss="$a";; esac
case "$a" in -C|-D|-A) [ -z "$action" ] && action="$a";; esac
prev="$a"
done
key="$chain:$mss"
case "$action" in
-C) grep -qxF "$key" "$RULES" 2>/dev/null && exit 0 || exit 1 ;;
-A) grep -qxF "$key" "$RULES" 2>/dev/null || echo "$key" >> "$RULES"; exit 0 ;;
-D) grep -qxF "$key" "$RULES" 2>/dev/null || exit 1
grep -vxF "$key" "$RULES" > "$RULES.t" 2>/dev/null; mv "$RULES.t" "$RULES"; exit 0 ;;
esac
exit 0
EOF
cat > "$BIN/ip" <<'EOF'
#!/bin/bash
echo "ip $*" >> "$STUB_LOG"
[ "${1:-}" = "-4" ] && echo "default via 192.168.10.1 dev wlan0 proto dhcp metric 600"
exit 0
EOF
cat > "$BIN/sysctl" <<'EOF'
#!/bin/bash
echo "sysctl $*" >> "$STUB_LOG"
exit 0
EOF
cat > "$BIN/nmcli" <<'EOF'
#!/bin/bash
echo "nmcli $*" >> "$STUB_LOG"
exit 0
EOF
chmod +x "$BIN"/*
echo " 桩命令: $(ls "$BIN" | tr '\n' ' ')"
echo " (没有桩 curl —— 健康检查走真实 curl,只读无副作用)"
export PATH="$BIN:$PATH"
echo " PATH 首位已切到桩目录"
# ────────────────────────────────────────────────────────────
echo
echo "── 2. 语法检查(真机 bash)──"
for f in deploy.sh deploy-tune.sh net-tune.sh; do
if bash -n "$SRC/$f" 2>/tmp/synerr; then
ok "$f 语法正确"
else
bad "$f 语法错误: $(head -2 /tmp/synerr)"
fi
done
# ────────────────────────────────────────────────────────────
echo
echo "── 3. --help(真机跑)──"
H=$(bash "$SRC/deploy.sh" --help 2>&1)
check "--help 退出码 0" "$?" "0"
echo "$H" | grep -q 'quic / http2 / both' \
&& ok "--help 讲清了 --protocol 三个取值" || bad "--help 没讲 --protocol"
echo "$H" | grep -q '8 条' \
&& ok "--help 写明了配比" || bad "--help 没写配比"
echo "$H" | grep -q '没有任何默认域名' \
&& ok "--help 声明了没有默认域名" || bad "--help 没声明默认域名策略"
# ────────────────────────────────────────────────────────────
echo
echo "── 4. 第一次部署(沙箱 + --skip-tunnel)──"
printf 'lanremote-relay\n' > "$STUB_LOG.state"
OUT1="$BASE/run1.log"
bash "$SRC/deploy.sh" --prefix "$SANDBOX" --skip-tunnel > "$OUT1" 2>&1
check "第一次退出码 0" "$?" "0"
REL="$SANDBOX/root/lanremote-relay"
UNIT="$SANDBOX/etc/systemd/system"
for f in relay.py admin.py landing.py showcase.py favicon.ico deploy-tune.sh net-tune.sh; do
[ -f "$REL/$f" ] && ok "部署了 $f" || bad "没部署 $f"
done
[ -f "$REL/site_shots/pc-main.png" ] && ok "部署了截图" || bad "没部署截图"
for u in lanremote-relay lanremote-nettune lanremote-tunnel lanremote-tunnel-h2; do
[ -f "$UNIT/$u.service" ] && ok "生成了 $u.service" || bad "缺 $u.service"
done
echo
echo " ── 真机生成的隧道单元 ExecStart ──"
grep -E '^ExecStart=' "$UNIT/lanremote-tunnel.service" | sed 's/^/ /'
grep -E '^ExecStart=' "$UNIT/lanremote-tunnel-h2.service" | sed 's/^/ /'
if grep -q -- '--protocol quic' "$UNIT/lanremote-tunnel.service" \
&& grep -q -- '--ha-connections 8' "$UNIT/lanremote-tunnel.service" \
&& grep -q -- '--metrics 127.0.0.1:20241' "$UNIT/lanremote-tunnel.service"; then
ok "主隧道 = quic + 8 + metrics 20241"
else
bad "主隧道参数不对"
fi
if grep -q -- '--protocol http2' "$UNIT/lanremote-tunnel-h2.service" \
&& grep -q -- '--ha-connections 2' "$UNIT/lanremote-tunnel-h2.service" \
&& grep -q -- '--metrics 127.0.0.1:20242' "$UNIT/lanremote-tunnel-h2.service"; then
ok "副本隧道 = http2 + 2 + metrics 20242"
else
bad "副本隧道参数不对"
fi
# ────────────────────────────────────────────────────────────
echo
echo "── 5. 幂等:塞哨兵再跑第二遍 ──"
echo '{"allowNew":false,"codes":[{"deviceId":"x","code":"789113"}]}' \
> "$REL/data/relay_state.json"
S1=$(md5sum "$REL/data/relay_state.json" | awk '{print $1}')
echo "keepme" > "$REL/data/devices.json"
: > "$STUB_LOG"
printf 'lanremote-relay\n' > "$STUB_LOG.state"
bash "$SRC/deploy.sh" --prefix "$SANDBOX" --skip-tunnel > "$BASE/run2.log" 2>&1
check "第二次退出码 0" "$?" "0"
S2=$(md5sum "$REL/data/relay_state.json" | awk '{print $1}')
check "relay_state.json 没被改(allowNew 保住)" "$S2" "$S1"
check "devices.json 没被冲掉" "$(cat "$REL/data/devices.json")" "keepme"
grep -qxF 'systemctl restart lanremote-relay' "$STUB_LOG" 2>/dev/null \
&& bad "无变化却重启了中转" || ok "无变化时没有重启中转"
# ────────────────────────────────────────────────────────────
echo
echo "── 6. 双隧道默认都启用 ──"
CFD="$SANDBOX/root/.cloudflared"
mkdir -p "$CFD"
echo '{"TunnelID":"deadbeef-0000-1111-2222-333344445555"}' \
> "$CFD/deadbeef-0000-1111-2222-333344445555.json"
: > "$STUB_LOG"
printf 'lanremote-relay\n' > "$STUB_LOG.state"
bash "$SRC/deploy.sh" --prefix "$SANDBOX" --hostname relay.test.example \
> "$BASE/run3.log" 2>&1
check "带域名部署退出码 0" "$?" "0"
grep -qxF 'systemctl enable lanremote-tunnel' "$STUB_LOG" \
&& ok "启用了主隧道" || bad "没启用主隧道"
grep -qxF 'systemctl enable lanremote-tunnel-h2' "$STUB_LOG" \
&& ok "启用了副本隧道" || bad "没启用副本隧道(应该双跑)"
echo
echo "── 6b. --protocol http2 只跑副本 ──"
: > "$STUB_LOG"
printf 'lanremote-relay\n' > "$STUB_LOG.state"
bash "$SRC/deploy.sh" --prefix "$SANDBOX" --protocol http2 \
> "$BASE/run4.log" 2>&1
check "--protocol http2 退出码 0" "$?" "0"
grep -qxF 'systemctl enable lanremote-tunnel-h2' "$STUB_LOG" \
&& ok "启用了副本" || bad "没启用副本"
grep -qxF 'systemctl disable --now lanremote-tunnel' "$STUB_LOG" \
&& ok "停用了主隧道" || bad "没停用主隧道"
# ────────────────────────────────────────────────────────────
echo
echo "── 7. 真机健康检查(真 curl,不是桩)──"
if curl -s --max-time 8 http://127.0.0.1:8080/v1/health 2>/dev/null | grep -q '"ok"'; then
ok "线上中转的 /v1/health 通"
else
bad "线上中转没响应"
fi
# ────────────────────────────────────────────────────────────
echo
echo "── 8. 确认线上环境一点没被碰(用真实命令查,不用桩)──"
NOW_RELAY=$("$REAL_SYSTEMCTL" is-active lanremote-relay 2>/dev/null | head -1 || echo "?")
NOW_TUNNEL=$("$REAL_SYSTEMCTL" is-active lanremote-tunnel 2>/dev/null | head -1 || echo "?")
NOW_TUNNEL2=$("$REAL_SYSTEMCTL" is-active lanremote-tunnel-h2 2>/dev/null | head -1 || echo "?")
NOW_MTU=$(cat /sys/class/net/wlan0/mtu 2>/dev/null || echo "?")
NOW_MSS=$(("$REAL_IPTABLES" -t mangle -S OUTPUT 2>/dev/null || true) | grep -c TCPMSS | head -1)
NOW_MSS=${NOW_MSS:-0}
NOW_KA=$(cat /proc/sys/net/ipv4/tcp_keepalive_time 2>/dev/null || echo "?")
echo " 现在: relay=$NOW_RELAY tunnel=$NOW_TUNNEL h2=$NOW_TUNNEL2 MTU=$NOW_MTU MSS=$NOW_MSS KA=$NOW_KA"
check "lanremote-relay 状态没变" "$NOW_RELAY" "$LIVE_RELAY_ACTIVE"
check "lanremote-tunnel 状态没变" "$NOW_TUNNEL" "$LIVE_TUNNEL_ACTIVE"
check "lanremote-tunnel-h2 状态没变" "$NOW_TUNNEL2" "$LIVE_TUNNEL2_ACTIVE"
check "wlan0 MTU 没变" "$NOW_MTU" "$LIVE_MTU"
check "MSS 规则数没变" "$NOW_MSS" "$LIVE_MSS"
check "keepalive 没变" "$NOW_KA" "$LIVE_KEEPALIVE"
# ────────────────────────────────────────────────────────────
echo
echo "── 9. 清理 ──"
rm -rf "$BASE"
echo " 已删除 $BASE"
echo " 线上中转再确认: $(curl -s --max-time 8 http://127.0.0.1:8080/v1/health 2>/dev/null | head -c 70)"
echo
echo "============================================================"
echo " 结果: 通过 $PASS 项,失败 $FAIL 项"
echo "============================================================"
[ "$FAIL" = "0" ] && exit 0 || exit 1