ROS 搭配 Clash 彻底抛弃 OpenWrt

之前自己一直在用主路由搭配旁路由的方式作为家里的网络架构,主路由为 MikroTik Routeros,旁路由为 OpenWrt。主路由通过设置 HDCP Options 给有需求的设备自动把网关和 DNS 指向旁路由。最终使用 VRRP 解决高可用的问题,防止旁路由下线时这些设备直接无法上网。但是使用 OpenWrt 作为旁路由有很多不满意的地方:

  1. OpenWrt 系统很臃肿,即使自己编译精简了很多没用的包,但是用起来还是卡卡的
  2. OpenWrt 系统无法平滑完成版本迭代,频繁升级导致重装系统很难受
  3. OpenClash 不好用,每次修改配置都要重启,每次重启都好久
  4. 一旦把网关指向旁路由,设备的所有流量都会指向旁路由,导致常规流量变慢
  5. 旁路由网关互指导致多次 nat,性能不佳

所以一直想着去摒弃 OpenWrt,直到看到了一个大神做的 YouTube 视频,发现这种方式很适合自己,所以趁着周末的时间重新搞了一下家里的网络。 image.png 之前家里主路由设备用的是 MikroTik RB450Gx4,这次趁机也升级成了 MikroTik RB5009 UPr,该设备除了有一个 2.4G 的网口和 7 个千兆的网口之外,还有一个 SFP 接口,方便以后直接使用光猫,这次就先不折腾了。另外设备还支持 POE 输出,正好可以给家里的 AP 设备供电。虽然内存还是 1GB 但是 CPU 提升了很多,所以性能会比之前那个强很多。那么我们来正式讲一下一种全新的分流方式:RBC

什么是 RBC

RBC 其实就是 Routeros + BGP + Clash,即主路由系统 + 边界网关协议 + 透明网关 image 1.png 我们的域名会通过 DNS 解析服务器转换为 IP 地址,ROS 则负责判断 IP 是否存在于静态路由表,如果存在的话它就会走透明网关(Clash),不存在的话就走我们的主路由。为了做到上述的分流方式,我们的主要工作就是实现上图中的彩色部分。

DNS 解析服务器

视频里面使用的是 easy mosdns,而我自己则选择使用 PaoPaoDNS,并不是后者一定比前者好用,只是自己随便选择了一个。 PaoPaoDNS 的安装方式很简单,直接使用 Docker 可以安装,我个人是使用 PVE 单独创建了一台虚拟机,Docker 会用 host 网络模式已获得最佳性能。

version: "3"

services:
  paopaodns:
    image: sliamb/paopaodns:latest
    container_name: PaoPaoDNS
    restart: always
    volumes:
      - ./data:/data
    environment:
      - TZ=Asia/Shanghai
      - UPDATE=weekly
      - DNS_SERVERNAME=PaoPaoDNS,blog.03k.org
      - DNSPORT=53
      - SOCKS5=no
      - CNAUTO=yes
      - IPV6=no
      - CNFALL=yes
      - AUTO_FORWARD=no
      - CN_TRACKER=yes
      - SAFEMODE=no
    network_mode: host

DNS 解析服务器我设置的 IP 地址是 192.168.1.2

透明网关

首先设置转发

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf

透明网关我选择使用的是虚空终端,现在很多人也开始推荐 sing-box 后续也可以切换试试。虚空终端的安装教程官网很详细,照着官网安装就行。

# 下载(注意版本)
wget https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha/mihomo-linux-amd64-compatible-alpha-df01582.gz

# 解压缩
gunzip mihomo-linux-amd64-compatible-alpha-df01582.gz

# 重命名
mv mihomo-linux-amd64-compatible-alpha-df01582 clash

# 赋予执行权限
chmod +x clash

# 移动到 /usr/local/bin
mv clash /usr/local/bin

# 创建配置文件夹
mkdir -p /etc/clash

配置文件的内容如下 config.yaml

######### 锚点 start #######
# proxy 相关
pr: &pr {type: select, proxies: [默认,香港,台湾,日本,新加坡,美国,其它地区,全部节点,自动选择,DIRECT]}

#这里是订阅更新和延迟测试相关的
p: &p {type: http, interval: 3600, health-check: {enable: true, url: http://www.google.com/generate_204, interval: 300}}

use: &use
  type: select
  use:
  - provider1

######### 锚点 end #######


# url 里填写自己的订阅,名称不能重复
proxy-providers:
  provider1:
    <<: *p
    url: ""

mode: rule
ipv6: false
log-level: info
allow-lan: true
mixed-port: 7890
unified-delay: false
tcp-concurrent: true
external-controller: 0.0.0.0:9090
secret: '123456789'

geodata-mode: true
geox-url:
  geoip: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat"
  geosite: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat"
  mmdb: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/country.mmdb"

profile:
  store-selected: true
  store-fake-ip: true
  tracing: true

sniffer:
  enable: true
  sniff:
    TLS:
      ports: [443, 8443]
    HTTP:
      ports: [80, 8080-8880]
      override-destination: true
interface-name: eth0
tun:
  device: utun
  enable: true
  stack: system
  auto-route: true
  auto-detect-interface: false

dns:
  enable: true
  listen: :1053
  ipv6: false
  enhanced-mode: redir-host
  fake-ip-range: 28.0.0.1/8
  fake-ip-filter:
    - '*'
    - '+.lan'
    - '+.local'
  default-nameserver:
    - 192.168.1.2
  nameserver:
    - 192.168.1.2
  proxy-server-nameserver:
    - 192.168.1.2
  nameserver-policy:
    "geosite:cn,private":
      - 192.168.1.2

proxies:
  # - name: "WARP"
  #   type: wireguard
  #   server: engage.cloudflareclient.com
  #   port: 2408
  #   ip: "172.16.0.2/32"
  #   ipv6: "2606::1/128"        # 自行替换
  #   private-key: "private-key" # 自行替换
  #   public-key: "public-key"   # 自行替换
  #   udp: true
  #   reserved: "abba"           # 自行替换
  #   mtu: 1280
  #   dialer-proxy: "dns"
  #   remote-dns-resolve: true
  #   dns:
  #     - https://dns.cloudflare.com/dns-query

proxy-groups:

  - {name: 默认, type: select, proxies: [DIRECT, 香港, 台湾, 日本, 新加坡, 美国, 其它地区, 全部节点, 自动选择]}
# - {name: dns, type: select, proxies: [DIRECT, WARP, 香港, 台湾, 日本, 新加坡, 美国, 其它地区, 全部节点, 自动选择]}  # 加入 WARP  
#分隔,下面是地区分组
  - {name: 香港, <<: *use,filter: "(?i)港|hk|hongkong|hong kong"}

  - {name: 台湾, <<: *use, filter: "(?i)台|tw|taiwan"}

  - {name: 日本, <<: *use, filter: "(?i)日本|jp|japan"}

  - {name: 美国, <<: *use, filter: "(?i)美|us|unitedstates|united states"}

  - {name: 新加坡, <<: *use, filter: "(?i)(新|sg|singapore)"}

  - {name: 其它地区, <<: *use, filter: "(?i)^(?!.*(?:🇭🇰|🇯🇵|🇺🇸|🇸🇬|🇨🇳|港|hk|hongkong|台|tw|taiwan|日|jp|japan|新|sg|singapore|美|us|unitedstates)).*"}

  - {name: 全部节点, <<: *use}

  - {name: 自动选择, <<: *use, tolerance: 2, type: url-test}

rules:
  # - AND,(AND,(DST-PORT,443),(NETWORK,UDP)),(NOT,((GEOSITE,cn))),REJECT # quic

  - MATCH,默认

里面有几个配置需要注意一下

  • provider1 url 填入自己的订阅地址,如果有多个订阅可以自己加 provider
  • dns 里的地址替换成自己 DNS 解析服务器的地址
  • interface-name 设置为自己网卡的名称
  • tun 的 device 名称为 utun

继续把透明网关注册为系统服务并设置为开机自动启动

sudo tee /etc/systemd/system/clash.service > /dev/null <<EOF
[Unit]
Description=Clash daemon, A rule-based proxy in Go.
After=network.target

[Service]
Type=simple
Restart=always
ExecStart=/usr/local/bin/clash -d /etc/clash

[Install]
WantedBy=multi-user.target
EOF

systemctl enable clash

透明网关我设置的 IP 地址为 192.168.1.6

静态路由表

静态路由表使用 nchnroutes 项目实现,每天 5 点自动生成最新路由表并通过 BGP 传递给 ROS。

安装 bird2

apt update
apt install bird2 -y

非本地 ip 表获取


# 下载 ip 表
wget https://raw.githubusercontent.com/haotianlPM/rosrbgprouter/main/iplist.sh
# 赋予执行权限
chmod +x iplist.sh
# 执行一次
./iplist.sh

# 配置定时执行
crontab -e
0 5 * * * /bin/bash /home/iplist.sh

配置 bird2

修改 bird2 配置文件为以下内容 ,文件在 /etc/bird/bird.conf

log syslog all;

router id 192.168.1.6;

protocol device {
    scan time 60;
}

protocol kernel {
    ipv4 {
        import none;
        export all;
    };
}

protocol static {
    ipv4 {};
    include "routes4.conf";
}

protocol bgp {
    local as 65531;
    neighbor 192.168.1.1 as 65530;
    source address 192.168.1.6;
    ipv4 {
        import none;
        export all;
    };
}

注意修改上述的 IP 地址。192.168.1.6 为透明网关的地址,192.168.1.1 为 ROS 路由器的地址。

查看 bird 执行状态

systemctl status bird

设置 ROS

ROS 分流有两种方式,一种是所有设备都可以分流,另一种是可以指定设备进行分流,这边我推荐后者。

首先在 ROS 创建一个路由表,Routing → Tables,名字为 bypass,且勾选 FIB。

然后打开 ROS 的终端,执行:

# 添加一条路由规则,距离为1,网关为pppoe-out1,路由表为bypass,注释为pass
/ip route
add distance=1 gateway=pppoe-out1 routing-table=bypass comment=pass

# 添加一个BGP连接,名称为clash,本地角色为ebgp,远程地址为192.168.1.6,自治系统号为65531,路由表为bypass,路由器ID为192.168.1.1,自治系统号为65530,启用多跳选项
/routing/bgp/connection
add name=clash local.role=ebgp remote.address=192.168.1.6 .as=65531 routing-table=bypass router-id=192.168.1.1 as=65530 multihop=yes

# 添加一个防火墙Mangle规则,动作为接受,链为prerouting,源地址为192.168.1.6
/ip firewall mangle add action=accept chain=prerouting src-address=192.168.1.6

注意我的网关名称为 pppoe-out1,需要根据自己的实际设置修改名称。

这个时候在 ROS 的 IP → Routes 里面就可以看到传过来的一大堆 ip 地址了

image 2.png

假设我有一个 ip 为 192.168.1.100 的设备想走分流,则需要继续设置:

# 添加一个地址列表,名称为proxy,包含地址 192.168.1.100
/ip firewall address-list add list=proxy address=192.168.1.100

# 添加一个防火墙Mangle规则,动作为标记路由,链为prerouting,源地址列表为proxy,连接类型tcp。目标端口为80和443,目标地址类型不是本地地址,新的路由标记为bypass
/ip firewall mangle add action=mark-routing chain=prerouting src-address-list=proxy dst-port=80,443 dst-address-type=!local protocol=tcp new-routing-mark=bypass

这是 192.168.1.100 的设备就已经可以成功分流了。

如果想继续添加其他分流设备,可以继续执行:

# 添加一个地址列表,名称为proxy,包含地址 192.168.1.101
/ip firewall address-list add list=proxy address=192.168.1.101

或者直接通过可视化界面进行添加,在 IP → Firewall,选择 Address Lists 选项卡,这个时候能看到我们已经添加过的 IP,这个时候继续添加就行了。

期间遇到的一些问题

IP 表无法同步到 ROS 里

当时定位这个问题甚至花费了 1 天的时候,Routing → BGP → Sessions 里面能正常看到设备,bird 执行 birdc show protocols 也显示正常,但是 ROS 里就是看不到同步的数据,最终才发现是我 clash 的配置里 tun 的设备名字不叫 utun 导致的,而下载的 ip 表是写的下一跳是 utun,而找不到设备导致无法同步。

实现高可用

参考这篇文章,整个系统创建好后,带来的问题就是 DNS 解析服务器的问题,因为我们所有的设备都会走 DNS 解析服务器,一旦 DNS 挂了大家就都不能上网了,所以需要实现故障检测功能,当 DNS 服务器挂了需要自动切换到 ROS 自己的 DNS 服务。

具体的做法就是创建两条脚本,一条切换 DNS 地址为 192.168.1.2,另一条切换为运营商的递归 DNS 地址,比如 114.114.114.114

/system/script
add dont-require-permissions=yes name=use-paopao-dns source=\
"/ip/dns/set servers=192.168.0.2\
\n/ip/dns/cache/flush"
add dont-require-permissions=yes name=use-ct-dns source=\
"/ip/dns/set servers=114.114.114.114\
\n/ip/dns/cache/flush

这里的 dont-require-permissions=yes 是必须的,否则 Netwatch 执行的时候会报权限错误。

然后配置 Netwatch 来检测 DNS 服务器的地址,如果宕机就自定执行脚本切换 DNS。

/tool/netwatch
add down-script=use-ct-dns host=192.168.1.2 interval=30s up-script=use-paopao-dns

特别感谢以下教程