Clash Rule 规则体系
2023 年 11 月删库潮之后,很多订阅从原版 Clash 语法迁到 mihomo,第一件事就是抄一条现成 rules。结果有人把 MATCH,proxy 写在第三行,后面跟着的 GEOIP,CN,DIRECT 从此成了摆设——整机流量全走代理,国内站点绕一大圈。查 /rules 接口才发现命中早停在第二条。这条规则的生死,全看你懂不懂它那条「先到先得」的顺序。
七种规则,三种维度:先分清再谈顺序
Clash 的 rules 有七种基本动作——DOMAIN、DOMAIN-SUFFIX、DOMAIN-KEYWORD、IP-CIDR、GEOIP、DST-PORT 和兜底的 MATCH。它们横跨三层心智模型:DOMAIN 系列是应用层的 DNS 名字,DST-PORT 是传输层端口,IP-CIDR 和 GEOIP 是网络层地址。最容易栽的坑是语义差异:DOMAIN-SUFFIX,google.com 会连 x.google.com 一起匹配,而 DOMAIN-KEYWORD,google 是子串匹配,googleads 也会命中;DOMAIN 才是精确匹配。把三者混用,等于把路由规则写成了正则猜谜。
先到先得:为什么 MATCH 必须放最后
规则按配置顺序逐条匹配,首条命中即停,后面再对也不看。这是整套模型的根,能解释两个高频事故:把 MATCH,proxy 写在中间,后面所有 GEOIP,CN,DIRECT 永不生效,整机全走代理;或 DOMAIN-SUFFIX,com,proxy 这种过宽规则把整个 .com 塞进代理,国内站点全部绕路。现象很典型——有些网站能开、有些永远直连不了,curl http://127.0.0.1:9090/rules 一看,命中停在倒数第二条。把它想成交换机接线员:第一个匹配的端口就接通,不问更优解。
2020 年 Premium 的 RULE-SET,把手写规则变成订阅
规则列表动辄上千行,手写维护不现实。2020-09 Clash Premium v1.0 引入 rule-providers + RULE-SET,支持 url + interval 远程拉取并本地缓存。但 RULE-SET 长期是 Premium 闭源专属,开源内核没有。直到 2021-12 MetaCubeX fork 出 Clash.Meta,才把这条能力开源,还补上 GEOSITE、PROCESS-NAME、REGEX。今天 Loyalsoldier/clash-rules、ACL4SSR 这些社区项目把广告/直连/代理规则做成可拉取的分发品,就是 RULE-SET 最典型的现实用法。
behavior 三兄弟:domain / ipcidr / classical 别混用
rule-provider 靠 behavior 声明文件内容格式:domain 只收域名、ipcidr 只收 CIDR、classical 按旧版全文规则。坑在于 behavior 必须与文件内容一致——把 ipcidr 列表塞进 behavior: domain 的 provider,轻则加载报错,重则静默丢规则,症状是某条该直连的流量突然走代理。2023-11 原版删库、Clash.Meta 改名 mihomo 后,这套生态定型成今天的模样:rules 里写一行 RULE-SET,reject,REJECT 就能引用远端规则集。注意 PROCESS-NAME 是客户端专属,搬到服务器上进程名对不上,规则等于不存在。
实战示例
最小可用配置,rules 顺序就是分流契约:
rules:
- DOMAIN-SUFFIX,cn,DIRECT
- DOMAIN-SUFFIX,google.com,proxy
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- GEOIP,CN,DIRECT
- MATCH,proxy
curl http://127.0.0.1:9090/rules 返回前两条,命中即停。子关键词
DOMAIN-SUFFIX 与子域匹配 后缀匹配连子域一起命中
DOMAIN-SUFFIX,google.com 同时匹配 google.com 和 x.google.com,按点分域名的后缀判断。想精确锁一个域名用 DOMAIN,做广告拦截用 DOMAIN-KEYWORD 子串匹配(googleads 也会中)。三个语义别混,这是规则集出 bug 的高发点。
GEOIP 与 GeoLite2 按 IP 归属地分流,依赖 mmdb 库
GEOIP,CN,DIRECT 靠 mmdb/GeoLite2-CN.mmdb 判断归属地,内核启动会加载。不写 no-resolve 会先解析域名拿 IP,等于把访问名单暴露给上游 DNS。自建分流常把它放在 MATCH 之前兜住国内流量。
rule-providers 的 behavior 声明文件内容格式,不可混用
behavior 三种取值:domain 只收域名、ipcidr 只收 CIDR、classical 收旧版全文规则。加载时按 behavior 解析,混用会报错或静默丢规则。RULE-SET 引用后规则写成一行,远程内容靠 interval 定时拉取并缓存本地。
DST-PORT 与免代理端口 按目标端口分流,适合局域网服务
DST-PORT 不触发 DNS 解析,适合把特定端口直接放行或拒绝。比如 DST-PORT,53,DIRECT 让 DNS 走本地,或按 SSH/RDP 端口放行内网。它和 IP-CIDR 同属不依赖域名的维度,配合 no-resolve 能少暴露解析。
PROCESS-NAME 的部署陷阱 客户端专属,服务器上等于没有
PROCESS-NAME 按进程名匹配,是桌面客户端特性,服务器/网关部署时进程名对不上,规则静默失效。机场订阅自带的这类规则搬到无头机器前应删掉,否则看似有规则实则不生效。
controller 的 /rules 接口 运行时查看实际加载的规则
curl http://127.0.0.1:9090/rules 返回当前生效规则数组,带 type/payload/proxy 字段,可 jq 切片看前几条。排障时对比它和配置文件,能定位「规则被前面吞了」这类顺序事故。注意它显示的是加载后的扁平列表,不是原始 YAML。
衍生角度
设计模式:策略表驱动分流
分流决策被拆成一张可热加载的表,内核只做顺序匹配,改表不重编译,换规则即换行为。这正是防火墙规则表思想的下放,也是它比 Surge 更可脚本化的根因。
概念拆解:首条命中 vs 最长匹配
IP 路由和 iptables 讲究最长前缀或精确逐条,Clash 却是先到先得。理解这个差异,才懂为什么规则顺序本身就是优先级,位置错了再对的规则也白搭。
风格架构:单二进制 + YAML 即代码
Go 单二进制、规则随 YAML 配置走,可 -t 校验、可 kill -HUP 热重载。和 Surge 闭源付费、平台绑定形成对照,这也是规则能进入 CI 版本管理的前提。
原理分析:GEOIP 与 no-resolve 的解析博弈
GEOIP 和 IP-CIDR 要拿真实 IP 比对,缺 no-resolve 就强制先解析,等于把访问名单暴露给上游 DNS,还拖慢首访。这是四层匹配天然的成本,规则作者必须显式选择。
实际用法与坑
- 把 MATCH 永远放 rules 最后一行,写完用 mihomo -t 校验再 reload。
- IP-CIDR 和 GEOIP 规则一律加 no-resolve,别让强制解析拖慢首访。
- 排障先 curl http://127.0.0.1:9090/rules,命中停在哪条一目了然。
- rule-provider 的 behavior 和文件内容严格对应,domain 里绝不混 CIDR。
- 订阅自带的 PROCESS-NAME 规则部署到服务器前删掉,进程名对不上等于没有。
下一步动手做
从最小规则集起步:先写 DOMAIN-SUFFIX,cn,DIRECT + MATCH,proxy 两行跑通,再逐条加 IP-CIDR/GEOIP 和 no-resolve。每条规则写完用 mihomo -t 校验、kill -HUP 热重载。稳定部分抽成 rule-provider 用 interval 拉取。排障时 curl /rules 看命中位置。
相关书籍
TCP/IP 详解(卷1:协议)
规则匹配建立在对 IP 与 DNS 的理解上,读 IP 协议与 DNS 章节,才能讲清 DOMAIN / IP-CIDR / GEOIP 各自吃哪一路信息。