用 geodata 实现分流路由
本配方搭建一份客户端侧的 etemenanki-app 配置,把流量分成四路:广告和跟踪器被丢弃,本地和私有目标直连,位于某个选定国家内的目标直连,其余流量经由代理服务器转发。广告域名、国内域名和国内地址段的列表都来自公开的 v2fly geodata 文件,所以你只需写六条规则,而不是成千上万条。
如果 etemenanki-app 运行在你自己的电脑或路由器上,作为远程服务器前面的本地 SOCKS 或 HTTP 代理,就可以使用本配方。本文介绍从哪里获取 geodata、规则为什么按这个顺序排列、嗅探起什么作用,以及如何让这些文件保持最新。
| 流量 | 出站 | 决定它的规则 |
|---|---|---|
| 你从广告列表中豁免的某个域名 | direct |
规则 1,domain_suffix |
| 广告和跟踪器域名 | block |
规则 2,geosite = ["category-ads-all"] |
localhost、*.lan、私有地址和回环地址 |
direct |
规则 3,geosite 和 geoip 的 private |
| 虽在国内列表中、但你希望走代理的某个域名 | proxy |
规则 4,domain_suffix |
| 目标为国外 IP 的流 | proxy |
规则 5,geoip = ["!cn"] |
| 国内的域名和 IP | direct |
规则 6,geosite 和 geoip 的 cn |
| 其余所有流量 | proxy |
[route].default |
示例使用 cn,因为两个 v2fly 文件对这个国家的覆盖最完整。要换成其他国家需要改哪些地方,见选择其他国家。
获取 geodata
Section titled “获取 geodata”etemenanki-app 读取 v2ray/Xray 的 .dat 格式:以 protobuf 编码的域名列表(geosite.dat)和 CIDR 地址段列表(geoip.dat)。v2fly 项目在 GitHub 上发布这两个文件,每个都附带一个 SHA-256 校验文件:
| 文件 | 下载 | 保存为 |
|---|---|---|
按国家划分的 IP 地址段,外加 private |
v2fly/geoip 发布的 geoip.dat |
/etc/etemenanki/geoip.dat |
cn、private、category-ads-all 等域名列表 |
v2fly/domain-list-community 发布的 dlc.dat |
/etc/etemenanki/geosite.dat |
域名列表以 dlc.dat 的名字发布。它与 geosite.dat 格式相同,只是文件名不同。
-
把两个文件及其校验文件下载到一个空目录:
终端窗口 cd "$(mktemp -d)"curl -fLO https://github.com/v2fly/geoip/releases/latest/download/geoip.datcurl -fLO https://github.com/v2fly/geoip/releases/latest/download/geoip.dat.sha256sumcurl -fLO https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.datcurl -fLO https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat.sha256sum -
校验文件。每一行都必须以
OK结尾:终端窗口 sha256sum -c geoip.dat.sha256sum dlc.dat.sha256sumgeoip.dat: OKdlc.dat: OK -
把它们安装到配置所指定的位置:
终端窗口 sudo install -D -m 0644 geoip.dat /etc/etemenanki/geoip.datsudo install -D -m 0644 dlc.dat /etc/etemenanki/geosite.dat
# A local client that splits traffic with v2fly geodata: ads are dropped,# private and in-country (cn) destinations go direct, and everything else# goes through a Trojan proxy. Download geoip.dat and geosite.dat first.
[[inbound]]tag = "socks-in"protocol = "socks"listen = "127.0.0.1"port = 1080# The default. A flow addressed by IP is matched by its TLS SNI or HTTP Host# as well, so the domain and geosite rules below still reach it.sniffing = true
[[inbound]]tag = "http-in"protocol = "http"listen = "127.0.0.1"port = 8080
# The first outbound. [route].default names it explicitly anyway.[[outbound]]tag = "proxy"protocol = "trojan"server = "proxy.example.com"port = 443
[outbound.stream]network = "tls"
[outbound.settings]password = "replace-with-a-long-random-password"
[[outbound]]tag = "direct"protocol = "freedom"
[[outbound]]tag = "block"protocol = "blackhole"
[route]default = "proxy"geoip = "/etc/etemenanki/geoip.dat"geosite = "/etc/etemenanki/geosite.dat"
# Rules are tried from top to bottom and the first match wins. Inside one# rule the matchers are alternatives: any one of them is enough.
# 1. Exception to rule 2: a name the ad list catches that you still need.[[route.rule]]outbound = "direct"domain_suffix = ["metrics.example.com"]
# 2. Ads and trackers are dropped.[[route.rule]]outbound = "block"geosite = ["category-ads-all"]
# 3. Local names and private addresses stay on the local network.[[route.rule]]outbound = "direct"geosite = ["private"]geoip = ["private"]
# 4. Exception to rule 6: names on the country list that you want proxied.[[route.rule]]outbound = "proxy"domain_suffix = ["example.com"]
# 5. A flow addressed by an IP outside the country goes through the proxy,# whatever name its payload claims. Rule 3 has already taken private IPs.[[route.rule]]outbound = "proxy"geoip = ["!cn"]
# 6. In-country names and addresses go direct. apple@cn and steam@cn add the# entries of those lists that carry the cn attribute, such as the# in-country download hosts, which the cn list does not contain.[[route.rule]]outbound = "direct"geosite = ["cn", "apple@cn", "steam@cn"]geoip = ["cn"]
# Everything else falls through to [route].default, the proxy.把 proxy.example.com 和密码换成你的 Trojan 服务器的地址和密码,或者把 proxy 出站换成任何能连到你服务器的其他出站;规则与它使用的协议无关。有一点需要注意:UDP 遵循同样的规则,而 http 和 shadowsocks 出站(包括 2022- 系列加密方法)不承载数据报,所以被路由到这些出站的 UDP 会被丢弃。把规则 1 和规则 4 中的 metrics.example.com 和 example.com 换成你想豁免的域名,或者直接删掉这两条规则。然后检查配置文件:
etemenanki-app --test -c /etc/etemenanki/config.tomlConfiguration OK.--test 会构建真正启动时构建的一切,包括路由表,因此它会打开两个 .dat 文件,并查找规则中用到的每一个代码。文件缺失或代码拼错会在这一步就报错,而不是等到启动代理时。
把应用程序指向 127.0.0.1:1080(SOCKS4、SOCKS4a 或 SOCKS5)或 127.0.0.1:8080(HTTP 代理)。两个入站共用同一套规则。
[route] 表包含默认出站和两个文件路径:
| 键 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
default | string | 否 | — | 所有规则都不匹配时使用的出站或负载均衡器的 tag。不写时,文件里的第一个 [[outbound]] 就是默认出站;负载均衡器不会被隐式选为默认。若该 tag 既不对应出站也不对应负载均衡器,则报 route references unknown outbound tag: <tag>。 |
geoip | path | 视情况 | — | v2ray 格式 geoip.dat 的路径。只要有规则使用 geoip 就必须设置(否则报 a geoip matcher is used but no geoip file is configured),没有规则使用时根本不会打开这个文件。相对路径按进程的工作目录解析,而不是配置文件所在目录。启动时、--test 时以及每次重载时都会读取该文件,只保留规则里引用到的 code。 |
geosite | path | 视情况 | — | v2ray 格式 geosite.dat 的路径。只要有规则使用 geosite 就必须设置(否则报 a geosite matcher is used but no geosite file is configured),没有规则使用时根本不会打开这个文件。相对路径的解析和读取时机与 geoip 相同。 |
rule | array of tables | 否 | [] | 路由规则,写作 [[route.rule]] 块(单数:[[route.rules]] 是未知字段)。按文件中的顺序逐条尝试,第一条匹配的规则决定出站。 |
这些规则只用到了三个匹配条件键:domain_suffix、geosite 和 geoip。其他匹配条件(domain_full、domain_keyword、domain_regex、cidr、source_cidr、port、network、inbound_tag)见路由。
规则如何求值
Section titled “规则如何求值”一切由两条原则决定:
- 规则之间,首条匹配生效。 etemenanki-app 从上到下依次尝试各个
[[route.rule]]块,在第一条匹配的规则处停止。没有任何规则匹配的流交给[route].default。 - 同一条规则内,任一匹配条件满足即可。 规则 3 匹配位于
private域名列表中的流,或位于private地址段内的流。一条规则无法要求两个匹配条件同时满足;路由介绍了如何借助规则顺序达到这种效果。
下图是一个新的流在示例配置中经过的路径:
flowchart TB
F["新的流:一个域名或 IP,可能附带嗅探到的域名"] --> R1{"1. 在 metrics.example.com 之下?"}
R1 -- 是 --> D["direct"]
R1 -- 否 --> R2{"2. 在 geosite category-ads-all 中?"}
R2 -- 是 --> B["block"]
R2 -- 否 --> R3{"3. geosite private 或 geoip private?"}
R3 -- 是 --> D
R3 -- 否 --> R4{"4. 在 example.com 之下?"}
R4 -- 是 --> P["proxy"]
R4 -- 否 --> R5{"5. geoip cn 之外的 IP?"}
R5 -- 是 --> P
R5 -- 否 --> R6{"6. geosite cn、apple@cn、steam@cn 或 geoip cn?"}
R6 -- 是 --> D
R6 -- "否:[route].default" --> P
一些流及其去向:
| 客户端请求 | 嗅探到的域名 | 首条匹配规则 | 出站 |
|---|---|---|---|
doubleclick.net:443 |
不需要 | 2 | block |
metrics.example.com:443,即使它在广告列表中 |
不需要 | 1 | direct |
192.168.1.1:80 |
无 | 3,geoip private |
direct |
nas.lan:445 |
不需要 | 3,geosite private |
direct |
adcdownload.apple.com:443 |
不需要 | 6,apple@cn |
direct |
| 国内某个 IP 的 443 端口 | 无 | 6,geoip cn |
direct |
| 国外某个 IP 的 443 端口 | 国内列表中的某个域名 | 5 | proxy |
| 国外某个 IP 的 443 端口 | doubleclick.net |
2 | block |
| 不在任何列表中的域名 | 不需要 | 无 | proxy(默认) |
规则为什么按这个顺序排列
Section titled “规则为什么按这个顺序排列”列表中的每个位置都有其原因。如果你要添加规则,请保持以下关系:
- 例外规则要放在它所豁免的宽泛规则之前。 规则 1 把一个域名从广告列表中豁免出来,它之所以有效,是因为它位于规则 2 之前。如果放在规则 2 之后,它永远不会被执行到:它覆盖的每个域名都已经被屏蔽了。规则 4 对规则 6 中的国内列表起同样的作用。
- 屏蔽规则要放在任何转发规则之前。 这样无论广告位于本地、国内还是国外,都会被丢弃。
private要放在!cn之前。!cn匹配所有不在cn列表中的地址,其中也包括192.168.0.0/16、10.0.0.0/8和回环地址。如果上方没有规则 3,规则 5 就会让你的局域网流量走代理。- 对于目标为 IP 的流,规则 5 让地址优先于嗅探到的域名。 地址才是数据真正发往的地方;嗅探到的域名只是客户端在第一个数据包里写的内容。一个发往国外地址、但 TLS SNI 在国内列表中的流会走代理。如果你更愿意相信域名,就把规则 5 移到规则 6 之下。
永远执行不到的规则不算错误,完全没有匹配条件键的规则也不算。etemenanki-app 接受这两种规则,它们只是永远不会匹配。当某条规则看起来不起作用时,先检查它上面的规则。
Geodata 匹配条件
Section titled “Geodata 匹配条件”geosite 和 geoip 的行为不同。最重要的区别在于它们与什么进行比较。
geosite |
geoip |
|
|---|---|---|
| 加载自 | [route].geosite |
[route].geoip |
| 比较对象 | 目标域名,以及嗅探到的域名 | 仅目标 IP |
| 目标为域名的流 | 按域名匹配 | 永不匹配。 etemenanki-app 不会为路由解析域名 |
| 目标为 IP 的流 | 只能通过嗅探到的域名匹配 | 按地址匹配 |
code |
该列表中的每一项 | 该列表中的每个地址段 |
code@attr |
仅带有该属性的项 | 不支持:geoip code not found: cn@ads |
!code |
不支持:geosite code not found: !cn |
该列表之外的所有地址 |
| 大小写 | 代码和属性都不区分大小写 | 代码不区分大小写 |
| 未知代码 | geosite code not found: <code> |
geoip code not found: <code> |
代码不要带前缀:写 geosite = ["cn"],而不是 geosite = ["geosite:cn"],后者会报错 geosite code not found: geosite:cn。
code@attr
Section titled “code@attr”许多 geosite 列表会给其中一部分条目打上属性。v2fly 文件使用了三种属性:cn 表示某个国际服务在国内提供服务的域名,!cn 与之相反,ads 表示某个公司列表中的广告域名。apple@cn 只保留 apple 列表中带有 cn 属性的条目,例如 adcdownload.apple.com,而 cn 列表本身并不包含它。这就是规则 6 在 cn 旁边加上 apple@cn 和 steam@cn 的原因。
第一个 @ 之后的所有内容都视为一个属性名,所以 google@!cn 同样有效。没有任何条目带有的属性也会被接受,只是得到一个空列表:apple@cm 能通过 --test,但永远不会匹配任何东西。属性的拼写需要你自己检查。
有些列表名本身就包含 !,例如 geolocation-!cn(大体上位于国外的网站)。这是一个普通代码,而不是取反:! 只有出现在 geoip 条目开头时才表示“非”。
geoip = ["!cn"] 匹配不在 cn 列表任何地址段内的目标 IP。这带来两个结果:
- 它同样会匹配私有地址、回环地址和链路本地地址。要像规则 3 那样,在它上方放一条
private规则。 - 与所有 IP 匹配条件一样,它永远不会匹配目标为域名的流。
!cn的意思不是“国外的一切”,而是“国外的所有地址”。没有被任何规则匹配的域名会交给[route].default。
- v2fly 的
geoip.dat为每个两字母国家代码提供一项,另外还有private和一个test项。对 IPv4,private覆盖0.0.0.0/8、RFC 1918 地址段、回环地址、链路本地地址、CGNAT(100.64.0.0/10)、文档和基准测试地址段,以及从224.0.0.0开始往上的所有地址。对 IPv6,它覆盖::和::1、唯一本地地址fc00::/7、链路本地地址fe80::/10和组播地址ff00::/8。 - v2fly 的
geosite.dat包含大约 1,500 个列表:apple、google、steam等按公司划分的列表,category-ads-all、category-games等按类别划分的列表,以及cn、geolocation-cn、geolocation-!cn等按国家划分的列表。private覆盖localhost、lan、local、internal、常见的路由器登录域名、反向解析区域,以及任何单标签域名。cn包含整个.cn顶级域名。
etemenanki-app 在加载列表时会把每个条目转为小写。无法使用的条目,例如被 Rust regex 引擎拒绝的正则表达式,会被跳过并记录一条警告(skipping invalid geosite regex …),列表的其余部分照常加载。
列表名称及其内容由 v2fly 维护,并会随版本变化。在依赖某个列表之前,请浏览 domain-list-community 仓库的 data/ 目录,查看它包含哪些内容。
以域名编写的规则,包括所有 geosite 规则,只能匹配 etemenanki-app 知道域名的流。它是否知道域名取决于客户端:
| 客户端设置 | etemenanki-app 收到的内容 |
|---|---|
使用远程 DNS 的 SOCKS5(curl 中的 socks5h://,Firefox 中的“使用 SOCKS v5 时代理 DNS 查询”),SOCKS4a |
域名 |
使用本地 DNS 的 SOCKS5(socks5://),SOCKS4 |
客户端自己解析出的 IP 地址 |
| HTTP 代理 | 通常是域名:CONNECT 目标,或普通请求的 Host 头 |
| TUN 入站 | 始终是 IP 地址 |
在第二和第四种情况下,以及对目标为 IP 地址的 HTTP CONNECT,嗅探能够找回域名。普通 HTTP 代理请求按其 Host 头路由,从不嗅探。当入站设置了 sniffing = true(这是默认值)时,etemenanki-app 会读取客户端在目标为 IP 的流上发送的最初几个字节,并从中提取域名:
- TLS ClientHello 提供其中的 SNI,因此 HTTPS 和大多数其他 TLS 流量都能覆盖;
- 明文 HTTP/1 请求提供绝对形式请求 URL 中的主机名,否则提供其
Host头。
找回的域名随后会交给每一个域名匹配条件(domain_suffix、domain_full、domain_keyword、domain_regex 和 geosite)尝试,目标本身也照常参与匹配。IP 匹配条件(cidr、geoip)会忽略它。
要预测一个流的去向,下面这些细节很重要:
- 只检查目标为 IP 的流式连接。 已经指明域名的流会立即按该域名路由。UDP 数据报(包括 QUIC)从不嗅探;每个数据包都按其自身的地址和端口路由。
- 域名只是路由提示,仅此而已。 出站仍然连接客户端请求的 IP 地址。SNI 或
Host中的 IP 字面量会被忽略。 - 嗅探最多等待 300 ms,最多读取 4 KiB。 一旦识别出 ClientHello 或请求,它就会提前结束。如果客户端在这段时间内没有发送任何内容(例如 SMTP 或 FTP 这类由服务器先发言的协议),或发送的字节既不是 TLS 也不是 HTTP(例如 SSH 客户端的版本行),这个流就会等满 300 ms,然后只按 IP 路由。
- 入站先作答。 在 SOCKS、HTTP
CONNECT或 Hysteria 2 入站上,客户端在代理回复之前不会发送任何内容。因此对于开启了嗅探、目标为 IP 的请求,etemenanki-app 会在路由和拨号之前就回复“已连接”。如果随后拨号失败,客户端已经被告知连接成功,所以它看到的是连接被关闭,而不是一个它可以处理的 HTTP502或 SOCKS 错误回复。
在入站上设置 sniffing = false 即可跳过以上全部过程。此后该入站上目标为 IP 的流只会被 IP 规则匹配。该字段与其他通用入站字段一起在入站中介绍。
运行示例配置后,比较 curl 使用 SOCKS 代理访问广告列表中某个域名的两种方式:
curl -x socks5h://127.0.0.1:1080 https://doubleclick.netcurl 发送的是域名,规则 2 直接匹配它。无论嗅探开启与否,结果都一样。
curl -x socks5://127.0.0.1:1080 https://doubleclick.netcurl 自己解析域名,只发送 IP。在 sniffing = true 时,其 ClientHello 中的 SNI 仍然会匹配规则 2。在 sniffing = false 时,这个流按地址路由,到达规则 5 或 6,页面正常加载。
被屏蔽的请求不会得到错误页面。blackhole 接受这个流,丢弃客户端发送的内容并立即结束数据流,因此 HTTPS 客户端会在 TLS 握手阶段失败,明文 HTTP 客户端则收不到任何回复。使用基于 OpenSSL 构建的 curl 时,表现如下:
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to doubleclick.net:443curl: (52) Empty reply from server选择其他国家
Section titled “选择其他国家”要按其他国家分流,修改示例中出现 cn 的地方:
-
规则 5 中的
geoip = ["!cn"]和规则 6 中的geoip = ["cn"]:换成该国家的两字母代码,大小写均可。v2fly 的geoip.dat为每个国家代码都提供了一项。 -
规则 6 中的
geosite = ["cn", "apple@cn", "steam@cn"]:v2fly 文件中拥有自己域名列表的国家很少。少数国家有,例如category-ru和tld-ru,或category-ir;请查看 domain-list-community 的data/目录。删掉@cn这几项,它们只对cn有意义。 -
没有列表时,自己匹配该国家的顶级域名。
domain_suffix = ["de"]匹配.de下的所有域名:[[route.rule]]outbound = "direct"domain_suffix = ["de"]geoip = ["de"]
保持规则 5 位于该国家规则之上,并换成新的代码。
让 geodata 保持最新
Section titled “让 geodata 保持最新”etemenanki-app 在构建配置时解码 .dat 文件:启动时、执行 --test 时以及重载时。运行中的进程把加载的内容保存在内存里,所以你可以随时覆盖这些文件而不影响它。反过来说,新文件只有在构建新配置时才会生效。
etemenanki-app 监视配置文件所在的目录。该目录每次发生变化时,它都会重新读取配置文件,把其字节与上次加载的版本比较,只有不同时才重载。替换 geoip.dat 或 geosite.dat 时,即使它们位于同一目录,配置文件的字节也没有变化,因此不会触发重载。你有两种选择:
- 修改配置文件。 任何字节的变化都算数,包括注释。重载会重新构建整个配置,并读取新的
.dat文件。如果只改了注释,日志会显示config reload: no changes;但新的 geodata 无论如何都已生效。 - 重启 etemenanki-app。
两种方式都会断开所有已打开的连接,因为重载会替换所有监听器和出站。见热重载。
下面的脚本完成整个更新过程。它在配置文件中维护一行标记注释,例如 # geodata: 2026-09-24T03:00:00Z:首次运行时添加,之后每次运行时改写,正是这一变化触发了重载:
#!/bin/sh# Update the v2fly geodata and make a running etemenanki-app load it.set -eudir=/etc/etemenankiconfig="$dir/config.toml"
tmp=$(mktemp -d)trap 'rm -rf "$tmp"' EXITcd "$tmp"
for url in \ https://github.com/v2fly/geoip/releases/latest/download/geoip.dat \ https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.datdo curl -fsSLO "$url" curl -fsSLO "$url.sha256sum"donesha256sum -c geoip.dat.sha256sum dlc.dat.sha256sum
# Keep the current files so a bad release can be rolled back.cp "$dir/geoip.dat" "$dir/geoip.dat.old"cp "$dir/geosite.dat" "$dir/geosite.dat.old"install -m 0644 geoip.dat "$dir/geoip.dat"install -m 0644 dlc.dat "$dir/geosite.dat"
# Every code the rules name must still exist in the new files.if ! etemenanki-app --test -c "$config"; then mv "$dir/geoip.dat.old" "$dir/geoip.dat" mv "$dir/geosite.dat.old" "$dir/geosite.dat" exit 1fi
# Change the config's bytes, which triggers the reload.stamp="# geodata: $(date -u +%Y-%m-%dT%H:%M:%SZ)"if grep -q '^# geodata: ' "$config"; then sed -i "s/^# geodata: .*/$stamp/" "$config"else printf '%s\n' "$stamp" >> "$config"fi以 root 身份通过每周一次的 cron 任务或 systemd timer 运行它。--test 这一步最关键:v2fly 偶尔会重命名或删除某个列表,而规则中用到的代码如果在新文件中不存在,就会报错 geosite code not found: <code>。先做检查,可以保证运行中的进程和下一次重启使用的都是可用的文件。
配置错误会同样导致 --test、启动和重载失败。三种情况下的错误原因相同,只有日志行的前缀不同:--test 为 configuration invalid:,启动为 failed to start:,重载为 reload: build failed, keeping current config:。
| 原因 | 起因 | 解决方法 |
|---|---|---|
a geosite matcher is used but no geosite file is configured |
有规则使用了 geosite,但没有设置 [route].geosite |
添加路径 |
a geoip matcher is used but no geoip file is configured |
有规则使用了 geoip,但没有设置 [route].geoip |
添加路径 |
No such file or directory (os error 2) |
某个 .dat 路径不存在。错误信息不会指明是哪个文件。相对路径相对于进程的工作目录解析,而不是配置文件所在的目录 |
使用绝对路径 |
Permission denied (os error 13) |
进程无法读取某个 .dat 文件 |
让运行 etemenanki-app 的用户能读取该文件 |
geosite code not found: <code> |
代码拼写错误、带了 geosite: 前缀、在 geosite 代码前加了 !、文件中已不存在该列表,或文件为空 |
在 domain-list-community 的 data/ 目录中核对名称 |
geoip code not found: <code> |
代码拼写错误、在 geoip 代码上加了 @attr,或精简文件(如 geoip-only-cn-private.dat)中没有该代码 |
使用两字母国家代码或 private,最多在开头加一个 ! |
geosite decode: failed to decode Protobuf message: … |
geosite 路径指向的不是 v2ray 格式的域名列表:可能是 geoip.dat、下载失败时保存下来的 HTML 页面,或其他工具格式的文件 |
重新下载 dlc.dat 并校验 |
geoip decode: failed to decode Protobuf message: … |
geoip 路径出现同样的问题 | 重新下载 geoip.dat 并校验 |
有些错误根本不会报错,只会表现为某条规则永远不匹配:
- 没有任何条目带有的属性,例如
apple@cm; - 带前导点的
domain_suffix:.example.com什么也匹配不到,应写成example.com; - 本想匹配域名的
geoip规则:它永远不会匹配目标为域名的流; - 针对以 IP 形式到达的流量的域名或
geosite规则,而入站设置了sniffing = false,或流量走的是 UDP; - 位于某条更宽泛规则之下的规则,而那条宽泛规则已经拿走了它的全部流。