Featured image of post 在 RouterOS 上利用 BGP 全表优化多线互联网访问

在 RouterOS 上利用 BGP 全表优化多线互联网访问

在家也要玩 BGP!

最近家里新拉了一条 1000M 的上海移动宽带,为了充分利用新宽带和已有的移动宽带,自然是需要用一点魔法来同时利用这两路上游。这里就以 CCR2004-16G-2S+ 路由器为例,介绍一下如何用 BGP 全表来优化网络访问。我们的目标策略非常清晰:

  1. 针对中国电信的 IP 地址(AS4134,AS4812),直接使用电信出口
  2. 针对所有剩下的非中国 ASN,使用局域网内的一个科学路由
  3. 其余走移动

基础网络配置:PPPoE 等

首先需要让我们的路由器连接上互联网。假设电信的网络接在 ether1,移动的接在 ether2,我们可以创建这样两个 PPPoE client:

1
2
/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 连接没有使用默认网关,这里我们可以直接加一个静态的路由来作为默认网关配置。假定我们计划使用移动的宽带作为默认上行:

1
/ip/route/add dst-address=0.0.0.0/0 routing-table=main gateway=pppoe-chinamobile distance=100

到此为止我们的路由器应该就能正确的访问网络了,不妨来用 traceroute 验证一下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[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 连接:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[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,从而拿到全表数据,非常容易,相关关键配置如下:

 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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:

1
2
3
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,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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 上,我们捏一个配置来创建接口:

1
2
3
4
5
6
7
8
9
[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:

1
2
3
4
5
/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 为例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
if (dst-len == 0) {
  reject
}

set distance 50;

if (not bgp-as-path [[:china_asn_list:]]$) {
  set gw 198.18.1.1;
  set gw-interface ipip-internet-proxy;

  accept;
} else {
  if (bgp-as-path 4134$|4812$) {
    set gw %pppoe-chinanet;
  } else {
    set gw %pppoe-chinamobile;
  }
}

accept;

有两个需要说明的是:

  1. china_asn_list 是一个从网上获取的在大陆使用的 ASN 列表,可以在 /routing/filter/num-list 中维护,比如增加 AS4134: /routing/filter/num-list/add list=china_asn_list range=4134

  2. 通过 set gw %interface 可以直接设置一个接口作为网关,让内核关注下一跳究竟给谁,可以减少很多维护成本

如法炮制一个 IPv6 的过滤器,内容基本一样,只有跨境访问的网关需要进行一些微调。准备好这两个 filter 之后,就只剩最后一步了——全表带回家

全表带回家

最后的工作就是在我们的 feeder 和路由器之间建立一个 BGP 邻居关系,首先我们在 speaker 上新增两个 protocol:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/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 地址来进行测试:

 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
# 电信
[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 地址,依然存在问题,之后的文章会介绍如何解决这个问题。最后展示一下成果:

Screenshot of ip.skk.moe

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