路由
入站接受的每条 TCP 流,以及 UDP 关联中的每个数据包,都会被交给恰好一个出站或负载均衡器。具体交给哪一个,由路由器根据 [route] 表及其 [[route.rule]] 块决定。路由器考察的内容包括:流的目标地址和端口、流是 TCP 还是 UDP、流从哪个入站进入、客户端地址,以及嗅探可能从开头几个字节中读出的域名。
本页介绍所有路由配置项、规则的尝试顺序、每种匹配条件能看到什么和看不到什么、嗅探和 UDP 如何参与路由,以及错误规则会产生的报错。当你希望部分流量走与其余流量不同的路径时,例如屏蔽广告、让本地流量直连,或者让某个服务经由特定代理,就可以参考本页。
一个客户端把所有流量都交给代理,只有 example.net 及其子域名直连:
[[outbound]]tag = "proxy"protocol = "socks"server = "proxy.example.com"port = 1080
[[outbound]]tag = "direct"protocol = "freedom"
[[route.rule]]outbound = "direct"domain_suffix = ["example.net"]这里没有设置 [route].default,所以不匹配任何规则的流会交给 proxy,也就是文件中的第一个 [[outbound]]。如果完全没有 [route] 部分,所有流都会交给它。
流如何被路由
Section titled “流如何被路由”flowchart TB
F["新的流或 UDP 数据包"] --> N{"还有下一条规则?"}
N -- "是" --> M{"本规则有任一匹配条件命中?"}
M -- "是" --> O["使用本规则的出站"]
M -- "否" --> N
N -- "否" --> D{"设置了 route.default?"}
D -- "是" --> DT["使用 route.default"]
D -- "否" --> DF["使用第一个出站"]
- 首条匹配生效。 规则按文件中的顺序从上到下尝试。第一条匹配的规则决定出站,它下面的规则不再参与判断。
- 同一条规则内,任一匹配条件命中即可。 只要规则中至少有一个匹配条件命中,规则就匹配;列表类匹配条件只要有任一条目命中就算命中。在一条规则内无法要求两个匹配条件同时成立,见组合条件。
- 没有匹配条件的规则永远不匹配。 只写了
outbound的[[route.rule]]会被接受,但不起任何作用。 - 兜底的是默认路由。 没有任何规则匹配的流交给
[route].default;未设置default时交给第一个[[outbound]]。 - TCP 只路由一次,UDP 逐包路由。 TCP 流在建立时路由一次,之后一直使用该出站。每个 UDP 数据包按各自的目标单独路由,见 UDP。
[route]
Section titled “[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]] 是未知字段)。按文件中的顺序逐条尝试,第一条匹配的规则决定出站。 |
default 可以指定一个出站或负载均衡器。不写时,由文件中的第一个 [[outbound]] 接收未匹配的流。负载均衡器定义在单独的 [[balancer]] 列表中,只有 default 明确指定它时,它才会成为默认路由。
显式设置 default 只需一行,却能避免有人调整出站顺序时兜底出站随之改变。
Geodata 文件
Section titled “Geodata 文件”geoip 和 geosite 指向 v2ray 和 Xray 使用的 .dat 文件:来自 v2fly geoip 项目的 geoip.dat,以及来自 v2fly domain-list-community 项目的 geosite.dat(发布时名为 dlc.dat)。etemenanki-app 直接读取其 protobuf 格式,无需转换。
- 只在用到时加载。 只有至少一条规则使用了
geoip或geosite匹配条件时才会打开对应文件;否则完全不检查该路径,连文件是否存在都不检查。 - 相对路径以工作目录为准。 相对路径按进程运行时所在的目录解析,而不是配置文件所在目录。服务配置中请使用绝对路径。
- 每次构建都会读取。 启动时、
--test时,以及每次重载一份能成功解析的已变更配置时,都会读取这些文件。内存中只保留规则引用到的 code。 - 替换文件不会触发重载。 热重载由配置文件字节内容的变化触发,所以更新
.dat文件后,也要改动一下配置文件(加一行注释即可)。见热重载。
[[route.rule]]
Section titled “[[route.rule]]”每条规则有一个必填项 outbound,以及任意数量的匹配条件。未知的配置项会被拒绝,所以拼错的匹配条件会明确报错,而不是变成一条永远不匹配的规则:
unknown field `domain`, expected one of `outbound`, `domain_suffix`, `domain_keyword`, `domain_full`, `domain_regex`, `cidr`, `source_cidr`, `port`, `network`, `inbound_tag`, `geosite`, `geoip`| 键 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
outbound | string | 是 | — | 接收本规则所匹配流的出站或负载均衡器的 tag。tag 不存在时报 route references unknown outbound tag: <tag>。 |
domain_suffix | array of strings | 否 | [] | 匹配该域名本身及其所有子域名,按标签边界匹配:example.com 匹配 example.com 和 a.example.com,不匹配 notexample.com。不区分大小写。不要加前导点:.example.com 什么也匹配不到。 |
domain_keyword | array of strings | 否 | [] | 域名中任意位置包含该字符串即匹配:ads 会匹配 ads.example.com,也会匹配 downloads.example.com。不区分大小写。 |
domain_full | array of strings | 否 | [] | 只匹配完全相同的域名,不含子域名。不区分大小写。 |
domain_regex | array of strings | 否 | [] | Rust regex 语法的正则表达式,对转为小写后的域名求值。不自动锚定:要匹配整个域名,请加上 ^ 和 $。表达式本身不会被转为小写,所以请用小写书写。表达式无效时报 invalid domain regex "<pattern>": …。 |
cidr | array of strings | 否 | [] | 目标 IP 地址落在该网段内即匹配,例如 10.0.0.0/8 或 2001:db8::/32。只写地址表示单个主机。主机位必须为零(否则报 invalid cidr "10.0.0.1/8": host part of address was not zero)。目标地址是域名时永远不匹配。 |
source_cidr | array of strings | 否 | [] | 客户端地址落在该网段内即匹配。语法和报错与 cidr 相同。来自 Unix socket 入站的流没有客户端地址,永远不匹配。 |
port | array of strings | 否 | [] | 目标端口,写成字符串:单个端口 "443",或闭区间 "8000-9000"。数字两侧可以有空格。直接写整数是类型错误(expected a string);下界大于上界的区间报 invalid port spec: "9000-8000" has a lower bound above its upper bound。 |
network | string (enum) | 否 | — | 流的传输协议:tcp 或 udp。只能写一个字符串,不能写数组,且只接受小写。其他取值报 invalid rule network "<value>" (expected "tcp" or "udp")。 |
inbound_tag | array of strings | 否 | [] | 从这些入站进入的流即匹配。按原样比较,区分大小写。不会与文件中的入站核对,所以写错的 tag 也能通过校验,只是永远不匹配。 |
geosite | array of strings | 否 | [] | [route].geosite 中的列表:写 code,或写 code@attr 只取带该属性的条目。code 和属性都不区分大小写。不要写 geosite: 前缀。code 不存在时报 geosite code not found: <code>;属性不存在时可以通过校验,但什么也匹配不到。 |
geoip | array of strings | 否 | [] | [route].geoip 中的列表:code 匹配列表内的目标 IP,!code 匹配列表外的目标 IP。不区分大小写。与 cidr 一样,两种写法都永远不会匹配以域名给出的目标地址。code 不存在时报 geoip code not found: <code>。 |
域名匹配条件
Section titled “域名匹配条件”有四种匹配条件用于测试域名。geosite 也测试域名,它的列表由同样四类条目组成。
| 匹配条件 | 规则值 | 匹配 | 不匹配 |
|---|---|---|---|
domain_suffix |
example.com |
example.com、www.example.com、a.b.example.com |
notexample.com、example.com.cdn.example.net |
domain_keyword |
track |
track.example.com、backtrack.example.net |
example.com |
domain_full |
www.example.com |
www.example.com |
example.com、a.www.example.com |
domain_regex |
^ad[0-9]+\. |
ad1.example.com、ad42.example.net |
bad1.example.com、ads.example.com |
大小写处理:
- 被测试的域名会先转为小写,无论它来自请求还是来自嗅探。
domain_suffix、domain_keyword和domain_full的值在加载配置时转为小写,因此规则中写Example.COM与写example.com效果相同。domain_regex的表达式不会转为小写。它们针对已转为小写的域名求值,所以需要大写字母才能匹配的表达式(例如^WWW\.)永远不会匹配。字面字母请用小写书写;\d、\W这类转义不受影响。
domain_regex 使用 Rust regex crate 的语法,不支持环视(look-around)和反向引用。除非用 ^ 和 $ 锚定,表达式可以匹配域名中的任意位置:单独的 example 会匹配 example.com 和 myexample.net。请用单引号把表达式写成 TOML 字面量字符串,这样反斜杠会原样传给正则表达式:
domain_regex = ['^ad[0-9]+\.', '^cdn-[a-z]+\.example\.com$']在双引号 TOML 字符串中,"\." 是无效转义,文件无法解析。
地址和端口匹配条件
Section titled “地址和端口匹配条件”cidr 测试目标地址,source_cidr 测试客户端地址。两者都使用 CIDR 表示法,例如 10.0.0.0/8、192.0.2.0/24 或 2001:db8::/32。只写地址(如 192.0.2.7)表示单个主机。地址的主机位必须为零:10.0.0.1/8 会被拒绝,而不是被悄悄扩大成整个网段。
source_cidr 看到的客户端地址取决于入站:
| 入站 | 客户端地址 |
|---|---|
TCP 监听器:socks、http、trojan、vless、vmess、shadowsocks |
TCP 连接的对端地址 |
hysteria2 |
QUIC 客户端的地址,按连接区分 |
tun |
本机数据包的源地址 |
| 任何监听在 Unix socket 上的入站 | 无:source_cidr 永远不匹配 |
etemenanki-app 不读取 X-Forwarded-For,也不支持 PROXY protocol。位于反向代理或 CDN 之后时,客户端地址就是该代理的地址。
port 测试目标端口。每个条目是一个字符串:"443",或闭区间 "8000-9000",数字两侧的空格会被忽略。"0-65535" 匹配所有端口。写整数是类型错误,所以请写 port = ["443"],而不是 port = [443]。
network 和 inbound_tag
Section titled “network 和 inbound_tag”network 是单个字符串 "tcp" 或 "udp",不是列表,也没有表示两者皆可的取值。不写 network 时,规则的其他匹配条件同时作用于 TCP 流和 UDP 数据包。要让一条规则捕获所有流,可以用 port = ["0-65535"],它匹配任何端口。
inbound_tag 是入站 tag 的列表,按原样精确比较。这些 tag 不会与 [[inbound]] 块核对,所以拼错的 tag 能正常加载,但永远不匹配。如果某条 inbound_tag 规则似乎被忽略了,请检查拼写。
geosite 和 geoip
Section titled “geosite 和 geoip”geosite 条目指定 geosite 文件中的列表:
category-ads-all使用该列表的全部条目。google@ads只使用google中带有ads属性的条目。这是一个条目同时要求两件事的情形:既要在该列表中,又要带该属性。
geoip 条目指定 geoip 文件中的列表:
private匹配列表内的目标地址。!cn匹配列表之外的目标地址。
code 和属性都不区分大小写:CN、cn 和 Cn 加载的是同一个列表。未知的 code 会导致配置无法加载,报 geosite code not found: <code> 或 geoip code not found: <code>。未知的属性不算错误:google@nosuchattr 能正常加载,只是什么也匹配不到。
在当前的 v2fly 发布版本中,private 包括 RFC 1918 网段、回环地址、链路本地地址、共享地址空间(100.64.0.0/10)、组播和保留地址空间、IPv6 唯一本地地址(fc00::/7),还包括文档用网段 192.0.2.0/24、198.51.100.0/24 和 203.0.113.0/24。用这些网段中的地址测试规则时请记住这一点:geoip = ["private"] 规则会捕获它们。
与 cidr 一样,geoip 只测试以 IP 地址给出的目标,取反形式也是如此。对于以域名为目标的流,无论该域名解析到什么地址,!cn 都不会匹配。
geosite 列表中类型未知的条目,以及无法编译的正则表达式,会被跳过并在日志中输出一条 skipping … 警告,而不会导致加载失败。
匹配条件能看到什么
Section titled “匹配条件能看到什么”每个匹配条件只测试与它相关的那个字段。流不带该字段时,匹配条件就不匹配。
| 匹配条件 | 测试对象 | 永远不匹配的情形 |
|---|---|---|
domain_suffix、domain_keyword、domain_full、domain_regex、geosite |
目标域名,以及嗅探得到的域名 | 目标是 IP 地址且没有嗅探到任何域名 |
cidr、geoip |
目标地址 | 目标是域名 |
port |
目标端口 | 总会测试 |
network |
tcp 或 udp |
总会测试 |
inbound_tag |
入站的 tag | 总会测试 |
source_cidr |
客户端地址 | 入站监听在 Unix socket 上 |
路由时不解析域名
Section titled “路由时不解析域名”路由器按客户端发来的原样匹配目标,从不为了判断哪些地址规则适用而解析域名。发送域名的客户端,例如通过 SOCKS5 使用远程 DNS 的浏览器,或 curl --socks5-hostname,产生的流永远不会被 cidr 和 geoip 规则匹配,即使该域名解析到的是私有地址。
如果同一个目标两种形式都可能出现,请在同一条规则中同时覆盖:用域名匹配条件处理域名,用地址匹配条件处理 IP 地址。完整示例对本地流量就是这样处理的,把 domain_suffix = ["lan", "local"] 与 geoip = ["private"] 放在一起。
反过来的情况,即流以 IP 地址为目标、而你希望按域名匹配,正是嗅探的用途。入站设置 sniffing = true(默认值)时,对于目标是 IP 地址的 TCP 流,etemenanki-app 会读取流的开头并查找域名:
- TLS ClientHello 中的 SNI;
- 若没有,则取 HTTP/1 请求的主机:绝对形式请求行(如
GET http://example.com/ HTTP/1.1)中的 authority,否则取Host头。
嗅探最多等待 300 毫秒,最多读取 4 KiB。mux.cool 子流是例外:只检查随其打开帧一起到达的数据,不做等待。已经以域名为目标的流永远不会被嗅探。嗅探不会导致连接失败:遇到无意义数据、被截断的 ClientHello,或者等待服务端先发言的客户端,流都按其地址路由。SNI 或 Host 中的 IP 字面量会被忽略。
路由器如何使用嗅探结果:
- 每个域名匹配条件都会尝试嗅探得到的域名,包括
geosite,同时也照常测试目标域名。域名规则正是这样捕获以 IP 地址到达的流的。 - 地址匹配条件忽略嗅探结果。
cidr和geoip仍然测试目标地址。 - 目标不会被改写。 出站仍然连接客户端请求的那个 IP 地址。嗅探得到的域名只影响路由决策。
- UDP 永远不嗅探。 数据包只按各自的目标路由。
嗅探开关按入站设置。入站页面说明了何时应该关闭它,以及它如何改变 SOCKS 或 HTTP 客户端看到的响应。
UDP 关联没有单一目标:每个数据包各自携带目标。因此 etemenanki-app 对每个数据包单独路由,使用与 TCP 相同的规则和相同的首条匹配顺序。匹配的是数据包的目标地址和端口,network 为 udp,关联的入站 tag 和客户端地址也会一并带上。由于 UDP 永远不嗅探,以 IP 地址为目标的数据包只能匹配地址、端口、network、入站和源地址规则。
因此一个关联可以同时使用多个出站:DNS 查询可以直连,其他数据包则经过代理。路由到 blackhole 的数据包会被丢弃,关联的其余部分照常工作。
http 和 shadowsocks 出站不承载 UDP,路由给它们的数据包会被丢弃。如果 UDP 很重要,请在指向它们的规则之前放一条 network = "udp" 规则。出站页面介绍了逐包路由背后的子链路及其限制。
一条规则是其所有匹配条件及其所有条目的 OR,没有 AND。但仍有三种方法可以表达某些组合。
使用本身就带组合的匹配条件。 geosite = ["google@ads"] 同时要求列表和属性。domain_regex 可以同时要求前缀和后缀,例如 '^api\.[a-z]+\.example\.com$'。"8000-9000" 这样的端口区间是一个条目,却从两侧限定了端口。
用顺序划出例外。 “example.com 下的所有域名都走代理,static.example.com 除外”可以写成两条规则,例外放在前面:
[[route.rule]]outbound = "direct"domain_full = ["static.example.com"]
[[route.rule]]outbound = "proxy"domain_suffix = ["example.com"]先认领补集。 要处理同时满足条件 A 和条件 B 的流,先把所有不满足 A 的流交给它们本来就该去的出站。这样只有满足 A 的流才会到达下一条规则,再由它测试 B。例如,屏蔽 QUIC(发往 443 端口的 UDP),让浏览器回退到 TCP:
# 所有 TCP 流在这里就被截住,交给它们本来就会去的出站。[[route.rule]]outbound = "proxy"network = "tcp"
# 只有 UDP 能到达这条规则,所以这里的 443 端口就意味着发往 443 端口的 UDP。[[route.rule]]outbound = "block"port = ["443"]代价是第一条规则会吃掉所有 TCP 流,所以任何针对 TCP 的规则都必须放在它上面。这一点普遍成立:被补集规则捕获的流会跳过它下面的所有规则。
这种方法只在两个条件之一的补集能写成匹配条件时才可行:
network只有两个取值,其补集就是另一个取值。- 端口的补集是一组区间。“
192.0.2.0/24中的客户端连接 25 端口”可以先把port = ["0-24", "26-65535"]交给常规出站,再把source_cidr = ["192.0.2.0/24"]交给专门的出站。 - 地址网段的补集是一组覆盖其余地址空间的 CIDR 块,写起来很长。对
geoip而言,code的补集是!code。对目标地址要记住:cidr列表和!code都不会匹配以域名给出的目标,所以这类流也会继续流向下一条规则。 - 某个入站的补集是你其他入站 tag 的列表。
- 域名和 geosite 匹配条件没有补集:没有任何条件能匹配“除这些之外的所有域名”。完全由域名和 geosite 条件构成的组合,例如“在
example.com下且属于category-ads-all”,无法表达。
一个本地 SOCKS5 客户端:屏蔽广告,本地流量直连,测速流量直连,屏蔽 QUIC,其余流量经由基于 WebSocket 和 TLS 的 VLESS 代理:
# A local client that routes with geodata: ads are dropped, local names and# private addresses go direct, and everything else goes through a VLESS proxy.# QUIC (UDP to port 443) is blocked so that browsers fall back to TCP.# Download geoip.dat and geosite.dat from the v2fly projects first.
[[inbound]]tag = "socks-in"protocol = "socks"listen = "127.0.0.1"port = 1080# sniffing = true is the default: a flow addressed by IP is matched by its# TLS SNI or HTTP Host as well, so domain and geosite rules still apply.
[inbound.settings]udp = true # the default; shown because rule 5 below is about UDP
# The first outbound. [route].default names it explicitly anyway.[[outbound]]tag = "proxy"protocol = "vless"server = "proxy.example.com"port = 443
[outbound.stream]network = "ws"security = "tls"
[outbound.stream.ws]path = "/ws"
[outbound.settings]id = "11111111-2222-3333-4444-555555555555"
[[outbound]]tag = "direct"protocol = "freedom"
[[outbound]]tag = "block"protocol = "blackhole"
[route]default = "proxy"# Read only because the rules below use geosite and geoip matchers.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. Ads and trackers are dropped, whatever the protocol.[[route.rule]]outbound = "block"geosite = ["category-ads-all"]
# 2. Local names and private addresses stay on the local network. The domain# matchers catch names; geoip catches flows addressed by IP.[[route.rule]]outbound = "direct"domain_full = ["localhost"]domain_suffix = ["lan", "local"]geoip = ["private"]
# 3. Any name that contains "speedtest" goes direct, so a speed test# measures the local line rather than the proxy.[[route.rule]]outbound = "direct"domain_keyword = ["speedtest"]
# 4 and 5 together block UDP to port 443. Rule 4 claims every remaining TCP# flow, so only UDP ever reaches rule 5. Rule 4 names the default outbound,# which leaves TCP routing unchanged.[[route.rule]]outbound = "proxy"network = "tcp"
[[route.rule]]outbound = "block"port = ["443"]部分流的路由结果:
| 流 | 规则 | 出站 |
|---|---|---|
TCP,目标是 category-ads-all 中的域名 |
1 | block |
TCP,目标是公网 IP 地址的 443 端口,TLS SNI 属于 category-ads-all |
1,经由嗅探 | block |
TCP,目标 nas.lan:445 |
2,domain_suffix |
direct |
TCP,目标 192.168.1.10:80 |
2,geoip |
direct |
TCP,目标 www.speedtest.example.com:443 |
3 | direct |
TCP,目标 www.example.com:443 |
4 | proxy |
| UDP,目标是公网 IP 地址的 443 端口 | 5 | block |
| UDP,目标是公网 IP 地址的 53 端口 | 无 | proxy,即默认出站 |
UDP,目标 192.168.1.1:53 |
2,geoip |
direct |
有三点值得注意:
- 规则 2 需要两类匹配条件。
nas.lan以域名形式到达,geoip从不测试域名;192.168.1.10以地址形式到达,domain_suffix从不测试地址。 - 表格中特意写“公网 IP 地址”。本站其他地方用作占位值的文档用网段都在
private列表中,规则 2 会把它们直连。 - 规则 1 到 3 同样作用于 UDP,因为它们位于仅限 TCP 的规则 4 之前。发往私有地址的 UDP 数据包会到达规则 2 并直连。
用 etemenanki-app --test -c routing.toml 检查该文件。--test 会构建完整的路由器,包括读取 geodata 文件并查找每个 code,但不绑定任何监听器。
下面每个错误都会让 --test 和启动失败。日志中,--test 时消息跟在 configuration invalid: 之后,启动时跟在 failed to start: 之后。热重载时,正在运行的配置保持不变,新配置被拒绝:对于 TOML 解析器报告的错误(未知字段、缺少字段、类型错误),日志为 reload: parse failed, keeping current config: …;其余错误为 reload: build failed, keeping current config: …。
被拒绝的重载不会自动重试。实例会记住被拒绝的文件内容,所以修好缺失或损坏的 geodata 文件后,需要再改动一次配置文件(加一行注释即可)来触发新的重载。
| 消息 | 原因 | 修复 |
|---|---|---|
route references unknown outbound tag: <tag> |
某条规则的 outbound 或 [route].default 指定的 tag 既不是出站也不是负载均衡器 |
修正 tag,或定义该出站 |
config defines no outbounds |
没有任何 [[outbound]],所以无处可路由,连默认出站都没有 |
至少定义一个出站 |
unknown field `<key>`, expected one of `outbound`, … |
匹配条件拼错,或使用了 domain、ip 等 Xray 配置项 |
使用规则表中的配置项 |
missing field `outbound` |
规则缺少 outbound |
加上它 |
invalid type: integer `443`, expected a string |
写成了 port = [443] |
给端口加引号:port = ["443"] |
invalid port spec: "<value>" |
端口不是 0 到 65535 之间的数字,或区间格式错误 | 写成 "443" 或 "8000-9000" |
invalid port spec: "9000-8000" has a lower bound above its upper bound |
区间上下界颠倒 | 交换上下界 |
invalid rule network "TCP" (expected "tcp" or "udp") |
取值未知或使用了大写,或写成 "tcp,udp" |
使用 tcp 或 udp,或者不写 network |
invalid type: sequence, expected a string |
写成了 network = ["tcp", "udp"] |
network 只接受一个字符串 |
invalid cidr "10.0.0.1/8": host part of address was not zero |
cidr 或 source_cidr 条目的主机位不为零 |
使用网络地址 10.0.0.0/8 |
invalid cidr "<value>": couldn't parse address in network: invalid IP address syntax |
根本不是地址,例如写了主机名 | 规则只能以 CIDR 形式匹配地址 |
invalid domain regex "<pattern>": regex parse error: … |
domain_regex 无法编译 |
修正表达式;消息会指出出错位置 |
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),或其他 OS 错误,如 Is a directory (os error 21) |
geodata 路径错误或不可读。消息中不会给出文件名 | 检查 [route].geoip 和 [route].geosite,并记住相对路径以工作目录为准 |
geosite code not found: <code> / geoip code not found: <code> |
文件中没有该名称的列表,或条目带有 geosite: 之类的 Xray 前缀 |
对照文件中的列表检查名称 |
geosite decode: failed to decode Protobuf message: … / geoip decode: … |
文件不是 v2ray 格式,或两个路径写反了 | 让每个配置项指向正确的文件 |
规则能加载却似乎从不匹配,几乎总是以下原因之一:
- 域名规则遇到以 IP 地址到达的流,而嗅探已关闭或没有可嗅探的内容;
cidr或geoip规则遇到以域名到达的流;- 前面的某条规则先匹配了,常见原因是
network或port匹配条件匹配的范围超出预期; inbound_tag拼错、domain_suffix带了前导点,或domain_regex中含有大写字母。