feat: debug4 参考维盟 4.0 改版,新增健康指数、DHCP、WAN 状态等(v0.2.0)

界面
- 新 logo“七层”与 favicon;左侧图标菜单 + 顶栏面包屑 / 功能搜索;淡灰紫底色 + 白色圆角卡片
- 登录页、安装向导改为左侧等距插画 + 右侧圆角卡片
- 主机监控分为 在线终端 / 全部终端 / DHCP 租约 / 命名管理

新功能
- 全网概览仪表盘:路由器状态、网络健康指数(逐项扣分与建议)、在线终端、DHCP 地址池、WAN 线路状态、历史在线数
- 终端在线时长、连接数;DHCP 租约列表;应用实时速率;顶栏搜索;CSV 导出
- RouterOS 只读账号额外读取地址池、DHCP 服务器 / 客户端、PPPoE 客户端
- 启动时自动为旧库补新增的列

应用识别(原 0.1.5)
- 导入 v2fly/domain-list-community(MIT),约 2800 个应用、3.8 万条域名
- SNI 识别:TLS ClientHello 与 QUIC Initial(RFC 9001/9369)解析,按连接识别
- 域名查找改为 连接 SNI → 终端 DNS/SNI → 全网 IP 表

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Su YijunandClaude Opus 5.5 committed 2026-10-22 23:41:00 +08:00
1 parent 28135807bd
commit 4c92db8042
59 files changed
+18014 -421

No files matched your search

+9 -6
View File
@@ -11,7 +11,7 @@ import sys
import time
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "backend", "tests"))
from packets import ipfix, tzsp_dns # noqa: E402
from packets import client_hello, ipfix, tcp_segment, tls_record, tzsp_dns # noqa: E402
# (域名, 典型下行 MB/分钟, 上行比例)
SITES = [
@@ -37,6 +37,7 @@ def main():
ap.add_argument("--minutes", type=float, default=3)
ap.add_argument("--hosts", type=int, default=14)
ap.add_argument("--fast", action="store_true", help="不等待,一次性发完")
ap.add_argument("--no-sni", action="store_true", help="不发 TLS 握手(只靠 DNS 识别)")
a = ap.parse_args()
rnd = random.Random(42)
@@ -58,11 +59,13 @@ def main():
for d, mb, up_ratio in prefs[ip]:
down = int(mb * 1024 * 1024 / 6 * rnd.uniform(.3, 1.6)) # 10 秒一轮
up = int(down * up_ratio)
port = 443
flows.append(dict(src=ip, dst=ips[d], sport=rnd.randint(30000, 60000), dport=port, proto=6,
nbytes=up, mac=mac))
flows.append(dict(src=ips[d], dst=ip, sport=port, dport=rnd.randint(30000, 60000), proto=6,
nbytes=down))
port, cport = 443, rnd.randint(30000, 60000)
if not a.no_sni and rnd.random() < .7:
# 模拟 RouterOS 镜像过来的连接首包(TLS ClientHello)
sock.sendto(tcp_segment(ip, ips[d], cport, port, rnd.randint(1, 2**31), tls_record(client_hello(d))),
(a.host, a.tzsp_port))
flows.append(dict(src=ip, dst=ips[d], sport=cport, dport=port, proto=6, nbytes=up, mac=mac))
flows.append(dict(src=ips[d], dst=ip, sport=port, dport=cport, proto=6, nbytes=down))
for i in range(0, len(flows), 20):
sock.sendto(ipfix(flows[i:i + 20], seq=rounds), (a.host, a.ipfix_port))
rounds += 1