最近家里新拉了一条 1000M 的上海移动宽带,为了充分利用新宽带和已有的移动宽带,自然是需要用一点魔法来同时利用这两路上游。这里就以 CCR2004-16G-2S+
路由器为例,介绍一下如何用 BGP 全表来优化网络访问。我们的目标策略非常清晰:
- 针对中国电信的 IP 地址(AS4134,AS4812),直接使用电信出口
- 针对所有剩下的非中国 ASN,使用局域网内的一个科学路由
- 其余走移动
基础网络配置:PPPoE 等
首先需要让我们的路由器连接上互联网。假设电信的网络接在 ether1
,移动的接在 ether2
,我们可以创建这样两个 PPPoE client:
csharp
/interface/pppoe-client/add name="pppoe-chinanet" interface=ether1 profile=default add-default-route=no dial-on-demand=no use-peer-dns=no allow=pap,chap,mschap1,mschap2
/interface/pppoe-client/add name="pppoe-chinamobile" interface=ether2 profile=default add-default-route=no dial-on-demand=no use-peer-dns=no allow=pap,chap,mschap1,mschap2
上面省略了 PPPoE 中账户和密码的部份,各位可以按照需要自行补全。不难注意到我们新建的 PPPoE 连接没有使用默认网关,这里我们可以直接加一个静态的路由来作为默认网关配置。假定我们计划使用移动的宽带作为默认上行:
css
/ip/route/add dst-address=0.0.0.0/0 routing-table=main gateway=pppoe-chinamobile distance=100
到此为止我们的路由器应该就能正确的访问网络了,不妨来用 traceroute
验证一下:
erlang
[admin@shanghai-router] > /tool/traceroute 223.5.5.5
Columns: ADDRESS, LOSS, SENT, LAST, AVG, BEST, WORST, STD-DEV
# ADDRESS LOSS SENT LAST AVG BEST WORST STD-DEV
1 <redacted> 0% 9 3.9ms 4.7 3.6 8.9 1.9
2 <redacted> 75% 9 timeout 3.7 3.6 3.7 0.1
3 <redacted> 12.5% 8 timeout 3.7 3.6 3.8 0.1
4 <redacted> 0% 8 12.9ms 7.8 3.9 20.3 5.5
5 100% 8 timeout
6 116.251.116.93 0% 8 6.2ms 12.9 5.6 38.3 10.6
7 100% 8 timeout
8 100% 8 timeout
9 223.5.5.5 0% 8 5.5ms 5.4 5.2 5.6 0.1
同时我们还可以通过指定 interface
参数来测试一下电信的 PPPoE 连接:
erlang
[admin@shanghai-router] > /tool/traceroute 223.5.5.5 interface=pppoe-chinanet
Columns: ADDRESS, LOSS, SENT, LAST, AVG, BEST, WORST, STD-DEV
# ADDRESS LOSS SENT LAST AVG BEST WORST STD-DEV
1 <redacted> 7.7% 13 3.8ms 4.7 1.8 7.7 1.8
2 <redacted> 0% 13 10.3ms 8.7 4.6 18.4 3.2
3 <redacted> 66.7% 13 timeout 6.6 3.9 14.2 4.4
4 <redacted> 66.7% 12 timeout 5.5 3.8 8.7 1.9
5 <redacted> 0% 12 3.2ms 6.3 3.1 25.3 6.7
6 <redacted> 0% 12 5.8ms 18.2 5.5 150.8 40
7 100% 12 timeout
8 100% 12 timeout
9 100% 12 timeout
10 223.5.5.5 0% 12 5.4ms 5.5 5.3 5.8 0.2
当然我们还需要在 /ip/firewall/nat
中创建一些 NAT 规则,让我们 LAN 中的设备也能访问互联网。这部分比较基础,就不赘述了。
拿到一份 BGP 全表
如果你是一个 BGP Player 完全可以跳过这一部分,但是如果不了解 BGP 或者是没有 ASN 的人则可能会有一点点困难。这里大家可以通过买一台最便宜的 Vultr 服务器来获得 BGP 全表数据,同时不需要拥有自己的 ASN,性价比非常高,相关操作步骤网上有很多材料,例如:
完成 Vultr 上的相关操作后,我们就可以按照官方教程和 Vultr 建立 BGP session,从而拿到全表数据,非常容易,相关关键配置如下:
ini
protocol device {}
protocol kernel kernel4 {
ipv4 {
import none;
export where source != RTS_DEVICE && proto != "bgp_vultr_v4";
};
}
protocol kernel kernel6 {
ipv6 {
import none;
export filter {
if source = RTS_DEVICE then reject;
if proto = "bgp_vultr_v4" then reject;
reject;
};
};
}
protocol static static_bgp_neighbor_v6 {
ipv6;
route 2001:19f0:ffff::1/128 via fe80::fc00:4ff:fe08:ee5f % enp1s0;
}
protocol static static_bgp_neighbor_v4 {
ipv4;
route 169.254.169.254/32 via a.b.c.1 % enp1s0;
}
protocol bgp bgp_vultr_v4 {
local as 142641;
neighbor 169.254.169.254 as 64515;
source address a.b.c.d;
multihop 2;
password "xxxxxxxx";
ipv4 {
import all;
export none;
};
}
protocol bgp bgp_vultr_v6 {
local as 142641;
neighbor 2001:19f0:ffff::1 as 64515;
source address 2001:db8::1;
multihop 2;
password "xxxxxxx";
ipv6 {
import all;
export none;
};
}
当然如果你有困难也可以从我这里获取全表数据,具体可以 E-mail 联系~
如果一切顺利,我们可以在 birdc
中使用命令 show protocol
看到我们的 session 状态已经是 Established:
sql
Name Proto Table State Since Info
bgp_vultr_v4 BGP --- up 2023-01-21 Established
bgp_vultr_v6 BGP --- up 2023-01-21 Established
我们也可以用 show route for
命令来查看具体的 FIB,例如:
yaml
bird> show route for 1.1.1.1 all
Table master4:
1.1.1.0/24 unicast [bgp_vultr_v4 2023-08-01 from 169.254.169.254] * (100/?) [AS13335i]
via 45.32.28.1 on enp1s0
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 64515 20473 13335
BGP.next_hop: 169.254.169.254
BGP.local_pref: 100
BGP.aggregator: 10.34.7.24 AS13335
BGP.community: (20473,200) (64515,44)
BGP.large_community: (20473, 200, 13335) (20473, 200, 45686)
这样我们就准备好了自己的 BGP speaker 了。
在 BGP feeder 和路由器之间建立隧道
家庭宽带通常没有固定 IP,为了尽可能减少配置变更,最简单的方法是在家里的路由器和有 BGP 全表的服务器之间拉一个隧道。这里可以使用 WireGuard,优点是比较新的内核和 RouterOS 都有原生的支持,而且是 UDP 协议,比较方便使用各种"转发"服务来提升可靠性。
在 feeder 上,我们捏一个配置来创建接口:
ini
[Interface]
PrivateKey = xxx
Address = 10.22.33.1/24, fd34:38a:f295::1/64
MTU = 1400
ListenPort = 114514
[Peer]
PublicKey = yyy
AllowedIPs = 10.22.33.2/32, fd34:38a:f295::2/128
然后,在 RouterOS 上也建立一个接口并增加 peer:
csharp
/interface/wireguard/add name="wg-bgp-speaker" private-key="xxx" mtu=1400
/interface/wireguard/peers/add endpoint-address=172.16.1.1 endpoint-port=3000 interface=wg-bgp-speaker public-key="qzgKhWy8u0DBvXcrhfFDYPwKzKipWzCsxR5ecNIYZFs=" persistent-keepalive=60 allowed-address=10.22.33.1/32,fd34:38a:f295::1/128
/ip/address/add interface=wg-bgp-speaker address=10.22.33.2/24
/ipv6/address/add interface=wg-bgp-speaker address=fd34:38a:f295::2/64
这样我们就弄出了一个直接连接的隧道了,这个隧道只能让我们的路由器和服务器点对点通讯,不过这样就够了。
准备路由过滤器
按照之前所说的规则,我们可以准备一下 RouterOS 侧的路由过滤器了,在这个 filter 中我们直接根据 AS path 的末尾来选择这条路由使用的网关,以 IPv4 为例:
lua
if (dst-len == 0) {
reject
}
set distance 50;
if (not bgp-as-path [[:china_asn_list:]]$) {
set gw %ipip-internet-proxy;
accept;
} else {
if (bgp-as-path 4134$|4812$) {
set gw %pppoe-chinanet;
} else {
set gw %pppoe-chinamobile;
}
}
accept;
有两个需要说明的是:
- china_asn_list 是一个从网上获取的在大陆使用的 ASN 列表,可以在
/routing/filter/num-list
中维护,比如增加 AS4134:/routing/filter/num-list/add list=china_asn_list range=4134
- 通过
set gw %interface
可以直接设置一个接口作为网关,让内核关注下一跳究竟给谁,可以减少很多维护成本
如法炮制一个 IPv6 的过滤器,内容基本一样,只有跨境访问的网关需要进行一些微调。准备好这两个 filter 之后,就只剩最后一步了------全表带回家
全表带回家
最后的工作就是在我们的 feeder 和路由器之间建立一个 BGP 邻居关系,首先我们在 speaker 上新增两个 protocol:
ini
protocol bgp bgp_speaker_v4 {
local as 65000;
neighbor 10.22.33.2 as 65001;
source address 10.22.33.1;
passive;
ipv4 {
import none;
export where proto = "bgp_vultr_v4" && 223.5.5.5 !~ net;
};
};
protocol bgp bgp_speaker_v6 {
local as 65000;
neighbor fd34:38a:f295::2 as 65001;
source address fd34:38a:f295::1;
passive;
multihop;
ipv6 {
import none;
export where proto = "bgp_vultr_v6";
};
};
接着在 RouterOS 上增加对应的 BGP connection:
ini
/routing/bgp/connection/add remote.address=10.22.33.1/32 remote.as=65000
local.address=10.22.33.2 remote.role=ebgp
routing-table=main router-id=10.22.33.2 as=65001 multihop=yes hold-time=infinity address-families=ip
output.filter-chain=bgp-speaker-export
input.filter=bgp-speaker-v4
/routing/bgp/connection/add remote.address=fd34:38a:f295::1/128 .as=65000
local.address=fd34:38a:f295::2 .role=ebgp
routing-table=main router-id=10.22.33.2 as=65001 multihop=yes hold-time=infinity address-families=ipv6
output.filter-chain=bgp-speaker-export
input.filter=bgp-speaker-v6
之后等待全表进入 FIB 后,就可以开始验证我们的分流效果了,我们分别用一个电信/移动/联通/Cloudflare 地址来进行测试:
css
# 电信
[admin@shanghai-router] > /ip/route/print where 221.227.153.1 in dst-address and routing-table=main and !static
Flags: D - DYNAMIC; A - ACTIVE; b, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
DST-ADDRESS GATEWAY DISTANCE
DAb 221.224.0.0/13 pppoe-chinanet 50
# 移动
[admin@shanghai-router] > /ip/route/print where 120.232.236.5 in dst-address and routing-table=main and !static
Flags: D - DYNAMIC; A - ACTIVE; b, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
DST-ADDRESS GATEWAY DISTANCE
DAb 120.192.0.0/10 pppoe-chinamobile 50
DAb 120.232.0.0/16 pppoe-chinamobile 50
DAb 120.232.236.0/22 pppoe-chinamobile 50
# 联通
[admin@shanghai-router] > /ip/route/print where 103.45.78.1 in dst-address and routing-table=main and !static
Flags: D - DYNAMIC; A - ACTIVE; b, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
DST-ADDRESS GATEWAY DISTANCE
DAb 103.45.76.0/22 pppoe-chinamobile 50
# Cloudflare
[admin@shanghai-router] > /ip/route/print where 1.1.1.1 in dst-address and routing-table=main and !static
Flags: D - DYNAMIC; A - ACTIVE; b, y - BGP-MPLS-VPN
Columns: DST-ADDRESS, GATEWAY, DISTANCE
DST-ADDRESS GATEWAY DISTANCE
DAb 1.1.1.0/24 198.18.1.1%ipip-internet-proxy 50
这样我们的双线宽带的 IPv4 就算是充分利用上了,v6 虽然通过这些方法实现了分流,但是因为没有 NAT,设备也不能智能的选择用哪个前缀的 IP 地址,依然存在问题,之后的文章会介绍如何解决这个问题。最后展示一下成果: