BGP路由聚合原理和实验(华为设备)是我觉得网络工程里特别值得花时间搞透的一块内容。干运维或者做网络设计的都有体会,BGP路由表一旦膨胀起来,设备CPU、内存、收敛速度全部跟着遭殃。尤其是在多出口、多AS的环境里,路由条目几千上万条,每台路由器都要维护、都要计算,压力非常实在。这篇文章我就从原理到华为设备上的配置实验,把BGP路由聚合完整过一遍。不管你是刚接触BGP的新人,还是已经被路由表膨胀折磨过的老手,应该都能从里面找到有用的东西。
我先说清楚这篇要讲什么:聚合是什么、为什么能减小路由表、华为设备上如何配置、实验效果怎样、以及聚合之后会踩到哪些坑。重点放在操作和验证,不搞那种念PPT式的理论堆砌。
1. BGP路由表膨胀时,聚合为什么值得做
1.1 路由表膨胀带来的真实压力
BGP是全球互联网的“寻路系统”,但它有个天生的特点:路由条目会不停增长。各家运营商、云厂商、企业出口都会宣告自己的网段,全球BGP路由表条目数早就突破了九十万条的大关,而且还在涨。普通企业网可能没这么夸张,但只要你的设备接入运营商,运营商下发的路由就可能多达几十万条。
我见过一台低端路由器接了运营商的BGP对等体之后,路由条目上来,设备CPU直接飙到80%以上的情况。BGP本身就是增量更新协议,但如果邻居重连、路由策略调整、表项翻动,设备需要重新处理大量Update报文。TCAM空间有限,路由表放不下就只能软件转发,性能断崖式下跌。更麻烦的是,路由条目越多,BGP收敛越慢。链路一抖动,整个网络几十秒甚至几分钟不稳定,业务影响就很明显了。
路由聚合在这种情况下是成本最低、见效最快的优化手段。它把多个具体网段合并成一条聚合路由对外宣告,上游设备只需要维护一条而不是几十条,路由表规模瞬间降下来。
1.2 聚合的工作逻辑:把多条明细合并成一条
聚合的本质是用CIDR(无类别域间路由)规则,把一组连续的子网合并成一条更短前缀的路由。比如你内部有172.16.1.0/24、172.16.2.0/24、172.16.3.0/24这三个网段,对外完全可以用172.16.0.0/16一条路由来表达。
为什么能这么合并?因为这三个/24的网段都在172.16.0.0/16这个地址块范围内。路由器转发时按最长前缀匹配,只要外部没有更具体的路由,发给这些网段的数据都会匹配到172.16.0.0/16这条聚合路由,流量就能到达内部网络。
打个比方,你寄快递的时候不需要写完整的“省-市-区-街道-门牌号”,如果收件人集中在一个城市,你只写“某省某市”也能保证包裹先送到这个区域再分发。聚合就是这个道理——对外不暴露内部细节,只暴露一个足够定位的地址范围。
在BGP里做聚合,还有一个额外的好处:路由条目减少之后,链路发生故障时,受影响的路由数量也少,翻动次数下降,网络稳定性自然就上来了。
1.3 聚合的代价与适用边界
聚合不是越多越好。超网聚合会让路由失去精确性,可能带来次优路径甚至黑洞风险。
最典型的场景:你的AS内有两个不连续的网段,比如172.16.1.0/24和172.16.3.0/24分别在不同位置接入。如果强行把它们聚合为172.16.0.0/16对外通告,对端路由器会认为172.16.2.0/24这个不存在的网段也在你这里。一旦有流量访问172.16.2.0/24,就会全部发给你,而你内部并没有这个网段的真实出口,数据包只能被丢弃。
所以选择在哪里聚合、聚合到什么程度,必须结合实际网段规划和真实拓扑来定。在企业出口、AS边界做聚合是主流做法;在核心层内部,为了精细选路反而要保留明细。
2. 华为设备BGP聚合的底层机制
2.1 先搞清楚BGP路由是怎么发布的
要理解聚合,得先知道BGP路由是怎么进入路由表的。华为VRP平台上,BGP路由来源主要有两种:一是用network命令把IP路由表中的某条路由引入BGP,二是用import-route命令把IGP或静态路由引入BGP。
用network命令有个硬性条件:这条路由必须已经存在于设备的IP路由表中。也就是说,设备得先有一条指向某个目标网段的可用路由,才能把它“翻译”成BGP路由通告给邻居。用import-route则没有这个限制,但它会把路由表中所有匹配的路由都引入,控制粒度相对粗一些。
BGP路由进入BGP路由表之后,设备会运行BGP选路规则,选出最优路由,然后才通告给邻居。聚合发生的位置就在BGP路由表生成的过程中——设备把多条明细BGP路由汇总成一条聚合路由,放进BGP路由表,再对外发布。
2.2 aggregate命令的完整形态
华为设备上,手动聚合使用aggregate命令。命令格式如下:
aggregate ip-address { mask | mask-length } [ as-set | attribute-policy route-policy-name | detail-suppressed | origin-policy route-policy-name | suppress-policy route-policy-name ]常用的可选参数有三个,必须理解清楚:
| 参数 | 作用 | 使用场景 |
|---|---|---|
detail-suppressed | 抑制明细路由的通告,只通告聚合路由 | 希望上游只看到一条汇总路由时 |
as-set | 在聚合路由的AS_PATH中携带所有明细路由的AS号集合 | 明细路由来自多个AS时,防止环路 |
attribute-policy | 为聚合路由设置独立的BGP属性 | 调整MED、LocalPref、Community等 |
如果不加detail-suppressed,设备会同时通告聚合路由和明细路由。这样上游路由表里既有汇总条目又有明细条目,聚合效果大打折扣。要做“干净的聚合”,这个参数是关键。
华为设备还有一个summary automatic命令,属于自动聚合。它能把BGP路由表里的子网自动聚合为自然类网络。比如172.16.1.0/24、172.16.2.0/24会被自动汇总为172.16.0.0/16(B类地址的默认掩码)。但它有几个限制:只对通过import-route引入的路由生效,对network宣告的路由不生效;且只能按自然类掩码聚合,不能自定义聚合范围。实际工程中手动aggregate用得更多,控制粒度更精确。
2.3 AS路径属性与防环逻辑
BGP防环依赖AS_PATH属性。每条BGP路由经过一个AS,该AS号就会被追加到AS_PATH中。收到路由的设备如果发现AS_PATH里包含自己的AS号,就知道这条路由绕了一圈回到自己这里,会直接丢弃,这就是BGP最基本的防环机制。
聚合之后问题就来了。假设AS 200里有两条明细路由10.1.1.0/24和10.1.2.0/24分别来自AS 300和AS 400,设备把它们聚合为10.1.0.0/16。如果聚合时丢失了这些明细的来源AS信息,AS_PATH只剩下一串不完整的序列,对端就无法判断这条聚合路由是否经过了某个AS,环路检测就失效了。
as-set参数就是解决这个问题的。聚合路由会携带一个AS集合,把所有被聚合明细路由的AS号都打包进去。在华为VRP的显示中,AS_SET部分会用方括号标识。例如:
*> 10.1.0.0/16 192.168.12.1 0 100 0 100 [300 400]i对端看到这个聚合路由时,知道它包含了AS 300和AS 400的来源信息,如果自己的AS号出现在这个集合中,同样可以判定环路并丢弃。所以结论就是:只要聚合的明细路由跨越了多个AS,就必须带as-set。
3. 实验:两条命令让路由表大幅瘦身
3.1 实验拓扑与地址规划
这个实验我建议在模拟环境或者真实设备上都跑一遍,不复杂,但能完整看到聚合前后的变化。拓扑需要三台路由器:
- R1:AS 100,模拟内部网络侧,下挂多个用户网段
- R2:AS 200,模拟外部网络侧,作为R1的EBGP邻居
- R3:AS 300,辅助验证多AS场景(可选,但建议加上)
地址规划如下:
| 设备 | 接口 | IP地址 | 用途 |
|---|---|---|---|
| R1 | GE0/0/0 | 192.168.12.1/24 | 连接R2 |
| R1 | LoopBack0 | 1.1.1.1/32 | 管理/标识地址 |
| R1 | 静态路由 | 172.16.1.0/24、172.16.2.0/24、172.16.3.0/24 | 模拟用户网段 |
| R2 | GE0/0/0 | 192.168.12.2/24 | 连接R1 |
| R2 | LoopBack0 | 2.2.2.2/32 | 管理/标识地址 |
为了让network宣告生效,需要在R1上先把三个模拟网段的静态路由写好,指向Null0即可,这样它们会出现在IP路由表中,BGP才能引入:
ip route-static 172.16.1.0 255.255.255.0 NULL0 ip route-static 172.16.2.0 255.255.255.0 NULL0 ip route-static 172.16.3.0 255.255.255.0 NULL0这是实验演示常用的技巧,现实中这些网段可能是直连网段或通过IGP学习到的明细路由,原理一样。
3.2 基础配置与EBGP邻居建立
R1的基础配置如下:
sys sysname R1 interface GigabitEthernet0/0/0 ip address 192.168.12.1 255.255.255.0 interface LoopBack0 ip address 1.1.1.1 255.255.255.255R2的基础配置如下:
sys sysname R2 interface GigabitEthernet0/0/0 ip address 192.168.12.2 255.255.255.0 interface LoopBack0 ip address 2.2.2.2 255.255.255.255BGP配置,R1属于AS 100,与R2建立EBGP邻居并宣告三个网段:
bgp 100 peer 192.168.12.2 as-number 200 network 1.1.1.1 255.255.255.255 network 172.16.1.0 255.255.255.0 network 172.16.2.0 255.255.255.0 network 172.16.3.0 255.255.255.0R2属于AS 200,配置EBGP邻居:
bgp 200 peer 192.168.12.1 as-number 100配置完成后,在R2上查看邻居状态:
display bgp peer正常情况下,邻居状态应该是Established。如果一直是Connect或Active,先检查地址配置和防火墙策略,这是BGP排障最基础的步骤。
3.3 宣告明细路由,观察聚合前的路由表
邻居建立后,在R2上查看BGP路由表:
display bgp routing-table此时应该能看到来自R1的4条路由:
BGP Local router ID is 2.2.2.2 Status codes: * - valid, > - best, d - damped, h - history Network NextHop MED LocPrf PrefVal Path/Ogn *> 1.1.1.1/32 192.168.12.1 0 100 0 100i *> 172.16.1.0/24 192.168.12.1 0 100 0 100i *> 172.16.2.0/24 192.168.12.1 0 100 0 100i *> 172.16.3.0/24 192.168.12.1 0 100 0 100i可以看到三条172.16.x.0/24的明细路由,Path/Ogn显示100i,表示来自AS 100、起源为IGP。此时R2的路由表中维护了三条几乎同源的明细路由。如果上游设备很多,这个数量会被无限放大。
3.4 配置聚合并验证效果
现在在R1上配置手动聚合,把三个/24聚合为一个/16,同时使用detail-suppressed抑制明细通告,并加上as-set:
bgp 100 aggregate 172.16.0.0 255.255.0.0 detail-suppressed as-set配置完成后,在R1上查看BGP路由表:
display bgp routing-table你会看到BGP路由表中多了一条聚合路由,同时原来的三条明细路由仍然存在于BGP路由表中(detail-suppressed只是抑制向邻居通告,并不删除本地路由表中的明细):
BGP Local router ID is 1.1.1.1 Status codes: * - valid, > - best, d - damped, h - history Network NextHop MED LocPrf PrefVal Path/Ogn *> 1.1.1.1/32 0.0.0.0 0 100 0 100i *> 172.16.0.0/16 0.0.0.0 0 100 0 100i *> 172.16.1.0/24 0.0.0.0 0 100 0 100i *> 172.16.2.0/24 0.0.0.0 0 100 0 100i *> 172.16.3.0/24 0.0.0.0 0 100 0 100i关键看R2收没收到聚合路由。在R2上再次查看BGP路由表:
display bgp routing-table此时你会发现,三条明细路由都消失了,只剩一条聚合路由:
BGP Local router ID is 2.2.2.2 Status codes: * - valid, > - best, d - damped, h - history Network NextHop MED LocPrf PrefVal Path/Ogn *> 1.1.1.1/32 192.168.12.1 0 100 0 100i *> 172.16.0.0/16 192.168.12.1 0 100 0 100i这就是聚合的直接效果。原来三条明细变成一条聚合,R2路由表减少了2条路由。如果实际网络里有几十条、几百条连续网段,压缩效果会非常可观。
在R2上查看聚合路由的属性:
display bgp routing-table 172.16.0.0能看到它的AS_PATH等信息。由于三个网段都来自AS 100,Path/Ogn显示为100i。如果明细来自多个AS,这里就会显示方括号形式的AS集合。
3.5 自动聚合实验对比
再看华为的自动聚合。在R1上取消手动聚合,改用summary automatic:
bgp 100 undo aggregate 172.16.0.0 255.255.0.0 summary automatic你会发现一个现象:network宣告的三条明细完全没有被聚合。因为前面说过,summary automatic只对import-route方式引入的路由生效。如果想把实验做完整,可以再配一条import-route static:
bgp 100 import-route static这时172.16.x.0/24的明细会以import-route方式进入BGP,summary automatic就能把它们自动聚合成172.16.0.0/16。但由于自动聚合只能按照自然类掩码,无法精确控制聚合范围,实际工程中我很少直接用这个命令,手动aggregate才是主流方案。
4. 聚合后的故障场景与排查速查
4.1 黑洞路由:聚合网段内部有空洞
前面提过,不连续网段强行聚合会产生黑洞。这里给一个具体的实验场景:R1只有172.16.1.0/24和172.16.3.0/24两个网段,但聚合成了172.16.0.0/16。外部网络(R2)访问172.16.2.0/24时,R2只有一条聚合路由,它会把数据发给R1,但R1实际上没有172.16.2.0/24的出口,数据就被悄悄丢弃。
验证方法是:在R2上ping目标是不存在的172.16.2.1,观察丢包;然后在R1上使用tracert或抓包工具定位数据包是否被丢弃。
解决套路有三类:
- 网段规划时保证被聚合的地址块连续,不留空洞
- 在聚合设备上配置指向Null0的黑洞路由,让异常流量在本地直接丢弃,而不是在网络上漫无目的地绕圈
- 通过路由策略只对实际存在的子网做聚合通告,比如使用
suppress-policy精确控制哪些明细被抑制
华为设备上配置黑洞路由的命令:
ip route-static 172.16.0.0 255.255.0.0 NULL0这条路由能让匹配聚合网段但无具体出口的流量在本地被丢弃,避免更复杂的环路问题。
4.2 聚合环路:没有as-set很容易翻车
聚合环路是最容易踩的坑之一。假设AS 100内有两台边界路由器,BR1和BR2都配置了聚合。BR1从AS 200学到了一条明细路由,本地聚合后通告给BR2;BR2也做了同样的聚合,又把聚合路由通告回BR1。如果没有as-set,BR1收到BR2的聚合路由时,AS_PATH里看不到自己的AS号(因为聚合把AS_PATH弄丢了),就会把它当作一条全新的路由收下,路由信息就可能在两台路由器之间互相传递,形成路由环路。
判断方法很直接:查看BGP路由表,如果一条聚合路由的AS_PATH里看不到自己AS应有的位置,或者路由表条目反复翻动、AS_PATH不断变化,就要怀疑环路。用display bgp routing-table多次间隔查看,观察是否出现重复的聚合条目。
华为设备上查看路由翻动状态可以用:
display bgp routing-table statistic如果某条路由的翻动次数异常偏高,基本可以确认环路或路由震荡。
解决办法就是所有跨AS聚合一律携带as-set参数,并且在边界设备上配合BGP路由过滤,只接收期望的邻居通告路由。用as-path-filter也能过滤掉包含特定AS号的聚合路由,比如:
ip as-path-filter 10 deny ^100这样能防止来自自己AS的聚合路由被错误接收。
4.3 次优路径与属性调整
聚合路由的下一跳和属性继承规则有时候会“继承错对象”。当多条明细路由都被聚合时,聚合路由默认继承其中最优明细的路径属性。如果内部不同网段的最优出口路径不一致,聚合后外部访问某些网段就可能绕远路。
比如内部有两个运营商出口,网段A走出口1更近,网段B走出口2更近。聚合后只保留了一条聚合路由,外部流量全部流向同一个出口,网段B的流量就可能走次优路径。
这时候需要用attribute-policy给聚合路由单独设置路径属性:
route-policy AGG_ATTR permit node 10 apply med 200 apply local-preference 150 bgp 100 aggregate 172.16.0.0 255.255.0.0 as-set attribute-policy AGG_ATTR通过attribute-policy,可以手动调整聚合路由的MED、LocalPref、Community等,让选路结果更可控。origin-policy和suppress-policy则用来精确控制哪些明细路由参与聚合、哪些明细被抑制,灵活度更高。
4.4 排查命令速查
聚合相关的日常排障,我用得最多的命令就是下面这些:
| 命令 | 作用 |
|---|---|
display bgp routing-table | 查看BGP路由表全貌,确认聚合路由是否存在 |
display bgp routing-table 172.16.0.0 | 查看单条聚合路由的详细属性,包括AS_PATH、Origin、MED |
display bgp routing-table as-set | 查看所有携带AS_SET的路由,检查聚合路由的as-set情况 |
display ip routing-table 172.16.0.0 | 确认聚合路由是否进入IP路由表,用于判断黑洞和转发路径 |
display bgp peer verbose | 查看邻居状态和收发的路由数量统计 |
display bgp routing-table statistic | 查看路由总数和翻动统计,辅助判断环路和震荡 |
terminal debugging bgp update | 打开BGP更新报文调试(慎用,生产环境慎开,日志量极大) |
用的时候注意,display bgp routing-table看到的是BGP路由表,display ip routing-table看到的是全局IP路由表,两者有区别。聚合路由只有在下一跳可达或者有对应静态路由时,才会出现在IP路由表中参与转发。
5. 聚合策略设计的几个心得
5.1 在哪个设备上做聚合
聚合尽量在AS边界设备上做,而且越靠近明细路由的源头越好。源头聚合能让明细在最早期被归纳,后续传递过程中都不会膨胀。比如内部有很多子网需要对外通告,那么在出口路由器或者汇聚路由器上就完成聚合,不要让明细一路传到骨干节点。
内部核心设备之间不建议随意聚合。核心层本来就需要精确选路,如果过早聚合,内部路径优化反而变得困难。很多实际故障都是因为在一台不应该做聚合的设备上配置了聚合,导致对内对外路由全部失真。
5.2 聚合与路由策略配合
聚合的威力在配合路由策略时才能完全发挥。针对不同邻居,可以宣告不同粒度的路由:同一台设备对内部邻居保留明细,对外部邻居只通告聚合。用outbound方向的filter-policy就能实现:
bgp 100 peer 10.0.0.2 as-number 200 peer 10.0.0.2 route-policy EXPORT_AGG export在EXPORT_AGG里用if-match ip-prefix匹配聚合路由,放行它,抑制明细路由。这样既保证了内部选路精确,又让外部路由表保持精简。
聚合还可以和Community配合使用。比如给聚合路由打上特定Community,在出口设备上识别这个Community来决定是否通告给某个邻居。这种设计在网络规模变大之后会非常实用。
最后再分享一个我在实际调试中积累的小技巧。配置聚合之前,先统计一下当前BGP路由表的明细条数:
display bgp routing-table | count聚合生效后再看一次,对比两条命令的输出结果,你就能直观量化这次聚合给路由表降了多少条。我在好几次网络优化项目里都这么干,汇报数据时直接用这个数字,比空口说“优化了性能”有说服力得多。
BGP聚合这个功能,用好了是路由表瘦身的神器,用错了也可能引发黑洞和环路。建议你在实验环境把这些命令和场景完整跑一遍,特别是as-set和detail-suppressed的区别、自动聚合和手动聚合的差异,亲手验证过才不会在真实网络里踩坑。