【计网实验——prj6】生成树机制实验

【计网实验——prj6】生成树机制实验

实验要求

1. 基于已有代码,实现生成树运行机制,对于给定拓扑(four_node_ring.py),计算输出相应状态下的最小生成树拓扑;

2. 自己构造一个不少于7个节点,冗余链路不少于2条的拓扑,节点和端口的命名规则可参考four_node_ring.py,使用stp程序计算输出最小生成树拓扑;

3. 在four_node_ring.py基础上,添加两个端节点,把第05次实验的交换机转发代码与本实验代码结合,试着构建生成树之后进行转发表学习和数据包转发。

实现方案

  生成树运行机制的主要过程为处理config消息,这个过程实现在文件stp.cstp_handle_config_packet函数中。

1. 实现逻辑

收到config消息后,将其与本端口的config信息进行优先级比较:

1)若收到的config优先级比本端口高,则说明该网段应该通过对方端口连接根节点,需要进行如下操作:

  • 将本端口的config替换为收到的config消息,本端口为非指定端口;
  • 更新节点状态,更新剩余端口的config
  • 如果节点由根节点变为非根节点,停止hello定时器;
  • 将更新后的config从每个指定端口转发出去;

2)若本端口的config优先级更高,则说明该网段应该通过本端口连接根节点:

  • 该端口是指定端口,发送config消息。

2. 代码展示

1)更新本端口的config,将本端口的config替换为收到的config消息,本端口为非指定端口:

  代码实现如下:

	//update config of this port with the config packet recieved
	p->designated_root = ntohll(config->root_id);
	p->designated_switch = ntohll(config->switch_id);
	p->designated_port = ntohs(config->port_id);
	p->designated_cost = ntohl(config->root_path_cost);
	stp_port_t *non_designated_ports[STP_MAX_PORTS];
2)更新节点状态,节点状态分为根节点与非根节点两种,根据如下条件判断节点是否为根节点:

①先判断是否存在根端口,遍历所有端口,满足如下条件的为根端口(root_port)

  • 该端口是非指定端口
  • 该端口的优先级要高于所有剩余非指定端口

②若存在根端口,该节点通过根端口连接到根节点,更新节点状态如下:
在这里插入图片描述
③否则,该节点更新为根节点:
在这里插入图片描述
  代码实现如下:

	//update switch
	int num_non_ports = 0; 
	for (int i = 0; i < stp->nports; i++) {
		if(!stp_port_is_designated(&stp->ports[i])) {
			non_designated_ports[num_non_ports] = &stp->ports[i];
			num_non_ports ++;
		}
	}
	int judge = 0;
	if (num_non_ports == 1) {
		stp->root_port = non_designated_ports[0];
	} 
	else {
		for(int i = 0; i < num_non_ports - 1; i++) {
			if (judge == 0) {
				if(!compare_priority(non_designated_ports[i], non_designated_ports[i+1], NULL, 0)) {
					stp->root_port = non_designated_ports[i];
					judge = 1;
				}
			} 
			else {
				if(!compare_priority(non_designated_ports[i], stp->root_port, NULL, 0){
					stp->root_port = non_designated_ports[i];
				}
			}		
		}
	}

	if (stp->root_port == NULL) {
		stp->designated_root = stp->switch_id;
		stp->root_path_cost = 0;
	} 
	else {
		stp->designated_root = stp->root_port->designated_root;
		stp->root_path_cost = stp->root_port->designated_cost + stp->root_port->path_cost;
	}
3)更新其他端口的config,有两种情况下端口的config需要更新:
  • 指定端口→指定端口:端口状态没有改变,但需要更新其认为的根节点和路径开销;
  • 非指定端口→指定端口:若一个非指定端口其config较网段内其他端口优先级更高,那么该端口更新为指定端口。

  代码实现如下:

	//update other ports' config
	for (int i =0 ; i < stp->nports; i++) {
		if(stp_port_is_designated(&stp->ports[i])) {
			stp->ports[i].designated_root = stp->designated_root;
			stp->ports[i].designated_cost = stp->root_path_cost;
		}
	}

  其中,第②种情况无需在此处添加额外代码即可实现。

4)config之间的优先级比较

  以上过程中涉及到两种config信息的比较,一种是端口与端口间的config比较,另一种是端口config与接收的config消息之间的比较。
  两种比较的区别是第二种由于接收到的包来自于网络抓包,因此收到的包需要先进行字节序的转换才能与本端口的config进行比较。
  两种比较的逻辑相同为:首先比较节点ID,相同则比较到根节点的开销,再相同则比较上⼀跳节点的ID,最后比较端口ID,所有的比较均为小的值优先级更高。因此,将两种比较用同一个函数实现,用一个传入参数transform区分进行的是哪一种比较,即是否需要将config包字节序转换后再进行比较:

//compare port config with config packet recieved(tranform==1) or config of another port(transform==0)
int compare_priority(stp_port_t *p, stp_port_t *q, struct stp_config *config, int transform){
	u64 root_id, switch_id;
	int root_path_cost, port_id;
	if(transform){
		root_id = ntohll(config->root_id);
		switch_id = ntohll(config->switch_id);
		root_path_cost = ntohl(config->root_path_cost);
		port_id = ntohs(config->port_id);
	}
	else{
		root_id = q->designated_root;
		switch_id = q->designated_switch;
		root_path_cost = q->designated_cost;
		port_id = q->designated_port;
	}
	if (p->designated_root != root_id) {
		return p->designated_root > root_id;
	} 
	else if (p->designated_cost != root_path_cost) {
		return p->designated_cost > root_path_cost
	} 
	else if (p->designated_switch != switch_id) {
		return p->designated_switch > switch_id;
	} 
	else if (p->designated_port != port_id) {
		return p->designated_port > port_id;
	} 
	else {
		return 1;
	}
}

运行结果

1. 利用网络拓扑进行四节点测试

  结果如下:
在这里插入图片描述

2. 自己构造七节点并进行测试

  reference程序与自己实现的程序运行结果相同:
  ./stp
在这里插入图片描述
  ./stp-reference
在这里插入图片描述

3. 结合上次实验进行转发表学习和数据包转发测试

  在拓扑结构中添加两个主机h1和h2,运行拓扑程序,等待一段之间后在xterm输入h1 ping h2,得到结果如下:
在这里插入图片描述
  说明h1与h2之间可以进行通信,构建生成树后数据包转发成功。

思考题

1. 调研说明标准生成树协议中,如何处理网络拓扑变动的情况:当节点加入时?当节点离开时?

  在交换网络中,交换机依赖MAC地址表转发数据帧。缺省情况下,MAC地址表项的老化时间是300秒。在节点加入或离开拓扑结构时,都将先重新进行STP计算,更新各节点状态与端口的配置信息,产生新的树形拓扑结构。如果生成树拓扑发生变化,交换机转发数据的路径也会随着发生改变,此时MAC地址表中未及时老化掉的表项会导致数据转发错误,因此在拓扑发生变化后需要及时更新MAC地址表项。
  拓扑变化过程中,根桥通过TCN BPDU报文(Topology Change Notification)获知生成树拓扑里发生了故障。根桥生成TC用来通知其他交换机加速老化现有的MAC地址表项。拓扑变更以及MAC地址表项更新的具体过程如下:

  • 在网络拓扑发生变化后,有端口转为转发状态的下游设备会不间断地向上游设备发送TCN BPDU报文;
  • 上游设备收到下游设备发来的TCN BPDU报文后,只有指定端口处理TCN BPDU报文。其它端口也有可能收到TCN BPDU报文,但不会处理;
  • 上游设备会把配置BPDU报文中的Flags的TCA位设置1,然后发送给下游设备,告知下游设备停止发送TCN BPDU报文;
  • 上游设备复制一份TCN BPDU报文,向根桥方向发送;
  • 重复步骤1、2、3、4,直到根桥收到TCN BPDU报文;
  • 根桥收到TCN BPDU后,会将下一个要发送的配置BPDU中的TCA位置位,作为对收到的TCN的确认,还会将该配置BPDU报文中的Flags的TC位置1,用于通知所有网桥拓扑发生了变化;
  • 根桥在之后的max age+forwarding delay时间内,将发送BPDU中的TC置位的报文,收到该配置BDPU的网桥,会将自身MAC地址老化时间缩短为forwarding delay。

  假设下图拓扑发生了变动,将重新进行STP计算,生成新的树形拓扑,并按照以上方式将拓扑改变的信息传递至整个网络,使得交换机上的MAC地址表很快收敛,避免无效流量浪费带宽:
在这里插入图片描述

  • 图中,经由STP计算,S1为根桥,S4的E1端口被阻塞。
  • 当S3的面向PC1的链路断掉之后,网络中的STP将进行重算,S4的E1端口将转变为指定端口,进入转发状态,此时S4会向上游发送TCN消息。
  • S2收到S3的TCN消息之后,将下一个配置BPDU中的TCA置位并从端口E3发送给S4,S2也从自己的根端口E1向根发送TCN BPDU。
  • S1收到S2发送的TCN消息之后,将下一个配置BPDU中的TCA和TC置位并从指定端口E1发送给S2。此后的(20秒+15秒)时间内,S1均将配置BPDU中的TC置位,各个网桥收到TC置位的BPDU后,将MAC地址的老化时间置为15秒。

2. 调研说明标准生成树协议是如何在构建生成树过程中保持网络连通的?

  在构建生成树的过程中,STP会将部分冗余链路强制转化为阻塞状态,其余则链路处于转发状态。当处于转发状态的链路不可用时,STP可以重新配置网络,集合合适的备用链路,恢复部分冗余链路的转发状态来确保网络的连通性。

3. 实验中的生成树机制效率较低,调研说明快速生成树机制的原理。

  快速生成树协议RSTP(Rapid Spanning Tree Protocol)在STP基础上实现了快速收敛,并增加了边缘端口的概念及保护功能。
RSTP的端口角色:
  RSTP在STP基础上新增加了2种端口角色:Backup端口和边缘端口。通过端口角色的增补,简化了生成树协议的理解及部署。

  • Backup端口:由于学习到自己发送的配置BPDU报文而阻塞的端口,指定端口的备份,提供了另外一条从根节点到叶节点的备份通路。
  • 边缘端口:如果端口位于整个交换区域边缘,不与任何交换设备连接,这种端口叫做边缘端口。边缘端口一般与用户终端设备直接连接。

RSTP的端口状态:
  RSTP的端口状态在STP的基础上进行了改进。由原来的五种缩减为三种。
在这里插入图片描述

  • 如果不转发用户流量也不学习MAC地址,那么端口状态就是Discarding状态。
  • 如果不转发用户流量但是学习MAC地址,那么端口状态就是Learning状态。
  • 如果既转发用户流量又学习MAC地址,那么端口状态就是Forwarding状态。

RSTP快速收敛机制:
  在STP中,为了避免出现临时环路,端口从启动到进入转发状态默认需要等待30秒的时间;也就是说STP只能依靠计时器被动的收敛。如果缩短端口从启动到转发的等待时间,可能会引起网络的不稳定。RSTP采用P/A(Proposal/agreement)协商机制,无需依靠计时器来保障无环,可以让交换机的互联接口快速进入转发模式。
1)边缘端口机制
  在RSTP里面,边缘端口不接收处理配置BPDU,不参与RSTP运算,可以由Disable直接转到Forwarding状态,且不经历时延,就像在端口上将STP禁用。但是一旦边缘端口收到配置BPDU,就丧失了边缘端口属性,成为普通STP端口,并重新进行生成树计算,从而可能引起网络震荡。
2)根端口快速切换机制
  如果网络中一个根端口失效,那么网络中最优的Alternate端口将成为根端口,进入Forwarding状态。因为通过这个Alternate端口连接的网段上必然有个指定端口可以通往根桥。
3)Proposal/Agreement机制
  当一个端口被选举成为指定端口之后,在STP中,该端口至少要等待一个Forward Delay(Learning)时间才会迁移到Forwarding状态。而在RSTP中,此端口会先进入Discarding状态,再通过Proposal/Agreement机制快速进入Forward状态。其收敛过程如下:

  • 初始状态都认为自己是根桥,所有端口为指定端口,处理Discarding状态,发送配置RST BPDU;
  • RST BPDU中的Proposal置位,与接收到的对端BPDU对比优先级,如果自己优,则丢弃对端BPDU,将Proposal置位的本地BPDU到对端;
  • 使用同步机制来实现角色端口的协商,当收到置位RST BPDU且优先级高于时,交换机设置所有下游端口为Discarding状态,但是Alternat和边缘端口不变;
  • 确认下游端口为Discarding状态后,设备发送RST BPDU回复上游设备发送的Proposal消息,在此过程中,端口被确认为根端口,报文中Flags字段里面设置Agreement标记位和根端口角色;
  • 上游交换机收到Agreement置位的RST BPDU后指定端口立即进入Forwarding状态,下游网段进行同样的协商端口角色。
    在这里插入图片描述
      如上图,当S1和S2之间新增了一条链路后,P/A机制工作如下:
  • S1通过端口E1发送Proposal置位的RST BPDU消息给S2;
  • S2收到该消息后,意识到E2为根端口,启用同步机制阻塞指定端口E1和E3以避免产生环路,然后将根端口设置为转发状态,并向S1发送Agreement消息;
  • S1收到Agreement消息后,指定端口E1马上进入转发状态;
  • S2处于同步状态的非边缘指定端口E1和E3发送Proposal报文;
  • S3收到S2发送的Proposal报文后,判断E1为根端口,启动同步过程,由于S3下游均为边缘端口,所以已经实现了同步,因此S3直接向S2回复Agreement消息;
  • S2收到S3发送的Agreement消息后,端口E1马上进入转发状态;
  • S4的处理过程如S3;
  • S2收到S4发送的Agreement消息后,端口E3马上进入转发状态;
  • P/A过程结束。

  RSTP与STP相比,主要有以下几点优势:

  • 如果旧的根端口已经进入阻塞状态,而且新根端口连接的对端交换机的指定端口处于Forwarding状态,在新拓扑结构中的根端口可以立刻进入转发状态;
  • 网络边缘的端口,即直接与终端相连,而不是和其它网桥相连的端口可以直接进入转发状态,不需要任何延时;
  • 增加了网桥之间的协商机制—Proposal/Agreement。指定端口可以通过与相连的网桥进行一次握手,快速进入转发状态。

  也正是由于这几点,使得RSTP的收敛速度比STP快很多,大大提高了构建生成树和网络传输的效率。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值