快速生成树之端口信息状态机

来源:互联网 发布:淘宝nba旗舰店是正品吗 编辑:程序博客网 时间:2024/06/03 21:50

 

1 源码

    rstplib.1.1.02/portinfo.c, portinfo.h。

2 功能

    端口信息状态机,负责接收BPDUs,维护端口的生成树信息。

3 代码简析

3.1 状态定义

#define STATES { /

  CHOOSE(DISABLED), /

  CHOOSE(ENABLED),  /

  CHOOSE(AGED),     /

  CHOOSE(UPDATE),   /

  CHOOSE(CURRENT),  /

  CHOOSE(RECEIVE),  /

  CHOOSE(SUPERIOR), /

  CHOOSE(REPEAT),   /

  CHOOSE(AGREEMENT),    /

}

STATES宏定义了状态机的各个状态(ENABLED态为IEEE 802.1y中定义,此处不讨论),其中CHOOSE也是一个宏,用于运行和调试的切换。

运行时:

#define CHOOSE(a) a

typedef enum STATES THE_STATE_T;

#undef CHOOSE // 取消定义

调试打印时可用来返回状态机名称(前加#转换为字符串,见《宏中"#"和"##"的用法):

#define CHOOSE(a) #a // 重新定义

static char    *state_names[] = STATES;

#undef CHOOSE

3.2 rcvBpdu

static RCVD_MSG_T rcvBpdu (STATE_MACH_T* this)

{

  int   bridcmp;

  register PORT_T* port = this->owner.port; // this状态机所属的端口

  ...

 

 /* 条件1:消息是从指定端口发出:

  * ( 该BPDU是RST BPDU && BPDU的Port Role==Designated) || BPDU是Config BPDU

  */

  if (RSTP_PORT_ROLE_DESGN == port->msgPortRole || 

      BPDU_CONFIG_TYPE == port->msgBpduType) { 

    bridcmp = STP_VECT_compare_vector (&port->msgPrio, &port->portPrio); // 条件2:消息优先级向量比端口优先级向量优越

    if (bridcmp < 0 ||

        (! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge,

                                       &port->portPrio.design_bridge) &&

         port->msgPrio.design_port == port->portPrio.design_port      &&

         STP_compare_times (&port->msgTimes, &port->portTimes)))

    {

      return SuperiorDesignateMsg; // 收到更优BPDU,返回更优信息SuperiorDesignateMsg

    }

  }

  /* 条件1:消息是从指定端口发出 */

  if (BPDU_CONFIG_TYPE == port->msgBpduType ||

      RSTP_PORT_ROLE_DESGN == port->msgPortRole) { // 条件2:msgPriority==portPriority && msgTimes==portTimes

    if (! STP_VECT_compare_vector (&port->msgPrio,

                                   &port->portPrio) &&

        ! STP_compare_times (&port->msgTimes, &port->portTimes)) {

        return RepeatedDesignateMsg; // BPDU来自当前指定网桥,返回重复信息RepeatedDesignateMsg

    }

  }

  /* 子网桥根端口统一本端口快速转移到转发态,返回确认信息ConfirmedRootMsg */

  if (RSTP_PORT_ROLE_ROOT == port->msgBpduType                    &&

      port->operPointToPointMac                                   &&

      ! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge,

                                    &port->portPrio.design_bridge) &&

      AGREEMENT_BIT & port->msgFlags) {

    return ConfirmedRootMsg;

  }

  return OtherMsg; // 返回其他信息OtherMsg

}

3.3 setTcFlags
设置拓扑变化相关标志:
rcvdTc = TRUE:当该BPDU是一个设置拓扑变化标志的 ConfigBPDU 或 RST BPDU 时
rcvdTcAck = TRUE:当该BPDU是一个设置拓扑变化确认标志的 ConfigBPDU 或 RST BPDU 时
rcvdTcn = TRUE:当该BPDU是一个 TCN BPDU 时
static Bool setTcFlags (STATE_MACH_T* this)
{
  register PORT_T* port = this->owner.port;
 /* 在STP_info_rx_bpdu()中"port->msgBpduType = bpdu->hdr.bpdu_type;"
  * 获取到接收BPDU的类型赋给端口变量msgBpduType,若为“PDU_TOPO_CHANGE_TYPE”
  * 则更新端口变量rcvdTcn为真,标识“收到拓扑变化通知消息信号”
  */
  if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { 
    port->rcvdTcn = True;
  }
 /* 在STP_info_rx_bpdu()中"port->msgFlags =  bpdu->body.flags;"获取BPDU头的flags字段,
  * 用来区分:
  * 1—拓扑变化标志(Topology Change flag )
  * 8—拓扑变化确认标志(Topology Change Acknowledgment flag)
  */
  else
   {
    if (TOLPLOGY_CHANGE_BIT & port->msgFlags) {
      port->rcvdTc = True;
    }
    if (TOLPLOGY_CHANGE_ACK_BIT & port->msgFlags) {
      port->rcvdTcAck = True;
    }
  }
  return True;
}
具体可参考RST BPDU格式的BPDU Type和Flags域。

3.4 updtBPDUVersion

#define BPDU_TOPO_CHANGE_TYPE    0x80

#define BPDU_CONFIG_TYPE               0x00

#define BPDU_RSTP                             0x02

static Bool updtBPDUVersion (STATE_MACH_T* this)

{

  register PORT_T* port = this->owner.port;

 

 /*STP有2种BPDU:Config BPDU,TCN BPDU(TOPO CHANGE)

  * RSTP有1种BPDU:RSTP BPDU 

  * 此信息由BPDU头部中的BPDU Types字段即msgBpduType标识,参见以上宏定义

  */

  if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) {

    port->rcvdSTP = True; // STP

  }

  if (BPDU_RSTP == port->msgBpduType) {

    port->rcvdRSTP = True; // RSTP

  }

  return True;

}

关于版本,RSTP能够识别STP BPDU,STP不能够识别RSTP BPDU,即同一拓扑中同时有RSTP设备和STP设备时,协议将退化为STP。

 

在一个RSTP网桥中,版本类型可以是:

(1) STP兼容模式

在此模式下将禁用可选端口到根端口的快速转移、指定端口快速转移到转发状态并且只发送接收 Config BPDUs 和 TCN BPDUs,丢弃任何收到的RST BPDUs。

(2) RSTP模式

在此模式下将允许可选端口到根端口的快速转移、指定端口快速转移到转发状态可以发送接收Config BPDUs 、TCN BPDUs和RST BPDUs。

 

3.5 STP_info_rx_bpdu

根据接收BPDU头部的BPDU Types字段信息,维护port的各成员变量的更新,提供状态机切换的触发事件。

void

STP_info_rx_bpdu (PORT_T* port, struct stp_bpdu_t* bpdu, size_t len)

{    

  /* check bpdu type */

  switch (bpdu->hdr.bpdu_type) {

    case BPDU_CONFIG_TYPE:

      port->rx_cfg_bpdu_cnt++;

      if (port->admin_non_stp) return; // 管理员配置为不支持STP兼容模式则直接返回

      port->rcvdBpdu = True; // 标记接收到BPDU

      break;

    case BPDU_TOPO_CHANGE_TYPE:

      ...

    case BPDU_RSTP:

      port->rx_rstp_bpdu_cnt++;

      if (port->owner->ForceVersion >= NORMAL_RSTP) { // NORMAL_RSTP = 2,大于等于2的版本号才支持RSTP

        port->rcvdBpdu = True;

      } else {          

        return;

      }

      break;

  }

  /* 更新端口信息 */

  port->msgBpduVersion = bpdu->hdr.version;

  port->msgBpduType =    bpdu->hdr.bpdu_type;

  port->msgFlags =       bpdu->body.flags;

  ...

}

 

3.6 updtRcvdInfoWhile

用来计算rcvdInfoWhile的值

有效年龄=Message Age +╔ max(1,1/16 Max Age)╗(Message Age、Max Age来自portTimes)

(1) 如果有效年龄 > Max Age:rcvdInfoWhile = 0

(2) 如果有效年龄<= Max Age:rcvdInfoWhile = min(Max Age - 有效年龄,HelloTime*3)

 

3.6 STP_info_enter_state

执行指定状态的特定动作。

void STP_info_enter_state (STATE_MACH_T* this)

{

  register PORT_T* port = this->owner.port;

  switch (this->State) {

    case BEGIN: // 开始态,自定义,用于状态机启动初始化

      port->rcvdMsg = OtherMsg;

      port->msgBpduType = -1;

      port->msgPortRole = RSTP_PORT_ROLE_UNKN;

      port->msgFlags = 0; // 此处无break,直接进入DISABLED态

    case DISABLED:

      ...//优先级和时间设为指定值,待重选角色

      break;

    case ENABLED: /* IEEE 802.1y */

      ...

      break;

    case AGED: // 过期态,为一过渡状态

      port->infoIs = Aged;

      port->reselect = True; port->selected = False; // 待重选角色

      break;

    case UPDATE:

      /* 端口变为指定端口,执行一系列固定配置 */

      STP_VECT_copy (&port->portPrio, &port->designPrio);

      STP_copy_times (&port->portTimes, &port->designTimes);

      port->updtInfo = False;

      port->agreed = port->synced = False; /* In UPDATE */

      port->proposed = port->proposing = False; /* in UPDATE */

      port->infoIs = Mine;

      port->newInfo = True;

      break;

    case CURRENT:

      break;

    case RECEIVE:

      port->rcvdMsg = rcvBpdu (this); // 从rcvBpdu () 获取接收信息,用于决定STP_info_check_conditions()的状态切换

      updtBPDUVersion (this); // 更新BPDU版本

      setTcFlags (this); // 更新拓扑标志

      port->rcvdBpdu = False; // BPDU已得到处理,复位BPDU接收标志

      break;

    case SUPERIOR: // 转到更优信息态时,说明当前该端口信息与生成树不一致,同步标志置FALSE且待重选角色

      STP_VECT_copy (&port->portPrio, &port->msgPrio);

      STP_copy_times (&port->portTimes, &port->msgTimes);

      updtRcvdInfoWhile (this);

      port->proposing = False; /* in SUPERIOR */

      port->proposed = recordProposed (this, "SUPERIOR");

      port->infoIs = Received;

      port->reselect = True;

      port->selected = False;

      break;

    case REPEAT:

 

     /* 记录Proposal标志,返回值赋给proposed

      * =TRUE:该BPDU是RST BPDU && 该BPDU的port role==Designated Port && 该BPDU的Proposal标志==TRUE &&  

      * operPointToPointMAC==TRUE

      */

      port->proposed = recordProposed (this, "REPEAT");

 

      updtRcvdInfoWhile (this);

      break;

  case AGREEMENT:

      port->agreed = True;

      port->proposing = False; /* In AGREEMENT */

      break;

  }

}

3.7 STP_info_check_conditions

检查状态机倒换条件并进行相应状态倒换。、

 

Bool STP_info_check_conditions (STATE_MACH_T* this)

{

  register PORT_T* port = this->owner.port;

  /* 端口禁止或处于BEGIN态时切换到DISABLED态 */

  if ((! port->portEnabled && port->infoIs != Disabled) || BEGIN == this->State) {

    return STP_hop_2_state (this, DISABLED);

  }

  /* 当前态 + 倒换条件 -> 下一状态 */

  switch (this->State) {

    case DISABLED:

      if (port->updtInfo) {

        return STP_hop_2_state (this, DISABLED);

      }

      ...

      break; 

    case AGED:

      if (port->selected && port->updtInfo) {

        return STP_hop_2_state (this, UPDATE);

      }

      break;

    case UPDATE:

      return STP_hop_2_state (this, CURRENT);

      break;

    ...

 

   /* RECEIVE态下根据收消息类型决定下一状态port->rcvdMsg在STP_info_enter_state()函数中

    * 由port->rcvdMsg = rcvBpdu (this) 获取

    */

 

    case RECEIVE: 

      switch (port->rcvdMsg) {

        case SuperiorDesignateMsg:

          return STP_hop_2_state (this, SUPERIOR);

        case RepeatedDesignateMsg:

          return STP_hop_2_state (this, REPEAT);

        case ConfirmedRootMsg:

          return STP_hop_2_state (this, AGREEMENT);

        default:

          return STP_hop_2_state (this, CURRENT);

      }

      break;

      ...

  }

  return False;

}

 

 

4 状态机

RSTP_port_information_state_machine

 

 

 


 

 

原创粉丝点击