在家也要玩BGP(1):简单的多运营商接入策略路由配置

拉了两条不同运营商的宽带,想要有效利用两条线路的带宽,但是这时候单纯的负载均衡体验并不好,因为随机分配的出口并不一定速度最快。BGP全球路由表里正好有我们需要的信息——每个IP到哪个运营商距离最近(某种意义来说,速度也应当最快)。那么怎么利用它来优化网络的体验呢?

基线配置

拉了两条宽带,一条中国电信,一条中国移动,均接在一台IOS XE 16.9设备上,分别对应Dialer 0和Dialer 1。基础的双线备份配置如下:

interface Dialer0
 description China Telecom
 ip address negotiated
 ip nat outside
 encapsulation ppp
 ip tcp adjust-mss 1452
 dialer pool 1
 dialer-group 1
 ppp authentication pap callin
 ppp pap sent-username xxxx password yyyy
!
interface Dialer1
 description CMCC
 ip address negotiated
 ip mtu 1480
 ip nat outside
 encapsulation ppp
 ip tcp adjust-mss 1432
 dialer pool 2
 dialer-group 1
 ppp authentication pap callin
 ppp chap hostname xxxx
 ppp chap password yyyy
 ppp pap sent-username xxxx password yyyy
!
track 1 interface Dialer1 ip routing
!
ip nat inside source route-map nat-ct-pppoe interface Dialer0 overload
ip nat inside source route-map nat-cmcc-pppoe interface Dialer1 overload
ip route 0.0.0.0 0.0.0.0 Dialer0 5 track 1
ip route 0.0.0.0 0.0.0.0 Dialer1 10
!
ip access-list standard nat-inside-ip
 permit 192.168.1.0 0.0.0.255
!
dialer-list 1 protocol ip permit
!
route-map nat-cmcc-pppoe permit 10
 match ip address nat-inside-ip
 match interface Dialer1
!
route-map nat-ct-pppoe permit 10
 match ip address nat-inside-ip
 match interface Dialer0

两条线路只是用来做备份有些浪费,况且中国电信上国内网站快,中国移动出国速度快,各有长处。那么怎么才能妥善利用呢?让我们用BGP来做一个策略路由吧。

收一个BGP全球路由表

有很多服务提供商可以给你做免费的BGP会话,在其中选一个便宜又能提供全表的,要一份全表。我用Vultr比较多,那么这次也用它好了。接下来要考虑怎么把全表收回来。因为BGP要求两端IP固定(除非你想用listen-range功能,但是这个功能有潜在的安全性问题),那么要么我们在路由器和收全表的服务器之间打一个隧道,用这台服务器做路由控制,要么先想办法把表收到离路由器逻辑上比较近的服务器上,后者作为路由控制器(control plane),处理数据后再转发给路由器。考虑到中国特色的网络问题,这里使用后一种方法,路由控制器放在内网。

考虑到打隧道不是本文重点,我们就简单地用ZeroTier One无脑打一个隧道,配上IP,在Vultr的VPS上启动一个BGP daemon,和网关起一个eBGP会话,和内网的路由控制器起一个iBGP会话即可。如果不想打隧道,那么简单的TCP转发服务也可以用来解决BGP session两端固定IP的问题。

服务器配置:

  • Vultr侧(下称feeder):5USD的虚拟机(1CPU/1G Memory),Ubuntu 18.04
  • 本地侧(下称controller):J1900 ESXi服务器上的虚拟机(2CPU/2G Memory),Debian 10

Feeder配置很简单,我用的VPS上已经装了Bird 1作为BGP daemon,那么只需要写两组BGP会话即可。参考配置:

# Feeder (Bird 1)
# /etc/bird/bird.conf

router id <your-public-ipv4>;

protocol device {}

protocol direct {}

protocol bgp bgp_vultr {
        local as <your-asn>;
        import all;
        export none;
        graceful restart on;
        multihop 2;
        neighbor 169.254.169.254 as 64515;
        password "hunter2";
}

protocop bgp bgp_controller {
        local as <your-asn>;
        import none;
        export all;
        graceful restart on;
        neighbor <controller-ip> as <your-asn>;
}

Controller用Bird 2作为BGP daemon。首先要注意的一点是,我们需要在路由器上硬编码controller到feeder的路由,以防路由改变以后BGP断开造成路由震荡。

ip route <controller-ip> 255.255.255.0 Dialer0

Bird 2配置(先确认全表能收进来,路由策略暂时空着,下一节再讲):

# Controller (Bird 2)
# /etc/bird/bird.conf

log syslog all;
router id <your-controller-ip>;
# debug protocols all;
watchdog warning 5 s;
watchdog timeout 30 s;
ipv4 table global_v4;
ipv4 table policy_v4;
protocol device {}
protocol direct { ipv4; }

template bgp bgp_feeder {
        local as 205610;
        neighbor as 205610;
        multihop;
        graceful restart on;
        rr client;
        ipv4 {
                table global_v4;
                import all;
                export none;
        };
}

template bgp bgp_router {
        local as 205610;
        neighbor as 205610;
        multihop;
        graceful restart on;
        rr client;
        ipv4 {
                table policy_v4;
                import none;
                export all;
                next hop self;
        };
}

protocol bgp bgp_feeder_0 from bgp_feeder {
        neighbor <feeder-ip>;
}

protocol bgp bgp_router_0 from bgp_router {
        neighbor <router-ip>;
}

filter policy_routing {
        reject;
}

protocol pipe global_to_policy {
        table global_v4;
        peer table policy_v4;
        export filter policy_routing;
        import none;
}

因为两边都是iBGP会话,为了让路由能在两边分发,需要把controller配成route reflector。

确定路由策略

先确定一个最简单的路由策略:

  • 电信(AS4134、AS4809):走电信
  • 移动(AS56041):走移动
  • 其余走移动

那么首先我们在bird配置文件里面完善一下filter:

define cmcc_asn = [
        56041
];

define chinanet_asn = [
        4809,
        4134
];

filter policy_routing {
        if source != RTS_BGP then reject;

        # CMCC
        if bgp_path.last ~ cmcc_asn then {
                bgp_community = -empty-;
                bgp_community.add((100,100));
                accept;
        }

        # ChinaNet
        if bgp_path.last ~ chinanet_asn then {
                bgp_community = -empty-;
                bgp_community.add((200,200));
                accept;
        }

        reject;
}

然后我们在路由器上配置策略路由规则:

track 1 interface Dialer1 ip routing
track 2 interface Dialer0 ip routing
!
ip route 192.0.2.100 255.255.255.255 Dialer 1 10 track 1
ip route 192.0.2.100 255.255.255.255 Dialer 0 20
ip route 192.0.2.200 255.255.255.255 Dialer 0 10 track 2
ip route 192.0.2.200 255.255.255.255 Dialer 1 20
!
ip community-list standard PBR_CMCC permit 100:100
ip community-list standard PBR_CT permit 200:200
!
route-map PBR permit 10
 match community PBR_CMCC
 set ip next-hop 192.0.2.100
!
route-map PBR permit 20
 match community PBR_CT
 set ip next-hop 192.0.2.200
!
router bgp <your-asn>
 bgp log-neighbor-changes
 neighbor <controller-ip> remote-as <your-asn>
 neighbor <controller-ip> description BGP Controller
 !
 address-family ipv4
  neighbor <controller-ip> activate
  neighbor <controller-ip> allowas-in 10
  neighbor <controller-ip> soft-reconfiguration inbound
  neighbor <controller-ip> route-map PBR in
 exit-address-family

这里说明一点:IOS/IOS XE的route-map不支持在BGP in上用set interface,会报以下错误:

% "PBR" used as BGP inbound route-map, set interface not supported

对动态IP的interface需要使用recursive方式设置路由,即用两个假的IP设置分别路由到两个interface,然后分别set ip next-hop到这两个IP上。

至于更复杂的路由策略,我在本系列的下一篇文章中会讲。

测试

分别找一个电信的IP和一个移动的IP看一下其路由即可。示例:

Router#show ip route 60.177.1.1 resolve
Routing entry for 60.176.0.0/12
  Known via "bgp 65001", distance 200, metric 0
  Tag 64515, type internal
  Last update from 192.0.2.200 00:01:10 ago
  Routing Descriptor Blocks:
  * 192.0.2.200, from 100.97.1.9, 00:01:10 ago, via Dialer0
      Route metric is 0, traffic share count is 1
      AS Hops 5
      Route tag 64515
      MPLS label: none
Router#show ip route 112.17.1.1 resolve
Routing entry for 112.17.0.0/16
  Known via "bgp 65001", distance 200, metric 0
  Tag 64515, type internal
  Last update from 192.0.2.100 00:01:00 ago
  Routing Descriptor Blocks:
  * 192.0.2.100, from 100.97.1.9, 00:01:00 ago, via Dialer1
      Route metric is 0, traffic share count is 1
      AS Hops 7
      Route tag 64515
      MPLS label: none

结论

整个系统的资源占用:

  • controller内存占用大概在400MiB以下
  • 需要分流的路由总计不到8000条,IOS XE可以轻松处理(如果给IOS XE发全表的话,4GiB内存是不够用的)
  • BGP会话带来的流量消耗大概一天300MiB

感谢某匿名CCNP玩家在本文写作过程中提供的帮助。

参考:

在家也要玩BGP(1):简单的多运营商接入策略路由配置》有1个想法

  1. Pingback引用通告: 在家也要玩BGP(1.5):我的双线分流规则 | Drown in Codes

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据