作为网络工程师,我敢说没有哪个人敢拍胸脯保证自己没被二层环路坑过。很多年前我第一次独立维护一个园区网,当时业务反馈“网络像瘫痪了一样”,核心交换机CPU疯狂飙升,接入层交换机端口指示灯狂闪,后来查了半天才发现是两根冗余线缆在没有启用STP的情况下直接把交换机连成了一个环。从那之后我养成了一个习惯——但凡涉及交换机互联,第一件事就是确认STP的状态。今天就用图文结合的方式把生成树协议这件事彻底说透,从它解决了什么致命问题,到它是怎么一票一票选出根桥和端口的,再到实际网络里怎么排查、怎么调优,一次讲明白。
1. 没有STP时,二层环路是如何“干掉”整个网络的
1.1 广播风暴的真实面目
先纠正一个非常流行的误解:很多人以为二层环路最怕的是数据帧绕圈,其实最怕的是广播帧绕圈。普通单播帧在环路里绕圈还不至于把整台设备打死,真正要命的是广播帧。
举个例子,假设交换机A和交换机B用两根线连在一起形成了物理环路,主机X发送一个ARP广播帧。这个广播帧会从交换机A的某个端口发出,经过链路1到达交换机B,交换机B发现自己除了接收端口之外还有一个端口连着交换机A,于是把广播帧又从这条链路转发回交换机A。交换机A收到后又从其他所有端口转发出去,其中就包括刚才那条链路1。于是这个广播帧在两条链路之间无限循环,而且每转一圈就会复制出新的副本,交换机端口的带宽被瞬间打满,所有正常业务帧全都挤不进去。我见过最夸张的一次,核心交换机出接口的计数器在几秒钟内就从0跳到了几十万,整个二层域内的主机全部断网,表现特征和ARP攻击几乎一模一样,这也是环路最阴险的地方——它看起来像攻击,其实只是物理拓扑出了问题。
1.2 MAC地址表震荡:比广播风暴更隐蔽的杀伤力
如果说广播风暴是把网络“明着打死”,那MAC地址表震荡就是“慢性中毒”。交换机的转发原理是基于MAC地址表来决定从哪个端口转发帧,但这个表是有学习机制的:当交换机从某个端口收到帧时,会把源MAC地址和入端口对应起来记录下来。
环路状态下,同一个MAC地址的帧会从两条甚至多条物理链路同时到达交换机。假设主机X的MAC地址先被记录在端口1,接着同样的帧又从端口2进来,交换机就会把MAC地址表改写成端口2,再过一会儿又改回端口1。这个过程中交换机的CPU被反复刷表消耗掉,更麻烦的是,交换机向主机X发送的帧可能被错误地从错误的端口转发出去,导致单播帧送不到目的地,出现间歇性的业务卡顿和丢包。你ping网关的时候可能大部分时间通,偶尔丢几个包,这种症状排查起来非常头疼,很多初学者会觉得是链路质量差或者设备老化,根本想不到是环路在作祟。
1.3 冗余需求与环路风险的矛盾
既然环路这么危险,为什么不在物理设计上彻底杜绝环路呢?答案很简单:网络需要冗余。核心层和接入层之间如果只有一条链路,一旦这条链路出现物理故障、光模块烧掉或者线缆被误拔,整个区域就完全失联了。生产环境绝对不能接受这种单点故障,所以工程上一定会做双上联、双核心这类冗余设计,也就是物理拓扑上不可避免地出现环。
STP的诞生就是为了解决这个矛盾:逻辑上让冗余链路保持阻塞状态,不转发数据帧,从而切断环路;一旦主用链路失效,阻塞链路立刻激活,自动接替转发工作。整个过程不需要人工干预,但原理上需要一套严谨的算法来保证“哪条链路该阻塞”这件事在所有交换机上达成一致,这就是生成树协议存在的全部意义。
2. 生成树协议的核心机制拆解
2.1 用一句话理解STP做的事
STP做的事情本质上非常简单:在一个存在物理环路的二层网络中,通过选举和比较,确定唯一的根桥作为逻辑中心,然后每台交换机计算自己到根桥的最优路径,最终把某些冗余链路阻塞掉,修剪出一棵没有环的逻辑树形结构。
这就像在一个圆形广场上,为了让人群不混乱,你必须指定一个旗杆作为中心,然后让所有人沿着不交叉的路径走向旗杆。会交叉的路径就临时封闭,但这个封闭不是永久性的,一旦主路径出了问题,原本封闭的路径立刻开放。
2.2 BPDU:STP的“投票单”
STP的一切决策都基于一种叫BPDU(Bridge Protocol Data Unit,桥协议数据单元)的帧。交换机之间通过不断交换BPDU来传递自己的身份信息和路径信息,所有交换机都基于这些信息进行统一计算。
BPDU里最关键的几个字段是根桥ID、根路径开销、发送者桥ID、发送者端口ID。桥ID由优先级和MAC地址组成,优先级取值范围是0到61440,默认32768,步长4096。工程上为了手动控制根桥位置,通常把核心交换机优先级设为4096或更低,这样它能稳定当选根桥,不会因为MAC地址大小这种随机因素导致根桥漂移。
2.3 根桥选举的完整规则
根桥选举看的是桥ID,规则只有一条:数值越小越优先。桥ID先比较优先级,优先级小的获胜;如果优先级相同,再比较MAC地址,MAC地址小的获胜。我打个比方,这就是全班选班长,先比三好学生加分项,加分一样就比学号,学号小的当班长。
参与选举的每台交换机一开始都认为自己是根桥,向外发送BPDU,同时不断接收别人的BPDU。如果收到的BPDU里根桥ID比自己小,就停止宣告自己,转而承认对方为根桥,并开始转发对方的BPDU信息。这个过程会在几秒钟内收敛完毕,最终全网唯一确定一个根桥。在实际网络中经常遇到的情况是,因为忘记了手工指定,两台同型号交换机因为MAC地址大小不同,较小的那台成了根桥,结果所有去往根桥的路径都绕到了那台设备上,虽然能工作,但流量模型极其不合理,核心设备一台闲一台忙。
2.4 端口角色的判定过程
根桥选出来之后,每台非根桥交换机需要为自己每个端口确定角色。STP定义了根端口、指定端口和阻塞端口三种角色。
根端口的定义是:非根桥交换机上到达根桥路径开销最小的那个端口。每台非根桥交换机有且只有一个根端口,它是这台交换机通向根桥的“必经之路”。端口到根桥的开销由两部分组成:自身接收BPDU的端口开销加上上游交换机通过BPDU通告过来的累计路径开销。路径开销和端口带宽有关,带宽越高开销越低,比如10Mbps开销是100,100Mbps是19,1Gbps是4,10Gbps是2。这个数值是标准定义的,不过不同标准版本数值有差异,实际工作中不需要死记硬背,只需要理解数值越小代表路径越优。
指定端口的定义是:每一个网段上,到达根桥路径开销最小的端口。这里的“网段”要特别解释一下,在STP语境下,可以把它理解为一根物理链路或者一个冲突域。根桥交换机上所有端口默认都是指定端口,因为根桥到根桥自己的开销为0,其他交换机比不了。非根桥交换机上的指定端口通常位于下行链路,向下级交换机通告BPDU。
既不是根端口也不是指定端口的其他端口,就是阻塞端口。阻塞端口同样会接收BPDU但不转发数据帧,只是持续监听BPDU,等待拓扑变化后重新计算。这个角色分工最后的结果是:从根桥出发,沿着每条链路上的指定端口和根端口一路延伸下去,就形成了逻辑上的樹状转发路径,所有阻塞端口所在链路被切断,环路消失。
2.5 端口状态迁移路径
端口状态比端口角色更好理解,它描述的是一个端口从启动到最终稳定转发之间经历的阶段。STP端口状态有五种:阻塞、监听、学习、转发、禁用。
端口刚启动时处于阻塞状态,这个状态下端口不转发数据帧,也不学习MAC地址,只接收BPDU。如果经过计算端口最终角色是阻塞端口,它会一直停留在这个状态。如果端口角色被确定为根端口或指定端口,它会进入监听状态,监听状态持续15秒,期间不学习MAC地址也不转发数据帧,只处理和发送BPDU,目的是保证整个网络的拓扑信息充分交换。
监听结束后进入学习状态,也是15秒,开始学习MAC地址但要继续等待,等15秒学习结束后进入转发状态,开始正式转发数据。这里一个关键点是:非根桥交换机根端口进入转发状态的等待时间还有一个延迟机制,叫最大老化时间,默认20秒。这个参数应对的情况是根桥失去联系时,其他交换机要等待20秒确认根桥真的消失了,才会开始重新选举。所以如果某个根桥设备突然故障,下游交换机可能需要等待最长50秒(20秒老化加15秒监听加15秒学习,实际并发计算大约50秒)才能恢复转发。
这也是传统STP最被诟病的地方——收敛太慢。一台交换机启动加入现有网络,端口从阻塞到转发需要大约30秒,这期间不能转发任何数据。如果网络里服务器和终端的DHCP请求正好撞上这段时间,就会出现获取不到IP地址的情况。快速生成树协议RSTP的出现很大程度上就是为了解决这个等待时间过长的问题,后面我会单独讲它如何优化。
3. 图文结合模拟一次完整的STP收敛过程
3.1 模拟项目拓扑与初始状态
为了把上面的概念串起来,用一个模拟项目X的拓扑来推演一遍。拓扑中有三台交换机,核心交换机SW-C,汇聚交换机SW-A和SW-B,SW-A通过两条千兆链路分别连到SW-C和SW-B,SW-B也有一条千兆链路连到SW-C。这个拓扑存在一个三角形环路,也就是SW-C到SW-A再到SW-B再到SW-C形成了一个物理环。
默认配置下所有交换机优先级都是32768,端口开销也相同。现在假设SW-C的MAC地址是三台里最小的,那么SW-C会成为根桥。如果三台里SW-B的MAC地址最大,SW-B会成为非根桥且路径最差的那个角色。
3.2 根端口与指定端口的计算推演
根桥SW-C的两个端口(连接SW-A、连接SW-B)都是指定端口,状态是转发。它向SW-A和SW-B发送BPDU,通告根桥ID为自身,路径开销为0。
SW-A收到来自SW-C的BPDU,得知根桥是SW-C,路径开销加上自己接SW-C那个端口的开销(千兆为4),所以它到根桥的总开销是4。同时SW-A还从连SW-B的端口收到SW-B转发的BPDU,SW-B在BPDU里通告的根路径开销为4,SW-A再加4,总开销变成了8。比较下来,接SW-C的端口开销4小于接SW-B的端口开销8,因此SW-A选接SW-C的端口为根端口,转发状态。同理SW-B也是选接SW-C的端口为根端口。
接下来给SW-A和SW-B之间的链路确定指定端口。这条链路两端分别连着SW-A的某个端口和SW-B的某个端口,谁的开销更小谁就是指定端口。SW-A到根桥的总开销是4,SW-B到根桥的总开销也是4,开销一样了。继续对比发送者桥ID,SW-A的MAC地址比SW-B小,所以SW-A的那个端口成为指定端口进入转发状态,SW-B连SW-A的那个端口既不是根端口也不是指定端口,被定为阻塞端口。
至此逻辑树形成没有环路:SW-C连接两个汇聚交换机,汇聚之间互连链路一侧转发一侧阻塞,所有终端的流量从汇聚经过核心转发,而汇聚与汇聚之间不会转发业务数据。
3.3 链路故障时的自动切换过程
假设某天SW-C和SW-B之间的光纤被误剪断。SW-B的根端口在最大老化时间20秒内收不到来自SW-C的BPDU。这块工地上讲究一点就会很麻烦——SW-B会认为根桥失去联系,开始重新发起根桥选举。
对比之后,SW-B发现从SW-A转发过来的BPDU中根桥ID仍然是SW-C,说明SW-A和根桥之间的链路还正常,于是SW-B把连SW-A的那个端口角色从之前的阻塞状态改为根端口,经过监听到学习再到转发,大约30秒后链路恢复数据转发。整个切换时间大约50秒,在普通办公网里这意味着核心链路的故障恢复需要接近一分钟的业务中断,这在今天的高可用网络中明显不够用。这也是为什么后来大家都在讲RSTP。
3.4 参数计算的细节说明
实际工程计算路径开销时要看清楚交换机遵循的是哪个标准。老设备常见的是IEEE 802.1D标准,千兆端口开销为4,万兆为2。新设备默认通常遵循802.1t标准,千兆开销是20000,万兆是2000。不同厂商设备混用时尤其要注意这个差异,不然可能出现两边算出来的路径开销不同,导致端口角色判定的结果和预期不符。我处理过一次某项目因为新老设备混用,老设备认为某条路径是一路,新设备认为是另一路,结果两边BPDU交互后出现了短暂的端口角色不稳定,排查了很久才发现是开销标准不一致导致的。
4. 配置STP时的关键实操要点
4.1 手工指定根桥和备份根桥
在生产网络里绝对不能把根桥选举全权交给默认优先级和MAC地址,必须手工指定。核心设备除了主根桥,还应该配置一个备份根桥,防止主根桥故障时网络里发生混乱的重新选举。
主根桥配置优先级设为4096,备份根桥设为8192,其余交换机保持默认32768。这样只要主根桥在线,它一定是根桥;主根桥挂了,备份根桥能立刻接管,不用等全网重新比较MAC地址。我习惯把主备根桥放在核心层同一台机房内两台设备上,让流量汇聚到核心,接入层和汇聚层之间不会出现额外的穿越路径。
4.2 边缘端口与快速端口
连接终端、服务器、打印机等不需要参与STP计算的端口,应该配置为边缘端口。边缘端口的概念很重要,它默认直接进入转发状态,不需要等待30秒,解决了终端开机慢的问题。在思科里是PortFast,华为里是边缘端口,本质上都是同一件事。
但边缘端口有一个安全风险:如果这个端口真的被接入了另一台交换机,可能形成意想不到的环路。所以有经验的工程师配置边缘端口时一定会同时开启BPDU保护。BPDU保护的作用是,只要这个端口收到了任何BPDU,立刻把端口置为errdisable状态,宁可这个端口失效也不能让它引发环路。我实测过很多次,这个组合是接入层交换机最稳妥的配置方案。
4.3 根桥保护与环路保护
除了边缘端口保护,生产环境里还建议配置根桥保护和环路保护。根桥保护针对的是某台非法优先级更低的设备加入网络后强行抢占根桥地位的情况。开启根桥保护后,如果指定端口收到了更优的BPDU,端口会进入不一致状态,不会让上游非法根桥影响全网拓扑。接入层如果有用户自己带了一台交换机接进来,这类问题非常常见。
环路保护应对的是单向链路故障。比如双向光纤一侧正常一侧中断,交换机只收到BPDU但发不出去,如果不用环路保护,端口可能会从阻塞状态错转到转发状态,造成环路。开启环路保护后,阻塞端口如果在一段时间内收不到BPDU,它会进入不一致状态而不是盲目转发,彻底杜绝单向链路引发环路的风险。
4.4 典型配置命令参考
以常见命令行体系为例,配置的核心逻辑如下:
spanning-tree mode rapid-pvst spanning-tree vlan 1 priority 4096 interface GigabitEthernet0/1 spanning-tree portfast edge spanning-tree bpduguard enable说明一下,如果网络里划分了多个VLAN,每个VLAN都会独立运行一棵生成树。这就是PVST或者RPVST的概念,不同VLAN可以阻塞不同的链路,实现负载均衡。比如VLAN 10阻塞一条上行链路,VLAN 20阻塞另一条,两个VLAN的流量分摊在两条物理链路上。
5. 为什么RSTP基本取代了传统STP
5.1 RSTP快速收敛的核心原理
快速生成树协议RSTP并不是完全另起炉灶,它在STP基础上做了大量优化,关键是引入了提议/同意的握手机制。传统STP依赖计时器等待,RSTP则通过端口之间的快速握手协商来确认拓扑状态,根端口可以直接切换,指定端口可以通过握手快速进入转发状态,不再需要等待30秒的监听学习期。
实际效果就是,一台交换机接入现网,端口在几秒内就能完成收敛进入转发状态。链路故障时,备份链路几百毫秒内就能激活。这种收敛速度在现代化数据中心和办公网里是基本要求。
5.2 RSTP的端口角色扩展
RSTP增加了备份端口和备用端口的概念。备份端口是同一个交换机上另一个端口的冗余备份,备用端口是另一条到达根桥路径的冗余备份。这些角色在传统STP里统一归为阻塞端口,区别不够细致。RSTP的改进让网络能更快地识别出哪条路径可以直接替代故障路径,而不需要重新计算整棵生成树。
5.3 什么时候仍然需要关注传统STP
虽然RSTP是几乎唯一的选择,但传统STP的知识仍然是必修课,原因有两个。第一,很多老旧型号的网络设备可能不支持RSTP,尤其是物联网终端里嵌入的交换芯片,遇到这类设备混用必须让整个网络统一运行某种模式,常见做法是让STP兼容模式运行,RSTP的BPDU会转换成STP格式。第二,STP的选举规则是所有生成树协议的基础,不懂RSTP选举规则的人根本理解不了为什么指定端口要那样握手。
6. STP故障排查与经验心得
6.1 快速定位环路的方法
遇到网络疑似环路,我一般按这个顺序排查。第一步看交换机CPU使用率,二层环路时CPU通常飙升,因为大量BPDU和广播帧需要处理。第二步看端口计数器,重点比较广播包和多播包的计数增量,如果某个端口的收发计数异常增大,大概率就是环路端口。第三步直接看生成树状态,找出所有阻塞端口,确认逻辑拓扑是否符合设计。
命令行下我常用的一条命令类似:
show spanning-tree summary show spanning-tree interface gigabitEthernet 0/1查看当前根桥是谁、每个端口的角色和状态,立刻能看出问题。如果发现某个不该阻塞的端口处于阻塞状态,或者出现了意料之外的根桥,顺着BPDU的来路就能找到问题设备。
6.2 几个容易踩的坑
第一个坑是忘开STP。很多傻瓜式交换机出厂默认STP是关闭的,一旦你把它和核心交换机组成环路,直接翻车。第二个坑是改了交换机配置后没保存,设备重启后回到默认状态,如果恰好和另一台设备形成环路,故障时机永远选在深夜或者节假日。第三个坑是边缘端口配置了但没开BPDU保护,结果环路照样形成。第四个坑是多VLAN环境下部分VLAN的STP没规划好,有的VLAN阻塞的是A链路,有的阻塞B链路,看起来都正常,但流量模型完全不符合预期,排障时会被误导。
6.3 我个人实际操作中的经验总结
做了几年网络维护,我对STP最深的体会是它就像一个默默无闻的交通警察,平时存在感极低,但一旦发生问题就是大事故。设计阶段把STP的角色、根桥、阻塞点全部画在拓扑图里,能避免后续一大半的故障排查时间。配置时坚持每个接入端口都开边缘端口加BPDU保护,宁可牺牲一两个非法接入设备,也不能让全网上不了网。排障时永远先看端口状态再看配置,因为流量异常和端口状态往往直接关联。
另外建议每隔一段时间做一次STP拓扑巡检,确认根桥位置没有漂移、是否出现了非预期的阻塞端口、所有边缘端口是否都是真正连接终端的端口。这么做了之后,你会发现原本“玄学”的网络环路问题,其实全部有迹可循,是完全可以掌控的。