家庭环境实现 DNS 国内外分流教程
主要使用到的是 chinadns-ng
可能有人以前用过,后面改过一次分流配置写法。并且引入了 ipset/nftset ,所以有人就搞不明白了。
现在我带大伙看看这个项目到底如何使用。
首先看一下下载哪个版本。
打开下载页面后 Releases · zfl9/chinadns-ng · GitHub
按 F12 点击我圈的地方,把 878 改成 1300。
这样就能把文件名完整的显示出来
按 Ctrl+F 搜索你需要的 CPU 架构。
本文以我自己的路由器为例,我的路由器是 ARM 的,我就直接搜 arm,如果你是 x86 的软路由,你就搜软路由。chinadns-ng+wolfssl 开头的版本支持 DoT 上游
wolfssl 已启用硬件加速指令
因为我的上游还有 smartdns 和 AdGuard 的本地 dns,所以我不需要 DoT 功能
我直接选择的 chinadns-ng@arm-linux-musleabihf@generic+v7a@fast+lto
chinadns-ng 一共可以定义两组 DNS 上游
china 组 (大陆 DNS)、trust 组 (国外 DNS)
chinadns-ng 一共有 3 种运行模式
1.chnlist 分流
他会读取你的 chnlist.txt 里的域名,这里面的域名代表是国内的域名,你的所有查询,如果命中了里面的域名。则会直接交给 china 组 (大陆 DNS) 上游进行查询,没有匹配上的走 trust 组 (国外 DNS)
下面的是官方的解释的域名匹配逻辑,意思是,如果你查询 www.bilibili.com , 首先检查一遍 www.bilibili.com 是否在规则中,如果没有就继续查询 bilibili.com,如果再没有就继续查询 com
当然到实际的查询中,肯定会有优化,官方默认的规则里就有 11w 的域名数据。
域名匹配顺序
收到 DNS查询 时,会对域名进行最长后缀匹配。举个例子,若查询的域名为x.y.z,则匹配顺序为:
x.y.z,检查数据结构中是否存在此域名后缀。
y.z,检查数据结构中是否存在此域名后缀。
z,检查数据结构中是否存在此域名后缀。
2.gfwlist 分流
跟 chnlist 分流相反,需要一个 gfwlist.txt 文件,里面的域名走 trust 组 (国外 DNS) 组,剩下的全部交由 china 组 (大陆 DNS)
3. chnroute 分流
这个模式需要 chnlist.txt 和 gfwlist.txt 两个文件。运作逻辑也和上述两个方案一样,匹配上 chnlist.txt 走 china 组 (大陆 DNS),匹配上 gfwlist.txt 的走 trust 组 (国外 DNS)
不同的是,剩下没有匹配上的域名,他同时向国内和国外两个 DNS 组发起请求,如果 china 上游返回国内 IP,则接受其结果,否则采纳 trust 结果
因此,为了判断 IP 归属,必须要有中国 IP 的 IP 段。
在 chinadns-ng 的官方配置示例中,chnroute 分流模式下,使用了如下的配置
ipset-name4 chnroute #iptables
ipset-name6 chnroute6 #iptables chnroute 代表的是中国 IPv4 的集合名,chnroute6 代表的是中国 IPv6 的集合名
chinadns-ng 默认不会帮我们把中国 IP 全都导入到这个集合中
我们需要先到 chinadns-ng/res at master · zfl9/chinadns-ng · GitHub 中下载 IP 规则集合(当然作者为我们写好了 update-xxx.sh,名字对应的就是下载的文件)
chinadns-ng 是使用 iptables 或 nftables 来查询 IP 知否命中 IP 规则集,所以我们需要根据你路由器所使用的防火墙来选择,选错了则无法正常导入 IP 集合,你需要借助 AI 或者固件里写的信息,来判断。
我的路由器比较老,用的还是 iptables
xxx.ipset 结尾的为 iptables,xxx.nftset 结尾的为 nftables。6 代表 ipv6
下载后,官方给出了导入 IP 库的命令,res/chnroute.ipset 为你上面下载的 IP 文件的路径
# ipset (chnroute, chnroute6)
ipset -R <res/chnroute.ipset
ipset -R <res/chnroute6.ipset
# nftset (inet@global@chnroute, inet@global@chnroute6)
nft -f res/chnroute.nftset
nft -f res/chnroute6.nftset
# 只需导入一次,除非对应集合已从内核移除(比如重启了) 如何更新 IP 库
# ipset (chnroute, chnroute6) https://github.com/zfl9/chinadns-ng?tab=readme-ov-file#%E5%A6%82%E4%BD%95%E6%9B%B4%E6%96%B0-chnrouteipsetchnroute6ipset # nftset (inet@global@chnroute, inet@global@chnroute6) https://github.com/zfl9/chinadns-ng?tab=readme-ov-file#%E5%A6%82%E4%BD%95%E6%9B%B4%E6%96%B0-chnroutenftsetchnroute6nftset 特别注意,官方配置中为 iptables 配置示例,如果你使用的是 nftables 需要更改写法
ipset-name4 chnroute #iptables
ipset-name6 chnroute6 #iptables ipset-name4 inet@global@chnroute #nftables
ipset-name6 inet@global@chnroute6 #nftables https://github.com/zfl9/chinadns-ng?tab=readme-ov-file#chnroute-%E5%88%86%E6%B5%81
三种模式已经介绍完了,大家可以根据自己的需求和喜好,进行选择。官方的配置示例可以在上面的网址进行查看。
不同的方式代表不同的取舍。
1. 如果你使用 chnlist 模式进行分流,那么 chnlist.txt 中势必会有一些国内域名没有包含在其中,尤其是一些小众域名很难会被收录进来。这样会导致这些域名的 CDN 返回的可能会是国外 IP(即便这下域名是有中国 IP 的节点)
2. 如果你是用 gfwlist 分流,需要保证你的 gfwlist.txt 配置保持最新,否则更新不及时,或 gfwlist.txt 收录不全,可能会查出来的是被污染的 IP。而且只要是没有在域名列表里的国外域名都会从国内 DNS 查询。
3. 如果你是用 chnroute 分流,查询出的是最精准的,但同样会导致未命中 gfwlist.txt 的域名出现 DNS 泄漏。比如 ipleak.net 等一众 DNS 泄漏检测网站,都不在域名列表里,这样 chinadns-ng 会同时向国内和国外的 DNS 上游进行请求,ipleak.net 里也会显示 DNS 泄漏。(虽然我认为 DNS 泄漏检测很不科学,但有很多人关心这个问题,我就得提一下)
下面说一下我自己使用的方案
我使用的是 chnlist 分流的保守方案。只有国内的域名会从国内的 DNS 查询
# 监听地址和端口
bind-addr ::
bind-port 53@udp # 国内上游、可信上游
china-dns 127.0.0.1#2053
trust-dns 127.0.0.1#2054 # 域名列表,用于分流
chnlist-file /etc/cng/final_cn.txt
default-tag gfw
#gfwlist-file /etc/cng/gfwlist.txt # chnlist-first # 收集 tag:chn、tag:gfw 域名的 IP (可选) #add-tagchn-ip chnip,chnip6 #add-taggfw-ip gfwip,gfwip6 # 测试 tag:none 域名的 IP (针对国内上游) #ipset-name4 inet@global@chnroute #ipset-name6 inet@global@chnroute6 # dns 缓存
cache 0 #cache-stale 86400 #cache-refresh 20 # verdict 缓存 (用于 tag:none 域名) #verdict-cache 65535 #verdict-cache-db /etc/cng/verdict-cache.db # 详细日志 #verbose china-dns 中设置的为 127.0.0.1#2053,这是我在 smartdns 上开的第一组 DNS,里面全是国内上游,监听端口为 2053。
trust-dns 中设置的为 127.0.0.1#2054,这是我在 smartdns 上开的第二组 DNS,里面全是国外上游,监听端口为 2054。
在国外上游中,我看到很多人都是指定个 8.8.8.8 或者 1.1.1.1,DNS 查询的时候会直接走 VPN 去查询。我不喜欢这样弄,而且我觉得这样抗风险太弱了,如果路由器上的节点挂掉,或者波动,会导致所有通过这条路径的查询全部超时。因为我使用的是 chnlist 模式,这条路径下不仅有像 Google 这样无法访问的域名查询,还有 cloudflare 这种国内能访问的域名查询,甚至还有一些国内视频网站的 CDN 不在 chnlist.txt 列表中,这些域名一样会挂掉。如果只是因为节点掉线 / 波动,这些国外网站就上不去了,代价过大。
所以我是自建了 DOH 直连进行查询。我的 smartdns 国外查询组里,写了 3 条,有两条是我自建的主备 DOH,另一条 fallback 是随便找的一个国外公共的 DOH。我 VPN 上做了域名分流,这些查询都是通过我的国内公网 IP 直接进行查询发送出去,不走 VPN。
自建的 DOH 查询上游是 Google 的 8.8.8.8,支持 ECS,大部分从 chinadns-ng 漏出来的国内域名走了 DOH 查询依然能查询出国内 IP。
自建的 DOH,可以看 https://linux.do/t/topic/1177430/110
其他的方法也可以用,你上游也可以使用 AdGuardHome,当然也可以直接在 china-dns 和 trust-dns 里填写 DNS 组,组内的多个上游服务器是并发查询的模式,采纳最先返回的那个结果。
# 国内上游、可信上游
china-dns 223.5.5.5,tcp://223.5.5.5,119.29.29.29,tcp://180.184.1.1
trust-dns 8.8.8.8,8.8.4.4 上述工作都准备好后,就可以给 /opt/chinadns-ng/chinadns-ng 运行权限,运行测试了
/opt/chinadns-ng/chinadns-ng -C /opt/chinadns-ng/config
测试完成可以运行后,可以让 AI 根据你的平台写一个进程守护之类的东西扔在路由器上,就可以自己跑了。
我的路由器经常守护文件是放在 /etc/init.d/chinadns-ng 里
#!/bin/sh /etc/rc.common
START=150
start(){
cd /etc/cng && (./chinadns-ng -C ./config </dev/null) >/dev/null 2>&1 &
echo "chinadns-ng is start"
}
stop()
{
killall chinadns-ng
echo "chinadns-ng is stop"
}
我的进程守护,放在你的路由器上不一定能跑。所以你还是最好测试一下。而且我的进程守护把所有日志都扔掉了。也不一定适合你。
