人员与场景 - 人员 / 家庭成员、部门 / 分组,设备归属,按人统计流量、在线时长、常用应用 - 家用:屏幕时间、游戏、短视频上限,熬夜,不良内容,学习时间玩娱乐;企业:工作时间娱乐占比、求职倾向、上班炒股 - 行为洞察(最近 7 天标签附依据)、效率 / 时长排行 - 终端类型识别:主机名、IEEE OUI 厂商、用过的应用、随机 MAC;可手动指定 审计 - 访问日志、时间线、报表按人员 / 部门;规则范围可选人员、部门 - 新规则:大流量外发、私接路由(IP TTL);私接检测页 - RouterOS 管控命令生成(封禁应用、定时断网、限速,附撤销),不写路由器 - Syslog 外发(RFC 5424);网页设置留存天数,显示数据库占用 通知(参照 EasySub) - 13 种渠道,磁贴选择,可用未保存配置测试,密钥遮盖,旧配置自动迁移 - 规则可选渠道;免打扰期间先记下,结束后汇总推送 界面 - 应用统计大类、小类固定列出,矩形树图;访问目标世界地图与国家分布 - 时间范围只保留 今日 / 昨日 / 本周 / 本月;上下行颜色全站统一 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
2.2 KiB
Python
50 lines
2.2 KiB
Python
"""私接路由 / 共享上网检测(参照深信服 AC 的“防共享上网”,只用镜像包里的 IP TTL 做被动判断)。
|
||
|
||
系统发出的包 TTL 初始值一般是 64(Linux、安卓、苹果)、128(Windows)或 255。每经过一个路由器减 1。
|
||
ROS-DPI 看到的是路由器镜像过来的包(forward 链,路由器自己已减过 1),所以正常设备一般是 63 / 127。
|
||
- 某台“设备”的包比全网常见的跳数还多一跳:它后面很可能接了路由器(私接路由、随身 WiFi、软路由)。
|
||
- 同一台“设备”同时出现两种系统的 TTL:后面可能有多台不同系统的设备在共享。
|
||
只作为提示:样本是 HTTPS / QUIC 握手和 DNS 之外的首包,数量有限;VPN、虚拟机、容器也可能造成同样的现象。
|
||
"""
|
||
from collections import Counter
|
||
|
||
BASES = (64, 128, 255)
|
||
MIN_PACKETS = 5
|
||
|
||
|
||
def hops(ttl: int) -> tuple[int, int]:
|
||
"""(初始值, 已经过的跳数)"""
|
||
base = next(b for b in BASES if b >= ttl)
|
||
return base, base - ttl
|
||
|
||
|
||
def suspects(ttl_by_host: dict[str, Counter]) -> tuple[int, list[dict]]:
|
||
"""返回 (全网常见跳数, 可疑终端列表)"""
|
||
usual = Counter()
|
||
for c in ttl_by_host.values():
|
||
if sum(c.values()) >= MIN_PACKETS:
|
||
usual[hops(c.most_common(1)[0][0])[1]] += 1
|
||
base_hops = usual.most_common(1)[0][0] if usual else 1
|
||
out = []
|
||
for host, c in ttl_by_host.items():
|
||
n = sum(c.values())
|
||
if n < MIN_PACKETS:
|
||
continue
|
||
by_os: Counter = Counter()
|
||
extra = 0
|
||
for ttl, k in c.items():
|
||
b, h = hops(ttl)
|
||
by_os[b] += k
|
||
if h > base_hops:
|
||
extra += k
|
||
reasons = []
|
||
if extra >= max(3, n * 0.3):
|
||
reasons.append("extra_hop")
|
||
if sum(1 for b, k in by_os.items() if k >= 3 and k >= n * 0.1) >= 2:
|
||
reasons.append("multi_os")
|
||
if reasons:
|
||
out.append({"host": host, "packets": n, "ttls": dict(c.most_common(6)), "reasons": reasons,
|
||
"extra_share": extra / n, "os": sorted(b for b, k in by_os.items() if k >= 3)})
|
||
out.sort(key=lambda x: (-len(x["reasons"]), -x["extra_share"]))
|
||
return base_hops, out
|