BGP这东西,看教材是一回事,真上手练完全是另一回事。很多网工应该都有过这种经历:把邻居状态机、选路规则背得很熟,上了设备敲完配置,show ip bgp summary一看邻居还是 ACTIVE,急得满头汗;等邻居好不容易 Established 了,show ip route里又看不到任何 BGP 路由。说实话,我学 BGP 被这种问题绊倒过很多次。后来我给自己定了一个练法:不背概念,直接搭一个多 AS 互联的组网,把 eBGP、iBGP、路由反射器、前缀过滤、选路策略从头到尾配一遍,出问题就现场修。这篇就把我完整跑过一遍的 BGP 练习思路和踩坑记录整理出来,给准备考证、要面网工岗、或者刚从 IGP 转向 BGP 的同学做个参考。
先说一下练 BGP 最忌讳什么:一上来就摆十几台设备,结果大半时间花在配置接口 IP 和打通底层链路上,真正的协议配置反而只写了几行。这套练习我建议一共只准备四到五台路由器,把拓扑分成三个自治域,既能练 eBGP,又能练 iBGP,再加上一条冗余链路专门用来做选路实验。下面按我的完整流程来拆。
1. 练习环境准备:先花十分钟把拓扑搭明白
1.1 模拟平台与系统镜像的选择
BGP 协议本身不挑硬件,练习阶段完全可以用模拟器跑。我试过几款主流的图形化网络模拟平台,也用过纯命令行的虚拟化方案,最后长期用的是“图形化界面 + 多设备镜像”这种组合。原因很简单:练习 BGP 需要频繁观察邻居状态、反复修改配置,图形界面可以一眼看到设备之间的连线关系,排障时压力小很多。如果你手头已经有熟悉的模拟平台,直接用就行,不必为了练习单独换工具。
镜像方面,建议选支持完整 BGP 功能集的设备系统镜像。判断标准很简单:能不能配route-reflector-client,能不能用ip prefix-list配合route-map做过滤,能不能看到show ip bgp neighbors里的完整状态输出。只要这几点支持,就足够完成整套练习。注意别用那种功能被裁剪过的轻量镜像,否则后面做路由反射器实验时会发现命令根本敲不进去,很影响心情。
练习环境不要贪大。我自己的经验是:四到五台路由器正好。设备太多,底层链路配置就要花掉大量时间;设备太少,iBGP 水平分割和路由反射这些核心特性又没法完整演示。下面这套拓扑我反复用过很多次,收敛速度合理,每个实验点都覆盖到了。
1.2 拓扑设计与 AS 规划
整套拓扑分成三个自治域,角色各不相同:
- AS 65001:只有一台路由器 R1,模拟一个需要把内部前缀通告出去的“客户侧”网络。
- AS 65002:有两台路由器 R2 和 R4,模拟一个“中间传输”自治域,重点用来练 iBGP、水平分割和下一跳问题。
- AS 65003:只有一台路由器 R3,模拟另一个邻居域,同时和 R1、R2 都有连接,用来做选路实验。
链路设计上,R1 和 R2 之间是 eBGP,R2 和 R3 之间是 eBGP,R3 和 R1 之间再加一条 eBGP 直连链路,形成三角结构。AS 65002 内部,R2 和 R4 建立 iBGP 邻居。为什么要把 R1 和 R3 也连起来?因为如果整个拓扑是一条直线,R3 只能看到一条到达 172.16.1.0/24 的路径,选路实验就无从谈起。有了三角结构后,R3 到 R1 通告的前缀就会有两条路径,一条直连、一条经 R2 中转,BGP 会做出选择,我们也可以人为干预这个选择,这就是练习的核心价值。
AS 号使用了 65000 到 65535 的私有区间。这是文档专门留出来的私有 AS 号,和私有 IP 地址类似,不会和公网冲突。练习环境里用私有 AS 号完全没问题,将来如果有机会接触真实运营商/企业的内部网络,也基本是同样的套路。
1.3 地址规划与底层连通性检查
地址规划看起来枯燥,但一定不要跳过。我见过很多人直接在两个接口上随便配个网段就开始建 BGP,结果到后面要加 loopback 邻居、要加路由反射器时,地址全乱套了。这套练习里统一用 10.255.255.x 用作 loopback,互联链路用 192.168.xy.0/30 这种一眼能看出端口关系的网段。
| 设备 | 接口 | IP 地址 | 用途说明 |
|---|---|---|---|
| R1 | G0/0 | 192.168.12.1/30 | 连接 R2 |
| R1 | G0/1 | 192.168.13.1/30 | 连接 R3 |
| R1 | Lo0 | 10.255.255.1/32 | 管理及 Router ID |
| R1 | Lo1 | 172.16.1.1/24 | 模拟客户前缀 |
| R2 | G0/0 | 192.168.12.2/30 | 连接 R1 |
| R2 | G0/1 | 192.168.23.2/30 | 连接 R3 |
| R2 | G0/2 | 192.168.24.2/30 | 连接 R4 |
| R2 | Lo0 | 10.255.255.2/32 | 管理及 Router ID |
| R3 | G0/0 | 192.168.23.3/30 | 连接 R2 |
| R3 | G0/1 | 192.168.13.3/30 | 连接 R1 |
| R3 | Lo0 | 10.255.255.3/32 | 管理及 Router ID |
| R3 | Lo1 | 172.16.3.1/24 | 模拟对端前缀 |
| R4 | G0/0 | 192.168.24.4/30 | 连接 R2 |
| R4 | Lo0 | 10.255.255.4/32 | 管理及 Router ID |
订好地址后,先把所有接口 IP 配上,然后用 ping 逐个验证直连链路。这一步花五分钟,后面能省五十分钟。BGP 邻居关系建立在 TCP 之上,如果底层链路都 ping 不通,BGP 状态机会一直卡在 Active 或者 Idle,看起来像是配置错误,实际就是接口没起来。我在练习里经常做的就是“跳过 ping 直接配 BGP”,每次都会踩坑,后来学乖了,先通底层、再谈协议。
另外,每台设备都要配 Router ID。BGP 如果没有显式配置bgp router-id,会从 loopback 或物理接口中自动选举一个,但在多台设备互联时容易选到重复或者不符合预期的地址,导致邻居关系异常。这个练习里我统一把 Router ID 设置成各设备的 loopback 地址,保证全局唯一。
2. BGP 配置的核心逻辑与基础命令
2.1 建立第一对 eBGP 邻居:从 Idle 到 Established
环境准备好以后,先做最简单的实验:在 R1 和 R2 之间建立 eBGP 邻居。R1 的配置如下:
router bgp 65001 bgp router-id 10.255.255.1 neighbor 192.168.12.2 remote-as 65002R2 的配置对应如下:
router bgp 65002 bgp router-id 10.255.255.2 neighbor 192.168.12.1 remote-as 65001这里有两个初学者最常犯的错误。第一,把router bgp后面的本地 AS 号和neighbor ... remote-as后面的对端 AS 号搞混。router bgp 65001意思是“本设备属于 AS 65001”,而neighbor 192.168.12.2 remote-as 65002是说“对端 192.168.12.2 在 AS 65002”。如果两边都写成了同样的 AS 号,设备会认为和自己建邻居的是同一自治域,形成 iBGP 关系,和预期完全不同。第二,只在一端配置了remote-as,另一端没配,此时邻居状态通常会卡在 Active——设备在反复尝试连接对端的 TCP 179 端口,但一直收不到 BGP OPEN 报文。
配置完以后,用show ip bgp summary查看邻居状态。关键指标是 State/PfxRcd 这一列,看到 Established 说明邻居已经建立成功。正常情况下,从 Idle 到 Established 的路径是:Idle → Connect → OpenSent → OpenConfirm → Established。如果一直停在 Active 或者 Idle,优先查三件事:两端 AS 号是否匹配、IP 是否可达、中间有没有 ACL 挡了 TCP 179。
2.2 network 通告:为什么我配了路由还是发不出去
邻居建立后,下一步是把 R1 上的客户前缀通告出去。这里必须强调一个很多人理解偏了的点:BGP 的network命令不是“把接口地址放进 BGP”,而是“要求 BGP 通告一条恰好匹配路由表中已有前缀的路由”。配置如下:
router bgp 65001 network 172.16.1.0 mask 255.255.255.0注意,mask子句不能乱省略。如果写成network 172.16.1.0,设备默认认为通告的是有类前缀,而 172.16.0.0/16 是一个 B 类地址,实际通告出去的可能和你预想完全不一样。这个坑我见过不少朋友踩过。
另外还有一层隐含条件:通告的前缀必须已经存在于本设备的路由表里。练习中 R1 的 Lo1 接口配置了 172.16.1.1/24,所以该前缀是直连路由,BGP 才能找到它。如果这个前缀是从 IGP 学来的、或者静态路由只写了一半,network配了也不会生效。
配置完成后,在 R1 上执行:
show ip bgp应该能看到一条*> 172.16.1.0/24的条目。*表示这条路由有效,>表示它是当前最优路径。然后在 R2 上执行show ip bgp,如果能收到相同的前缀,说明 eBGP 通告已经通了。如果 R1 的 BGP 表里没有这条路由,重点检查network前缀和路由表是否精确匹配;如果 R1 有但 R2 没有,再去查show ip bgp neighbors 192.168.12.1 advertised-routes,看 R1 到底往外发了什么。
2.3 iBGP 的水平分割与下一跳问题:最经典的“隐形杀手”
现在开始给 AS 65002 增加内部结构。R2 和 R4 之间建立 iBGP 邻居,配置如下:
router bgp 65002 bgp router-id 10.255.255.2 neighbor 10.255.255.4 remote-as 65002R4 对应配置:
router bgp 65002 bgp router-id 10.255.255.4 neighbor 10.255.255.2 remote-as 65002这里有一个细节:iBGP 邻居通常用 loopback 地址建立,而不是物理接口地址。原因是 loopback 地址是稳定的,只要底层路由可达,不会因为某一条物理链路抖动导致邻居反复断开。但用 loopback 建邻居的前提是,双方必须知道对方 loopback 地址的路由。练习环境里最简单的方式是写静态路由,R2 上指向 10.255.255.4 的下一跳是 192.168.24.4,R4 上指向 10.255.255.2 的下一跳是 192.168.24.2。
邻居建起来之后,有意思的事情就来了。R2 已经从 R1 学到了 172.16.1.0/24,但 R4 的 BGP 表里就是看不到这条前缀。原因有两层。
第一层是 iBGP 水平分割规则:从 eBGP 邻居学到的路由,不会通告给 iBGP 邻居。这个规则的初衷是防止 iBGP 域内出现环路,但它带来一个直接后果——iBGP 必须全互联,或者借助路由反射器/联盟机制打破限制。我们后面的进阶练习里会用路由反射器解决这个问题,现在先记住:两台设备之间只建了一对 iBGP 邻居,不等于所有路由都会自动传过去。
第二层是即使打破了水平分割(比如通过路由反射器),R4 收到 172.16.1.0/24 后也未必会把它装进路由表。因为 BGP 在计算路由有效性时,会检查下一跳的可达性。R2 从 R1 学到这条路由时,下一跳是 192.168.12.1,这个地址在 AS 65002 内部没有任何路由可达。R4 只有一个直连网段 192.168.24.0/30,它根本不知道 192.168.12.0/30 怎么走,于是这条 BGP 路由虽然出现在 BGP 表中,却因为“下一跳不可达”而不会被放行到全局路由表。
解决办法是加上next-hop-self:
router bgp 65002 neighbor 10.255.255.4 next-hop-self意思是:R2 在通告给 iBGP 邻居 R4 时,把下一跳改成自己的地址 10.255.255.2。这样 R4 检查下一跳时,发现 10.255.255.2 有一条静态路由指向,可达性检查就过了。
这个“下一跳不可达”的问题,是我在练习中花时间最多的地方。如果不做一次实验,只在书上看“iBGP 下一跳不变”这句话,很难理解为什么路由表里明明有 BGP 条目,全局路由表却空空如也。所以这个练习里,我特意建议不要一开始就配next-hop-self,故意让路由“学不到”,然后一步步查出来,印象非常深刻。
3. 进阶练习:路由反射器、过滤与选路策略
3.1 路由反射器配置:把全互联的成本降下来
iBGP 水平分割规则决定了,一个 AS 内如果有 10 台路由器,理论上需要两两建立 iBGP 邻居,连接数是 10×9/2=45 对。现实网络不可能这样玩,所以需要路由反射器(RR)打破水平分割规则。
继续用刚才的拓扑,把 R2 配置成路由反射器,R4 作为客户端。如果日后拓扑里再加一台 R5,同样挂在 R2 下面也行。配置命令非常简单:
router bgp 65002 neighbor 10.255.255.4 route-reflector-client neighbor 10.255.255.5 route-reflector-clientR4 和 R5 上什么都不用改,它们仍然把 R2 当成普通 iBGP 邻居。反射器的作用是取代全互联:客户端只需要和反射器建立邻居,反射器会把从一个客户端学到的路由反射给其他客户端和非客户端。
反射规则看起来简单,但初学者容易记混淆。我的记忆方法是:从客户端学到的路由,反射给所有其他邻居;从非客户端学到的路由,只反射给客户端,不反射给其他非客户端。这套规则的目的还是防环,练习时不需要死记硬背,只要知道“不是所有路由都会被反射”就够了。
这里还有一个实操细节:RR 反射路由时,默认不会修改下一跳和 AS_PATH。如果客户端 R4 收到的反射路由下一跳不可达(比如还是指向 AS 外部的某个地址),问题和前面一样会出现“BGP 表有、路由表没有”的现象。常见的做法是继续在反射器上对客户端启用next-hop-self,或者确保 AS 内部 IGP 已经能够通告外部下一跳。练习里我建议直接在 RR 上配next-hop-self,简单有效。
配置完成后,在 R4 上执行show ip bgp 172.16.1.0/24,应该能看到这条前缀,并且下一跳是 10.255.255.2。然后show ip route 172.16.1.0/24,如果能看到路由,说明 RR 和下一跳问题都解决了。
3.2 前缀过滤与 AS 路径控制:学会说“不”
路由能传过去之后,练习的下一层是策略控制。真实网络里不可能来者不拒,必须对邻居发送的路由做过滤。前缀过滤最常用的工具是前缀列表,比访问控制列表更灵活的地方在于,它可以精确表达“允许/拒绝某个前缀以及前缀的范围”,并能和ge、le配合使用。
举个例子,假设 AS 65003 的管理策略是:拒绝来自 172.16.0.0/24 且掩码大于等于 25 的路由,其他路由全部接收。配置如下:
ip prefix-list DENY-SPECIAL seq 5 deny 172.16.0.0/24 ge 25 ip prefix-list DENY-SPECIAL seq 10 permit 0.0.0.0/0 le 32 route-map FILTER-IN permit 10 match ip address prefix-list DENY-SPECIAL router bgp 65003 neighbor 192.168.23.2 route-map FILTER-IN in这里有几个关键点。第一,前缀列表默认末尾是隐式 deny,所以前面必须有一条宽的 permit 规则兜底,否则所有路由都会被拒收。第二,ge 25表示“掩码长度大于等于 25”,和我这条规则前的 /24 精确匹配配合,就能拒绝 172.16.0.0/25 以及更长掩码的子网。第三,应用方向是in,意思是过滤从对端发进来的路由;如果方向写成out,作用就变成限制本端发给对方的路由了。方向搞反是练习中最高频的错误,建议每次配置完都回头确认一遍。
除了前缀过滤,还可以用 AS 路径访问列表控制路由来源。比如只想接收来自 AS 65001 以及它直连上游的路由:
ip as-path access-list 1 permit ^(65001|65002)_ router bgp 65003 neighbor 192.168.23.2 filter-list 1 inAS_PATH 过滤对控制路由来源非常有效,也能防止别人把不合适的前缀“借道”传进来。不过要注意,正则匹配的是 AS_PATH 里的自治域编号序列,_代表空格或边界,这个符号很容易写错,练习时多比对几遍输出。
3.3 选路策略实验:用 local-preference 和 AS_PATH 改变路径
现在把三角拓扑利用起来。R3 能看到两条到达 172.16.1.0/24 的路径:一条从 R1 直连来,AS_PATH 是 [65001];另一条从 R2 中转来,AS_PATH 是 [65002, 65001]。BGP 默认选路规则里,AS_PATH 越短越优,所以 R3 应该会选择直连 R1 的路径。在 R3 上执行:
show ip bgp 172.16.1.0/24输出里通常有两行,右侧会标注路径属性,最前面带>号的表示当前优选路径。如果确实优先的是直连路径,说明 AS_PATH 比较在生效。
想要人为改变选路结果,比较直观的方式是让 R1 在通告这条路由时把 AS_PATH 变长。在 R1 上配置一个出方向的 route-map:
ip prefix-list CUST permit 172.16.1.0/24 route-map PREPEND permit 10 match ip address prefix-list CUST set as-path prepend 65001 65001 65001 router bgp 65001 neighbor 192.168.13.3 route-map PREPEND outset as-path prepend的意思是在原有 AS_PATH 前面重复添加三个 65001。这样 R3 从 R1 直连收到的路径变成 [65001, 65001, 65001, 65001],长度就不再优于经 R2 转发的 [65002, 65001]。此时再看show ip bgp 172.16.1.0/24,优选路径会切换成 R2 那条。
如果想用另一种方式,在 R3 上调整入站路由的本地优先级。本地优先级(local-preference)是 AS 内部传递的属性,数值越大越优先。例如想让从 R1 直连进来路由的优先级变低:
route-map SET-LOCAL permit 10 set local-preference 50 router bgp 65003 neighbor 192.168.13.1 route-map SET-LOCAL in注意,set local-preference 50是设置一个较低值,而 R3 从 R2 学到的同一条前缀保留默认的 100 优先级,数值一对比,R3 就会优先走 R2 那条路径。练到这里,“weight、local-preference、AS_PATH、MED”这些背书时的抽象概念,就全部变成看得见摸得着的行为了。
4. 排障命令与故障注入练习:把自己练成“问题猎手”
4.1 从 show 命令出发的排查路径
BGP 排障最忌讳乱查。我自己的排查顺序永远是:邻居状态 → BGP 表 → 全局路由表 → 过滤策略。每一步都有对应的核心命令。
第一步看邻居:
show ip bgp summary这个命令输出所有 BGP 邻居的 AS 号、状态、收到的前缀数量。State/PfxRcd列出现Established说明邻居正常,如果出现Active、Idle或者数字(比如1但路由不对),就要继续往下查。
第二步看 BGP 表:
show ip bgp show ip bgp 172.16.1.0/24show ip bgp能一眼看出某个前缀在 BGP 表中是否存在、是否有效(*)、是否最优(>)。如果前缀压根不在表里,问题多数出在邻居通告阶段或者入站过滤。如果前缀在但前面没有任何*,说明路由有效性检查没过,重点查下一跳可达性。
第三步看全局路由表:
show ip route bgp show ip route 172.16.1.0/24BGP 表中的最优路由会被安装进全局路由表,但前提是下一跳可达。show ip route输出里如果只有一条静态路由到 10.255.255.2,但 172.16.1.0/24 没有出现,就说明 BGP 最优路由没有通过下一跳检查。
最后可以查邻居收发方向的明细:
show ip bgp neighbors 192.168.13.1 advertised-routes show ip bgp neighbors 192.168.13.1 received-routesadvertised-routes看本端发了什么,received-routes看从对端收回了什么。注意有些设备默认不缓存received-routes,需要先在邻居配置里加上soft-reconfiguration inbound,否则查不到接收明细。练习中如果需要反复比对收发内容,建议一开始就配置,省得后面再折腾。
4.2 常见问题速查表:照着表查就能省半小时
练习过程中我积累了一张问题匹配表,基本覆盖了大部分新手踩坑场景。
| 现象 | 可能原因 | 检查与处理办法 |
|---|---|---|
| 邻居状态一直 Active | 对端没有配置 remote-as,或者 AS 号写错;TCP 179 被过滤 | show ip bgp summary对比两端 AS,检查接口可达性,确认 ACL 放通 179 |
| 邻居状态一直 Idle | Router ID 冲突或设备没有可用接口地址 | 检查bgp router-id,保证每台设备唯一 |
| 邻居 Established 但 BGP 表为空 | 本端没配network,或入站有 prefix-list 过滤 | show ip bgp看本端有没有前缀;检查邻居方向的 route-map / prefix-list |
| BGP 表有前缀但全局路由表没有 | 下一跳不可达 | show ip bgp 前缀确认下一跳,添加next-hop-self或补齐底层 IGP 路由 |
| 通告的前缀掩码错误 | network没带mask或前缀和路由表不精确匹配 | 核对前缀列表和路由表条目,确认掩码完全一致 |
| 路由被意外反射到了不该去的地方 | RR 反射规则理解错误,非客户端之间不反射 | 确认 RR 客户端配置,必要时画一张反射关系表 |
| 发出去的路由对端收不到 | 出站方向过滤写反了in/out | show ip bgp neighbors x.x.x.x advertised-routes确认实际发送内容 |
这张表不是用来背的,而是练习中出现异常时先对照现象锁定方向。大部分问题用两三条show命令就能定位,真正难的是不知道先查什么。只要按“邻居→BGP 表→路由表→过滤”的顺序走,半小时内基本都能破案。
4.3 故障注入练习:故意搞坏再修好
练习最后一步,我强烈建议做故障注入。做法很简单:把环境调到“正常状态”,然后人为制造一个故障,不告诉你原因,让你只用show命令排查定位。这个方法比自己老老实实配一遍有效得多,因为你会亲眼看到“坏掉”的现象和“修复后”的差异。
适合故障注入的改动有很多。第一个是改错 AS 号,把 R2 的neighbor 192.168.12.1 remote-as 65001故意改成65002,观察邻居状态变化,你会发现两端变成了 iBGP 邻居但实际上谁也认不出谁,状态会一直卡在 Active。第二个是删掉 RR 上的next-hop-self,让 R4 的 BGP 表里有路由、路由表却干干净净。第三个是加一条 prefix-list 把合法前缀 deny 掉,然后在 R3 上观察路由消失,再通过received-routes对比定位入站过滤问题。第四个是做 AS_PATH prepend,然后观察选路切换。
注入故障时有一点要提醒:每轮注入前先保存一份正常配置作为基线,改完测试完再恢复。没有基线的话,修到一半很容易忘了原配置长什么样,反而浪费时间。我习惯在每台设备上把正常状态下的show ip bgp summary和show ip bgp都截图保存,这就是基线。
4.4 实验记录:把每次排障过程留痕
做完故障注入,最后一步是把整个练习过程记录成文档。记录的重点不是把配置抄一遍,而是写清楚三件事:预期结果是什么、实际现象是什么、最终怎么修的。特别是那种“BGP 表有路由、全局路由表没有”的案例,现象、原因、修复命令一条条写清楚,下次碰到类似问题直接翻笔记,比重新查书快得多。
记录方式我用的是 Markdown 表格加代码片段。每个实验小节固定结构:实验目标、拓扑说明、关键配置、验证命令、踩坑记录。这样整份笔记既是练习总结,也是将来带新人的素材。练完一轮以后,甚至可以把自己的笔记做成一套“排障案例集”,分享出去时价值非常高。
我个人在实际操作中最大的体会是:BGP 练习最值钱的地方,永远不是把neighbor和network敲得多溜,而是形成一个“看到现象→推测根因→验证假设”的排障思路。故障注入练习是最快的成长方式。这一套多 AS 拓扑跑顺以后,接下来还可以往 BGP 联盟、路由认证、反射器集群、MPLS 相关方向扩展,但前提都是先把 eBGP/iBGP、下一跳、选路和过滤这些基本功练到条件反射。最后再分享一个小技巧:每次练习完,把关键show命令的输出保存到本地,对比不同状态下的差异,比单纯看书理解深刻得多。