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.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 狀態機