交互式介绍生成树协议
摘要
本文提供交互式的生成树协议介绍,通过示例和可视化展示阐述其如何防止以太网中的环路问题。
<p><a href="https://lobste.rs/s/ivr1bw/interactive_introduction_spanning_tree">评论</a></p>
查看缓存全文
缓存时间: 2026/08/25 13:43
# 生成树协议交互式入门
来源:https://vincent.bernat.ch/en/blog/2026-spanning-tree
**警告** 本文包含交互式示例。若要可视化与操作这些示例,您需要启用JavaScript。
想象您为三天的活动租用了办公室空间。您快速架设了几个以太网交换机,并在地板上粘贴一些线缆让大家都能上网。不幸的是,您最笨手笨脚的同事斯坦每次起身去咖啡机时都会踢掉一根线缆。您可以添加额外的线缆,但那样会导致广播风暴:以太网数据包循环并倍增,直到其他所有通信都被阻塞。这时*生成树协议*(STP)就派上用场了。STP会阻塞足够多的备用线缆,只留下一棵无环的树。当斯坦再次制造麻烦时,它能在一秒钟内重建树结构,给单人支持团队成员布洛比留出时间重新连接线缆。眼见为实:下方的图表在您的浏览器中运行着真正的STP实现! `` :demo A1 @0,0 prio=4096 A2 @0,1 A3 @0,2 A4 @0,3 B1 @1,0 prio=8192 B2 @1,1 B3 @1,2 B4 @1,3 C1 @2,0 prio=8192 C2 @2,1 C3 @2,2 C4 @2,3 A1 -- A2 hazard=0 A2 -- A3 hazard=0 A3 -- A4 hazard=0 B1 -- B2 B2 -- B3 B3 -- B4 C1 -- C2 hazard=0 C2 -- C3 hazard=0 C3 -- C4 hazard=0 A1 -- B1 cost=10 B1 -- C1 cost=10 A4 -- B4 cost=20 B4 -- C4 cost=20 Leo @-0.3,0.7 proto=none icon=👦🏻 Mia @-0.3,1.3 proto=none icon=👧🏽 Joy @0.3,0.7 proto=none icon=👱🏻♀️ Roy @0.3,1.3 proto=none icon=👨🏾 A2 -- Leo hazard=0 A2:edge A2 -- Mia hazard=0 A2:edge A2 -- Joy hazard=0 A2:edge A2 -- Roy hazard=0 A2:edge Max @-0.3,1.7 proto=none icon=👨🏽 Zoe @-0.3,2.3 proto=none icon=👩🏾 Ada @0.3,1.7 proto=none icon=👵🏾 Amy @0.3,2.3 proto=none icon=👩🏼 A3 -- Max hazard=0 A3:edge A3 -- Zoe hazard=0 A3:edge A3 -- Ada hazard=0 A3:edge A3 -- Amy hazard=0 A3:edge Eli @0.7,0.7 proto=none icon=👦🏼 Jay @0.7,1.3 proto=none icon=👨🏻 Kai @1.3,0.7 proto=none icon=🧑🏽 Ben @1.3,1.3 proto=none icon=👱🏼 B2 -- Eli hazard=0.2 B2:edge B2 -- Jay hazard=0.2 B2:edge B2 -- Kai hazard=0.2 B2:edge B2 -- Ben hazard=0.2 B2:edge Ava @0.7,1.7 proto=none icon=👩🏻 Lea @0.7,2.3 proto=none icon=🧑🏾🦱 Ivy @1.3,1.7 proto=none icon=🧕🏽 Rex @1.3,2.3 proto=none icon=👴🏿 B3 -- Ava hazard=0.2 B3:edge B3 -- Lea hazard=0.2 B3:edge B3 -- Ivy hazard=0.2 B3:edge B3 -- Rex hazard=0.2 B3:edge Ana @1.7,0.7 proto=none icon=👩🏿 Eve @1.7,1.3 proto=none icon=👧🏼 Abe @2.3,0.7 proto=none icon=🧓🏿 Ian @2.3,1.3 proto=none icon=🧔🏾 C2 -- Ana hazard=0 C2:edge C2 -- Eve hazard=0 C2:edge C2 -- Abe hazard=0 C2:edge C2 -- Ian hazard=0 C2:edge Ned @1.7,1.7 proto=none icon=👨🏼🦳 Lou @1.7,2.3 proto=none icon=🧑🏿 Fay @2.3,1.7 proto=none icon=👧🏻 Sue @2.3,2.3 proto=none icon=👩🏽🦰 C3 -- Ned hazard=0 C3:edge C3 -- Lou hazard=0 C3:edge C3 -- Fay hazard=0 C3:edge C3 -- Sue hazard=0 C3:edge ``
**注意** 本文也提供视频版本 (https://vincent.bernat.ch/en/blog/2026-spanning-tree-video),但我建议您继续在此阅读以体验交互式演示。
- [基础知识](https://vincent.bernat.ch/en/blog/2026-spanning-tree#the-basics)
- [历史插曲](https://vincent.bernat.ch/en/blog/2026-spanning-tree#historical-interlude)
- [选举根桥](https://vincent.bernat.ch/en/blog/2026-spanning-tree#electing-the-root-bridge)
- [为端口分配角色](https://vincent.bernat.ch/en/blog/2026-spanning-tree#assigning-roles-to-ports)
- [端口状态转换](https://vincent.bernat.ch/en/blog/2026-spanning-tree#port-state-transition)
- [拓扑变更通知](https://vincent.bernat.ch/en/blog/2026-spanning-tree#topology-change-notification)
- [安全性](https://vincent.bernat.ch/en/blog/2026-spanning-tree#security)
- [为何现在使用 RSTP?](https://vincent.bernat.ch/en/blog/2026-spanning-tree#why-rstp-today)
- [网络能有多大?](https://vincent.bernat.ch/en/blog/2026-spanning-tree#how-large-can-a-network-be)
- [RSTP有多快?](https://vincent.bernat.ch/en/blog/2026-spanning-tree#how-fast-is-rstp)
- [关于 MSTP](https://vincent.bernat.ch/en/blog/2026-spanning-tree#about-mstp)
- [关于交互式示例](https://vincent.bernat.ch/en/blog/2026-spanning-tree#about-the-interactive-examples)
## 基础知识
设计于80年代的*生成树协议*已演变为“快速”版本(RSTP)和“VLAN感知”变体(MSTP)[1](https://vincent.bernat.ch/en/blog/2026-spanning-tree#sidenote-history)。任何有理智的网络工程师都知道存在更好的替代方案,比如 [BGP EVPN VXLAN](https://vincent.bernat.ch/en/blog/2017-vxlan-bgp-evpn)。然而,由于任何交换机都支持它,这一经典的生成树协议至今仍有一席之地。
我们聚焦于 **RSTP**:它于2004年取代了原始协议。为了消除网络环路,RSTP实现了一个复杂的状态机。定时器、链路状态变化以及网桥从邻居收到的链路本地控制帧驱动着其状态转换。这些以太网帧就是*网桥协议数据单元*(BPDU)。您可以在下方观察它们的运行过程:点击“开始”按钮。 `` :protocol rstp :tx-hold 10 A1 @0,1 C11 @1,0 prio=4096 icon=🌳 C12 @1,2 prio=4096 icon=🌳 C21 @2,0 prio=4096 icon=🌳 C22 @2,2 prio=4096 icon=🌳 A2 @3,1 H1 @0,0.2 proto=none icon=💻 H2 @0,1.8 proto=none icon=🖨️ H3 @3,0.2 proto=none icon=📠 H4 @3,1.8 proto=none icon=📺 A1 -- C11 A1 -- C12 A2 -- C21 A2 -- C22 C11 -- C12 C11 -- C21 C11 -- C21 C11 -- C22 C12 -- C21 C12 -- C22 C21 -- C22 A1 -- H1 A1:edge A1 -- H2 A1:edge A2 -- H3 A2:edge A2 -- H4 A2:edge ``
经过一段时间[2](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:5,...),拓扑收敛为一棵树:从根C11出发,存在到达每个网桥[3](https://vincent.bernat.ch/en/blog/2026-spanning-tree#sidenote-bridge)的路径,且没有环路。右上角的界面显示树形图标 🌳 及其达到该状态所用的时间。尝试[切断一条链路](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:9,C11--C12,...),观察协议如何在不到一秒内找到到达C12的备用路径。
您可以停止模拟、单步前进、重置到初始状态,或使用“蜗牛”模式 🐌 慢速运行。不用担心所有显示的信息:我稍后会解释。
所有示例均在您的浏览器中运行,由 [MSTPD](https://github.com/mstpd/mstpd) 提供支持——这是一个开源的用户空间[4](https://vincent.bernat.ch/en/blog/2026-spanning-tree#sidenote-kernel) RSTP实现[5](https://vincent.bernat.ch/en/blog/2026-spanning-tree#sidenote-incomplete)。
## 历史插曲
Radia Perlman ([https://hiddenheroes.netguru.com/radia-perlman](https://hiddenheroes.netguru.com/radia-perlman)),2014年入选[互联网名人堂](https://www.internethalloffame.org/inductee/radia-perlman/),用这首诗总结了她在DEC发明的STP前身,该诗后来被收录在一项[美国专利](https://patents.google.com/patent/US7339900B2/en)中:
> 我想我将永不会见到
> 比树更可爱的图。
> 树的关键属性是
> 无环的连通性。
> 这棵树必须确保跨越
> 使数据包能到达每个局域网。
> 首先,必须选出根。
> 由ID,它被选举出。
> 从根开始追踪最小成本路径。
> 在树中,这些路径被放置。
> 由像我这样的人构建网格,
> 然后网桥找出一棵生成树。
>
> ― *Radia Perlman*, [Algorhyme](https://hiddenheroes.netguru.com/radia-perlman)
## 选举根桥
为了构建一棵树,RSTP首先将具有**最低网桥标识符**的网桥选举为**根桥**。网桥标识符结合了优先级和MAC地址:`8192.6e:2b:10:a0:5f:29`。
在下面的示例中,S1和S2的优先级分别为4,096和8,192:S1成为根桥。S4的优先级为12,288,而S3保持默认优先级32,768[6](https://vincent.bernat.ch/en/blog/2026-spanning-tree#sidenote-priority):S4成为根桥。S5和S6没有指定优先级,因此最低的MAC地址胜出,S5成为根桥。 `` :protocol rstp S1 @0,0 prio=4096 S2 @0,1 prio=8192 S1 -- S2 S3 @1,0 S4 @1,1 prio=12288 S3 -- S4 S5 @2,0 S6 @2,1 S5 -- S6 ``
最初[7](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:2,S2-%3ES1,@),每个网桥都宣称自己是根: `` Spanning Tree Protocol Protocol Identifier: Spanning Tree Protocol (0x0000) Protocol Version Identifier: Rapid Spanning Tree (2) BPDU Type: Rapid/Multiple Spanning Tree (0x02) Root Identifier: 8192.02:00:00:01:00:01 Bridge Identifier: 8192.02:00:00:01:00:01 ``
一旦网桥收到一个宣称更优根桥的BPDU,它会将此新信息[传播](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:3,S2-%3ES1,@)给其邻居。 `` Spanning Tree Protocol Protocol Identifier: Spanning Tree Protocol (0x0000) Protocol Version Identifier: Rapid Spanning Tree (2) BPDU Type: Rapid/Multiple Spanning Tree (0x02) Root Identifier: 4096.02:00:00:00:00:00 Bridge Identifier: 8192.02:00:00:00:00:01 ``
## 为端口分配角色
第二步是为每个端口分配角色。RSTP定义了五种角色,每种用字母表示:
- 根端口(R)
- 指定端口(D)
- 替代端口(A)
- 禁用端口(X)
- 备份端口(B)[8](https://vincent.bernat.ch/en/blog/2026-spanning-tree#sidenote-backup)
每个非根网桥选择其**根端口**,即到达根路径开销最低的端口。除非您手动覆盖,每个网桥根据链路速率推导链路开销:1 Gbps为20,000。如果开销相等,则最低的端口标识符胜出。
每个剩余的端口,如果其发送的BPDU比收到的BPDU“更好”,则成为**指定端口**。否则,它成为**替代端口**。稍后,如果根端口失效,“最佳”的替代端口将成为新的根端口。
最佳BPDU的决胜条件是:
1. 最低的根桥标识符
2. 到根的最低累计路径开销
3. 最低的网桥标识符
4. 最低的端口标识符
`` :protocol rstp S1 @1,0 prio=4096 icon=🌳 S2 @0,1 S3 @2,1 S1 -- S2 S1 -- S3 S1 -- S3 S2 -- S3 ``
在上面的示例中,[收敛后](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:13),S1是根桥,因为其优先级为4,096,而其他网桥优先级为32,768。它的所有端口都是指定端口,因为到根的累计路径开销为0。
S2面向S1的端口成为根端口,因为它到根的累计路径开销最低——20,000 对比 40,000。S3有两个面向S1的端口,其中端口标识符最低的成为根端口——`0x8000` 对比 `0x8001`。另一个候选端口是替代端口,因为链路上的远端端口发送了更好的BPDU,其累计路径开销为0。
在S2和S3之间的网段上,S2的端口胜出:虽然两个网桥到根的累计路径开销相同(20,000),但S2的网桥标识符更小——`32768.02:00:00:00:00:01` 对比 `32768.02:00:00:00:00:02`。 `` Spanning Tree Protocol Protocol Identifier: Spanning Tree Protocol (0x0000) Protocol Version Identifier: Rapid Spanning Tree (2) BPDU Type: Rapid/Multiple Spanning Tree (0x02) Root Identifier: 4096.02:00:00:00:00:00 Root Path Cost: 20000 Bridge Identifier: 32768.02:00:00:00:00:01 Port identifier: 0x8002 ``
如果您[切断S1和S3之间的活动链路](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:13,S1--S3:1),S3会将“最佳”的替代端口提升为根端口。如果您[同时禁用第二条链路](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:13,S1--S3:1,S1--S3:2),S3会选择剩余的替代端口作为根端口。
但是,如果您[禁用S1和S2之间的链路](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:13,S1--S2,@,...),S2需要更多时间来选举新的根端口,因为它没有替代端口。除非发生特定事件,否则指定端口每[2秒](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:13,S1-%3ES3:2,S2-%3ES3,S1-%3ES2,@)发送一次BPDU[9](https://vincent.bernat.ch/en/blog/2026-spanning-tree#sidenote-hello)。如果网桥在连续3个hello周期内未从邻居收到BPDU,它会认为邻居已失效并移除该端口的信息。
## 端口状态转换
每个端口可以处于三种状态之一。图表为每种状态显示背景颜色:
- 阻塞(红色)
- 学习(黄色)
- 转发(绿色)
*根端口*会自动转换到转发状态。*替代端口*保持在阻塞状态。*指定端口*有两种选项从阻塞状态转换到转发状态:
- 如果该端口是**边缘端口**(通过配置或因为远端设备不运行任何STP版本),网桥假设它不会参与协议且不会形成环路。在这种情况下,指定端口立即转换到转发状态。
- 否则,它会向其下游邻居发送一个**提议**。如果远端网桥同意收到的BPDU比为其他端口存储的任何其他BPDU“更好”,它会将接收端口选为其根端口,并开始**同步**过程:它将所有非边缘、未同步的指定端口转换到阻塞状态以避免环路。然后,它回送一个**同意**。收到同意后,对端的指定端口转换到转发状态[10](https://vincent.bernat.ch/en/blog/2026-spanning-tree#sidenote-learning)。 `` :protocol rstp S1 @1,0 prio=4096 icon=🌳 S2 @1,1 S3 @0,2 S4 @2,2 S5 @0,3 prio=8192 icon= S6 @2,3 H1 @0,1.2 proto=none icon=🖨️ H2 @2,1.2 proto=none icon=📠 H3 @2.5,1.3 proto=none icon=📺 H4 @2.5,2.3 proto=none icon=💻 S1 -- S2 S2 -- S3 S2 -- S4 S3 -- S5 S4 -- S6 S4 -- S3 S5 -- S6 S3 -- H1 S3:edge S4 -- H2 S4:edge S4 -- H3 S4:edge S6 -- H4 S6:edge ``
在上述拓扑中,H1、H2、H3和H4是不参与协议的终端设备。我们将它们连接的端口配置为边缘端口,因此这些端口立即进入转发状态。
使用“步进”按钮单步运行模拟。时钟前进到1秒。[再次步进](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:2,S1-%3ES2,S2-%3ES1,@),S1和S2互相发送提议。这是来自S2的提议: `` Spanning Tree Protocol Protocol Identifier: Spanning Tree Protocol (0x0000) Protocol Version Identifier: Rapid Spanning Tree (2) BPDU Type: Rapid/Multiple Spanning Tree (0x02) BPDU flags: 0x4e, Agreement, Port Role: Designated, Proposal 0... .... = Topology Change Acknowledgment: No .1.. .... = Agreement: Yes ..0. .... = Forwarding: No ...0 .... = Learning: No .... 11.. = Port Role: Designated (3) .... ..1. = Proposal: Yes .... ...0 = Topology Change: No Root Identifier: 32768.02:00:00:00:00:01 Root Path Cost: 0 Bridge Identifier: 32768.02:00:00:00:00:01 Port identifier: 0x8001 ``
S1忽略了它:它自己的根标识符更低。当S2收到来自S1的类似提议时,它接受S1为其根桥。它还将指向S1的端口选为根端口并开始同步过程。两个指定端口已经处于阻塞状态,所以这里没有变化。[再次步进](https://vincent.bernat.ch/en/blog/2026-spanning-tree#mstp:3,S2-%3ES1#2,@),S2向S1发送两个BPDU。其中一个BPDU的同意位为1,提议位为0。它还表明S2已接受S1作为根桥,其根端口现在处于转发状态。收到此BPDU时,S1将自身的指定端口转换到转发状态。此时,S1和S2之间的链路开始转发用户流量。 `` Spanning Tree Protocol Protocol Identifier: Spanning Tree Protocol (0x0000) Protocol Version Identifier: Rapid Spanning Tree (2) BPDU Type: Rapid/Multiple Spanning Tree (0x02) BPDU flags: 0x42, Agreement, Port Role: Designated 0... .... = Topology Change Acknowledgment: No .1.. .... = Agreement: Yes ..0. .... = Forwarding: No ...1 .... = Learning: No .... 01.. = Port Role: Designated (2) .... ..0. = Proposal: No .... ...0 = Topology Change: No Root Identifier: 32768.02:00:00:00:00:00 Root Path Cost: 0 Bridge Identifier: 32768.02:00:00:00:00:00 Port identifier: 0x8001 ``
相似文章
网络与互联网,从基本原理出发
一篇深入的教育文章,从基本原理出发解释网络与互联网,涵盖分组交换、TCP、DNS、TLS以及数据如何在全球传输。
构建无源以太网分流器
一篇博文,介绍如何使用RJ45分线板和面包板构建一个无源以太网分流器,以监控网络流量而不注入数据。
@vivekgalatage: 数据包在Linux内核中的路径 https://net.in.tum.de/fileadmin/TUM/NET/NET-2024-04-1/NET-2024-04-1_16.pd…
这份来自TUM的技术报告详细描述了数据包在Linux内核中的路径,涵盖了网络内部机制。
以任务为中心的 iproute2 用户指南
一本以任务为中心的 iproute2 指南,iproute2 是 Linux 网络工具包,涵盖 ip、bridge 和 ss 命令及实际使用示例。
@iximiuz: 容器网络工作原理:从零开始构建桥接网络。要真正理解Docker和Kubernetes的网络…
本教程说明如何使用标准的Linux工具(如网络命名空间、veth对、桥接器和NAT)从零开始为容器构建桥接网络,以揭示Docker和Kubernetes网络的奥秘。