未完成,仅仅只搭建了实验环境。所以先把命令一股脑扔在这里,日后有空再完善。
# 0. 背景
RouterOS 作为路由器。
- 共有 ether0 到 ether7 共 8 个网口
- ether0 接入 ISP 网线,建立 PPPoE Client 接口 pppoe-out1,作为 WAN
- ether1 到 ether6 设立桥接设备,名为 bridge,作为 LAN 区域
- IP: 192.168.50.1/24
- DNS: 223.5.5.5, 119.29.29.29
- ether7 独立接口,不加入网桥,单独设置该网口作为 OSPF 专用子网:
- IP: 192.168.255.1/24
任意 Linux 服务器作为分流出口,安装 Bird,作为 OSPF 路由器。
- 共有 eth0 一个网口
- eth0 和 RouterOS 的 ether7 相连,设置为:
- IP: 192.168.255.254/32
- Gateway: 192.168.255.1
- DNS: 不设置
# 1. RouterOS:打通 LAN 子网和 OSPF 子网
由于 OSPF 子网中的设备仍然需要能访问 WAN 区域,所以将 OSPF 子网的流量 直接通过 PPPoE Client 接口转发出去,避免网络成环。
/routing table
add fib name=bypass
/ip/route
add distance=1 gateway=pppoe-out1@main dst-address=0.0.0.0/0 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=192.168.0.0/16 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=10.0.0.0/8 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=169.254.0.0/16 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=172.16.0.0/12 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=224.0.0.0/4 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=255.255.255.255/32 routing-table=bypass
/ipv6/route
add distance=1 gateway=pppoe-out1@main dst-address=::/0 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=::ffff:0:0/96 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=::ffff:0:0:0/96 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=64:ff9b::/96 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=100::/64 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=2001::/32 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=2001:20::/28 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=2001:db8::/32 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=2002::/16 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=fc00::/7 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=fe80::/10 routing-table=bypass
add distance=1 gateway=bridge@main dst-address=ff00::/8 routing-table=bypass
/routing/rule
add interface=bridge table=main
add interface=ether7 table=bypass
如果有某些接口提供了一些私网地址,那么应该修改上面的私网地址,让它们从对应的接口出去,
而不是直接使用 bridge@main
。例如,当 WireGuard 接口 wg0
绑定了地址段 10.0.0.0/24
时,
上面命令中的这一条:
add distance=1 gateway=bridge@main dst-address=10.0.0.0/8 routing-table=bypass
应该相应地修改为:
add distance=1 gateway=wg0@main dst-address=10.0.0.0/24 routing-table=bypass
依此类推。反正就是照抄 main
表里的东西。
此外,@main
这个标记是可以省略的,因为这是 ROS 6.x 版本的命令语法。
现在应该可以在 LAN 子网下的设备上 ping 通 OSPF 子网的设备了, 反过来也是一样。同时,OSPF 子网的设备也可以访问 WAN 区域正常上网。
# 2. RouterOS:高可用 DNS
新建两个脚本,分别设置使用默认 DNS 和使用 X DNS。
/system script
add dont-require-permissions=yes name=use-default-dns owner=admin \
policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
source="/ip dns set allow-remote-requests=yes servers=119.29.29.29,223.5.5.5 \r\n/ip dns cache flush"
add dont-require-permissions=yes name=use-ospf-dns owner=admin \
policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
source="/ip dns set allow-remote-requests=yes servers=192.168.255.254\r\n/ip dns cache flush"
然后在 netwatch 中添加任务,每 10 秒通过 ICMP ping 确认 X 服务的运行状态。 当 X 服务未启动时,切换到默认 DNS。 当 X 服务正常相应时,切换回 X DNS。
/tool netwatch
add down-script=use-default-dns host=192.168.255.254 interval=10s up-script=use-ospf-dns
# 3. RouterOS:启用 OSPF
/routing ospf instance
add disabled=no name=default-v2 router-id=192.168.255.1
/routing ospf area
add disabled=no instance=default-v2 name=backbone-v2
/routing ospf interface-template
add area=backbone-v2 cost=10 disabled=no interfaces=ether7 networks=192.168.255.0/24 priority=1
/routing ospf instance
add disabled=no name=default-v3 router-id=192.168.255.1 version=3
/routing ospf area
add disabled=no instance=default-v3 name=backbone-v3
/routing ospf interface-template
add area=backbone-v3 cost=10 disabled=no interfaces=ether7 priority=1
# 4. OSPF 路由:初始化
本文使用的环境为 Ubuntu Server 22.04
# 开启 IPv4 和 IPv6 转发
编辑 /etc/sysctl.conf
取消注释下列内容
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net.ipv4.icmp_echo_ignore_all=1
其中 net.ipv4.icmp_echo_ignore_all=1
会让本机不响应 ICMP Echo 请求,
但是在上面的 netwatch 中,我们是通过 ICMP Echo 来判断 X 服务是否正常运行的,
因此,在后续的脚本中,会在 X 服务正常运行时,将这个值改为 0。
然后
sudo sysctl -p /etc/sysctl.conf
# 释放本机 53 端口
Ubuntu Server 会默认启用 systemd-resolve
,这个会占用本机的 53 端口,可以通过修改 /etc/systemd/resolved.conf
来禁用它:
DNSStubListener=no
然后
systemctl stop systemd-resolved
systemctl disable systemd-resolved
rm /etc/resolv.conf
# netplan 设置静态 IP
network:
ethernets:
eth0:
dhcp4: false
addresses: [192.168.255.254/24]
routes:
- to: default
via: 192.168.255.1
nameservers:
addresses: []
# Crontab
systemctl enable --now cron
# 5. OSPF 路由:X
略。注意:
- 启用 X DNS,监听 53 端口
- 启用 TUN 设备,假定为
utun
# 6. OSPF 路由:部署 bird
安装 bird 服务:
sudo apt install bird
来到 /etc/bird
下,编辑 bird.conf
,内容如下:
router id 192.168.255.254;
protocol kernel {
scan time 60;
import none;
export all;
}
protocol device {
scan time 60;
}
protocol static {
include "routes4.conf";
}
protocol ospf {
export all;
area 0.0.0.0 {
interface "eth0" {
};
};
}
编辑 bird6.conf
,内容如下:
router id 192.168.255.254;
protocol kernel {
scan time 60;
import none;
export all;
}
protocol device {
scan time 60;
}
protocol static {
include "routes6.conf";
}
protocol ospf {
export all;
area 0.0.0.0 {
interface "eth0" {
};
};
}
准备好路由分流文件,一般 ISP 会提供,将这些加入到 crontab 中,定时更新:
0 2 */1 * * curl -s <ROUTES4> -o /etc/bird/routes4.conf
0 2 */1 * * curl -s <ROUTES6> -o /etc/bird/routes6.conf
0 3 * * * birdc configure
0 3 * * * birdc6 configure
如果不行让路由太复杂,可以只路由部分 IP 段,比如:
route 198.18.0.0/16 via "utun";
和
route 2001:too:long::/48 via "utun";
然后手动执行一下上面的命令,启动 bird 服务。
# 7. OSPF 路由:网络可用性检测
crontab 如下:
* * * * * /root/check-network.sh
然后编辑 /root/check-network.sh
,内容如下:
#!/usr/bin/bash
COUNT=0
MAX_COUNT=3
URL=https://www.google.com/generate_204
while [ $COUNT -lt $MAX_COUNT ]
do
SER=0
NET=0
if [ $(curl --connect-timeout 10 --interface utun -w "%{http_code}" -s $URL) -eq 204 ];then
NET=1
fi
if /etc/init.d/bird status | grep Active | grep -q running; then
SER=1
fi
if [ $NET -eq 1 ] && [ $SER -eq 0 ];then
echo "Network is ready, starting bird and bird6"
/etc/init.d/bird start
/etc/init.d/bird6 start
echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all
exit 0
fi
if [ $NET -eq 0 ] && [ $SER -eq 1 ];then
let COUNT+=1
if [ $COUNT -eq $MAX_COUNT ];then
echo "Network is down, stopping bird and bird6"
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
/etc/init.d/bird stop
/etc/init.d/bird6 stop
exit 0
fi
continue
fi
if [ $NET -eq 1 ] && [ $SER -eq 1 ]; then
echo "Force enabling icmp echo"
echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all
fi
if [ $NET -eq 0 ] && [ $SER -eq 0 ]; then
echo "Force disabling icmp echo"
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
fi
e
echo "Everything goes well."
exit 0
done
# 8. DNS 上直接分流
经过上面的设置之后,如果 X DNS 的运行模式为 Fake IP, 那么在使用 X DNS 时,所有的 DNS 请求都会返回 198.18.0.0/16 中的地址, 而这段地址无疑时需要 RouterOS 交给 X 进行分流的。
这很不好,因为这样依然会导致所有的流量都被分流到 X 上,然后 X 内部再根据 配置文件的规则进行分流。因此,当我们访问 baidu.com 这种有路线优化 的网站时,流量也会被 RouterOS 分流到 X 上,然后 X 内部自己的分流规则再将流量 转发回 RouterOS,这很不好。
因此,我们需要在 DNS 上直接分流,这样就可以避免上面的问题。
# OSPF 路由:部署 SmartDNS
将 X DNS 的监听端口设置为 10053,然后安装 SmartDNS:
apt install smartdns
配置 /etc/smartdns/smartdns.conf
,内容如下:
bind :53
cache-size 0
prefetch-domain no
serve-expired no
speed-check-mode none
log-level info
log-file /var/log/smartdns.log
server 127.0.0.1:10053
server 119.29.29.29 -group CN -exclude-default-group
server 223.5.5.5 -group CN -exclude-default-group
conf-file /etc/smartdns/cn-domains.conf
文件 /etc/smartdns/cn-domains.conf
内容如下:
nameserver /.baidu.com/CN
这表明,所有 cn-domains.conf
中的域名都会直接使用 223.5.5.5 和 119.29.29.29 进行解析,
而这个文件之外的所有域名再交给 X DNS 处理。这样可以将大部分常用的国内域名直接解析它们的真实地址。
cache-size 0
表示不缓存任何 DNS 记录,因为 RouterOS 自己会维护 DNS 缓存。而且我发现 RouterOS
的 DNS 缓存会遵守 Fake IP 的 TTL 为 1 的规则,这样会导致 Fake IP 部分的缓存会很快失效,并不会出现
X 和 smartdns 重启后短时间内 RouterOS 缓存失效的问题。
网上也有根据某列表进行的 DNS 分流,但这种处理方式并不能保证所有不可访问的域名都能被 X DNS 处理, 因此我选择了上面的方式,我称呼为“域名白名单”。
在后续的使用中,可以根据 X Dashboard 中的日志,将更多常用的一定有线路优化的域名加入到 cn-domains.conf
中。
启动 SmartDNS:
systemctl enable --now smartdns
# Always Direct Devices
由于已经在 DNS 上进行了策略路由,因此,对于不需要策略路由的设备, 我们可以直接让这些设备发来的 DNS 请求劫持到“不具有分流能力”的 DNS 服务器上。
/ip/firewall/nat
add action=dst-nat chain=dstnat protocol=udp \
dst-address=192.168.50.1 dst-port=53 \
src-address=<IP> \
to-addresses=223.5.5.5 to-ports=53
也可以使用 /ip/firewall/address-list
:
/ip/firewall/address-list
add list=direct_ips address=<IP1>
add list=direct_ips address=<IP2>
/ip/firewall/nat
add action=dst-nat chain=dstnat protocol=udp \
dst-address=192.168.50.1 dst-port=53 \
src-address-list=direct_ips \
to-addresses=223.5.5.5 to-ports=53
也可以使用 /routing/rules
,但这个方法和 DNS 劫持有本质区别,
使用前最好知道加入下面的规则以后,<IP>
这个设备的两类流量是如何路由的:
/routing/rule
add src-address=<IP> action=lookup table=bypass
当然也可以用 mangle 表,即 /ip/firewall/mangle
,
但我不喜欢。