一: Spanning Tree
生成树是为了防止在网络的拓扑中出现循环(loop)进而产生广播风暴(broadcast streams)的技术。而且,借着应用原本防止循环的这项功能,当网络发生问题的时候,则可以达到确保网络的拓扑被重新计算的目的,如此一来就不会让部分的问题影响整个网络的连通。生成树有许多的种类,例如:STP、RSTP、PVST+、MSTP…等不同的种类。本章将说明最基本的STP。STP(spanning tree protocol:IEEE 802.1D)是让网络的拓璞在逻辑上是树状的结构。经由设定每一个交换器(本章节中有时候会使用网桥称呼)的连接埠让讯框(frame)的传送为可通过与不可通过,来防止循环的产生进而达到阻止网络风暴发生。
![]()
STP会在网桥之间交换BPDU(Bridge Protocol Data Unit)封包,分析及比对网桥之间的连接埠信息,决定哪些连接埠可以传送哪些不行。具体来说,会以下列的顺序完成。
1.Root交换器(Root bridge)选举网桥之间的BPDU封包在交换过后,拥有最小的网桥ID即为Root。接下来的Root网桥会发送original BPDU,而其他的网桥仅接收及转发BPDU。
备注:网桥ID的计算方式是组合已经被设定的网桥priority和特定端口的MAC位址而成。网桥ID:
Upper 2byte | Lower 6byte |
桥接器priority | MAC 位址 |
2.决定连接埠的角色基于每一个连接埠到Root网桥的距离来决定该连接埠的角色。
Root port网桥内连接到Root距离最短的连接埠。该连接埠将会接收来自Root的BPDU。
Designated port该连接埠从各个联机到Root网桥距离最短。主要转送来自Root桥接器的BPDU封包,Root网桥的所有连接埠均为此种类。
Non designated port除了Root port、designated port以外的连接埠。讯框的传送是被禁止的。
备注:每一个连接埠到Root网桥的距离是基于收到BPDU中的设定值,并加上下列的比较计算出来。
第一优先:比较root path cost的值各网桥在转送BPDU的时候,会将封包中root path cost值加上设定的path cost。因此root path cost值即是各个端口到Root网桥的总和。
第二优先:root path cost相同的话,则比较网桥ID
第三优先:若是网桥ID相同时(每一个端口都连接到相同的网桥),则比较连接埠ID连接埠ID
3.连接埠的状态变化连接埠的角色决定了之后(STP的处理完成),每一个连接埠会处于LISTEN状态。之后会如下图进行状态的转换,最后每一个连接埠的角色会进入FORWARD状态或者BLOCK状态。若是设定为无效的连接埠之后,就会进入DISABLE状态,接着不会进行任何状态的转移。
当这些程序在每一台网桥开始执行之后,进行连接埠传送封包或抑制封包的决定,如此一来便可以防止循环在网络拓璞中发生。另外,断线或BPDU封包的最大时限(预设20秒)内未收到封包的故障侦测、新的连接埠加入导致网络拓扑改变。这些变化都会让每一台网桥执行上述1,2和3程序以建立新的树状拓扑(STP re-calculation)。
二:执行Ryu应用程序
1.建置实验环境
首先是mininet的拓扑代码:
#!/ usr/bin/env python
from mininet.cli import CLI
from mininet.link import Link
from mininet.net import Mininet
from mininet.node import RemoteController
from mininet.term import makeTerm
# 创建 Mininet 对象
net = Mininet(controller=RemoteController)
# 添加控制器
c0 = net.addController('c0')
# 添加交换机
s1 = net.addSwitch('s1')
s2 = net.addSwitch('s2')
s3 = net.addSwitch('s3')
#添加主机
h1 = net.addHost('h1')
h2 = net.addHost('h2')
h3 = net.addHost('h3')
#连接
Link(s1 , h1)
Link(s2 , h2)
Link(s3 , h3)
Link(s1 , s2)
Link(s2 , s3)
Link(s3 , s1)
# 构建拓扑并启动所有节点
net.build()
c0.start()
s1.start([ c0])
s2.start([ c0])
s3.start([ c0])
# 添加终端节点
net.terms.append(makeTerm(c0))
net.terms.append(makeTerm(s1))
net.terms.append(makeTerm(s2))
net.terms.append(makeTerm(s3))
net.terms.append(makeTerm(h1))
net.terms.append(makeTerm(h2))
net.terms.append(makeTerm(h3))
CLI(net)
net.stop()
输入net指令可以看到:
2.设定 OpenFlow 版本
对Node s1,s2,s3:
ovs-vsctl set Bridge s1 protocols = OpenFlow13
ovs-vsctl set Bridge s2 protocols = OpenFlow13
ovs-vsctl set Bridge s3 protocols = OpenFlow13
3.执行switching hub
对Node c0:
ryu-manager ./simple_switch_stp_13.py
OpenFlow交换器启动时的STP计算
每一台OpenFlow交换器和Controller的连接完成后,BPDU封包的交换就开始了。包括Root网桥的选举、连接埠的角色、连接埠的状态转移。
以上的结果,最后每一个连接埠分别为FORWARD状态或BLOCK状态。
为了确认封包不会产生循环现象,从host 1向host 2发送ping指令。在ping命令执行之前,先执行tcpdump命令以确认封包的接收状况。
Node s1,s2,s3:
tcpdump -i s1-eth2 arp
tcpdump -i s2-eth2 arp
tcpdump -i s3-eth2 arp
在使用script进行网络拓璞的建构的终端机中,进行接下来的指令,从host 1向host 2发送ping封包。
mininet> h1 ping h2 -c4
从tcpdump的结果看来,ARP并没有出现循环的状态已被确认。
网络发现故障时重新计算STP接下来,确认断线发生的时候会进行STP的重新计算。在每一个OpenFlow交换器启动之后以及STP的计算完成之后,执行下列指令后让线路中断。
Node s2:
ifconfig s2-eth2 down
断线被侦测到的时候,STP会被重新计算。
在此之前s3-eth2为BLOCK状态,但现在连接埠的状态为FORWARD,而讯框将可以再次被传送。
从线路故障的状态回复时重新计算STP接下来,断线回复的时候STP将被重新计算。在断线的状态下执行下列的命令让连接埠恢复。
Node s2:
ifconfig s2-eth2 up
联机恢复后被侦测到,STP就会进行再次的计算。可以确认目前的状态跟应用程序启动时有相同的树状结构,而讯框可以再次被传送。
3.使用OpenFlow完成生成树
让我们看一下在Ryu生成树应用程序中,如何使用OpenFlow完成生成树的功能。OpenFlow 1.3提供config来设定连接埠的状态。发送Port Modification信息到OpenFlow交换器以控制连接埠对讯框的转送行为。
名称 | 说明 |
OFPPC_PORT_DOWN | 连接埠无效状态 |
OFPPC_NO_RECV | 丢弃所有接收到的封包 |
OFPPC_NO_FWD | 停止转送封包 |
OFPPC_NO_PACKET_IN | table-miss发生时,不发送Packet-In信息 |
为了控制连接埠接收BPDU封包和非BPDU封包,收到BPDU封包就发送Packet-In的Flow Entry和接收BPDU以外的封包就丢弃的Flow Entry,分别透过Flow Mod信息新增到OpenFlow交换器中。
Controller对各个OpenFlow交换器进行下面port设定和Flow Entry的管理,以达到控制连接端口状态对于BPDU的接收传送和MAC位址的学习(BPDU以外则是封包的接收)和讯框的转送(BPDU以外则是封包的传送)。
备注:为了精简化,Ryu里的生成树函式库并不处理LEARN状态的MAC位址(接收BPDU以外的封包)学习。为了做这些设定,Controller产生BPDU封包基于OpenFlow交换器连接时所收集的连接埠信息和每一个OpenFlow交换器所接收的BPDU封包中所设定的Root网桥信息,来发送Packet-Out信息达到交换器之间互相交换BPDU的效果。
4.使用Ryu实作生成树
接下来,检视一下Ryu所用来实作生成树的原始码。生成树的原始码存放在Ryu的原始码当中。ryu/lib/stplib.py ryu/app/simple_switch_stp_13.py stplib.py是用来提供BPDU封包的交换和连接埠的角色、状态管理的生成树函式库。simple_switch_stp_13.py是一个应用程序,用来让交换器的应用程序新增生成树函式库以增加生成树功能使用。
STP函式库(STP实体)侦测到OpenFlow交换器和Controller已经连接时,Bridge类别的实体和Port类别的实体就会被产生。当每一个类别的实体产生、启动之后。
从STP类别实体接收到OpenFlow信息。
Bridge类别实体运算STP(Root网桥的选择,每一个连接埠的角色选择)
Port类别实体的状态变化,BPDU封包接收及传送
以上动作完成后,即可达成生成树的功能。
5.代码解析