使用BGP进行基于路由的分流
前言
最近看到一篇文章,里面使用了OSPF进行分流。OSPF作为一种IGP协议,其可承载的路由属性少,而且在面对大量路由时显得力不从心,因而有本文。本文基于 BGP 协议,使用 BIRD 路由套件与 RouterOS 通讯,以渐进难度展示了数种不同的路由获取方案。
背景介绍
BIRD是一款轻量化的路由套件,具有占用路由少(在收全表时内存占用相比其它平台尤为突出)、路由协议较为齐全等优点。实测,在存储 950k 条路由时,内存占用不足 400 MiB。BGP 协议基础不再赘述。
RouterOS 本文采用的是 v6.49 版本。
BIRD 基础
本处仅讲述 BIRD 的第二个大版本,一般安装时的名字叫做 bird2
。BIRD2 与第一个大版本的最大差异在于 IPv4 与 IPv6 路由可以在同一个进程中处理,即原本的 bird
和 bird6
合并成 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 端配置
继续阅读之前,您需要:
- 在 RouterOS 与 Linux 之间建立好隧道,并开启 RouterOS 侧隧道的 Clamp TCP MSS 功能(在 RouterOS 连接路由模式的 PPPoE 光猫的情况下可能会误判 MTU,安全起见可手动设置隧道 MTU 为 1380)。
- 开启 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,贴入回车即可:
添加防火墙规则,使得从指定 LAN 桥进入的流量先寻找指定的路由表:
1
2/ip firewall mangle
add action=mark-routing chain=prerouting in-interface=lan-lounge new-routing-mark=d passthrough=yes建立 Route Filter,为输入的每个路由设置 Routing Mark:
1
2/routing filter
add action=accept chain=internet-access set-routing-mark=d建立 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 | router id 59.200.240.9; |
这里声明了一个 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 | /ip route |
⚠️警告:添加此项后,如果您的流量被 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 的方法有:
- 与具有全表远端机器交换路由;
- 打通隧道,与提供全表的供应商路由器直接交换。
方法 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 | export filter { |
替换原有的 export all;
,注意分号。birdc c
之后,开启 RouterOS 端的 peer,这样路由就被灌进去了。这个过滤条件约可过滤出 39k 条路由,这样的数目即使是 128M 内存的机种也可以轻松应付。