3、STP协议基础:STP的诞生背景、BPDU报文结构、根桥与端口角色选举
3.1 为什么需要STP?——广播风暴的噩梦
咱们做无线网络优化的,天天跟数据包打交道。但你有没有想过,有线网络里有个“老毛病”——广播风暴,差点让整个互联网设计思路都拐了个弯。
早期的以太网,说白了就是一根同轴电缆,大家往上挂。后来换成了集线器(Hub),再后来是交换机。交换机比集线器聪明,它能学习MAC地址,知道哪个口对应哪台设备。但聪明反被聪明误,一旦网络里出现了环路,麻烦就来了。
我刚开始做网络那会儿,在机房调试一个二层网络。不小心把两根网线插到了同一台交换机的两个端口上——嗯,就是手滑。结果整个办公室的网络瞬间瘫痪,所有人都喊“上不了网了”。我当时还纳闷,交换机不是挺智能的吗?后来才知道,这就是典型的广播风暴。
为什么会这样?你想想看:
- 广播帧没有“寿命”——交换机收到广播帧,除了接收端口,其他所有端口都转发。
- 环路导致“无限循环”——A交换机把广播帧发给B,B又发回A,A再发给B……帧在环路里疯狂复制。
- 带宽瞬间被吃光——这些重复的广播帧占满了链路,正常的数据帧根本发不出去。
核心结论:二层网络必须防环路。没有环路,就没有广播风暴。但现实是,网络设计为了冗余,又不得不引入环路。STP(Spanning Tree Protocol)就是来解决这个矛盾的。
3.2 BPDU报文结构——STP的“心跳”
STP怎么工作的?靠的是BPDU(Bridge Protocol Data Unit)。你可以把它理解成交换机之间互相打招呼的“心跳包”。每台交换机都会定期发送BPDU,告诉邻居“我还活着,我的状态是这样的”。
BPDU报文结构其实不复杂,我习惯把它分成三块:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| Protocol Identifier | 2 | 固定为0x0000,表示STP |
| Protocol Version Identifier | 1 | STP为0,RSTP为2,MSTP为3 |
| BPDU Type | 1 | 0x00=配置BPDU,0x80=TCN BPDU |
| Flags | 1 | TC(拓扑变化)和TCA(确认)标志位 |
| Root Identifier | 8 | 根桥的桥ID(优先级+MAC地址) |
| Root Path Cost | 4 | 到根桥的路径开销 |
| Bridge Identifier | 8 | 发送该BPDU的桥ID |
| Port Identifier | 2 | 发送该BPDU的端口ID |
| Message Age | 2 | BPDU从根桥发出后经过的时间 |
| Max Age | 2 | BPDU的最大生存时间(默认20秒) |
| Hello Time | 2 | BPDU发送间隔(默认2秒) |
| Forward Delay | 2 | 端口状态迁移延迟(默认15秒) |
这里面最重要的字段,我个人觉得是Root Identifier和Root Path Cost。根桥是谁?我到根桥有多远?这两个信息决定了整棵树的形状。
小技巧:抓包的时候,直接过滤 stp 就能看到BPDU。我习惯先看Root Identifier,再看Bridge Identifier。如果两者相等,说明这台交换机就是根桥。
3.3 根桥选举——谁才是“老大”?
STP的第一步,就是选出一个“老大”——根桥(Root Bridge)。所有其他交换机都围绕根桥来构建树形拓扑。
选举规则很简单,就一句话:桥ID最小的交换机成为根桥。
桥ID = 桥优先级(2字节) + MAC地址(6字节)。优先级范围是0~65535,默认32768。数值越小,优先级越高。
我曾经在项目里遇到过一个问题:两台交换机都是默认优先级,结果根桥被一台老旧的交换机抢到了。那台老交换机性能差,导致整个网络延迟偏高。后来我把核心交换机的优先级改成4096,问题就解决了。
选举过程是这样的:
- 每台交换机启动时,都认为自己是根桥。
- 它们互相发送BPDU,里面包含自己认为的根桥ID。
- 收到BPDU后,比较根桥ID。如果对方的根桥ID更小,就更新自己的根桥信息。
- 最终,所有交换机达成一致,根桥诞生。
注意:根桥不是一成不变的。如果根桥挂了,或者链路断了,STP会重新选举。这个过程需要30~50秒(收敛时间),对于现代网络来说太慢了。所以后来才有了RSTP(快速生成树协议)。
3.4 端口角色选举——谁转发?谁阻塞?
根桥选出来了,接下来就是给每个端口分配角色。STP定义了三种端口角色:
- 根端口(Root Port,RP)——非根桥上,到根桥路径开销最小的端口。每个非根桥只有一个根端口。
- 指定端口(Designated Port,DP)——每条链路上,到根桥路径开销最小的端口。每条链路只有一个指定端口。
- 阻塞端口(Blocking Port,BP)——既不是根端口也不是指定端口的端口,逻辑上阻塞,不转发数据帧。
选举顺序是:先选根桥,再选根端口,然后选指定端口,剩下的就是阻塞端口。
我画了一张图,帮你理清这个逻辑:
端口选举的细节,我再说几个容易踩坑的地方:
- 路径开销(Path Cost)——早期STP用带宽计算,10Mbps=100,100Mbps=19,1Gbps=4。后来改成了非线性的公式。不同厂商的默认值可能不一样,跨厂商对接时要注意。
- 端口ID——如果路径开销相同,就比较端口ID(端口优先级+端口号)。优先级默认128,数值越小越优先。
- 对称性——根端口和指定端口的选举是“双向”的。A认为B是根端口,B不一定认为A是指定端口。这个逻辑有点绕,我建议你抓包看BPDU里的字段,一目了然。
一句话总结:STP通过BPDU交换信息,选举出唯一的根桥,然后为每个端口分配角色(根端口、指定端口、阻塞端口),最终构建一个无环的逻辑树形拓扑。冗余链路被阻塞,环路被打破,广播风暴被消灭。
3.5 避坑指南——我踩过的STP的坑
做了这么多年网络,STP相关的故障我见过不少。挑几个典型的说说:
- 根桥被“抢”——新接入一台交换机,优先级没改,结果它成了根桥。整个网络的流量路径全变了,延迟飙升。解决办法:核心交换机手动设低优先级(比如4096)。
- 收敛时间太长——STP默认收敛要30~50秒。服务器集群的心跳超时可能只有3秒。结果链路一断,集群脑裂了。后来我换成了RSTP,收敛时间降到1秒以内。
- BPDU Guard没开——用户私自接了一台交换机,导致STP拓扑变化,全网震荡。我后来在接入端口上开了BPDU Guard,一旦收到BPDU就直接err-disable,省心多了。
重要提醒:STP是二层网络的“安全带”,但别指望它解决所有问题。现代网络里,RSTP和MSTP才是主流。不过STP的基础概念——根桥、端口角色、BPDU——这些是通用的,学透了后面事半功倍。