前言

最近看到一篇文章,里面使用了OSPF进行分流。OSPF作为一种IGP协议,其可承载的路由属性少,而且在面对大量路由时显得力不从心,因而有本文。本文基于 BGP 协议,使用 BIRD 路由套件与 RouterOS 通讯,以渐进难度展示了数种不同的路由获取方案。

背景介绍

BIRD是一款轻量化的路由套件,具有占用路由少(在收全表时内存占用相比其它平台尤为突出)、路由协议较为齐全等优点。实测,在存储 950k 条路由时,内存占用不足 400 MiB。BGP 协议基础不再赘述。

RouterOS 本文采用的是 v6.49 版本。

BIRD 基础

本处仅讲述 BIRD 的第二个大版本,一般安装时的名字叫做 bird2。BIRD2 与第一个大版本的最大差异在于 IPv4 与 IPv6 路由可以在同一个进程中处理,即原本的 birdbird6 合并成 bird,这使得它可以在一个会话中传输 IPv4 和 IPv6 路由,此功能我们之后再说。

路由最基础的一件事情:每一台路由器都有属于自己的名字(Router ID):一个 32 位整数,或者说一个 IPv4 地址。如果两台路由器具有相同的 Router ID,那么会互相认为是连上了自己,为了防止产生环路,会拒绝建立会话。

BIRD 基于路由表分发,我们需要先定义路由表(默认已经存在 master4 和 master6 两个表分别存放 IPv4 和 IPv6 路由),然后通过各种各样的 protocol 注入路由。每个 protocol 通常都有 ipv4 和 ipv6 两个 channel,分别分发不同 address family 的路由资讯,每个 channel 都有两个方向,import 和 export。export 是指从 BIRD 路由表向 protocol 的远端,例如:

  • BGP 从 BIRD 路由表到 peer
  • static 从 BIRD 路由表到 static 自身(通常不会用到)

import 和 export 的时候,可以定义过滤器,决定是否发送路由,以及修改路由属性。

最简单的静态路由分流

RouterOS 端配置

继续阅读之前,您需要:

  1. 在 RouterOS 与 Linux 之间建立好隧道,并开启 RouterOS 侧隧道的 Clamp TCP MSS 功能(在 RouterOS 连接路由模式的 PPPoE 光猫的情况下可能会误判 MTU,安全起见可手动设置隧道 MTU 为 1380)。
  2. 开启 Linux 下的 masquerade,设定条件为匹配输出网卡。

接收路由(如与 peer 连接上并开始交换路由时)、应用 Route Filter 修改时,如果 WinBox 停止响应,请耐心等待路由器处理。

本文中,RouterOS 侧隧道命名为 gre-linux,RouterOS 侧地址为 59.201.250.14/30,Linux 侧为 59.201.250.13/30,RouterOS 使用路由表 d

以下是命令行配置,点击 WinBox 左侧的 New Terminal,贴入回车即可:

  1. 添加防火墙规则,使得从指定 LAN 桥进入的流量先寻找指定的路由表:

    1
    2
    /ip firewall mangle
    add action=mark-routing chain=prerouting in-interface=lan-lounge new-routing-mark=d passthrough=yes
  2. 建立 Route Filter,为输入的每个路由设置 Routing Mark:

    1
    2
    /routing filter
    add action=accept chain=internet-access set-routing-mark=d
  3. 建立 BGP 会话,先建立 BGP instance,再为该 instance 设定 peer:

    1
    2
    3
    4
    /routing bgp instance
    add as=4206040001 name=split-routing router-id=59.201.250.14
    /routing bgp peer
    add instance=split-routing in-filter=internet-access name=d1 remote-address=59.201.250.13 remote-as=141237 ttl=1 update-source=gre-linux

这样,在这个 BGP instance 收到的所有路由,将会进入路由表 d

Linux 端配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
router id 59.200.240.9;

protocol device {}

protocol static {
route 1.1.1.1/32 reject;

ipv4 {};
}

protocol bgp {
local as 141237;
neighbor 59.201.250.14 as 4206040001;
source address 59.201.250.13;

ipv4 {
import none;
export all;
};
}

这里声明了一个 protocol static 以注入静态路由,一个 protocol bgp 以实现 BGP 通信。将内容覆盖 /etc/bird/bird.conf,控制台执行 sudo birdc c 即可重载配置——请记住,后文将不再重复此操作。

调试

如果一切正常,我们应该可以在 RouterOS 的 Routing - BGP - Peers 窗口看见 d1 peer 状态为 Established,同时在路由表(IP - Route)里也可以看见 1.1.1.1,具有 Routing Mark d。在 RouterOS 上测试 Traceroute,应该可以见到设定 Routing Table 参数时,包的路径发生了变化,从 Linux 端出去。

接下来需要配置 NAT 规则。RouterOS 端执行 /ip firewall nat add chain=srcnat out-interface=gre-linux action=masquerade,这样 LAN 下的设备也能出去了。两次 NAT 显然是不好的,改善方案我们将后续提到。

使用 cn-routefeed

cn-routefeed 是一个可以从 APNIC 定时拉取中国 IP 分配表(基于 whois 信息),并以 BGP 协议分发出去的小工具。该工具需要从源码构建,GitHub 上已给出详细步骤,照做即可。

在 Linux 端先关掉 BIRD:service bird stop,按照 GitHub 步骤启动 routefeed,RouterOS 端即可连上(需要 AS 一致,-n参数设置为 Linux 端的隧道IP),这样就可以实现 CN IP 走 Linux 了。那么怎么反过来呢?

使用 Route Filter 实现“路由取反”

路由取反,简单来说即我们收到中国路由表,但是需要让这些路由直出,而不是向 BGP 路由发出的路由器走。

由于 routefeed 不会发默认路由,我们需要手动指定:

1
2
/ip route
add distance=1 gateway=gre-linux routing-mark=d target-scope=100

⚠️警告:添加此项后,如果您的流量被 RouterOS 标记(例如匹配了接口),您将无法连接上路由器。您可尝试在此规则之前添加对内网、本机路由的一些规则。

Gateway如何选择:对于 PPPoE 上网,将 IP 改为接口名即可,如果是 DHCP 上网等二层方式,则需要指定为你的 DHCP 路由器 IP。对于二层网络(如 DHCP 上网),指定接口将会用 ARP 找目标主机而不是通过在那个接口的默认路由出去。

为上文所创建的 Route Filter,修改 Actions - Set In Nexthop (Direct),根据上文提到的规则选用。这样,我们就可以在路由表中看见,收到的路由变成直出,而其它路由则从隧道出去。

使用这种方法也无法解决内网互联、上级内网经隧道出去的问题。

接收全表

使用此方法将在接收路由时长时间占满路由器 CPU,并长期占用大量内存,在接收全表并进行之后的配置前,需要先断开与 RouterOS 的 BGP 会话。

使用 BGP 接收全表的好处在于,运营商之间的互联都是使用 BGP,使得 BGP 属性中携带的路径信息变得很有用。例如中国三大运营商、HiNet等,都具有自己的 ASN,发送、传递的路由都会带上自己的 ASN,显而易见——我们再不需要什么 CNList,只需要收表,如果拥有带 L3 Offloading 的机种或将比 Address List 更好用——而且它还是实时更新的!

将全表拉到我们身边那台可怜的 RouterOS 的方法有:

  1. 与具有全表远端机器交换路由;
  2. 打通隧道,与提供全表的供应商路由器直接交换。

方法 2 通常需要一个 ASN,并且鉴于本文读者所处的网络环境通常不具备打通隧道的条件,我们选择 1。

获得全表的方法有一定门槛,我们假定读者已用 BIRD 获得了一张全表,并且存放在 master4 表中。为节省可怜的 RouterOS 的 CPU 时间,我们将在 BIRD 中做预处理。

在与提供商的 session(BIRD中称为 protocol bgp)中,使用过滤器组合 import all; export none; 代替某厂商示例中的相应位置以接收全表。

中国方向的路由通常有个特征,那就是这些 AS 不会给别国 Transit,意味着只要 BGP Path 中出现了这些 AS,那么几乎可以肯定是中国方向的路由了。修改 BIRD 的 RouterOS BGP session 过滤器如下(此 AS 表不一定全面):

1
2
3
export filter {
if bgp_path ~ [4134, 4809, 36678, 4837, 9929, 9808, 9394, 4538, 23910] then accept; else reject;
};

替换原有的 export all;,注意分号。birdc c 之后,开启 RouterOS 端的 peer,这样路由就被灌进去了。这个过滤条件约可过滤出 39k 条路由,这样的数目即使是 128M 内存的机种也可以轻松应付。