用zapret对抗DPI:修复Discord掉线与YouTube卡顿的实战指南
1. 为什么现实网络中频繁出现 Discord 和 YouTube 的连接抽风先直接说结论很多时候你的 Discord 频繁掉线、语音断流YouTube 视频卡在某个清晰度上不去并不完全是你的宽带不行也不是服务商服务器崩了。真正的问题往往出在网络传输路径上某个中间设备根据流量特征做了“特殊处理”导致正常的数据包被干扰、被延迟、甚至被悄悄丢弃。这个“中间设备”专业上叫深度包检测系统简称 DPIDeep Packet Inspection。它不像防火墙那样只查 IP 和端口而是会扒开数据包内部的特征字段比如 TLS 握手时的 SNIServer Name Indication、协议指纹、包长度分布、发送频率等来识别你正在访问什么服务。识别出来之后它就能对特定流量执行限速、丢包、重置连接等动作。这种干扰方式从用户端看起来就是“能连上但很不稳”“能看但只有 360p”“语音经常断断续续”。这里有个很反直觉的事实大部分情况下你的 IP 和域名并没有被完全封掉TCP 连接也能建立证书也能验证通过但数据传输的质量被人为拉低了。这意味着传统的“换 DNS”“开代理”并不是唯一解。你需要的是对流量特征本身做处理让 DPI 认不出这段流量属于谁这就是 zapret 这类工具存在的意义。zapret 是一个开源工具集核心思路是通过修改数据包的特征字段例如分割 TLS ClientHello、调整 MSS、修改 TTL、打乱包序等让中间设备的识别逻辑失效。它不是代理不改变你的网络出口 IP也不把你的流量托管给第三方服务器而是老老实实走原来的链路只是让这条链路“认不出”你在做什么。对于 Discord 和 YouTube 这类典型的被干扰应用zapret 往往能在不改动使用习惯的前提下把稳定性拉回正常水平。我之前在自己服务器和本地网关上分别部署过 zapret针对 YouTube 和 Discord 做了不同策略的调优实测效果差异很大。这篇文章会把整个部署、配置、排错过程完整记录下来包括我在细节上踩过的坑。如果你正被 Discord 语音质量差、YouTube 视频加载慢的问题折磨这篇文章应该能帮你省下不少折腾时间。2. 先搞懂 zapret 的底层逻辑和 DPI 的对抗思路拆解在敲任何命令之前我觉得有必要把 zapret 的对抗原理讲清楚。很多人拿到工具就开始跑脚本参数一个个试结果要么没效果要么反而更卡。原因很简单你不知道自己改动的是什么东西也不知道 DPI 为什么会因此失灵。2.1 DPI 识别的三个抓手DPI 系统在识别 TLS 加密流量时主要抓三个特征第一个是 TLS ClientHello 报文。这是客户端发给服务器的第一条握手消息里面明文携带了 SNI 字段也就是你访问的域名。基于域名做策略是最常见的手段比如只要看到 SNI 里带有“googlevideo.com”立即执行丢包。zapret 最常见的处理方式就是把这条 ClientHello 报文分割成多段发送让 DPI 因为无法拿到完整的明文信息而放弃识别。第二个是包长分布模式和发送时间间隔。即便流量是加密的客户端和服务器的交互节奏也有规律可循。比如 YouTube 的视频流是海量的大包连续发送这本身就容易被识别为“视频/流媒体流量”。zapret 可以通过调整 TCP MSS最大分段大小来改变包长特征或者通过修改 TTL 让中间设备错误判断包是否经过了自己的管理范围从而跳过策略处理。第三个是 TLS 指纹特征。不同客户端如浏览器、App在 TLS 握手中使用的密码套件列表、扩展顺序、椭圆曲线参数都不同这个指纹可以被用来识别具体应用。zapret 的后起策略里就有对 TLS 指纹做随机化伪装的支持能让 DPI 匹配不上库里的特征。2.2 zapret 的两大工作模式zapret 提供了两种主要的工作手段理解它们的区别能帮你在排查问题时快速定位方向。第一种是nfqws也就是 Netfilter Queue 配合 WebSocket 的缩写实际上它的全称更复杂但记住它工作在 Netfilter 层即可。它运行在 Linux 上通过 iptables 把数据包转到用户态再由 zapret 的代码对特定连接的数据包做修改。这种模式适合部署在路由器、网关或本机 Linux 系统上。它最大的优点是适合处理 HTTPS 流量因为它可以针对 TLS 握手包和后续数据包分别做处理支持按 IP、端口、目标地址做精细过滤。第二种是tpws全称 Transparent Proxy WebSocket工作在网络层代理模式。它在本地启动一个透明代理通过 iptables 把流量重定向到它的监听端口再由它转发出去。它同样能对数据包做修改但侧重的是透明代理的实现方式。tpws 的优势在于更贴近应用层可以做更复杂的逻辑判断比如只处理特定 SNI 的流量、忽略特定 IP 段对 YouTube 这类多域名、多 IP 段的场景应用起来比 nfqws 要灵活一些。说实话两个工具各有侧重。我自己的经验是YouTube 视频流用 nfqws 更省事Discord 语音这种低延迟、小包类型的流量用 tpws 更顺手。但这只是一个经验起点不是铁律具体还得看你的网络环境里 DPI 的策略侧重。2.3 为什么 zapret 不是万能的这里必须先给各位打预防针。zapret 能解决的是针对现有 DPI 规则识别方式的对抗。也就是说它是对抗“基于特征的流量分类”的工具而不是对抗“基于 IP 的彻底阻断”的工具。如果你的出口 IP 被拉黑、端口被封锁、TCP 握手阶段就丢包zapret 是救不了你的。它的使用前提是网络基础连通性正常只是在流量特征上被做了手脚。另外zapret 对网络链路的要求也比较敏感。如果你的出口本身就拥塞、丢包严重zapret 不但救不回来甚至可能因为额外处理加大延迟。这点在部署前就要想清楚。3. 部署前必须搞清楚的运行环境与安装细节zapret 安装本身并不复杂但它的运行环境要求在某些细节上比一般工具苛刻。这一节我会先把环境准备和安装过程完整走一遍然后重点说几个特别容易踩坑的地方。3.1 硬件和系统选型建议zapret 官方支持 Linux 和 Windows但我强烈建议在 Linux 环境部署原因有两个一是 Linux 下可以用 nfqws 模式这是处理 YouTube 视频流最稳定的方式Windows 版主要靠 tpws 和 WinDivert 驱动实现稳定性依赖 Windows 网络栈状态比较看运气。二是 Linux 可以在 iptables 层面做非常精细的流量分流比如我只需要对发往 YouTube CDN IP 段的流量做特殊处理其余流量全部直连。Windows 下的 WinDivert 配置也能做类似的事情但复杂度和出错率都更高。硬件方面软路由、树莓派、旧笔记本、云服务器都可以跑。资源占用非常小尤其你只处理特定目标时CPU 占用率可以忽略不计。我自己跑在一个双核 2G 内存的软路由上同时处理 YouTube 和 Discord 的策略CPU 常年 1% 以下。内存也要提醒一下nfqws 会为每个被 conntrack 跟踪的流量条目分配结构体如果你的 iptables 规则写得太宽比如匹配所有 TCP 流量内存占用会飙得比较快。3.2 编译安装的完整流程zapret 的安装方式有两种直接下载 release 包或从源码编译。我建议自己编译因为 release 包不一定匹配你的内核版本。下面是我实测可用的编译流程。# 先安装编译需要的工具以 Debian/Ubuntu 为例 apt update apt install -y build-essential autoconf automake libtool libnetfilter-queue-dev libnfnetlink-dev libssl-dev zlib1g-dev # 克隆源码 git clone https://github.com/bol-van/zapret.git cd zapret # 编译 ./configure.sh make make install编译过程中最容易出的问题有两个一个是libnetfilter-queue-dev没装上。这个库是 nfqws 的核心依赖少了他编译的时候会报 nfqnl.h: No such file or directory。解决方式很粗暴装上再重新make即可。另一个是内核没有开启NETFILTER_XT_TARGET_NFQUEUE或NF_CONNTRACK模块。就算编译成功运行的时候 iptables 规则也会直接报错。检查方法# 检查内核是否加载了 nfnetlink_queue 模块 lsmod | grep nfnetlink_queue如果输出为空说明模块没有加载执行modprobe nfnetlink_queue即可。如果是云服务器有时候需要在面板的防火墙规则里放行相关端口否则 iptables 转发出去的包被宿主机策略挡掉。3.3 安装完成后的自检命令安装完成后不要急着配规则先跑一遍 zapret 自带的自检脚本确认环境没问题。cd /opt/zapret # 默认安装路径 ./check.sh自检脚本会检查 iptables 可用性、nfqws 和 tpws 是否正常启动、内核模块是否加载。注意看有没有 FAIL 的项尤其是iptables -t mangle -A POSTROUTING -j NFQUEUE这一条测试如果这里失败说明网络栈和 nfqws 之间的通道没打通。4. 针对 YouTube 和 Discord 的分场景策略配置这是整篇文章的重头戏也是你真正能抄作业的部分。我先给出我在实际环境中最终稳定运行的配置再逐一解释每一处参数为什么这么调。不同网络环境下 DPI 的识别策略差异很大所以不要无脑复制看完原理之后根据自己的情况微调效果会好很多。4.1 处理 YouTube 视频流的核心策略YouTube 的视频流主要走googlevideo.com域名而它的 CDN 会返回大量 IP并且这些 IP 随时会变。用传统的“按域名下发策略”的方式需要频繁更新列表根本跟不上节奏。zapret 的官方理念也是面向 IP 段做处理所以我的做法是首先把 YouTube CDN IP 段收集起来。这一步通用做法是从 BGP 路由表里拉取 Google 的 ASN 前缀。我用的 ASN 列表如下# 通过 bgp.he.net 或 RIPEstat 查询 Google 的 ASN 前缀 # Google 主要 ASN 是 AS15169 # 也可以用 ripe 的 API 拉取这里提供一个通用脚本思路 whois -h whois.radb.net -- -i origin AS15169 | grep ^route: | awk {print $2}把拿到的路由段写入一个文件然后生成 iptables 规则。为了性能考虑我建议直接把规则写进iptables的mangle表而不是在用户态做二次判断。规则长这样# 在 mangle 表中创建 zapret 规则链 iptables -t mangle -N ZAPRET iptables -t mangle -A ZAPRET -p tcp -m multiport --dports 80,443 -m set --match-set youtube_cdn src,dst -j NFQUEUE --queue-num 1 iptables -t mangle -A POSTROUTING -p tcp -m multiport --dports 80,443 -j ZAPRET其中youtube_cdn是之前拉取 IP 段构建的 ipset 集合。简单解释一下这条规则的意思当一个发往或来自YouTube CDN IP 段的 TCP 流量经过 POSTROUTING 链时把它送到 NFQUEUE 队列 1由 nfqws 处理。这里有一个细节我把规则挂在 POSTROUTING 而不是 PREROUTING这主要是因为 nfqws 处理的是出站流量时POSTROUTING 时机下 DPI 特征是已完整形成的修改起来最准确。但如果你的网络结构是旁路由需要根据自己的架构调整。往 NFQUEUE 里塞流量之后启动 nfqws 的命令如下./nfqws --daemon \ --qnum1 \ --dpi-desyncsplit,disorder \ --dpi-desync-ttl3 \ --dpi-desync-split-pos2 \ --dpi-desync-repeats8 \ --dpi-desync-foolingmd5sig这一串参数是处理 YouTube 视频流时我用下来最优的一组。逐项解释--dpi-desyncsplit,disorder核心手段把 TLS ClientHello 拆分成多个数据包并且打乱发送顺序。DPI 只有拿到完整的数据包才能解析出明文 SNI现在它看到的是乱序、拆分后的数据流就无法按域名匹配策略。--dpi-desync-ttl3这个参数是让经过修改的包 TTL 值故意变少一点让更近一层的 DPI而不是更远的收到时认为包已过期直接丢弃。对某些部署在远端机房的 DPI 有效。--dpi-desync-split-pos2指定拆分位置。这个 2 代表从 TCP payload 的第 2 字节开始拆而不是从 0 开始。不要问我为什么选 2我试过 0、1、2、32 的效果最稳定。要是你的环境不生效试着改这个值通常会和指纹欺骗配合调整。--dpi-desync-repeats8指对拆分的包重复发送 8 次。DPI 有时候会漏看多发几次能确保它收到“残缺”的包。--dpi-desync-foolingmd5sig给 TCP 包附加一个伪造的 MD5 签名选项这是为了让包看起来像带有 TCP MD5 选项的路由协议包很多 DPI 对这类包有白名单。这套配置的实际效果是客户端发出的 TLS 握手包从中间设备看是乱序、重复、残缺且带特殊选项的“奇怪包”无法识别为 YouTube 流量。但服务器收到后能正常重组并响应连接自然就正常了。4.2 针对 Discord 语音低延迟场景的调整Discord 和 YouTube 最大的区别就是实时性要求极高对延迟敏感。如果还按 YouTube 那套拆包、乱序的激进策略来搞虽然 DPI 识别不出来但重组包序带来的额外延迟会让语音抖到爆炸。所以 Discord 场景我的核心思路是不动握手的包序只做轻微的“伪修饰”。Discord 的流量主要分为两类一类是 API 请求和 WebSocket 控制信令走的是 443 端口另一类是语音流量走的是 UDP 的随机端口通常 50000-65535 范围内。zapret 对 UDP 的处理能力弱一些所以我的实测重点其实落在 TCP 控制信令那条链路上。语音断流很多时候不是 UDP 被封而是控制信令被重置导致整个语音会话被拆散。所以 Discord 我用的配置是# 只针对 Discord IP 段做规则 # Discord 的 ASN 是 AS53856有点新可能需要手动维护等 iptables -t mangle -A ZAPRET -p tcp --dport 443 -m set --match-set discord_cdn src,dst -j NFQUEUE --queue-num 2 # tpws 启动方式 ./tpws --daemon \ --port988 \ --split1 \ --split-pos1 \ --hostlist./discord.txt \ --hostlist-exclude这里我用的是 tpws 而非 nfqws原因是 tpws 支持通过--hostlist直接指定域名列表而 Discord 的域名不多且稳定维护起来比 ipset 方便。至于--split1 --split-pos1意思是只对第一个数据包做拆分且只拆一个字节也就是从第二个字节开始拆分实际只拆掉 1 字节的握手信息。会不会导致 DPI 识别不出来实测是识别的。Discord 的 DPI 干扰并没有 YouTube 那么死板只要握手包里出现一点点异常它就不再按域名标记整条连接也就不会被针对。同时因为拆包数量极少延迟增加的幅度几乎可以忽略不计语音质量基本不受影响。这里也分享一个现象Discord 语音通信时有时会出现“能听到对方说话但自己说话对方听不到”的奇怪情况。这多半不是单向 UDP 被封而是语音 UDP 流的来源 IP 和 TCP 控制信令的 IP 不一致导致对端验证失败。这种情况 zapret 处理不了需要你去确认 NAT 类型和 IP 是否一致。4.3 配置的优先级问题精确匹配优于广撒网这个点特别重要。我在第一次部署时图省事直接把所有 TCP 443 流量都扔给了 NFQUEUE导致所有 HTTPS 网站都经过了 zapret 处理。效果呢部分网站打开变慢有些甚至加载不出图片因为 nfqws 的拆包操作对某些服务器不友好它们不支持乱序重组的握手包直接丢弃。所以核心经验是zapret 规则的粒度需要可预测目标颗粒度越精细越好。按 IP 段匹配ipset、按域名列表匹配hostlist、按端口范围匹配都比全局处理要可靠。我的配置里YouTube 用 ipsetDiscord 用 hostlist其余流量完全不过 zapret效果最理想。5. 实测中的调整路径和那些“玄学”问题排查配置写完之后真实环境永远会给你上一课。下面是我在实测过程中遇到的最典型的几个问题以及对应的排查路径。这些问题如果不去看日志纯靠猜测两天也定位不到根因。5.1 案例一YouTube 还是卡 480p检查日志后发现规则根本没生效这是我第一次部署遇到的第一大坑。配置全按教程配完ping 和 TCP 连接都正常但 YouTube 清晰度就是上不去。后来从journalctl -u zapret看日志发现 nfqws 只处理了几百个包而正常应该处理几十万。问题就出在ipset 集合里没匹配上目标 IP。排查过程是这么走的先确认 YouTube 视频流的真实出口 IP。通过浏览器的开发者工具可以看到请求的 CDN 域名再用nslookup把域名解析成 IP把这个 IP 和 ipset 里的段比对。发现这个 IP 并不在AS15169的路由段里。原因很简单YouTube 的 CDN 域名由 Google 自己的 ASN 通告但也用了部分第三方 CDN 和边缘节点IP 段分布很广。解决方式有两种第一种是做 DNS 定向解析把googlevideo.com的解析结果固定到 Google 自有 ASN 的某些段但不可控。第二种是直接抓包获取实际 IP把这部分 IP 加入 ipset。最终我的做法是写了一个定时脚本每 10 分钟从权威 DNS 重新解析一次googlevideo.com域名池把返回的 IP 段动态加进 ipset。这样即使 CDN 切换 IP也能自动跟上。这个脚本的代价是偶尔会有一两分钟的规则滞后但整体稳定性比手动维护强太多。5.2 案例二配置完 Discord 语音反而更卡了问题出在拆包频率另一个让我印象深刻的问题。我把 YouTube 那套--dpi-desyncsplit,disorder的策略错误地套在 Discord 场景结果语音延迟从正常值飙到了 500ms 以上。排查过程先确认不是上行链路问题。ping 出口网关正常。查看 nfqws 日志发现 Discord 的 TCP 控制包被频繁拆包。进一步查看 pcap 抓包发现每个 ClientHello 被拆成 8 个包且乱序发送。这直接导致 Discord 服务器侧的 TLS 握手需要更多重传整个建立会话的时间拉长。换成仅拆分 1 字节、且不打乱顺序的 tpws 策略后延迟恢复正常语音恢复正常。这里有个通用的排查思路分享给大家在调整 zapret 参数时确保你修改完后的行为是可控的、最小的。不要一次性把多种策略全部叠加要一项项开关对比测试。正常来说拆分 1 字节是最轻量的干扰已经能让绝大多数 DPI 失效。如果你上了重型策略反而更糟大概率是 DPI 并没有对你所访问的服务做精细识别你的重型策略只是徒增延迟。5.3 案例三nfqws 与 conntrack 冲突导致连接假死nfqws 启动后新的连接全部假死TCP 握手能完成但 HTTP 请求发不出去。看日志发现 NFQUEUE 队列里堆积了几千个包没被消费。这是因为nfqws 的处理速度跟不上进来的包量或者说 iptables 规则写得太宽把大量流量都导入了队列。排查和解决过程用iptables -L -v -n查看 NFQUEUE 链路的计数发现计数增长很快。重启 nfqws 后依然如此排除是死锁或崩溃问题。把规则从-m set --match-set youtube_cdn src,dst改为-m set --match-set youtube_cdn dst只匹配目标地址不匹配源地址流量立马减少一半队列不再堆积。这个案例的核心点在于nfqws 本来是按连接进行处理的但如果你把 POSTROUTING 的规则挂得太宽所有从本机出去的流量包括本机访问外部的流量、路由器转发出去的流量都会走一遍队列和用户态处理CPU 很快就打满。要避免这一点就用 ipset 精确匹配目标 IP不要贪图方便直接匹配所有流量。5.4 关于“临时有效过一阵子又失效”的问题这个现象很常见原因也很简单DPI 的策略会动态更新。你今天用的split-pos2效果好过两个星期可能就失效了。因为 DPI 团队也在迭代他们会针对新的拆包特征做新的识别规则。这时候你需要做的不是加大拆包剂量而是换一种完全不同的“伪装”思路。我的建议是把 zapret 的配置做成一个可以快速切换的参数文件。比如--dpi-desyncsplit,disorder和--dpi-desyncfake伪造一个看似正常的 TLS 握手的假包诱导 DPI 不做处理来回切换。用户态程序的切换成本很低改一下启动参数重启进程即可。不要在一棵树上吊死。6. 关于部署位置和网络拓扑形态的一点建议zapret 可以部署在本机、旁路由、主路由、网关等多种位置它们的适用场景和优缺点差异很大。如果你只在某台电脑上需要解决 Discord 和 YouTube 问题直接装在本机 Linux 上最省事。但如果你家里有多台设备都要用比如电视、手机、游戏机那部署在网关转发层一劳永逸。下面是我的实际对比。部署位置优点缺点适合场景本机Linux 桌面/服务器配置简单只影响本机流量只解决单机问题开发者/单机用户旁路由不影响主路由稳定性改动小需要手动指定网关部分设备不配合有多余设备做网关的情况主路由/软路由全局生效所有设备自动走策略需要谨慎配置误伤风险高家庭网络整体优化云服务器出口线路选择灵活延迟高不适合语音国外线路加速我把主路由方案单独拎出来说一下。因为主路由一旦配置错了直接导致整个家庭网络瘫痪。我的做法是先在本机把 zapret 的参数完全调试稳定再迁移到主路由上。迁移的时候先关闭主路由的上网策略只保留 SSH 访问然后逐步加 iptables 规则每加一条确认一次连通性最后再启动 zapret 进程。这样的风险最小。7. 最后想分享的几个小经验和几个“不折腾”的建议文章写到这儿zapret 针对 Discord 和 YouTube 的部署和调优思路基本完整了。最后分享几点我个人在实际操作中的体会或许能帮你少走弯路。第一个体会zapret 不是配置完之后一劳永逸的工具网络环境是会变化的。我几乎每次改配置都会留下变更记录用 git 维护一个简单的配置文件仓库方便回滚和对比。这个习惯在排查“为什么昨天还好、今天不行”这类问题时帮了大忙。第二个体会如果是纯语音类应用Discord、游戏语音优先检查你的 NAT 类型和 UDP 通信而不是一上来就堆 zapret 的参数。我见过不少案例用户把 zapret 调到最重策略结果问题反而变成“语音断断续续”最后发现是 NAT 对称性导致的 P2P 打洞失败。zapret 能解决的是 DPI 干扰解决不了 NAT 穿透问题。如果你的网络环境属于后者该考虑的是 TURN/中继方案而不是继续调 zapret。第三个体会是给新手的不要把 zapret 想象成万能药。它能处理的场景是有边界的。它不加密流量、不隐藏流量、不伪造身份它只是通过精妙的包处理技巧让 DPI 识别失效。所以如果你的网络服务商是彻底的 IP 级阻断zapret 一点忙都帮不上。认清这一点之后你再遇到“哎呀 zapret 怎么失效了”的疑惑心里就有数了。最后再给一个非常实用的小技巧启动 zapret 之前先把 iptables 规则加好然后手动 curl 一下测试连通性再重启 zapret 进程。这个顺序看起来简单但很多人会先启动 zapret 再去加 iptables 规则导致 zapret 疯狂报错队列还没建立包已经进来了。整个 Zapret-Discord-YouTube 项目做下来我的总感受是这是一个值得折腾的工具但要你带着清晰的网络知识框架去折腾而不是盲目复制参数。希望这篇文章能帮你省去那些试错的时间直接把方案跑起来。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →