快速生成樹之端口信息狀態機

 

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 (TOLPLO GY_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

 

 

 


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章