说个真实的场景。某公司网络改造,内网OSPF跑得四平八稳,所有业务部门都夸网络稳定。结果一接触运营商专线,立刻翻车——对方根本不跟你谈OSPF区域,只丢过来一句话:“我们这边起BGP,边界网关协议,给你一个AS号,你自己把路由送过来”。那一刻我才真正意识到,IGP管得了内部,管不了边界;BGP才是跨自治域的唯一通行证。这篇文章就是想把我这些年折腾BGP和IGP关系的经验完整写出来,给所有刚摸到边界路由的工程师一份可以直接上手的参考。
1. 为什么全网路由不靠OSPF,非要用BGP来兜底
1.1 IGP的算路逻辑为什么不适合跨自治域
先看IGP的设计初衷。OSPF、IS-IS这类链路状态协议,核心思路是整个域内所有路由器共享一份一致的链路状态数据库,然后每台设备独立跑SPF算法,算出一棵以自己为根的最短路径树。这个模型在单一园区、单一数据中心里非常好用,因为域内拓扑变化是可控的,洪泛链路状态的开销也可控。
可一旦把视角放到整个互联网,这套逻辑就崩了。互联网是几千上万个独立管理域互相连接的网络,每个域有自己的路由策略、自己的成本考量,没有任何一个组织能让全网路由器共享同一份链路状态数据库。你不可能让某家运营商的内部拓扑细节洪泛到全网,也不可能让全网SPF算法收敛一次就等上几个小时。链路状态协议天生是一个“单域思维”的产物,它假设域内是可信的、透明的,而互联网恰恰是互相警惕的。
BGP换了一个思路:我不关心你内部怎么跑OSPF还是IS-IS,我只看你告诉我“你能到达哪些前缀,经过了哪些AS”。这种路径矢量模型,让每个AS只需要和邻居交换可达性信息,不需要知道邻居内部的任何细节。这也是BGP敢承担互联网路由的根本原因——它的信息模型天然尊重管理边界。
1.2 路径矢量协议:BGP看的是“路径”而不是“拓扑”
路径矢量和链路状态的区别,用大白话说:链路状态协议描绘的是“地图”,每个路由器都拿着完整的道路图自己规划路线;BGP描绘的是“路牌”,每个AS只告诉隔壁“顺着我这条牌子能走到哪里”。
BGP的路由更新里携带的是一串AS路径,比如“65001 65002 65003”,这条路径就是一块块路牌拼出来的。路由器收到这条路由,它不需要知道AS 65002内部长什么样,只需要知道“流量交给下一跳,下一跳会顺着这条路牌继续走”。这种设计带来的直接好处是策略极其灵活——每个AS可以随意增删路径、修改属性、选择要不要通告,完全可以基于商业关系做决策。
但也正因为如此,BGP学到的路由和实际转发路径之间存在一层“信任”关系。它不验证路径是否真的最优,只验证路径是否存在且无环。这也是后来很多BGP事故的根源——比如某条AS路径被人为“修饰”过,或者某台设备通告了不存在的明细路由。理解这一点,是理解BGP一切行为特征的前提。
2. BGP和IGP之间的“你中有我、我中有你”
2.1 IGP是BGP的“跑路基础”:下一跳递归解析
很多新手最大的困惑是:BGP学到的路由,怎么才能指导实际转发?答案藏在“递归下一跳”这四个字里。
BGP路由的下一跳,往往不是直连接口地址,而是一个环回口地址,甚至可能是隔着好几跳的地址。路由器是怎么处理的?它会把BGP下一跳丢进路由表里做递归查询,查这个下一跳地址通过哪条IGP路由可达,然后才把最终的出接口和下一跳写进FIB。换句话说,BGP告诉路由器“目标在哪里”,IGP告诉路由器“怎么迈出第一步”。
我见过太多故障都是这个环节出的问题:BGP表里有路由,路由表里也有BGP路由,但下一跳不可达,导致路由一直处于“非最优”状态,流量直接黑洞。所以做BGP设计时有一个铁律——所有BGP对等体的更新源地址(通常是环回口),必须通过IGP或者静态路由在全网可达。环回口一断,BGP会话再稳都是白搭。
2.2 BGP把路由交给IGP的时机与代价
另一个常见的疑惑是:BGP和IGP能不能互相倒腾路由?答案是原则上“能不倒腾就不倒腾”,但实际中很难完全避免。
先说不倒腾的场景。业务网段自然存在于IGP里,BGP侧只需要用network命令精确匹配这些前缀,就能把它们通告给对等体。这样IGP里是网络前缀,BGP里也是同一份前缀,两边各管一摊,互不干扰,这是最干净的设计。
再说不得不倒腾的场景。典型情况是数据中心出口:边界路由器从运营商学到的BGP默认路由,要不要下发到内网OSPF里?如果不发,内网设备无法访问互联网;如果发,就要小心路由震荡。BGP默认路由一旦抖动,OSPF全域都要跟着重算。我的处理办法是:边界路由器上配置一条静态默认路由指向运营商BGP邻居,再把这条静态默认路由重发布进OSPF,同时用路由策略加上tag,而不是直接把BGP路由甩进IGP。这样即使BGP波动,IGP侧的默认路由始终存在,数据流到了边界路由器再由BGP决策出方向。这是实战里很管用的隔离手段。
2.3 同步规则的旧恨与新解
老工程师嘴里常念叨的“BGP同步规则”,现在的主流实现已经默认关闭了,但它的历史教训值得提。早年的设计里,BGP要求:从一个AS学到的外部路由,只有在IGP里也存在相同前缀时,才能通告给其他AS。目的是防止一个AS内部某些路由器还没学会这条BGP路由时,就被迫做转发,造成中间黑洞。
听起来合理,后来为什么废了?因为全网路由都靠这条规则的话,AS内的IGP得承载全量Internet路由,IGP根本扛不住。后来业界意识到,只要AS内部跑iBGP全网状连接或者部署了路由反射器,就能保证每台需要转发路径的设备都具备BGP路由,同步规则就成了多余且有害的东西。现在新部署的网络基本都不开同步,但我建议年轻工程师还是把这段历史搞明白,面试时被问到“BGP为什么能和IGP解耦”,能不能答出这个逻辑,差距就在这里。
3. BGP选路到底看什么:从Weight到MED的属性博弈
3.1 本地属性:Weight与LOCAL_PREF
BGP选路不是看带宽,不是看延迟,而是看一堆属性按固定优先级排队。排在最前面的两个属性,只在本地路由器或本地AS内有效,理解它们的关键在于“作用范围”。
Weight是设备本地概念,最狠也最直接。它只影响本台路由器,不传给任何邻居。比如双出口场景,我想让去往某条前缀的流量全走运营商A,就在入接口上把Weight调高。Weight值越大越优,默认是0。因为它是本地的,调试方便,排障时第一条命令就是检查Weight。
LOCAL_PREF是AS内部传播的属性,它影响的不是“从哪个邻居进来”,而是“AS内的其他路由器更倾向于从哪里出去”。它是IBGP邻居之间传递的,对EBGP邻居既不改也不传。你想让整个AS的所有流量优先走某个出口,就往那个出口方向收到的路由上打高LOCAL_PREF。这个属性是出口选路最核心的调优手段。
3.2 全局属性:AS_PATH和MED的博弈
AS_PATH是BGP的“身份证”,它记录了路由经过的AS序列。选路规则是路径越短越优。这个属性非常透明,因为每次经过一个AS都会被追加进去。实际排障里最常见的问题是“为什么这条路明明更好走却没人走”——往往是AS_PATH被做了手脚。比如AS路径前置,故意在通告给邻居时多追加几个自己AS号,让邻居觉得这条路很远,从而把流量赶到别的出口去。这在多线接入的IDC场景里是控制入流量的标准手法。
MED和AS_PATH的位置不同,它只影响入方向流量,而且只在相邻两个AS之间传递,下一跳AS绝不会把这个属性再传给别家。MED的取值是越小越优,本质上是告诉邻居:“你从我这里进来的代价评估”。但这里有个坑——不同厂商处理MED的默认行为不一样,有的默认比较MED,有的默认忽略MED。跨厂商对接时一定要先确认双方的MED策略,否则你以为的优选路径根本不是实际结果。
3.3 完整选路决策链,我用一个表格讲透
BGP决策过程在主流实现里大约有十几步,但真正日常能影响结果的就这么几步,按优先级排列如下:
| 优先级 | 属性 | 作用范围 | 数值偏好 | 典型用途 |
|---|---|---|---|---|
| 1 | Weight | 仅本地设备 | 越大越优 | 单台设备出口偏好 |
| 2 | LOCAL_PREF | 本AS内 | 越大越优 | AS内统一出口策略 |
| 3 | 本地起源路由 | 本设备 | 优于学习路由 | network命令生成的直连/静态路由 |
| 4 | AS_PATH | 全局 | 越短越优 | 跨AS路径择优 |
| 5 | Origin类型 | 全局 | IGP优于EGP优于Incomplete | 路由来源可信度 |
| 6 | MED | 相邻AS之间 | 越小越优 | 入流量负载调整 |
| 7 | eBGP优于iBGP | 本设备 | eBGP优先 | 控制内部转发路径 |
| 8 | IGP metric | 本设备 | 越小越优 | 到BGP下一跳的内部开销 |
这个表格是排障的“章程”,遇到路由选择不符合预期,按表格从上到下逐项排查,多数问题都能定位。我自己的习惯是先把第二行LOCAL_PREF和第七行eBGP/iBGP差异查完,这两处出问题的概率最高。
4. eBGP和iBGP的分工,以及水平分割那条铁律
4.1 eBGP与iBGP的行为差异
BGP邻居分两类:跨AS的eBGP,AS内部的iBGP。两者行为差异远不止一个“跨域”这么简单,很多路由异常都源于混淆了这两类邻居。
先说最基础的差异。eBGP的TTL默认是1,只允许直连邻居建立会话,防止环路和资源消耗;iBGP的TTL默认是255,因为内部邻居之间可能隔着很多跳。再比如路由优先级,eBGP学来的路由管理距离默认是20,iBGP是200,这意味着eBGP路由天然比iBGP路由更“可信”,哪怕两条一样的路由,设备也会优先选eBGP那条。这个设计有它的道理——毕竟跨AS的路由经过了更多的策略博弈,而iBGP路由本质上是“内部转述”。
还有个必须牢记的点:eBGP会把收到的路由原样转发给自己的其他eBGP邻居,但iBGP不会把从某个iBGP邻居学到的路由再转发给另一个iBGP邻居。这就是iBGP水平分割规则,它的直接后果是AS内必须建立全互联或使用路由反射器。
4.2 iBGP水平分割为什么逼出了RR
iBGP水平分割的初衷是防环——路由在AS内部转来转去会形成环路。但它带来的最大成本是扩展性:假如AS内有10台边界路由器,iBGP全互联需要建立45对邻居;30台就要435对;100台直接到了4950对。会话数量指数级膨胀的后果不只是配置量大,每台设备的CPU和内存都扛不住。
于是有了路由反射器(RR)。RR允许把从iBGP邻居学到的路由反射给其他iBGP邻居,但会通过ORIGINATOR_ID和CLUSTER_LIST属性防止环路。RR的部署是BGP生产环境里必考的技能点。我建议至少设置两台RR做冗余,同时把RR的客户端部署在关键节点上,并且不要让RR成为单一故障点。有一个常见误区是RR只能做客户端路由的反射,实际上RR对非客户端邻居之间依然遵守水平分割,只有从客户端学到的路由才会被反射给其他客户端和非客户端。
4.3 next-hop-self:一个最容易忽略的小动作
iBGP有一条特性让所有新手都吃过亏:它从eBGP邻居学到的路由,传递给iBGP邻居时,默认不修改下一跳地址。什么意思?A边界路由器从运营商学了一条路由,下一跳是运营商那边的对端地址,它把这路由传给AS内部的B路由器时,下一跳依然写着运营商地址。B路由器做递归查询,发现这个地址根本不在IGP里——它不认路,路由直接变无效。
解决办法就是给iBGP邻居配置next-hop-self,强制本设备把自己设置为下一跳。这个操作非常不起眼,但漏掉它的后果是“路由表里明明有BGP路由,却怎么也ping不通远端”。我早期有一次排查这种问题整整花了半天,最后发现就是对端少配了next-hop-self。从那以后我每次建iBGP邻居都会顺手检查这一项。
5. 实战配置:在三台模拟路由器上跑通一个BGP双出口
5.1 环境拓扑与地址规划
纸上谈兵没意思,我用模拟环境搭一个最能暴露问题的拓扑:三台路由器,R1属于AS 65001,R2和R3属于AS 65002。R1通过两条物理链路分别连接R2和R3,模拟一个双出口的企业接入场景。R2和R3之间跑OSPF,模拟AS内部的IGP互通。
地址规划如下:
- R1的环回口:1.1.1.1/32,与R2互联地址10.0.12.0/30,与R3互联地址10.0.13.0/30
- R2的环回口:2.2.2.2/32,与R1互联地址10.0.12.0/30,与R3互联地址10.0.23.0/30
- R3的环回口:3.3.3.3/32,与R1互联地址10.0.13.0/30,与R2互联地址10.0.23.0/30
- AS 65002内部业务网段:192.168.0.0/24,挂在R2上作为模拟用户业务
这个拓扑的关键点在于R1和R2、R3分别建立了两个eBGP会话,而R2和R3之间又有iBGP会话。配置一遍就能把前面讲的eBGP/iBGP差异、下一跳问题全部踩一遍。
5.2 IGP先行:把底层跑稳
先把R2和R3之间的OSPF配置出来,确保AS 65002内部互访正常。注意两点:一是把环回口通告进OSPF,因为后面iBGP会话要用环回口做更新源,环回口不通,iBGP就起不来;二是把互联链路口都加进OSPF,保证物理链路可达。
配置思路如下(以主流厂商命令风格为例):
R2: interface Loopback0 ip address 2.2.2.2 255.255.255.255 ! router ospf 100 router-id 2.2.2.2 network 10.0.23.0 0.0.0.3 area 0 network 2.2.2.2 0.0.0.0 area 0 R3: interface Loopback0 ip address 3.3.3.3 255.255.255.255 ! router ospf 100 router-id 3.3.3.3 network 10.0.23.0 0.0.0.3 area 0 network 3.3.3.3 0.0.0.0 area 0配置完成后先验证OSPF邻居状态是Full,再互相ping对方的环回口。很多BGP问题其实都是IGP没跑通导致的,建立BGP邻居之前必须把底层验证到百分之百可用。环回口通了,iBGP会话的TCP连接才不会因为源地址不可达而反复重置。
5.3 BGP邻居建立与路由宣告
先配置R1与R2、R3的eBGP会话。eBGP用物理接口地址建邻居即可,不需要绕环回口,因为eBGP默认TTL只有1,邻居必须直连。
R1: router bgp 65001 neighbor 10.0.12.2 remote-as 65002 neighbor 10.0.13.2 remote-as 65002再配置R2的eBGP(指向R1)和iBGP(指向R3)。这里iBGP就必须用环回口了,因为R2和R3之间不是直连,中间有物理链路通过,也顺便验证一下更新源的重要性。R2配置如下:
R2: router bgp 65002 neighbor 10.0.12.1 remote-as 65001 neighbor 3.3.3.3 remote-as 65002 neighbor 3.3.3.3 update-source Loopback0 ! R3: router bgp 65002 neighbor 10.0.13.1 remote-as 65001 neighbor 2.2.2.2 remote-as 65002 neighbor 2.2.2.2 update-source Loopback0邻居起来以后,进入宣告阶段。在R2上宣告业务网段,注意network命令的语义是“匹配本地路由表中已存在的精确前缀”,不是随便搭个网段就能通告。这个语义是BGP和IGP最大的区别之一——IGP通告的是接口网段,BGP通告的是“我想让外部看到的前缀,并且这个前缀必须在本地路由表里能查到”。
R2: router bgp 65002 network 192.168.0.0 mask 255.255.255.0R1作为接收端,此时BGP表里应该有一条来自R2和R3的192.168.0.0/24路由。因为我们用了双出口,R1会收到两条相同前缀,一条来自R2直连邻居,一条来自R3直连邻居。选谁?如果所有属性都一样,eBGP路由之间比的就是到下一跳的IGP开销,但因为R1到R2和R3都是直连,两条等价,R1会同时保留两条作为等价路由,后续流量的转发决策就有多条路径可用。如果你的目的是主备,就得靠Weight或LOCAL_PREF去干预,这正是前面讲属性的实战意义。
5.4 验证与测试命令
邻居和路由都配完,一套标准验证命令就是排障的“体检报告”。从上到下逐条看:
show ip bgp summary:先看邻居状态,重点是Established数量,只要不是Established,后面都不用查了。show ip bgp:看BGP表里的前缀、下一跳、AS_PATH、MED等属性,这一步能确认路由“学没学到”。show ip route bgp:看BGP路由是否进入了全局路由表,并查看是否是“最优”标记。ping远端业务地址:最终验证。
我个人的习惯是每配置一台设备就做一次验证,而不是三台全配完再统一看。BGP配置错一个AS号或者错一个接口地址,排查成本远高于“配一台、验一台”。曾经帮某朋友处理过一次故障,BGP邻居始终起不来,命令行敲了半小时最后发现是把对端的AS号写错了,这种低级错误其实占了BGP排障里相当大比例。
6. BGP排错实录:状态机卡死、下一跳黑洞、路由翻车
6.1 邻居状态卡在Active/Idle的排查链路
BGP状态机从Idle转到Connect,再经过OpenSent、OpenConfirm到Established。生产环境里最常见的两个淤积点是Idle和Active。卡在Idle说明本地根本不想发起连接或者配置有问题,卡在Active说明TCP连接一直在失败,一直在重试。
排查链路我建议按以下顺序走:
- 先检查IP连通性,telnet对端的179端口通不通。
- 确认TCP包有没有被ACL或安全策略过滤,这是经常被忽略的坑。
- 核对对端AS号配置,两端必须互相匹配,错一个数字都起不来。
- 确认update-source配置,iBGP场景下源地址不对,对方回包无法识别。
- 检查最大前缀限制,部分设备配置了maximum-prefix以后,超限会自动关闭邻居。
- 看日志有没有TCP重传或BGP Notification,这些信息会直接告诉你原因在哪。
这套链路我走过太多遍,最讽刺的是第一项“ping得通吗”被跳过引起的故障最多。有人建邻居前不看基础连通性,直接配置BGP,发现状态卡住就开始怀疑协议配置,结果最后发现是对端接口down了。基础不查,后面全是无用功。
6.2 路由学得到但总是不优:下一跳陷阱
邻居状态Established,BGP表里也能看到前缀,但路由表里就是不认这条路,这是典型的“下一跳陷阱”。前面说过,BGP路由的下一跳必须递归到IGP可达才能生效。iBGP场景因为默认不修改下一跳,所以最容易踩。
我复盘过几个真实故障案例,都有同一个模式:AS内部一台非边界路由器从RR学来的路由,下一跳指向某台边界路由器的环回口,但这台设备上的IGP里根本没环回口这条路由,结果BGP表里路由永远不是最优,转发直接黑洞。解决办法有两条路:一个是给所有iBGP邻居配置next-hop-self,让下一跳收敛到本设备;另一个是确保边界路由器的环回口地址全部通告到IGP里。两个办法各有利弊,next-hop-self改动小,但会增加边界路由器的转发压力;IGP通告环回口更彻底,但要求环回口地址规划必须稳定,不能随意变动。
6.3 聚合、过滤、抖动:进阶场景的三道坎
BGP基础跑通以后,真正折磨人的是进阶场景。聚合是第一道坎,很多网络管理员喜欢用手动聚合把明细路由汇总成一条通告给上游,减少路由表体积。但聚合有一个必须处理的细节:路由集属性。如果聚合的路由来自多个AS,必须配置as-set特性,否则被聚合掉的明细路由的AS路径信息会丢失,可能导致上游选路混乱甚至环路。而且被聚合的明细一旦有抖动,聚合路由的震荡也难免,需要在聚合源头做好抑制策略。
过滤是第二道坎。生产环境里BGP路由过滤是一个高风险操作,最常见的问题是入方向过滤没做导致路由表被灌爆,或者出方向过滤太严格导致业务不可达。我的做法是双向都基于前缀列表或路由策略做白名单,入方向过滤“不该来的”,出方向只通告“必须给的”。过滤规则每一条都要有注释,否则半年后翻配置,没人看得懂那条拒绝规则当初是为了防什么。
抖动抑制是第三道坎。BGP的路由抖动会影响全网,所以有阻尼机制的配置空间。但这里有个反直觉的坑:阻尼虽然能抑制频繁震荡的路由,但在收敛性要求高的核心网里,过激的阻尼反而会导致恢复后的路由长时间处于抑制状态,业务恢复被延迟。后来很多厂商默认调低甚至关闭了部分场景的阻尼,就是因为这个副作用。真实生产环境里,比起依赖阻尼,更重要的是先把抖动的源头处理掉。
这三道坎每一道我都踩过,而且踩得很深。聚合丢属性和过滤误杀业务,这两个坑的恢复过程往往伴随着全网路由振荡,处理时特别考验心态。BGP这东西,配置只是入门,真正值钱的是对每一个属性、每一条规则背后设计意图的理解。边界网关协议这四个字,读起来像是一个“协议”,但用起来更像一套“江湖规矩”——你只有理解了每个AS的立场,才能理解它为什么要这样通告、为什么要那样选路。