Linux网桥实现分析-STP的实现分析_第1页
Linux网桥实现分析-STP的实现分析_第2页
Linux网桥实现分析-STP的实现分析_第3页
Linux网桥实现分析-STP的实现分析_第4页
Linux网桥实现分析-STP的实现分析_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、#1  Linux网桥实现分析-第三部份,STP的实现分析初步Linux网桥实现分析作者:kendo版权所有,转载请注册出处第三部份,STP的实现分析初步一、STP的框架结构STP发送的是wikiBPDU/wiki包,该包有所有两种类型:配置和TCN(拓朴变更通知);对于BPDU包的处理,有两种:接收和发送(废话),对于配置类型的BPDU包的发送,它是靠定时器来完成的,参BPDU包的几个定时器参数;对于wikiTCP/wiki类型的BPDU包的发送,从名字可以看出来,它是当发现拓朴结构发生变更时发送的,如本机网桥配置的变化,物理接口的变动,分析其它机器变动后发出来的STP包

2、等等。BPDU的封包采用的是IEEE802封包(本想把封包结构的图片贴上来,找不着在哪儿上传图片)。前面分析过, br_handle_frame函数中,当网桥开启了STP,且根据目的物理地址判断出这是一个STP包,则交给br_stp_handle_bpdu函数处理。br_stp_handle_bpdu函数主要是判断是哪种类型的BPDU包,然后调用相关的处理函数,即:if(type=config)    br_received_config_bpdu();else if(type=tcn)    br_received_tcn_bpdu();这是对接收到B

3、PDU包的处理,关于config类型的BPDU包的发送,后面再分析;TCN包的发送,有一部份是在接收包处理过程中处理的(因为分析config类型的BPDU包的时候,发现拓朴变更,当然要发送TCN包了),所以这里一起来分析。二、Config类型的BPDU包的接收处理这个处理过程是在拆完BPDU包后,调用br_received_config_bpdu函数完成的。还是得先交待一些理论的东西:STPwiki协议/wiki最终是为了在网络中生成一棵无环状的树,以期消除广播风暴以及单播数据帧对网络的影响。它始终在选举三样东东:1、根网桥;2、根端口;3、“指定端口”和“指定网桥”(这三个概念非常重要,如果

4、你还不清楚,建议查阅相关文档先,否则下边的代码分析也无从谈起了)然后再根据选举出来的这三个东东,确定端口的状态:阻塞、转发、学习、监听、禁用要选举出这三样东东,得有一个判断标志,即算法,STP的判断标准是:1、判断根桥ID,以最小的为优;2、判断到根桥的最小路径开销;3、确定最小发送发BID(Sender BID)4、确定最小的端口ID如果前面你查阅了BPDU的封包结构,根桥ID、最小路径开销、发送方网桥的ID、端口ID这几个概念应该没有问题了,不过这里还是简单交一下:1、根桥ID,我们配置了网桥后,用brctl命令会发现8000.XXXXXX这样一串,这就是网桥的ID号,用一标识每一个网桥,

5、后面的XXXX一般的桥的MAC地址,这样ID值就不会重复。根桥ID,是指网络中所有网桥的ID值最小的那一个,对应的具有根桥ID的桥,当然也是网络的根桥了;2、最小路径开销动态路由中也类似这个概念,不过这里用的不是跳数(局域网不比广域网,不一定跳数大就慢,比如跳数小,是10M链路,跳数大的却是千兆链路),最初的开销定义为1000M/链种带宽,当然,这种方式不适用于万兆网了所以后来又有一个新的,对每一种链路定义一个常数值详请请查阅相关资料;3、发送方ID网桥之前要收敛出一个无环状拓朴,就需要互相发送BPDU包,当然需要把自己的ID告诉对方,这样对方好拿来互相比较;4、端口ID端口ID由优先级+端口

6、编号组成,用于标识某个桥的某个端口,后面比较时好用。生成树算法就是利用上述四个参数在判断,判断过程总是相同的:1、确定根桥,桥ID最小的(即把包中的桥ID,同自己以前记录的那个最小的桥ID相比,机器加电时,总是以自己的桥ID为根桥ID)的为根桥;2、确定最小路径开销;3、确定最小发送方ID;4、确定最小的端口ID:这四步非常地重要,后面的所以比较都是这四个步骤。有了这些概念,来看看对config类型的BPDU包的处理:void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) 

7、;       struct net_bridge *br;        int was_root;        if (p->state = BR_STATE_wikiDIS/wikiABLED)                return;        

8、br = p->br;        read_lock(&br->lock);        /*自己是根桥吗?用自己的br_ID和BPDU包中的根ID相比较*/        was_root = br_is_root_bridge(br);                /

9、*比桥BPDU包中的信息(bpdu)和原先的对应的信息(p),如果需要更新,返回1,相同返回0,不需更新返回-1*/        if (br_supersedes_port_info(p, bpdu)                 /*刷新自己的相关信息*/                br_record_config_infor

10、mation(p, bpdu);                /*进行root_bridge、port的选举*/                br_configuration_update(br);                /*设置端口状态*/     

11、60;          br_port_state_selection(br);以上这一段的逻辑概念很简单:1、把收到的BPDU包中的参数同自己原先记录的相比较,(遵循前面说的四个比较步骤),以判断是否需要进行更新br_supersedes_port_info(p, bpdu)。2、如果判断需要进行更新,即上述四个步骤中,有任意一项有变动,则刷新自己的保存记录:br_record_config_information(p, bpdu);3、因为有变动,就需要改变自己的配置了:br_configuration_update(br);即

12、前面说的,根据四步判断后选举根桥(注:根桥不是在这里选举的,前文说过,它是定时器定时发送BPDU包,然后收到的机器只需改变自己的记录即可)、根端口、指定端口;4、设置物理端口的转发状态:br_port_state_selection2.1 br_supersedes_port_info(p, bpdu)/* called under bridge lock */static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_bpdu *bpdu)      &#

13、160; int t;/*第一步*/        t = memcmp(&bpdu->root, &p->designated_root, ;        if (t < 0)                return 1;        else if (t >

14、 0)                return 0;/*第二步*/        if (bpdu->root_path_cost < p->designated_cost)                return 1;        else if (

15、bpdu->root_path_cost > p->designated_cost)                return 0;/*第三步,要同两个桥ID比:已记录的最小发送ID和自己的ID*/        t = memcmp(&bpdu->bridge_id, &p->designated_bridge, ;       

16、0;if (t < 0)                return 1;        else if (t > 0)                return 0;        if (memcmp(&bpdu->bridge_id, &

17、p->br->bridge_id, )                return 1;/*第四步*/        if (bpdu->port_id <= p->designated_port)                return 1;       

18、; return 0;2.2 br_record_config_information如果检测到有变动,则刷新自己的记录先:/* called under bridge lock */static void br_record_config_information(struct net_bridge_port *p, struct br_config_bpdu *bpdu)        p->designated_root = bpdu->root;       

19、; p->designated_cost = bpdu->root_path_cost;        p->designated_bridge = bpdu->bridge_id;        p->designated_port = bpdu->port_id;/*设置时间戳,关于STP的时间处理,后面来分析*/        br_timer_set(&p

20、->message_age_timer, jiffies - bpdu->message_age);p对应的四个成员的概念对照BPDU封包结构,不难理解其含义:        p->designated_root:                指定的根网桥的网桥ID        p->designated_cost :   

21、60;            指定的到根桥的链路花销        p->designated_bridge:                指定的发送当前BPDU包的网桥的ID        p->designated_port:       

22、        指定的发送当前BPDU包的网桥的端口的ID2。3 br_configuration_update前面说过,根桥的选举不是在这里进行,这里进行根端口和指定端口的选举/* called under bridge lock */void br_configuration_update(struct net_bridge *br)                        b

23、r_root_selection(br);/*选举根端口*/        br_designated_port_selection(br);/*选举指定端口*/2.3.1 根端口的选举br_root_selection根端口的选举同样是以上四个步骤,只是有一点小技巧:它逐个遍历桥的每一个所属端口,找出一个符合条件的,保存下来,再用下一个来与之做比较,用变量root_port 来标志:/* called under bridge lock */static void br_root_selection(struct net_bridg

24、e *br)        struct net_bridge_port *p;        int root_port;        root_port = 0;/*获得桥的所属端口列表*/        p = br->port_list;/*这个循环非常重要,它遍历桥的每一个端口,进行以上四步判断,找到一个,将其“保存”下来,然后再用下一个与

25、保存的相比较,直至遍历完,找到最优的那个,这个“保存”打了引号,是因为它仅仅是记当了端口编号:root_port = p->port_no;,然后再将其传递给比较函数br_should_become_root_port*/        while (p != NULL)                 if (br_should_become_root_port(p, root_port)    

26、                   root_port = p->port_no;                p = p->next;                br->root_port = root_port;/*找完

27、了还没有找到,则认为自己就是根桥*/        if (!root_port)                 br->designated_root = br->bridge_id;                br->root_path_cost = 0;      &#

28、160;  /*否则记录相应的值*/               else                 p = br_get_port(br, root_port);                br->designated_root = p->designated_ro

29、ot;                br->root_path_cost = p->designated_cost + p->path_cost;        br_should_become_root_port函数用以判断端口p是否应该变成根端口,与它相比较的是原来那个根端口,函数第二个参数则为此的ID号,在函数中调用 br_get_port获取该端口:/* called under bridge lock */

30、static int br_should_become_root_port(struct net_bridge_port *p, int root_port)        struct net_bridge *br;        struct net_bridge_port *rp;        int t;        br = p->br;

31、/*若当前端口是关闭状态或为一个指定端口,则不参与选举,返回*/        if (p->state = BR_STATE_DISABLED |            br_is_designated_port(p)                return 0;/*在根端口的选举中,根桥是没有选举权的*/    

32、   if (memcmp(&br->bridge_id, &p->designated_root,  <= 0)                return 0;/*没有指定等比较的端口ID(因为第一次它初始化为0的)*/        if (!root_port)         

33、0;      return 1;/*获取待比较的根端口*/        rp = br_get_port(br, root_port);/*又是四大步,像打蓝球*/        t = memcmp(&p->designated_root, &rp->designated_root, ;        if (t < 0)   

34、             return 1;        else if (t > 0)                return 0;        if (p->designated_cost + p->path_cost <     &

35、#160;      rp->designated_cost + rp->path_cost)                return 1;        else if (p->designated_cost + p->path_cost >              &#

36、160;  rp->designated_cost + rp->path_cost)                return 0;        t = memcmp(&p->designated_bridge, &rp->designated_bridge, ;        if (t < 0) 

37、;               return 1;        else if (t > 0)                return 0;        if (p->designated_port < rp->designated_port) 

38、              return 1;        else if (p->designated_port > rp->designated_port)                return 0;        if (p->port_id <

39、 rp->port_id)                return 1;        return 0;这样,遍历完成后,根端口就被选出来了。2。3。2 指定端口的选举br_designated_port_selection/* called under bridge lock */static void br_designated_port_selection(struct net_bridge *br)  &#

40、160;     struct net_bridge_port *p;        p = br->port_list;        while (p != NULL)                 if (p->state != BR_STATE_DISABLED &&   

41、0;                br_should_become_designated_port(p)                        br_become_designated_port(p);             

42、  p = p->next;        事实上这个过程与根端口的选举过程极为类似,没有分析的必要了!2。3。3 端口状态选择/* called under bridge lock */void br_port_state_selection(struct net_bridge *br)        struct net_bridge_port *p;        p = br->port

43、_list;        while (p != NULL)                 if (p->state != BR_STATE_DISABLED)                         if (p->port_no = br->roo

44、t_port)                                 p->config_pending = 0;                             &

45、#160;  p->topology_change_ack = 0;                                br_make_forwarding(p);                    

46、0;    else if (br_is_designated_port(p)                                 br_timer_clear(&p->message_age_timer);              

47、0;                 br_make_forwarding(p);                         else                     

48、0;           p->config_pending = 0;                                p->topology_change_ack = 0;           

49、0;                    br_make_blocking(p);                                            

50、;            p = p->next;        函数的逻辑结构也很简单:遍历整个桥所属端口:while (p != NULL)如果端口已经DISABLED,则没有判断的必要了:p->state != BR_STATE_DISABLED如果端口是根端口,或者是指定端口,就让让它forwarding,否则就让它blocking:             

51、;           if (p->port_no = br->root_port)                                 p->config_pending = 0;           &#

52、160;                    p->topology_change_ack = 0;                                br_make_forwarding(p);   

53、;                      else if (br_is_designated_port(p)                                 br_timer_clear(&p->messa

54、ge_age_timer);                                br_make_forwarding(p);                         else    

55、;                             p->config_pending = 0;                                p-&

56、gt;topology_change_ack = 0;                                br_make_blocking(p);                        /* call

57、ed under bridge lock */static void br_make_forwarding(struct net_bridge_port *p)        if (p->state = BR_STATE_BLOCKING)                 printk(KERN_INFO "%s: port %i(%s) entering %s staten",   &#

58、160;                   p->br->, p->port_no, p->dev->name, "listening");                p->state = BR_STATE_LISTENING;       

59、         br_timer_set(&p->forward_delay_timer, jiffies);        /* called under bridge lock */static void br_make_blocking(struct net_bridge_port *p)        if (p->state != BR_STATE_DISABLED &&

60、0;           p->state != BR_STATE_BLOCKING)                 if (p->state = BR_STATE_FORWARDING |                    p->state = BR_STATE_

61、LEARNING)                        br_topology_change_detection(p->br);                printk(KERN_INFO "%s: port %i(%s) entering %s staten",    

62、;                   p->br->, p->port_no, p->dev->name, "blocking");                p->state = BR_STATE_BLOCKING;        

63、0;       br_timer_clear(&p->forward_delay_timer);        都是设置p->state 相应状态位就可以了! 三、选举完成之后实在不会取名字了,前面分析了br_received_config_bpdu中前面的判断、刷新、选举、设置端口状态的过程,然而,如果桥认为当前这个BPDU是一个“最优的”(即符合前面判断四步中的某一步),所作的动作不止于此:1、如果因为这个BPDU导致拓朴变化了,如自己以前是根桥,现在不是了,需要发送T

64、CN包,进行通告;2、需要把这个BPDU包继续转发下去(如果自己收到数据的端口是根端口的话,那么就有可能有许多交换机(网桥)串在自己的指定端口下边,总得把这个包能过指定端口再发给它们吧,否则交换机就不叫交换机了)指下来继续看代码:/*前面说的第1步*/                     if (!br_is_root_bridge(br) && was_root)         &#

65、160;               br_timer_clear(&br->hello_timer);                        if (br->topology_change_detected)            

66、0;                    br_timer_clear(&br->topology_change_timer);                                br_transmit_tcn(br);                             &

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论