Skip to Content
其他 使用 mosdns 提前进行 dns 进行分流

使用 mosdns 提前进行 dns 进行分流

前言

之前我是用 clash 的 dns 接管的路由的 dns,然后 clash dns 服务器内国外分流到 mosdns(因为我想国外 ipv4 优先)

后面想了一下,能不能提前就用 mosdns 作为主 dns 服务器,并且 mosdns 是专门搞 dns 的,性能处理上应该是比 clash 要好的并且还能额外实现很多的特性,以及实现一些特殊的逻辑。

于是就有这篇文章进行实验,个人使用一个月下来,感觉没什么问题,并且 redir-host 和 fake-ip 都可以使用。

fake-ip 模式下遵循路由规则,即使 openclash 配置了不代理 ipv6,也会跟着路由规则走。比如访问 IPv6 only 的资源,域名路由规则到 Match 设置为默认走代理,那么访问当前域名就会通过代理节点访问,必须要代理节点支持 ipv6,否则访问不通,Match 设置为直连的话就是直连

OpenClash 的“实验性:绕过中国大陆 IP“功能是通过 Dnsmasq 进行实现,旨在将国内 IP 通过防火墙转发规则,国内 IPv4 和 IPv6 的流量不经过 Clash(Mihomo)内核处理,增强直连性能,降低 OpenWRT 软路由的资源占用。对于进阶使用用户来说,在使用上存在诸多不便,例如无法与 MosDNS、Adguard Home 完美配合。

本文将使用 mosdns 作为 dns + openclash(mihomo 内核) 作为代理 + 手动防火墙前置绕过大陆 IP( 这步可以不用,推荐直接用 openclash 自带的绕过,下面那一块实验性:绕过中国大陆 IP 的原理 就可以不用看了,直接看 配置流程 部分 )。

实验性:绕过中国大陆 IP 的原理 (后面发现其实可以不用这个)

OpenClash 的大部分核心外功能实现基本都是通过 /etc/init.d/openclash 这个启动文件实现。在文件中,涉及实验性:绕过中国大陆 IP 的主要功能部分包括:

  • ip set/nft set 管理,也就是 IP 集合管理
  • China_ip_route 规则匹配及重定向
  • 利用 Dnsmasq 进行国内域名列表(/etc/openclash/accelerated-domains.china.conf)内的域名解析

IP 集合

OpenClash 内置了两个 IP 集合,分别为China_ip_route集合和China_ip_route_pass集合,通过插件设置 - 大陆白名单订阅 进行维护,没有使用 GeoIP:CN 进行维护。

China_ip_route 规则匹配

在 Openclash 选择绕过大陆 IP 时,通过 fw4 进行进行流量检测,当检测到目标 IP 地址属于中国大陆时,通过openclash链、openclash_mangle链、以及openclash_mangle_out链进行return操作,使流量按原链继续进行,不再经由 clash 内核进行处理。

openclash_mangle 链

openclash_mangle 链的作用是用于修改流量的特性,通常用于更改数据包的标记、TTL(生存时间)等,对流量进行细颗粒度控制,标记流量进行后续处理等。

可以在这里看到源代码

以 ipv4 的为例子:

if [ "$china_ip_route" != "0" ]; then if [ "$china_ip_route" = "1" ]; then rule="ip daddr @china_ip_route" elif [ "$china_ip_route" = "2" ]; then rule="ip daddr != @china_ip_route" fi [ "$enable_redirect_dns" != "2" ] && rule="$rule ip daddr != @china_ip_route_pass" nft "add rule inet fw4 openclash_mangle $rule counter return" fi
  • 检查变量 china_ip_route 是否不等于 0。如果是,表示启用了绕过中国大陆 IP 或者是海外用户回国模式。
  • 检查变量 enable_redirect_dns 是否不等于 2。这里 0 是不劫持 DNS,1 是通过 dnsmasq 劫持,2 是通过防火墙劫持。
  • 绕过中国大陆 IP 情况下,如果 enable_redirect_dns 不等于 2,即不使用防火墙劫持:nft 'add rule inet fw4 openclash_mangle ip daddr @china_ip_route ip daddr != @china_ip_route_pass counter return’
  • 绕过中国大陆 IP 情况下,如果 enable_redirect_dns 如果等于 2:nft 'add rule inet fw4 openclash_mangle ip daddr @china_ip_route counter return’
  • 海外用户回国模式同上。

openclash 链

openclash 链主要用于处理进入的流量。这个链通常用于根据特定的规则对流量进行分类和处理。

源代码:

if [ "$china_ip_route" != "0" ]; then if [ "$china_ip_route" = "1" ]; then rule="ip daddr @china_ip_route" elif [ "$china_ip_route" = "2" ]; then rule="ip daddr != @china_ip_route" fi [ "$enable_redirect_dns" != "2" ] && rule="$rule ip daddr != @china_ip_route_pass" nft "add rule inet fw4 openclash $rule counter return" fi

条件判断同上。

openclash_mangle_output 链

openclash_mangle_output 链专门用于处理输出流量,即从本地系统发出的流量(路由器本机流量)。

源代码:

if [ "$china_ip_route" != "0" ]; then if [ "$china_ip_route" = "1" ]; then rule="ip daddr @china_ip_route" elif [ "$china_ip_route" = "2" ]; then rule="ip daddr != @china_ip_route" fi [ "$enable_redirect_dns" != "2" ] && rule="$rule ip daddr != @china_ip_route_pass" nft "add rule inet fw4 openclash_output skuid != 65534 $rule counter return" fi

大部分判断条件同上,其中多出来的部分,skuid≠65534为非特权用户,即nobody用户。

China_ip_route_pass 规则匹配

China_ip_route_pass 这个 IP 集是通过劫持 Dnsmasq,使用设定的 dns 对国内常见域名(也就是/etc/openclash/accelerated-domains.china.conf 文件内域名)进行解析并生成的 IP 集。这部分非常依赖于 Dnsmasq,但在我们这里由于已经有 MosDNS 进行分流,所以不用过于关注。

配置流程

openclash 的配置 (使用 openclash 配置绕过大陆)

  • 流量控制 - 实验性:绕过指定区域 IP 设置为 绕过中国大陆
  • 禁用 DNS设置 - *本地 DNS 劫持

openclash 的配置 (手动添加防火墙规则)

  • 禁用 流量控制 - 实验性:绕过指定区域 IP选项,因为我们要手动添加防火墙规则进行绕过
  • 禁用 DNS设置 - *本地 DNS 劫持
  • 覆写设置 - DNS设置 全部关闭

clash 的 DNS 配置文件:

dns: enable: true listen: 0.0.0.0:7874 respect-rules: false ipv6: true use-hosts: true prefer-h3: true # 是否开启 DOH 的 http/3。 default-nameserver: - 223.5.5.5 - 119.29.29.29 proxy-server-nameserver: - https://223.5.5.5/dns-query - https://1.12.12.12/dns-query nameserver: - tls://1.0.0.1#dns # 这里使用 dns 代理组内的节点进行解析,没有 dns代理组 的话改成自己的代理组 - tls://9.9.9.9#dns # 同上,避免 DNS 泄露

添加防火墙规则

由于防火墙规则采用顺序匹配,如果我们采用 add 的方式,无论如何只会附加规则至每条防火墙规则链的底部,所以这里采用 insert 方式。可以直接添加在 openclash 内的开发者选项内。

ipv4 的规则:

nft 'insert rule inet fw4 openclash ip daddr @china_ip_route ip daddr != @china_ip_route_pass counter return' nft 'insert rule inet fw4 openclash_mangle ip daddr @china_ip_route ip daddr != @china_ip_route_pass counter return' nft 'insert rule inet fw4 openclash_mangle_output skuid != 65534 ip daddr @china_ip_route ip daddr != @china_ip_route_pass counter return'

ipv6 的规则(没开启 ipv6 代理不需要配置):

nft 'insert rule inet fw4 openclash_v6 ip6 daddr @china_ip6_route ip daddr != @china_ip6_route_pass counter return' nft 'insert rule inet fw4 openclash_mangle_v6 ip6 daddr @china_ip6_route ip daddr != @china_ip6_route_pass counter return' nft 'insert rule inet fw4 openclash_mangle_output_v6 skuid != 65534 ip6 daddr @china_ip6_route ip daddr != @china_ip6_route_pass counter return'

iptables 和 nft

两种的写法都差不多,就命令使用上的区别,有需求可以自己研究。

mosdns 配置

PS: mosdns 所使用的 geoX 文件,可以通过 ln -s 软连接 link 到 openclash 的 geoX 文件,这样就不用维护两份还方便管理了。

mosdns 使用自定义配置文件,并且打开 DNS 转发

log: level: info file: "/tmp/log/mosdns.log" # API 入口设置 api: http: "0.0.0.0:9091" include: [] plugins: # hosts - tag: hosts type: hosts args: files: - "/etc/mosdns/rule/hosts.txt" # 缓存 - tag: lazy_cache type: cache args: size: 20000 lazy_cache_ttl: 86400 # 国内域名 - tag: geosite_cn type: domain_set args: exps: - "lan" - "local" - "arpa" files: - "/etc/mosdns/rule/whitelist.txt" - "/var/mosdns/geosite_cn.txt" # 国外域名 - tag: remote_domain type: domain_set args: files: - "/etc/mosdns/rule/greylist.txt" # 转发至本地服务器 - tag: forward_local type: forward args: concurrent: 2 upstreams: - addr: 223.5.5.5 - addr: 119.29.29.29 # 转发至远程服务器 - tag: forward_remote type: forward args: concurrent: 3 upstreams: - addr: tls://8.8.8.8 enable_pipeline: true - addr: tls://1.0.0.1 enable_pipeline: true - addr: tls://9.9.9.9 enable_pipeline: false # 国内解析 - tag: local_sequence type: sequence args: - exec: $forward_local # 用 clash 的 DNS 国外解析 - tag: clash_sequence type: sequence args: - exec: prefer_ipv4 - exec: forward 127.0.0.1:7874 - exec: ttl 1 # 国外解析 fallback 避免 openclash 没开时无法解析国外域名 - tag: remote_sequence type: sequence args: - exec: prefer_ipv4 - exec: $forward_remote - exec: ttl 1 # 有响应终止返回 - tag: has_resp_sequence type: sequence args: - matches: has_resp exec: accept # fallback 用远程服务器 sequence - tag: fallback type: fallback args: primary: clash_sequence secondary: remote_sequence threshold: 500 # always_standby: secondary 会和 primary 一并执行。secondary 的结果将会在需要回滚时,立刻被采用。 always_standby: false # 查询国内域名,灰名单内的域名,直接走 fallback - tag: query_is_local_domain type: sequence args: - matches: qname $remote_domain exec: return - matches: qname $geosite_cn exec: $local_sequence # 主要的运行逻辑插件 # sequence 插件中调用的插件 tag 必须在 sequence 前定义, # 否则 sequence 找不到对应插件。 - tag: main_sequence type: sequence args: - exec: $hosts - exec: jump has_resp_sequence # drop https query type - matches: - qtype 65 exec: reject 3 # handle local ptr - matches: - qtype 12 exec: $local_sequence - exec: jump has_resp_sequence - exec: $lazy_cache - exec: $query_is_local_domain - exec: jump has_resp_sequence # 这里不是国内名单就直接走远程服务器,避免 dns 泄露或者有域名没被 clash 处理 - exec: $fallback # 启动 udp 服务器。 - tag: udp_server type: udp_server args: entry: main_sequence listen: ":5335"

参考

https://github.com/vernesong/OpenClash/issues/3467

https://songchenwen.com/tproxy-split-by-dns

https://www.dolingou.com/article/experimental-bypass-china-ip-without-dnsmasq-mihomo

Last updated on