路由表条目数这件事,在中小规模网络里往往被忽略,等到某天突然发现邻居会话频繁中断、设备CPU飙高、内存吃紧,才开始回头翻配置。BGP手动聚合就是在这个节点上被重新捡起来的手段。它解决的问题很朴素:把一堆连续的明细网段合并成一条覆盖范围更大的路由,对外只宣告这一条。但真到动手时你会发现,同样叫聚合,发不发明细、AS_PATH怎么处理、属性继承谁的、掩码边界算到哪,每一个选择都会直接影响对端的转发路径。这篇内容围绕BGP手动聚合展开,把自动聚合和手动聚合的边界划清楚,把四种发布形态的代价讲透,把AS_SET、原子聚合、Aggregator这些属性账算明白,再结合几个实际踩过的坑给出完整的排查链路和设计取舍建议。适合已经能配BGP邻居、想进一步控制路由规模与稳定性的网络工程师,也适合正在做地址规划、需要提前想清楚聚合边界的规划人员参考。
1. 先把“手动聚合”和它的自动版本彻底分开
很多人第一次接触BGP聚合,是在设备上看到一条默认开着的auto-summary,顺手配了个主类网络的宣告,发现邻居那边条目数确实少了,就以为聚合这事已经完成。等到后来地址段变复杂、需要聚合一个 /22 或者一个非自然类边界的前缀时,才发现自动聚合根本不听使唤。
1.1 自动聚合为什么在现代网络里基本等于废的
自动聚合(auto-summary)的核心逻辑只有一个:把明细路由按有类网络边界归并。也就是说,10.10.1.0/24、10.10.2.0/24 这些前缀,在自动聚合的作用下会被强行归到 10.0.0.0/8 这个A类主网里去。这个行为诞生于有类路由时代的末期,那时候地址分配本身就有类,归并到主类网络是符合地址规划逻辑的。
问题在于,现在的地址规划早就没有“类”的概念了。你从地址池里拿到一段 10.10.0.0/16,隔壁部门拿到的是 10.20.0.0/16,如果双方都开自动聚合,对外宣告的都是 10.0.0.0/8,那这条路由到底代表谁的地址、流量该往哪送,完全失去了意义。更麻烦的是,自动聚合不给你任何细节控制手段——你不能指定“只抑制这几条明细”“这条明细保留别抑制”“聚合路由带上AS_SET”。它就是一个开关,开了就按主类归并,关了就什么都不做。
我在实际环境里见过不止一次这样的事故:某台边界设备上auto-summary是默认开启的,本来只打算宣告自己的一段 /24,结果设备把它归并成了主类网络发出去,对端收到后把整个大段的流量都指了过来,本端出口被打满,而对端也没意识到问题出在一条聚合路由上。排查的时候大家盯着明细路由的下一跳、盯着策略,唯独没想到去看这条主类网络是怎么冒出来的。
所以行业里的共识很明确:只要你的地址规划不是严格的按类划分,自动聚合就必须关掉。手动聚合才是真正能用的工具,因为它允许你指定任意掩码长度,并且对聚合之后发布什么、抑制什么有完全的掌控权。
1.2 手动聚合真正控制的是三件事
把手动聚合拆开看,它其实只管三件事,但每一件都足以改变网络的转发行为。
第一件是聚合的边界。你告诉设备“把 10.10.0.0/24 到 10.10.3.0/24 这四条合并成 10.10.0.0/22”,设备就会检查自己的BGP路由表里是否存在这些明细,存在就生成一条聚合路由。这个掩码是你亲手算的,不是设备按类推的,这就意味着覆盖范围完全可控。
第二件是明细的去留。聚合路由生成之后,原来那些明细还发不发出去?这就是后面要详细讲的几种形态:全发、全抑制、选择性抑制。发与不发,决定了对端看到的是一个“概览”还是“概览加地图”。
第三件是属性的传递。明细路由上的AS_PATH、MED、Origin、Community,聚合之后怎么合并成一条路由的属性,这直接关系到对端的选路结果和防环能力。这也是最容易出问题、文档里又最语焉不详的地方。
这三件事想清楚了,聚合配置本身其实就几行命令。想不清楚,配完之后路由是有了,但流量跑不对,排查起来比重配一遍痛苦得多。
2. 四种发布形态:聚合路由怎么发,明细留不留
聚合命令在不同厂商的命令行里长得不太一样,但核心参数是相通的。以常见的两种风格为例,一种是aggregate ipv4-address { mask | mask-length } [as-set] [detail-suppressed] [suppress-policy ...]这一路,另一种是aggregate-address address mask [as-set] [summary-only] [suppress-map ...]这一路。下面统一用通用写法描述,你在自己的设备上对照关键字即可。
2.1 默认形态:聚合加明细,最安全也最不省
只敲聚合前缀、不带任何抑制参数,设备会同时发布聚合路由和所有明细路由。这是最保守的形态。
它的好处很直接:对端既能拿到汇总的概览,也能拿到每条明细的精确路径。如果网络是多出口的,或者明细路由之间存在不同的下一跳、不同的MED,这种形态能保证对端的选路信息不丢失。转发层面出现问题时,明细路由还在,回退的余地大。
缺点也同样直接:条目数一条没省。你只是额外多了一条聚合路由,邻居的BGP表反而多了。如果做聚合的初衷是压住路由规模,这个形态等于白做。
这种形态真正的用武之地是过渡期。比如你要从“全明细”切到“只发聚合”,中间必然要经历一个验证阶段:先让对端同时看到聚合和明细,观察对端是否优先选了明细、转发路径是否正常;确认无误之后再加抑制参数,把明细收掉。这个渐进的过程能避免一次性切断回退路径。
2.2 只发聚合:最省条目,也最容易制造绕路
加上抑制参数(有的实现叫detail-suppressed,有的叫summary-only)之后,设备只对外发布聚合路由,所有落在这个聚合范围内的明细全部被抑制。抑制是对本设备的对外宣告生效,不删除本地路由表里的明细,本机转发时依然按明细走,这点要分清楚。
这是大多数人在做路由瘦身时想要的形态:原本要发 200 条明细,现在只发 1 条,邻居的BGP表、内存、UPDATE消息量全部降下来。在明细路由大量抖动(比如接入侧链路频繁up/down)的场景里,抑制还能顺带吸收抖动——只要聚合范围内还有至少一条明细存在,聚合路由就稳定不动,对端感知不到内部的变化。
代价是路径信息被压缩成了一条。假如聚合范围内有两条明细分别从两个不同的出口学到,一条走A运营商、一条走B运营商,抑制之后对端只能看到一条聚合路由,它从你这里学到的下一跳只有一个。原本可以走B出口的流量,现在全部挤到A出口。这种次优在多出口场景里非常常见,而且因为对端“有路由、能通”,故障表征很隐蔽,只是延迟变高、带宽跑不满,很难第一时间怀疑到聚合头上。
2.3 选择性抑制:用抑制策略给明细留活口
既要省条目又不想丢失关键路径信息,折中手段就是选择性抑制:用一个策略(route-policy、route-map、suppress-map)匹配明细路由,命中的才抑制,没命中的继续发布。
典型用法是这样的:聚合范围内大部分明细都是普通的接入网段,路径信息意义不大,全部抑制;但其中某几条明细是双出口的、或者承载了需要区分路径的业务,就单独把它们从抑制列表里排除出去。
配置上的思路大致如下,以一个策略匹配为例(伪代码,具体语法按设备适配):
# 定义一条"需要保留明细"的策略 route-policy KEEP-DETAIL permit node 10 if-match ip-prefix KEEP-LIST ip ip-prefix KEEP-LIST index 10 permit 10.10.2.0 24 ip ip-prefix KEEP-LIST index 20 permit 10.10.3.0 24 # 聚合时引用抑制策略 bgp 65001 aggregate 10.10.0.0 255.255.252.0 suppress-policy KEEP-DETAIL这里要注意一个很容易搞反的点:不同实现对策略的语义定义不完全一致。有的实现里,策略命中的是要被抑制的路由;有的实现里,策略命中的是要保留、不被抑制的路由,没命中的才被抑制。名字上叫 suppress-policy,直觉上应该是“命中就抑制”,但实际配置前一定要在实验环境验证一遍,或者翻一遍对应版本的配置手册确认语义。我见过有人在生产环境直接按直觉配,结果把该保留的明细抑制了、该抑制的全发出去,聚合做完等于反向操作。
2.4 生成但不发布:先聚合再加工的用法
还有一类用法比较进阶:设备上生成聚合路由,但暂时不发布,或者发布之后再用属性策略加工一遍。典型场景是配合as-set生成带AS集合的聚合路由,或者用属性策略调整聚合路由的MED、Community,让它和明细路由产生区分。
实现手段通常是先在BGP里配聚合、然后通过出方向的策略对聚合路由做过滤或改属性。有些实现提供了专门的属性策略参数(类似attribute-policy),可以直接在聚合命令上挂策略,改聚合路由的属性,比在出方向策略里匹配前缀再改要清晰得多。
这种形态适合在大型网络中做聚合的分层治理:核心层做一次粗聚合,汇聚层做一次细聚合,每一层都用属性策略给聚合路由打上不同的Community标签,便于在上层做策略路由或者流量调度。前提是你对整网的地址规划和策略体系有清晰的把握,否则标签打多了没人记得住每个标签对应什么含义。
3. AS_PATH、原子聚合这些属性账,不算清楚就要出事
聚合最难的部分不在命令,在属性。一条聚合路由的属性不是凭空生成的,它是从参与聚合的多条明细里“继承”和“合并”出来的,而不同的合并规则会直接影响对端的选路和环路防护。这一块厂商实现差异不小,下面讲的是通用原则和需要重点确认的几个点。
3.1 as-set 生成的大括号到底值不值
默认情况下,聚合路由的AS_PATH属性来自参与聚合的明细路由中最优的那一条。这意味着一个问题:假如明细路由来自多个不同的AS,聚合之后只有最优那条的AS_PATH被保留下来,其他AS的路径信息全丢了。
后果有两个。一是防环能力下降:如果聚合路由被发回某个它其实已经经过的AS,因为AS_PATH里没有那个AS号,对端不会认为这是环路,可能形成环路或次优。二是回流流量的路径不真实:对端做AS_PATH长度比较时,只看得到被保留的那一条路径的长度,判断依据不完整。
加上as-set参数之后,聚合路由的AS_PATH会变成一个AS集合,形如65001 {65002 65003 65004},把所有参与聚合的明细携带的AS号都收进大括号里。这样既保留了防环信息,又在AS_PATH长度比较时只把集合算作1跳(不同实现的计算方式可能略有差异,但通常按长度1计),不会因为聚合一堆路由就把路径长度算得特别长。
这个参数在单归接入的环境里几乎用不着,因为明细的AS_PATH都差不多。但在多归或者有多个下游AS的场景里,它基本是必配的。不配的话,聚合路由发出去之后对端无法判断真实路径,回流流量可能绕路,甚至在某些拓扑下出现环路。
需要提醒的是,as-set不是只有好处。它会让聚合路由的UPDATE报文变大(AS集合里塞的AS号越多,报文越长),也会让对端在排查路径时看到一串集合,可读性下降。更关键的是,如果参与聚合的明细路由的AS_PATH差异非常大,生成的AS集合可能包含几十个AS号,这个属性一旦被传递到很远的邻居那里,会带来额外的处理和排障成本。
3.2 Atomic Aggregate 与 Aggregator:给下游一个“信息不全”的免责声明
当一台设备做聚合时丢失了AS_PATH信息(也就是没配as-set),标准里规定它应该在UPDATE里携带ATOMIC_AGGREGATE(原子聚合)这个公认酌情属性。这个属性的含义翻译成人话就是:“这条路由我聚合过了,路径信息不全,你后面别拿它的AS_PATH长度当绝对依据。”
配套的还有AGGREGATOR属性,用来标识是谁做的聚合,里面带聚合设备的Router ID和AS号。这个属性在排查时非常有用——当你看到对端收到一条可疑的聚合路由,想知道是哪台设备聚出来的,直接看AGGREGATOR字段就能定位到源头。
这两个属性在实际排障中的价值往往被低估。我曾经遇到过一个案例:两个AS之间的流量走向和预期不符,对端做AS_PATH比较时认为某条路径更短,但实际那条路径是聚合出来的,真实路径要经过三个AS。抓包之后在UPDATE里看到ATOMIC_AGGREGATE标记,才意识到对端的选路依据本身就不完整。这种情况下,要么调整聚合方式(加as-set),要么在对端用Local Preference之类的属性做覆盖,让选路不依赖AS_PATH长度。
需要注意的是,这两类属性的携带行为在不同实现、不同版本之间可能存在差异。有的实现在没有任何路径信息丢失时就不带原子聚合标记,有的实现只要有聚合动作就带。做跨厂商对接时,这一块建议直接抓包确认,别只看配置文档。
3.3 明细属性不一致时,聚合路由继承谁
这是聚合里最模糊、也最容易产生预期之外结果的部分。参与聚合的多条明细路由,各自的MED、Local Preference、Origin、Community可能都不一样,聚合之后只会生成一条路由,那这条路由的这些属性取谁的值?
通用原则大致是这样:AS_PATH一般取参与聚合的最优明细的属性(或者配了as-set之后生成集合),MED、Local Preference这类度量属性在不同实现里规则不同,有的取最小值,有的取最优明细的值,有的干脆不聚合属性差异过大的路由。Origin属性同理,如果明细里有IGP来源的、也有INCOMPLETE来源的,聚合路由的Origin取哪个,不同实现给出的答案可能不一样。
这就带来一个很实际的问题:属性差异大的明细路由,最好不要放进同一个聚合里。比如一条MED是100的明细和一条MED是500的明细,如果被聚合成一条,聚合路由的MED无论是取100还是取500,都会让其中一部分流量的路径判断失真。与其事后调策略去纠偏,不如在规划阶段就把属性相近的明细归到一组,分多个聚合前缀来发。
下面这张表是我在排查时常用的一张对照,用来快速判断聚合路由的属性来源是否合理:
| 属性 | 常见继承规则 | 需要重点确认的点 |
|---|---|---|
| AS_PATH | 取最优明细的路径;配as-set后生成AS集合 | 是否丢失了关键AS号,防环是否还成立 |
| MED | 各实现差异较大,常见为最小值或最优明细值 | 多出口场景下是否导致流量集中 |
| Local Preference | 通常取最优明细的值 | 本AS内部是否会出现次优出口 |
| Origin | 常见为取最优明细或统一置为INCOMPLETE | 对端若有origin偏好策略会受影响 |
| Community | 一般不自动继承,需用属性策略显式添加 | 上层流量调度标签是否还在 |
| Next Hop | 聚合路由的下一跳为本设备的对等体地址 | 是否需要next-hop-self配合 |
这张表不是标准答案,是提醒你每一项都要在自己的设备上验证一遍。做法很简单:搭两台设备、造几条属性不同的明细路由、配聚合、抓包看生成的聚合路由属性,一次就能把你这套设备的实际行为摸清楚,比翻十份文档都管用。
4. 五个真实踩过的坑与它们的排查链路
聚合的坑有个共同特点:配置敲下去不报错,路由也确实出来了,问题往往在流量层面才暴露出来,而且症状五花八门。下面这几个是我印象最深的,每一个都附上从发现到定位的完整链路,方便你按同样的思路复现。
4.1 坑一:掩码算错,把别人的地址段吸进了自己家
现象是某段时间内,本端出口的入向流量突然涨了一大截,但没有对应的业务增长。查对端路由表,发现对端把一大片不属于本端的地址段的流量都指了过来。
根因在聚合的掩码边界。假设本端实际拥有的地址是 10.10.0.0/24、10.10.1.0/24、10.10.2.0/24 这三条,另外 10.10.3.0/24 是隔壁部门在用。为了把三条合成一条,有人算了个 10.10.0.0/22 出来——这个前缀确实覆盖了那三条,但它同时把 10.10.3.0/24 也覆盖进去了。聚合路由一发出去,对端就会认为 10.10.3.0/24 的流量也该走本端。
这个错误的隐蔽性在于,内部看一切都是通的,因为内部有 10.10.3.0/24 的明细路由(从别的设备学到的),转发不受影响。只有在对端才会表现为流量错向,而且如果对端也有 10.10.3.0/24 的明细路由,它可能会按最长匹配选明细,问题暂时不暴露;一旦对端的明细消失,流量立刻涌过来。
防范的要点只有一个:聚合前先把聚合前缀的覆盖范围算清楚,逐条核对范围内每一个 /24 是否都属于自己。10.10.0.0/22 覆盖的是 10.10.0.0 到 10.10.3.255 这1024个地址,一个都不能少算。做不到连续覆盖,就不要硬聚,宁可把这三条分成 10.10.0.0/23 加 10.10.2.0/24 两条发出去。
顺带提一句,聚合路由还有一个“次生风险”:如果对端因为聚合路由把流量送过来了,而本端的明细路由恰好失效,聚合路由本身还在(因为它靠的是别的明细支撑),那这部分流量就会在本端形成黑洞。所以配完聚合之后,建议在边界设备的入方向加上基于明细的ACL或者uRPF校验,挡住不属于自己的源地址。
4.2 坑二:明细都在,聚合路由就是不出来
配置敲完了,show一看聚合路由没生成。第一反应是命令配错了,反复检查前缀和掩码,都没问题。
这种情况我遇到的原因大致有几种。最常见的是明细路由不在BGP表里。聚合命令生效的前提是设备自己的BGP路由表中存在属于该聚合范围的、被优选的路由。如果你只是把网段宣告出去了(network),但对应的IGP路由没起来;或者明细是从邻居学来的但被入方向策略过滤了,聚合就不会触发。检查方法很直接:查BGP表,看范围内的明细是否处于优选状态,状态码里带*>的才是被优选的。
第二种原因是明细路由的来源不符合聚合的触发条件。有些实现对参与聚合的路由来源有要求,比如只认可从EBGP学到的、或者只认可本设备originate的。这种情况下需要看设备的具体实现规则。
第三种原因比较隐蔽:明细路由的掩码长度和聚合前缀不匹配。例如聚合写成了 10.10.0.0/22,但实际的明细是 10.10.0.0/24 和 10.10.2.0/24 这两条,中间缺了 10.10.1.0/24 和 10.10.3.0/24。这种情况下聚合路由有时候能生成、有时候不能,取决于实现是否要求范围内所有子网都齐全。稳妥的做法还是让明细覆盖完整。
排查顺序建议固定下来:先看BGP表里有没有明细、再看明细是否优选、再看聚合命令的前缀是否精确、最后看设备日志有没有相关告警。这个顺序能覆盖绝大多数情况。
4.3 坑三:前缀列表精确匹配,聚合路由被自己家门口拦下
这个坑我认为是最值得单独拎出来讲的,因为它属于“聚合本身没问题,但被周边配置干掉了”的典型。
场景是这样的:本端在出方向配了一条前缀列表,用来控制向邻居发布的路由,列表里逐条写了要发的明细前缀,比如只允许 10.10.0.0/24 到 10.10.3.0/24 这四条。聚合配好之后,设备生成了 10.10.0.0/22 这条聚合路由,但它不匹配前缀列表里的任何一条,于是被出方向策略直接deny掉。结果就是:本端认为聚合发出去了,对端一条都没收到。
排查的时候容易走弯路,因为你在本端查BGP表能看到聚合路由,查邻居的advertised-routes却看不到。有人会怀疑是聚合没生效,反复折腾聚合命令,实际上问题在策略。
解决办法有两个方向。一是修改前缀列表,把聚合前缀加进去,比如增加一条permit 10.10.0.0 22。二是把前缀列表的匹配方式从精确匹配改成范围匹配(ge/le参数),让聚合前缀也能命中。两种方式各有适用场景,前者更精确但需要维护,后者省事但权限放得宽。
我的习惯是:聚合和前缀列表必须成对设计。配聚合的时候,同时把出方向策略过一遍,确认聚合前缀、明细前缀、抑制之后的实际发布集合,三者对得上。有条件的话,配完之后直接看邻居的advertised-routes,那才是真正发出去的东西,比看本地BGP表靠谱。
4.4 坑四:明细全消失,聚合跟着撤,对端开始抖
聚合路由的生命周期是跟着明细走的:范围内只要还有至少一条明细存在,聚合路由就在;所有明细都消失了,聚合路由随之撤销。这个设计本身是合理的,但在某些场景下会带来麻烦。
比如本端的接入网段是通过动态路由学来的,某次链路故障导致整个聚合范围内的明细全部消失,聚合路由瞬间撤销,对端收到撤销消息,把这片地址的流量全部切走。等链路恢复,聚合路由重新生成,流量再切回来。一次故障引发两次全局路由变化,对端如果有多条路径,可能触发路由震荡和收敛风暴。
解决办法是用一条静态的汇总路由做支撑。思路是在本端配置一条指向null0的静态汇总路由,然后用network或者重分发的方式把它注入BGP,让它和聚合路由形成“双保险”。这样即使所有明细消失,汇总路由还在,对外的宣告不会中断。
# 用静态黑洞路由兜底,保证汇总前缀始终存在 ip route 10.10.0.0 255.255.252.0 null0 # 在BGP中宣告该汇总路由 router bgp 65001 network 10.10.0.0 mask 255.255.252.0 aggregate-address 10.10.0.0 255.255.252.0 summary-only这样配的副作用是流量黑洞:明细全消失的时候,汇总路由还活着,对端依然会把流量送过来,而本端只有一条指向null0的路由,流量直接被丢弃。这是有意为之的取舍——宁可丢包也不要引发全局路由震荡。如果你不能接受丢包,那就不要用这个方案,接受聚合路由随明细撤销的行为,同时在对端做好快速收敛的配置。
这个技巧在很多大型网络的边界上都能见到,但用之前一定要和业务方确认清楚:短时间内丢包和全网路由震荡,哪个代价更小。
4.5 坑五:AS_SET导致邻居选路偏好改变
配了as-set之后,聚合路由的AS_PATH从一条普通路径变成了带集合的路径。表面上只是多了几个AS号,实际上可能改变邻居的选路结果。
原因是AS_PATH长度在BGP选路里是排在前面的比较项。一条普通路径65001 65002长度是2,一条带集合的路径65001 {65002 65003}长度也是2(集合算1)。如果邻居原本因为路径长度较短而优选了某条路由,聚合之后长度计算方式变了,优选结果可能就翻过来了。更麻烦的是,不同实现对AS_SET的长度计算规则不完全一致,跨厂商对接时这种差异会被放大。
我处理这类问题的思路是:不要依赖AS_PATH长度做精细选路。如果你的网络里有明确的出口偏好需求,用Local Preference或者MED去做,这两个属性是本地可控的,不会因为对端的聚合行为而改变。AS_PATH长度留给它该干的事——粗粒度的路径比较和防环。
排查这类问题时,抓包看UPDATE里的AS_PATH是最直接的手段。把聚合前后的报文对比一下,看看集合里到底塞了哪些AS、长度怎么算的,答案一目了然。
5. 从本端BGP表到对端转发面的排查顺序
上面几个坑讲完,你会发现排查的路径其实高度重合。这里把它整理成一个固定的顺序,遇到聚合相关的异常按这个顺序走,能少走很多弯路。
5.1 本端三看
第一看明细。查BGP路由表,确认聚合范围内的明细路由存在且处于优选状态。命令上,通用写法是查BGP表并按前缀过滤,看状态码。优选的路由会带*>标记(不同实现的符号略有差异),只有优选的路由才能触发聚合,也只有优选的路由属性才会被聚合路由继承。
第二看聚合。查聚合路由本身是否存在、它的属性是什么、AS_PATH里有没有你预期的集合、Next Hop是不是自己。有些实现会在聚合路由上打一个特殊标记(比如s表示suppressed、S表示summary),对照文档确认这些标记的含义,别把正常的聚合标记当成异常。
第三看抑制。查明细路由的状态,被抑制的明细会带上抑制标记。确认抑制的范围是不是你想要的:该抑制的抑制了没有,该保留的保留了没有。这一步最容易发现“策略语义搞反”的问题。
三条命令大致长这样(按通用风格示意,具体按设备适配):
# 查聚合范围内的明细及状态 show bgp ipv4 unicast 10.10.0.0/22 longer-prefixes # 查聚合路由本身的属性 show bgp ipv4 unicast 10.10.0.0/22 # 查邻居实际收到的路由(更接近真相) show bgp ipv4 unicast neighbors 192.0.2.1 advertised-routes最后那条命令是重点。本端BGP表里有聚合路由,不代表它就发出去了,出方向策略、邻居的地址族配置、能力协商都可能影响。看advertised-routes才是看“真正发了什么”。
5.2 对端两查
一查是否收到。在对端查邻居的received-routes,确认聚合路由到了。如果没到,问题在本端的发布侧;如果到了,问题在接收侧的选路。
二查是否优选。收到了不等于用上了。查对端的BGP表,看这条聚合路由是否处于优选状态,以及它和明细路由、其他来源的路由之间的比较结果。这里要特别关注:如果对端同时从多个邻居收到了覆盖范围重叠的聚合路由,最长匹配原则会让更具体的那条胜出,聚合路由可能被明细压住,导致你以为聚合生效了其实没生效。
这一步还有一个容易忽略的点:对端的转发表。BGP表优选了,不代表装进了转发表。有些设备在FIB资源不足的时候会丢弃部分路由,或者下一跳不可达导致路由不装载。查一下转发表里有没有对应条目,能省掉很多扯皮。
5.3 量化验证:UPDATE数量和条目数
定性排查做完,建议再做一次量化验证,用数据说话。
| 观察项 | 聚合前 | 聚合后 | 说明 |
|---|---|---|---|
| 邻居BGP表条目数 | 记录基线 | 应显著下降 | 下降幅度≈明细条数减1 |
| 邻居收到的UPDATE消息数 | 记录基线 | 应下降 | 尤其在新会话建立时明显 |
| 内存占用 | 记录基线 | 应下降 | 大规模网络里差异明显 |
| 收敛时间 | 记录基线 | 可能变长或变短 | 变长要查原因,变短是聚合吸收抖动的效果 |
| 出方向报文大小 | 抓包看 | 单条UPDATE变大但总数变少 | as-set会显著增大单条报文 |
抓包这一项很多人嫌麻烦跳过,但它在跨厂商对接时的价值极高。抓一次BGP会话建立的完整过程,在Wireshark里过滤bgp.type == 2只看UPDATE,把聚合路由的UPDATE展开,AS_PATH、ATOMIC_AGGREGATE、AGGREGATOR、MED这些字段一目了然。这份抓包文件在后续扯皮的时候也是最好的证据。
# 抓BGP会话,只抓握手和更新,控制文件大小 tcpdump -i eth0 -n -s 0 'tcp port 179' -w bgp-aggregate.pcap6. 设计层面的取舍:聚合点、地址规划与不该聚合的场景
配置层面的问题解决了,剩下的就是设计。聚合这件事,配错了能改,设计错了要动地址规划,代价完全不在一个量级。
6.1 聚合点放在哪一层
聚合点的位置直接决定了聚合的效果和风险。放在太靠下的位置,聚合范围小、省不了多少条目;放在太靠上的位置,聚合范围大、路径信息压缩得厉害,出问题的爆炸半径也大。
我的经验是分层聚合:接入层不做聚合,让明细尽量完整地上传;汇聚层做第一级聚合,把同一片区域的连续网段合并;核心层或者边界做第二级聚合,把汇聚层的聚合结果再合并一次。每一层聚合的粒度不同,出问题时可以逐层往上排查,定位范围小。
需要特别注意的是路由反射器上的聚合。反射器本身不参与转发,但它反射的路由会影响一大片Client。如果在反射器上做聚合,聚合路由的属性继承的是反射器看到的最优路由,而反射器可能并没有完整的路径信息。这种配置在很多环境里是默认做法,但要清楚它的局限:聚合之后的路径判断依据变弱了,防环更多依赖as-set和人工规划。
6.2 地址规划倒推聚合边界
理想情况下,聚合边界是在地址规划阶段就定好的,而不是网络跑起来之后回头补。规划的原则很朴素:按聚合单元分配地址。
举个例子,如果计划在某个汇聚点做 /20 的聚合,那么给这个汇聚点下挂的所有网段就应该从这个 /20 里连续分配,不要跨 /20 边界去用别的池子。这样后续配聚合的时候,前缀一算就出来,覆盖范围天然对齐,不存在“掩码算错把别人地址吸进来”的问题。
反过来,如果地址是零散分配的,比如一个汇聚点下有 10.10.5.0/24 和 10.20.8.0/24 两段,这两个根本聚不到一起(除非用一条极粗的 /8 把它们都包进去,那显然不可接受),你就只能分别发两条,聚合的意义大打折扣。这种情况下不如老老实实发明细,配合前缀列表做控制。
我在做地址规划评审时有一个固定问题:“这个网段未来三年内会不会和别的部门的地址合并成一个聚合前缀?”如果答不上来,说明规划还没做到位。这不是吹毛求疵,聚合做不了往往就是规划阶段留下的债。
6.3 三类不适合做聚合的场景
有几类场景,聚合看起来能用,实际上不该用。
第一类是Anycast场景。多个站点宣告同一个地址段,靠BGP的路径选择把流量分配到最近的一个。这种场景下如果做聚合,聚合路由会把多个站点的宣告合并成一条,Anycast的“就近接入”直接失效,所有流量都跑到一个点去了。Anycast地址必须精确保留明细,一条都不能聚合。
第二类是需要精确路径控制的多出口场景。如果你的网络靠BGP属性做出口选择,不同的明细走不同的出口,那聚合会把这种区分抹平。除非你能保证聚合范围内的所有明细路径完全一致,否则不要聚合。实在要聚合,就用前面说的选择性抑制,把关键的明细保留下来。
第三类是流量清洗、黑洞路由相关的地址段。这类地址的路由宣告往往带有特殊的Community标记,聚合之后标记可能丢失,导致清洗策略失效。这类地址建议单独维护,不进聚合。
最后分享一个我在实际操作中的体会:聚合配置一定要留注释,而且要留到能看懂的程度。过半年再回来看,aggregate 10.10.0.0 255.255.252.0 detail-suppressed这一行看不出为什么要这么配、覆盖的明细是哪些、有没有配套的前缀列表和静态兜底路由。我的做法是在配置旁边写清楚:覆盖哪几个网段、抑制了哪些、保留哪些、配套的静态汇总路由在哪、相关部门是谁。这行注释在交接和排障时省下来的时间,远超写它的那几分钟。