MikroTik RB4011iGS+RM 上手玩

年轻人的第一台 BGP 路由器

这台路由器是我在 2023 年 4 月 18 号从淘宝买来的二手产品,规格参数大致如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Product code                 RB4011iGS+RM
Architecture                 ARM 32bit
CPU                          AL21400
CPU core count               4
CPU nominal frequency        auto (533 - 1900) MHz
Switch chip model            RTL8367SB
Dimensions                   228 x 120 x 30 mm
RouterOS license             5
Operating System             RouterOS (v7 only)
Size of RAM                  1 GB
Storage size                 512 MB
Storage type                 NAND
MTBF                         Approximately 200'000 hours at 25C
Tested ambient temperature   -40°C to 70°C
IPsec hardware acceleration  Yes
Suggested price              $219.00

购买这么一个路由器的原因也很简单:之前一直是使用虚拟化的软路由,好处颇多,唯独宿主机维护起来十分困难,只要设计硬件上的调整就会导致全屋断网,断得多了难免惹得合租人不高兴。如果想用 Proxmox 的 HA 来做热迁移高可用又需要搭建一个 Ceph 存储服务和另外两台新的 EPYC 服务器,硬件和运行成本实在太高不如单独弄个 1U 赛扬来当路由器(

吐槽一句这路由器的型号和索尼产品的型号风格命名不能说一模一样 只能说是旗鼓相当……

硬件规格

我自己有一段时间没有关注消费级无线路由器,姑且就不做横向的比较了。就我自己的需求而言,除了常规的 RJ45 GbE 接口以外,还需要至少一个 SFP+ Cage 用来战未来:上海这边的 ISP 基本都已经铺开了 1000Mbps 的宽带,考虑到路由器更新频率低,买一个支持 10Gbps 接口的路由器来准备用到未来千兆以上的宽带应该不算离谱(

这台设备支持 10 个千兆以太网口和一个 SFP+ 笼子,基本上完美满足我的需求,同时配备了性能还算够用的 1.9GHz ARM 处理器,可以满足我在 WireGuard 上跑 500Mbps 的需求。

不过需要说明的是,这个路由器的 10 个以太网接口并不是全部连接到交换芯片/CPU,而是分为两组分别连接到一个 2.5G 的交换芯片再连接到 CPU。可以看下这张示意图:

Block diagramOpen

也就是说,在连接设备时,需要尽可能避免大流量跨交换机访问,否则容易撞到 2.5Gbps 的交换芯片通讯限制。不过这个问题对我而言可以忽略,我预期的使用方式是 Ether1 接电信的光猫,SFP+ 接 ZTE 的交换机,局域网内的流量基本不会经过路由器,也就根本撞不到这个限制啦。

软件配置

这里的东西就开始比较杂乱了,因为我的需求就真的很乱(

  1. 需要支持 BGP:我自己有一个自治号和公网可达的 IPv6 前缀,在香港和上游建立 BGP 会话并宣告,而各个地区内每个路由器使用的地址和前缀则使用私有 ASN 在一个大内网中路由,也使用 BGP 作为路由协议。
  2. 需要支持 WireGuard:我的大内网基本上是使用 WireGuard 互相连接通讯的,如果这个设备不支持会比较麻烦(IPsec 和 OpenVPN 都不如 WireGuard 好用想必大家基本都同意)。
  3. 性能足够从外部接受一份 BGP 全表用来做代理分流:懂的都懂,老一点的设备可能做不到。
  4. VLAN,VRF,策略路由这些基本功能当然也要支持

所幸 RouterOS v7 基本能满足以上全部需求~

上行 ISP 配置

先配置一个 PPPoE Client 用来上网:

1
2
3
4
5
6
7
[admin@MikroTik] > /interface/pppoe-client/print detail
Flags: X - disabled, I - invalid; R - running
 0  R name="pppoe-chinanet" max-mtu=auto max-mru=auto mrru=disabled
      interface=ether1 user="redacted" password="redacted" profile=default
      keepalive-timeout=60 service-name="" ac-name="" add-default-route=yes
      default-route-distance=100 dial-on-demand=no use-peer-dns=no
      allow=pap,chap,mschap1,mschap2

很快啊,我们就和电信顺利握手,拿到了公网 IPv4 和 IPv6 地址:

1
2
3
4
5
[admin@MikroTik] > /ip/address/print where interface=pppoe-chinanet
Flags: D - DYNAMIC
Columns: ADDRESS, NETWORK, INTERFACE
#   ADDRESS            NETWORK       INTERFACE
0 D 114.51.4.233/32  114.51.4.1  pppoe-chinanet

然后再来找电信要一下 IPv6 前缀:

1
2
3
4
5
6
7
8
9
[admin@MikroTik] > /ipv6/dhcp-client/print detail
Flags: D - dynamic; X - disabled, I - invalid
 0    ;;; ChinaNet DHCPv6 PD
      interface=pppoe-chinanet status=bound duid="redacted"
      dhcp-server-v6=fe80::ce1a:faff:feeb:d1c0 request=prefix
      add-default-route=no use-peer-dns=no dhcp-options=""
      pool-name="chinanet-ipv6-pd" pool-prefix-length=56 prefix-hint=::/0
      script=redacted
      dhcp-options="" prefix=240e:redacted::/56, 32m8s

这样基本的互联网访问就大差不差了。

WireGuard 内网配置

RouterOS 的 WireGuard 和别的系统配置风味都不太一样,它把 Interface 和 Peers 都打散了,增加 Peers 的时候需要显式指定一个 Interface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[admin@MikroTik] /interface/wireguard> print detail where name=sddn-unicom-cz
Flags: X - disabled; R - running
 2  R name="sddn-unicom-cz" mtu=1400 listen-port=redacted
      private-key="redacted"
      public-key="redacted"

[admin@MikroTik] /interface/wireguard> peers print detail where interface=sddn-unicom-cz
Flags: X - disabled
 0   interface=sddn-unicom-cz
     public-key="redacted"
     endpoint-address=redacted endpoint-port=redacted
     current-endpoint-address=redacted current-endpoint-port=redacted
     allowed-address=0.0.0.0/0,::/0 persistent-keepalive=25s rx=830.5MiB
     tx=507.9MiB last-handshake=1m5s

接口配置完成了之后,我们还需要分别在 /ip/address/ipv6/address 给我们的接口配置上 v4 和 v6 地址。需要注意 RouterOS 对使用 /32 的 IPv4 地址建立 BGP 会话不太友好,最好要使用 /3️1 或者更大的接口地址以便后续配置 BGP:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[admin@MikroTik] /routing/bgp> /ip/address/print where interface=sddn-unicom-cz
Columns: ADDRESS, NETWORK, INTERFACE
# ADDRESS       NETWORK    INTERFACE
;;; IPv4 Address for BGP Session with Unicom-Changzhou
2 28.0.15.9/30  28.0.15.8  sddn-unicom-cz

[admin@MikroTik] /routing/bgp> /ipv6/address/print where interface=sddn-unicom-cz
Flags: D - DYNAMIC; G, L - LINK-LOCAL
Columns: ADDRESS, INTERFACE, ADVERTISE
#    ADDRESS                      INTERFACE       ADVERTISE
;;; IPv6 Address for BGP Session with Unicom-Changzhou
1  G 2001:db8:reda:cted::/126     sddn-unicom-cz  no
2 DL fe80::6b4e:4fe3:b34:cf1c/64  sddn-unicom-cz  no

内网 BGP session

和 bird 一样,我们最好先准备一个模板:

1
2
3
4
5
[admin@MikroTik] /routing/bgp> template/print
Flags: * - default; X - disabled, I - inactive
 0 *  name="default" routing-table=main router-id=28.1.1.1 as=4233331011
      multihop=no address-families=ip,ipv6
      output.redistribute=connected,static

然后用这两个模板分别创建 v4 和 v6 的 BGP 配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[admin@MikroTik] /routing/bgp> connection/print where name="bgp-sddn-unicom-cz-v4" or name="bgp-sddn-unicom-cz-v6"
Flags: D - dynamic, X - disabled, I - inactive
 0   name="bgp-sddn-unicom-cz-v6"
     remote.address=2001:db8:redacted::a .as=4233330010
     local.address=2001:db8:redacted::9 .role=ebgp
     routing-table=main router-id=28.1.1.1 templates=default as=4233331011
     nexthop-choice=default multihop=no address-families=ipv6
     output.redistribute=connected,static,dhcp .filter-chain=sddn-export
     .no-early-cut=no .keep-sent-attributes=no
     input.filter=sddn-cn-import

 1   name="bgp-sddn-unicom-cz-v4"
     remote.address=28.0.15.10 .as=4233330010
     local.address=28.0.15.9 .role=ebgp
     routing-table=main router-id=28.1.1.1 templates=default as=4233331011
     multihop=no address-families=ip
     output.redistribute=connected,static .filter-chain=sddn-export
     input.filter=sddn-cn-import

当然我们也许要增加一些 filter 用来按需对路由优先级做一些调整:

1
2
3
4
5
6
7
[admin@MikroTik] /routing/bgp> ../filter/rule/print where chain=sddn-cn-import
Flags: X - disabled, I - inactive
 0   chain=sddn-cn-import
     rule="if (afi ipv4) {\r\n  set pref-src 28.0.99.11;\r\n}\r\n\r\nif (afi
     ipv6) {\r\n  set pref-src 2001:db8:redacted::28.0.99.11;\r\n}\r\n\r\nif
     (bgp-large-communities includes 142641:100:2) {\r\n  set distance
     +5;\r\n} else {\r\n  set distance -5;\r\n}\r\n\r\naccept"

除了上面展示的这个到常州联通的 WireGuard + BGP,我还配置了一个到香港和日本的连接,此处就不多赘述了。

BGP 全表带回家

目前最容易获得一份全表的方案依然是 Vultr 的丐中丐 VPS,网上有很多相关的文章了,有需要的自然会操作。不过在 RouterOS 上,我的实现策略比较神秘:用 BGP 接受非大陆的 IPv4 路由和大陆的 IPv6 路由。

针对 IPv4 这一块,我希望能直接用 PPPoE 拿到的 Gateway 来上网,将非大陆的路由前缀拉到 main 表中,并通过 filter rule 将这些路由设置成一个内网内的 ipip 隧道接口。因为 PPPoE 连接建立之后 gateway(peer)是动态的,此处希望尽量避免使用这个参数。

1
2
3
4
5
[admin@MikroTik] /routing/bgp> ../filter/rule/print where chain=bgp-speaker-proxy-v4
Flags: X - disabled, I - inactive
 3   chain=bgp-speaker-proxy-v4
     rule="if (dst-len > 0) {\n  set gw 28.0.15.14;\n  set gw-interface ipip-
     internet-gateway;\n  accept;\n} else {\n  reject;\n}"

而 IPv6 我是在大内网的路由器里宣告了默认路由,这个是动态的。恰好 IPv6 的 link-local 地址机制让我可以稳定的用一个 fe80::xxxx%pppoe-chinanet 网关作为出口,非常固定,所以才用了和 v4 相反的模式:

1
2
3
4
5
6
[admin@MikroTik] /routing/bgp> ../filter/rule/print where chain=bgp-speaker-direct-v6
Flags: X - disabled, I - inactive
 4   chain=bgp-speaker-direct-v6
     rule="if (dst-len > 0) {\r\n  set gw fe80::redacted;\r\n  set  gw-interface
     pppoe-chinanet;\r\n  set distance -5;\r\n  accept;\r\n} else {\r\n  reject;
     \r\n}"

这样我们分别用 1.1.1.1, 223.5.5.5, 240e:96c:6100:b15:3e::2001:470:0:1f9::2 来测试一下路由器的选路效果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[admin@MikroTik] /routing/bgp> /ip/route/print where 1.1.1.1 in dst-address and
routing-table=main
Flags: D - DYNAMIC; A - ACTIVE; b, v, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS  GATEWAY                         DISTANCE
DAv 0.0.0.0/0    pppoe-chinanet                       100
DAb 1.1.1.0/24   28.0.15.14%ipip-internet-proxy        20

[admin@MikroTik] /routing/bgp> /ip/route/print where 223.5.5.5 in dst-address and routing-table=main
Flags: D - DYNAMIC; A - ACTIVE; v, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS  GATEWAY         DISTANCE
DAv 0.0.0.0/0    pppoe-chinanet       100

[admin@MikroTik] /routing/bgp> /ipv6/route/print where 240e:96c:6100:b15:3e:: in dst-address and routing-table=main
Flags: D - DYNAMIC; A - ACTIVE; b, v, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS    GATEWAY                                   DISTANCE
DAb ::/0           2001:db8:redacted:cc23::6                       20
D b ::/0           2001:db8:redacted:cc23::a                       25
D v ::/0           pppoe-chinanet                                 100
DAb 240e::/20      fe80::ce1a:faff:feeb:d1c0%pppoe-chinanet        15
DAb 240e:96c::/30  fe80::ce1a:faff:feeb:d1c0%pppoe-chinanet        15
DAb 240e:96c::/32  fe80::ce1a:faff:feeb:d1c0%pppoe-chinanet        15

[admin@MikroTik] /routing/bgp> /ipv6/route/print where 2001:470:0:1f9::2 in dst-address and routing-table=main
Flags: D - DYNAMIC; A - ACTIVE; b, v, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
    DST-ADDRESS  GATEWAY                   DISTANCE
DAb ::/0         2001:db8:redacted:cc23::6       20
D b ::/0         2001:db8:redacted:cc23::a       25
D v ::/0         pppoe-chinanet                 100

可以看到基本上都是符合预期的正确路由了。

IPv6 前缀翻译

此时此刻我的路由器同时有来自中国电信的 IPv6 前缀和来自我自己从 LIR 那薅来的 IPv6 前缀,但是因为 LAN 侧 SLAAC 分发了自己的前缀,所以即使利用全表数据进行了分流,但是源地址依然不太对劲:电信的接口看到了一个完全不是自己分发的地址来了个包。比较滑稽的是电信没有过滤此类流量,而是正确的用自己的网络将报文传递到了最终目标。目标服务器返回数据时,开始“符合预期”地照着之前报文里源地址作为目标地址,而地址是由我的 ASN 在香港宣告的,也会被传送到香港的路由器,然后通过自己的大内网传递到家里,最终导致网络报文会绕全球,冲浪体验质量极差。

解决这个问题也很简单,我们只需要在 IPv6 的防火墙上加一点魔法,对通过中国电信网关的流量进行 NAT 即可:

1
2
3
4
5
6
7
8
9
[admin@MikroTik] /routing/bgp> /ipv6/firewall/nat/print
Flags: X - disabled, I - invalid; D - dynamic
 0    chain=dstnat action=netmap to-address=2001:redacted::/56
      dst-address=240e:redacted::/56 in-interface=pppoe-chinanet log=no
      log-prefix=""

 1    chain=srcnat action=netmap to-address=240e:redacted::/56
      src-address=2001:redacted::/56 out-interface=pppoe-chinanet log=no
      log-prefix=""

当然因为这些规则都写了电信给我路由的前缀,我们需要对之前的 DHCP client 做一些微调,用一个脚本来动态的更新这俩防火墙规则:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
:log info ("dhcpv6 client script started");

:local ChinaNetPrefix [/ipv6/pool/get [find name="chinanet-ipv6-pd"] prefix];
:log info "prefix assigned by ChinaNet is: $ChinaNetPrefix";

:local ChinaNetPPPLinkLocalAddress [/ipv6/dhcp-client/get [find pool-name="chinanet-ipv6-pd"] dhcp-server-v6];
:log info "link local address ChinaNet is: $ChinaNetPPPLinkLocalAddress";

/routing/filter/rule/set rule="if (dst-len > 0) {set gw $ChinaNetPPPLinkLocalAddress;set gw-interface pppoe-chinanet;set distance -5;accept;} else {reject;}" [find chain="bgp-speaker-direct-v6"];
:log info "filter update okay";

:local ChinaNetNewPrefix [:pick "$ChinaNetPrefix" 0 ([:len "$ChinaNetPrefix"] - 3)]
/ipv6/firewall/nat/set dst-address="$ChinaNetNewPrefix/56" [find chain=dstnat action=netmap];
/ipv6/firewall/nat/set to-address="$ChinaNetNewPrefix/56" [find chain=srcnat action=netmap];

:log info "netmap update okay";

局域网内多 VLAN 和游戏 / Steam 下载加速

这几个问题的解决方式也比较统一,就是建立多个不同的路由表,并在表里设置好各自的路由,再将每个 VLAN 接口的配置用 /routing/rule 关联到这些表即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[admin@MikroTik] /routing/bgp> /routing/rule/print
Flags: X - disabled, I - inactive
 0   src-address=10.0.0.0/24 interface=wg-as4134 action=lookup
     table=egress-pppoe

 1   interface=vlan-gaming-hk action=lookup table=gaming-hk

 2   interface=vlan-gaming-jp action=lookup table=gaming-jp

[admin@MikroTik] /routing/bgp> /ip/firewall/mangle/print where chain=prerouting

Flags: X - disabled, I - invalid; D - dynamic
 0  D ;;; special dummy rule to show fasttrack counters
      chain=prerouting action=passthrough

 5    ;;; allow Hong Kong gaming VLAN accessing other intranet
      chain=prerouting action=mark-routing new-routing-mark=main
      passthrough=no src-address=28.1.4.0/24 dst-address=28.0.0.0/8
      in-interface=vlan-gaming-hk log=no log-prefix=""

 6    ;;; allow Japan gaming VLAN accessing other intranet
      chain=prerouting action=mark-routing new-routing-mark=main
      passthrough=no src-address=28.1.5.0/24 dst-address=28.0.0.0/8
      in-interface=vlan-gaming-jp log=no log-prefix=""

 7    ;;; HKRPG overseas UDP
      chain=prerouting action=mark-routing new-routing-mark=gaming-jp
      passthrough=no protocol=udp dst-port=23301 log=no log-prefix=""

一些别的坑

RouterOS 的 ipip / gre 隧道实现参考了 Cisco 的 keep alive 实现,在和 Linux 设备一起用的时候无法判断对方是否在线,具体原因可以看看这里:在 Linux 上实现对 GRE keepalive 包的回复


以上差不多就是整个 RB4011 在我自己的网络环境下使用的情况啦,实际用下来除了偶尔 BugTik 有些奇怪的问题以外基本没有遇到问题,最后来一个 WebFig 截图吧:

WebFig

comments powered by Disqus
Except where otherwise noted, content on this blog is licensed under CC-BY 2.0.
Built with Hugo
主题 StackJimmy 设计