
假设你在一家公司做网络,老板说:跟运营商那条线已经接通了,把我们的网段发布出去,也让员工能访问外面。你打开配置,第一反应可能是配个 OSPF。想了两秒又觉得不对——运营商凭什么让你看见它的拓扑?你又凭什么让它看见你的?这个"凭什么",基本上就是 BGP 存在的全部理由。
一、拿 OSPF 去连运营商,会出什么事
先说 AS。它指的是"同一个组织管理下、使用统一选路策略的设备集合"。
注意后半句。重点不是"同一个老板",是"同一套策略"。两个 AS 之间,策略很可能不一样,甚至是对着干的——你希望流量从这条链路走,隔壁希望它从那条走。这不叫故障,这叫商业。
而 OSPF、IS-IS 这 类 IGP,是为"自己家"设计的。它们干活的前提是:所有路由器都愿意把完整拓扑交出来。我告诉你有几条链路、连到哪儿、代价多少,你也告诉我。
这套逻辑在家里没问题,因为是一家人。跨 AS 就不成立了。
最直接的一条是拓扑泄密。你把 LSA 发给运营商,等于把内网结构、网段划分、设备互联全摊开了;反过来也一样。两家公司之间,这种事没人愿意干。
第二条是规模。全球互联网的 IPv4 路由现在接近一百万条,还在涨。让一台设备用 SPF 去算这么大的图,内存和 CPU 都顶不住。更难受的是收敛——任何一条链路抖动都要引发重算,这个代价放到跨域尺度上没法接受。
第三条更实际:IGP 建邻居要求直连。两家之间隔了好几跳,还得先架一条 GRE 隧道造个"逻辑直连"出来。这套路在运营商互联里根本不现实。
早年间确实有个专门跑 AS 之间的 协议 ,叫 EGP。但它设计得太简陋——只告诉你"这个网段我能到",不选路,也不防环。很快就顶不住了。BGP 就是来接替它的,1990 年前后开始在 RFC 1163 里引入路径属性这个概念,从此才有了"基于属性选路"这回事。
顺便说一句,BGP 不是只有运营商才碰。大型企业总部和几十个分支之间,如果每边都有多条出口线路,也常拿 BGP 跑——一个分支一个 AS,靠 BGP 互相通报。企业接运营商那一侧更是标准做法:用 BGP 从运营商拿到具体路由,而不是一条默认路由打天下。
默认路由当然省事。但你看不见外面到底通不通,也做不了"这条走电信、那条走联通"这种精细活。
二、BGP 说"最优",跟距离没关系
这是 BGP 最容易被 IGP 思维带偏的地方。
OSPF 选路只有一个指标:开销。谁小走谁。技术上很干净,但放到商业场景里就有点幼稚了。
举个例子。运营商 A 和 B 都能到某个网段,A 的路径短 10 毫秒,但 A 是按流量计费的,B 是包月的。你走哪个?
BGP 的答案是:不替你算,让你自己定。
所以每条 BGP 路由都背着一堆属性。AS_PATH 记录这条路由经过了哪些 AS;Local_Pref 表示"本 AS 内部有多想走这条路",只在自家传;MED 用来跟隔壁说"从我这边进来比较便宜";还有 Community ,可以自己打标签玩,配合策略做各种花样。
选路的时候,这些属性按固定顺序过一遍:先比 Local_Pref,再比 AS_PATH 长度,再比 Origin ,再比 MED,一路往下,前面的能分出胜负就不看后面的。到下一跳的 IGP 开销排在挺后面。
所以 BGP 干的事不是"算"出一条路,是"挑"出一条路。
AS_PATH 还有个副产品,就是防环。
一条路由从 AS 100 出去,经过 200、300,AS_PATH 里就记着 100 200 300。哪天它绕了一圈又回到 AS 100,AS 100 一看路径里有自己,直接丢掉。
不用额外设计什么机制,路径本身就是证据。这就是"路径矢量"这个名字的由来——它不像 OSPF 那样共享一张地图,它是把自己走过的路写在一张清单上,谁看见自己的名字谁就知道绕回来了。
看一眼真实的 BGP 路由表,这个"挑"的过程就很直观了:
<R1> display bgp routing-table 10.0.45.0 24 Paths: 2 available, 1 best, 1 select From: 10.0.2.2 (10.0.2.2) Original nexthop: 10.0.4.4 AS-path Nil, origin incomplete, MED 0, localpref 100, pref-val 0, valid, internal, best, select, active, pre 255, IGP cost 2 From: 10.0.3.3 (10.0.3.3) AS-path Nil, origin incomplete, MED 0, localpref 100, pref-val 0, valid, internal, pre 255, IGP cost 2, not preferred for peer address
同一个 10.0.45.0/24,有两条路可选,2 available, 1 best。两条的属性几乎一样,最后是拿 peer 地址做的裁决——所以第二条标了 not preferred。
开头那两个符号也值得记一下:* 表示这条路由有效(下一跳可达),> 表示它是最优。两个都有,才是能用的那条。
三、一条腿门里,一条腿门外
AS 边界上有两种会话,性质完全不同。
跨 AS 的那种叫 EBGP,两边 AS 号不一样。这是真正的"外交关系",默认要求两台设备直连——因为 EBGP 报文的 TTL 默认是 1,一跳就没了。想跨跳,得显式配 ebgp-max-hop。这个默认值是有意为之:边界通常就是一根物理线,限制在一跳内既是安全考虑,也符合商业现实。
同一 AS 内部的叫 IBGP,两边 AS 号一样。这个不是外交,是内部通报
| EBGP | IBGP | |
| 两端 AS 号 | 不同 | 相同 |
| 默认是否要求直连 | 是(TTL = 1) | 否,可以跨多跳 |
| 源地址通常用 | 直连接口地址 | Loopback 地址 |
| 跨跳要靠什么 | ebgp-max-hop | 靠 IGP 提供到达对等体的路由 |
假设 R1 和 R3 在同一个 AS 里,中间还有条冗余路径经过 R4。如果直接用直连地址建 IBGP,R1 和 R3 之间那根线一断,BGP 会话就断了。可问题是——R1 和 R3 之间的 IP 连通性其实没断,还能绕 R4 过去。
会话凭什么断?
所以标准做法是用 Loopback 建 IBGP,再用 connect-interface 指定源接口。Loopback 永远 Up,只要 IGP 还能算出到对方的路径,BGP 会话就能一直挂着。AS 内部的冗余,也就真正被 BGP 用上了。
这里有个小坑:connect-interface 两端都要配。一端用 Loopback、一端用物理口做源,TCP 是握不上手的——报文的源地址和对方 peer 命令里写的地址对不上。
四、会话是怎么起来的
BGP 跑在 TCP 上,端口 179。这个选择挺务实:可靠传输、重传、排序这些事全交给 TCP,BGP 自己只管内容。也不用像 OSPF 那样自己做确认和重传。
握手过程分两段。
先是 TCP 三次握手。谁先启动 BGP,谁就先发 SYN,用随机端口去连对方的 179。
TCP 通了之后,双方各发一个 Open 报文。这里面装了三样东西:自己的 AS 号、Hold Time(保持时间,用来协商后面 Keepalive 的发送间隔)、还有 BGP Identifier(也就是 Router ID)。参数对得上,就回一个 Keepalive。收到对方的 Keepalive,会话就算 Established 了。
一共五种报文。除了上面两种,还有 Update(带路由,这个是主角)、Notification(出错时通知对方,然后立刻断连)、Route-refresh(让对方把某类路由重发一遍,改完策略时用)。
Update 值得多看一眼,因为它一次能干两件事。
发新路由的时候,这些前缀放在 NLRI 字段里,格式是"前缀 + 前缀长度"的二元组。有个约束:同一个 Update 里的路由必须有相同的路径属性,属性不一样的只能拆成几个报文分别发。
撤路由的时候,要撤销的网段放在 Withdrawn Routes 字段里。
一个报文同时管"加"和"减",这是 BGP 高效的一个原因——它不像 IGP 那样周期性重发全量,只在有变化时说一句,还能把多条变化打包带走。
状态机有六个状态,但排障时真正要记的只有三个。
Idle 是起点。这时候连 TCP 都还没开始发——最常见的原因是路由表里根本没有到对等体的路由,SYN 都发不出去。所以看到会话一直卡在 Idle,先别急着查 BGP 配置,去查路由。
Active 这个名字起得很坑。它听起来像"活着"“已激活”,实际意思是"我在尝试建 TCP,但没建成,正在重试"。第一次见的人十个有八个理解反。
Established 才是真的好了。
中间还有 Connect、OpenSent、OpenConfirm 三个过渡态,一闪而过,正常网络里基本看不到它们停留。
五、BGP 自己不产生路由
这大概是 BGP 和 IGP 最反直觉的一处差别。
OSPF 会自己发现路由:配个 network 宣告网段,它就去算、去泛洪。BGP 不干这个。BGP 没有拓扑,也没法从拓扑推出路由,它本质上是个搬运工——从别处拿到路由,再发给对等体。
拿到的方式有三种。
network 是精确注入。这里有个前提容易被忽略:路由表里必须已经有这条路由,而且前缀和掩码都得完全对得上。写 network 10.1.0.0 24,路由表里就得有一条 10.1.0.0/24;写成 /23 或者压根没有,这条就注入不进去。所以配完之后发现 BGP 表里空的,先回头看看 IGP 那边到底有没有这条。
import-route 是批量注入,按协议整批导进来——直连、静态、OSPF、IS-IS 都行。路由条目多的时候用这个,比一条条 network 省事得多。
aggregate 是聚合。跟 IGP 里的路由汇总一个思路,把几条明细合成一条,detail-suppressed 表示只发汇总、不发明细。
三种方式各有各的场合,但共同点是:BGP 从头到尾没"发现"过任何东西。
还有一件事值得单独提一句:路由衰减。
互联网上一条链路时通时断是常态。要是没有防护,一条路由反复 Up/Down,就会像涟漪一样扩散到全球,让几百万条路由跟着抖。BGP 的做法是给每条路由记一个"惩罚值"——抖一次加一点,抖得太频繁,超过阈值,这条路由就先被压下去、暂时不采用;等它安分了,惩罚值慢慢衰减回零,再放出来。
说白了就是:你再抖我就不信你了,等你不抖了我再考虑。
这东西配合路由聚合,是 BGP 能在全球尺度上稳住的两块压舱石。一个管"别让明细满天飞",一个管"别让抖动放大"。
六、四条规矩,两条最要命
路由进了 BGP 表之后,往外发的时候要守四条规矩。
第一条,只发最优的。也就是前面那个 * 和 > 都齐了的。
第二条,从 EBGP 学到的,发给所有对等体——EBGP 的、IBGP 的都发。这符合直觉,从外面听来的消息,家里人和外人都该知道。
第三条要绕一下,叫 IBGP 水平分割:从 IBGP 学到的路由,不再发给其他 IBGP。
为什么?因为 IBGP 之间理论上全互联。如果 A 从 IBGP 收到一条,再转给另一个 IBGP,那在 n 台设备的全互联里,这条路由会被反复传,绕成环。所以干脆一刀切死:IBGP 来的,只进不再出。
代价马上就来了。R2 把路由传给 R1,R1 不能再传给 R3,R3 就学不到。除非 R2 和 R3 之间也拉一条 IBGP。
这就叫 IBGP 全互联:n 台设备要建 n(n-1)/2 条会话。10 台是 45 条,20 台是 190 条,50 台是 1225 条。
没人会这么干。所以真实网络里几乎见不到全互联,都是用路由 反射 器把这张网状图压成树——那是下一层的话题了。
第四条是同步规则:从 IBGP 学到的路由,得等 IGP 也学到同一条,才能用、才能往外发。
它是为了防"路由黑洞"。设想 R3 从 IBGP 学到了 10.0.4.0/24,下一跳指向 R2。可 R3 和 R2 中间隔着一台 R1,R1 不跑 BGP。R3 把包发给 R1,R1 查路由表找不到这个网段,直接扔了。包就这么进了黑洞。
同步规则的思路是:既然中间那台不跑 BGP,那就要求 IGP 也得知道这条路由,保证转发路径上的每一跳都认识它。
出发点没问题,但现实很骨感——谁会为了同步,把几十万条外部路由灌进 IGP?所以这条规则在现网基本是关着的,默认就不启用。黑洞问题改由全互联或者反射器来绕开。
七、结语
回头看,BGP 那些看着啰嗦的设计,几乎都能从一句话推出来:它要跨越信任边界。
因为不信任,所以只传路由、不传拓扑;因为不信任,所以宁可用 TCP 而不是裸奔,还要把 TTL 卡在一跳;因为跨过边界之后"最优"不再由距离定义,所以才有一堆属性和十几步的比较顺序;也因为是两个组织之间的事,防环才做成"把走过的路写在清单上,看见自己就回头"这种朴素但可靠的形式。
OSPF 回答的是"我家里怎么走最快",BGP 回答的是"我怎么跟别人家打交道"。这两个问题的答案,从一开始就不可能长得一样。
还有一件事没说透:IBGP 全互联那 n(n-1)/2 条会话怎么办。10 台 45 条还能忍,50 台 1225 条——这不叫设计,这叫灾难。路由反射器和联盟就是为这个来的,它们要做的事只有一件:把那张网状的 IBGP 连接,压成一棵树。
HarmonyOS开发|ArkTS UI颜色API通用规则
