【RouterOS(ROS)】IPv4 & IPv6 自动分流设置

本文基于 ROS 7.18.2 编写

前言

IPv6 日渐普及,最近闲来无事,想着把 RouterOS 上的分流规则改为 IPv4 & IPv6,因为 IPv6 的分流设置和 IPv4 有诸多不同,在此记录一下

自动更新分流规则的脚本

左侧 System → Scripts,新建一个用于自动更新分流规则的脚本
【RouterOS(ROS)】自动更新分流规则 by 2022 之前的文章有一个适用于 IPv4 的更新脚本,我们在这里需要做一下修改,使它可以适用于 IPv4 & IPv6

这里使用的是已经制作好的 IP 列表文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 下载 IPv4 并重命名
/tool fetch url=http://www.iwik.org/ipcountry/mikrotik/CN dst-path=CN_ipv4

# 下载 IPv6 并重命名
/tool fetch url=http://www.iwik.org/ipcountry/mikrotik_ipv6/CN dst-path=CN_ipv6

# 关闭日志输出避免刷屏
/system logging disable 0

/import file-name=CN_ipv4
:local CNv4 [:len [/ip firewall address-list find list="CN"]]
/file remove [find name="CN_ipv4"]

/import file-name=CN_ipv6
:local CNv6 [:len [/ipv6 firewall address-list find list="CN"]]
/file remove [find name="CN_ipv6"]

# 恢复日志输出
/system logging enable 0
:log info ("CN列表更新: IPv4 " . $CNv4 . "条, IPv6 " . $CNv6 . "条")

成功的话应该能在防火墙和日志中看到如下内容:
脚本运行状态

路由表设置

左侧 Routing → Tables,新建一个路由表,名字自己随便写,我这里使用的是 openwrt,路由表用于防火墙标记和指定流量出口

  • FIB: 勾选
    路由表

IPv4 路由设置:

左侧菜单 IP → Routes,新建一条路由规则:
IPv4 路由表

  • Dst.Address:0.0.0.0/0
  • Gateway:旁路网关IP地址
  • Distance:2 # (建议使用大于1的低优先级)
  • Routing Table: 选中刚刚新建的路由表,例如 openwrt
    点击Apply → OK,保存提交。
    使用命令导入:
    /ip route add dst-address=0.0.0.0/0 gateway=192.168.1.250 distance=2 routing-table=openwrt

IPv6 路由设置:

左侧菜单 IPv6 → Routes,新建一条路由规则:
IPv6 路由表

  • Dst.Address:::/0
  • Gateway: 旁路网关的本地链路 IPv6 地址 ,例如 fe80::be24:11ff:fe79:7afe%bridge1 [1]
  • Distance:2 # (建议使用大于1的低优先级)
  • Routing Table: 选中刚刚新建的路由表,例如 openwrt
    点击Apply → OK,保存提交。
    使用命令导入:
    /ipv6 route add dst-address=::/0 gateway=fe80::be24:11ff:fe79:7afe%bridge1 immediate-gateway=fe80::be24:11ff:fe79:7afe%bridge1 distance=2 routing-table=openwrt

注:如果 Gateway 填写的是本地链路 IPv6 地址,如 fe80:: ,则需要在地址后面加上接口名称或者网口名称,如 bridge1,则为 fe80::be24:11ff:fe79:7afe%bridge1 ,如果使用的是 ULA 地址,直接在 Gateway 处填入 ULA 地址即可,无需接口名称

防火墙设置

IPv4 防火墙规则

跳过旁路网关流量

首先我们需要对旁路网关发送来的流量进行特殊处理,使其跳过流量标记,避免路由环路
左侧 IP → Firewall → Mangle,新建一条 prerouting 规则
IPv6 路由表

General

  • Src.Address: 填写旁路网关的IP地址
  • In.Interface: 选择桥接口名字或者网口名称,如 bridge1、ether2

Advanced

  • Src.MAC Address: 填写旁路网关流量出口的 MAC 地址,和 Src.Address 二选一即可。

Action

  • Action: Accept (表示通过和跳过下列的防火墙规则)
国外流量标记

再新建一条 prerouting 规则用于流量标记
IPv4 防火墙规则

General

  • Dst.Address List: 目标地址列表,选择 CN 和 前面的空白方块 [!] ,表示目标地址不在 CN 列表中时才命中规则
  • Src.Address List: 源地址列表,如果希望局域网内只有部分设备使用这个自动分流规则,那么可以自己去 Address List 添加一个列表,留空时为所有设备

Extra

  • Dst.Address Type: 下拉选择 local,然后点击前面空白方块为 [!] ,即目标地址不属于局域网地址时才命中规则

Action

  • Action: makr routing 重新标记路由
  • New Routing Mark: 选择之前添加的路由表
  • Passthrough: 如果下方还需要对这条流量做别的处理 ,则需要勾选

使用命令导入:

my_device 为防火墙自定义 Address List

1
2
3
4
5
6
/ip firewall mangle
add action=accept chain=prerouting in-interface=bridge1 src-address=\
192.168.1.250
add action=mark-routing chain=prerouting dst-address-list=!CN \
dst-address-type=!local new-routing-mark=openwrt src-address-list=\
my_device

注:

  1. 必须保证”跳过流量标记”的防火墙规则在”流量标记”规则的上方
  2. 如果指定了 Address List 列表内设备才进行分流,且列表中不包含旁路网关地址时,不需要跳过旁路网关流量,如我的使用环境中,有一部分流量是全局的,所以还是需要 “Bypass OpenWrt” 兜底
  3. 如果防火墙规则中包含了 QoS 等包标记,不想使用 Accept 时,可以删除之前的规则,改为在”国外流量标记”规则后方添加规则,给旁路网关发起的流量重新打上 main 路由标记
    IPv4 防火墙规则
  4. 如果需要将 DNS 修改为旁路网关,需检查 NAT 规则,确保未开启 DNS 劫持
  5. 检查 IP 伪装规则,如果使用的是 WAN 接口伪装,需要修改为整个局域网子网段,否则需要在旁路网关防火墙处设置 IP 伪装规则
    或参考文末”其他设置”中的处理方案
    IPv4 防火墙规则

IPv6 防火墙规则

跳过旁路网关流量

左侧 IP → Firewall → Mangle,新建一条 prerouting 规则,IPv6 通常为动态公网单播地址,无法使用 Src.Address,需要改为 MAC 地址 Src.MAC Address

General

  • In.Interface: 选择桥接口名字或者网口名称,如 bridge1、ether2

Advanced

  • Src.MAC Address: 填写旁路网关流量出口的 MAC 地址

Action

  • Action: Accept (表示通过和跳过下列的防火墙规则)
跳过 ICMPv6 (ND) 和 DHCPv6 的流量

IPv6 防火墙规则和 IPv4 有一些区别,除了”跳过流量标记”和”流量标记”外,还需要跳过 ICMPv6 (ND) 和 DHCPv6 的流量,否则会造成 IPv6 网络异常
这里有几种跳过流量包的方法,看个人喜好和具体需求:

  1. 标记国外流量标记时,勾选 [!]multicast,跳过 IPv6 组播流量
  2. 标记流量包
    下图为标记流量包
    ICMPv6(ND): protocol=58 (icmpv6)
    DHCPv6:dst-port=546-547 protocol=udp
    IPv6 防火墙规则
国外流量标记

IPV6 通常不需要勾选 [!]local,因为流量都是公网单播地址(GUA)
但如果没有设置上方的规则,需要勾选 [!]multicast,跳过 IPv6 组播流量
因为 GUA 一般为动态的(宽带前缀 + 设备后缀),不像 IPv4 是静态地址,IPv6 防火墙无法使用 Src.Address List,如果并非全局分流,需要每个设备一条规则,使用 MAC 地址
IPv6 防火墙规则

General

  • Dst.Address List: 目标地址列表,选择 CN 和 前面的空白方块 [!] ,表示目标地址不在 CN 列表中时才命中规则

Advanced

  • Src.MAC Address: 填写需要分流设备的 MAC 地址(如果你不需要全局分流)

Action

  • Action: makr routing 重新标记路由
  • New Routing Mark: 选择之前添加的路由表
  • Passthrough: 如果下方还需要对这条流量做别的处理 ,则需要勾选

使用命令导入:

1
2
3
4
5
6
7
8
9
10
/ipv6 firewall mangle
add action=accept chain=prerouting in-interface=bridge1 src-mac-address=\
AA:AA:AA:AA:AA:AA
add action=accept chain=prerouting comment="Bypass ICMPv6 (ND)" protocol=\
icmpv6
add action=accept chain=prerouting comment="Bypass DHCPv6" dst-port=546-547 \
protocol=udp
add action=mark-routing chain=prerouting comment="My Device" \
dst-address-list=!CN new-routing-mark=openwrt \
src-mac-address=BB:BB:BB:BB:BB:BB

注:

  1. 必须保证”跳过流量标记”的防火墙规则在”流量标记”规则的上方
  2. 如果指定了 MAC 地址才进行分流,旁路网关通常不会产生环路流量,这里其实不需要设置跳过旁路网关流量
  3. 如果防火墙规则中包含了 QoS 等包标记,不想使用 Accept 时,可以删除之前的跳过 ICMPv6 (ND) 和 DHCPv6 规则,改为使用 [!]multicast;如果是全局分流,还需要添加规则,给旁路网关发起的流量重新打上 main 路由标记
  4. 如果需要将 DNS 修改为旁路网关,需检查 NAT 规则,确保未开启 DNS 劫持
  5. 在能过获得前缀的情况下,IPv6 通常不需要开启 IP 伪装,如果错误的开启 IP 伪装,会导致外部服务器只能获取到网关 IPv6 地址,而非真实的设备 IPv6 地址
    IPv6 防火墙规则

兼容安卓残疾 IPv6

IPv6 ND 设置

总所周知,安卓的 IPv6 网络功能愚蠢又弱智,既不支持 DHCPv6,也不能指定 DNS 地址,也不支持网关宣告的 fe80 本地链路 DNS 地址,只能使用 GUA 或 ULA,这给分流带来了很多不方便,所以如果你使用的是安卓设备,又不想通过防火墙拒绝 ICMPv6 (ND) 包来禁用安卓设备的 IPv6 功能的话,可以把 ND DNS 设置清空
IPv6 ND 设置

  • 删除 DNS 地址信息或使用桥接接口的 fe80:: 本地链路地址
  • 如果清空了 DNS 信息,取消勾选,如果使用 fe80:: 本地链路地址,保持勾选
    这样可以使安卓设备仅使用 IPv4 DNS 地址
    当 ND 信息中不包含 DNS 时,可能会造成部分设备需要手动指定 DNS 才能上网,如某些单网口的 OpenWrt,谨慎使用
    分发 fe80:: DNS 一般也可以实现安卓设备仅使用 IPv4 DNS,因为安卓并不支持 LLA DNS
    如果你非要给安卓设备指定旁路网关的 IPv6 地址,请使用 ULA 地址,但网关离线时很难自动切换,这应该和我们使用旁路网关的初衷不一致
    IPv6 ND 设置
其他设置

其实经过上面的设置之后,基本上已经可以正常上网了,但如果 IPv6 防火墙开启了丢弃无效的转发包,部分安卓设备会因为路由标记非对称路由(?),造成 TCP 握手未完成且包被丢弃,造成 IPv6 无法上网
有以下几种解决方案:

  1. 直接关闭”丢弃无效的转发包”规则
    IPv6 ND 设置
  2. 新增一条 IPv6 伪装规则,将分流的流量出口地址伪装为 RouterOS 发出,使回程流量正常响应(反正本来就是代理流量,无所谓公网和无法嗅探真实地址)
    IPv6 防火墙设置

General

  • Routing Mark: 选择之前添加的路由表,表示仅处理已被标记的包

Action

  • Action: masquerade 将源地址从 OpenWrt 伪装为 RouterOS,以正常响应回程包
1
2
3
/ipv6 firewall nat
add action=masquerade chain=srcnat comment="defconf: masquerade IPv6" \
routing-mark=openwrt
  1. IPv4 其实也可以这样处理
    IPv4 防火墙设置

注:

  1. 关于 DNS 的分配,可以使用 tools - Netwatch 故障转移、VRRP 热备、DHCP 全局分配、DHCP Option 选项给指定设备分发(如需同时分发两个 DNS,需要转换为十六进制 HEX)等方式,这里不再详细说明
    在 ROS、OpenWrt 上 设置 VRRP
  2. RouterOS 自身代理需要 Mangle 标记 input output

IPv6 测试

谷歌 IPv6
IPv6 测试
IPv6 测试