1. 介紹
STP(Spanning Tree Protocol)即生成樹協議,標准為IEEE802.1D-1998
STP是一種二層冗余技術,利用STA算法構建一個邏輯上沒有環路的樹形網絡拓撲結構,並且可以通過一定的方法實現路徑冗余
2. 概念
2.1 角色
生成數有如下角色
- 根橋(Root Bridge) RB, 橋ID最小的橋
- 根端口(Root Port) RP, 該端口到達根橋的路徑是該端口所在網橋到達根橋的最佳路徑
- 指定端口(Designated Port)DP, 每一個網段選擇到根橋最近的網橋作為指定網橋, 該網橋到這一網段的端口為指定端口
- 可選端口(Alternated Port)AP, 即阻塞端口, 既不是指定端口, 也不是根端口的端口
2.2 端口狀態
STP端口狀態有如下幾種
- 斷開(Disable): 不收發任何報文
- 阻塞(Blocking): 不接收或轉發數據, 接收但不發送BPDU, 不進行地址學習
- 監聽(Listening): 不接收或轉發數據, 接收並發送BPDU, 不進行地址學習
- 學習(Learning): 不接收或轉發數據, 接收並發送BPDU, 開始地址學習
- 轉發(Forwarding): 接收並轉發數據, 接收並發送BPDU, 進行地址學習
一個網橋開啟STP后,端口會經歷如下過程:
Blocking(20s) –> Listening(15s) –> Learning(15s) –> Forwarding
這樣大約50秒的時間非根端口轉變成為根端口或指定端口
2.3 相關參數
2.3.1 網橋參數
Bridge Identifier: 標識本網橋, 由網橋的優先級(2 bytes)和網橋的MAC(6 bytes)組成 Root Path Cost: 從非根橋到根橋的路徑開銷的總和 Root Port: 根端口, RP Max Age: 在協議的配置信息被丟棄之前收到的配置信息保留的時間(默認20s) Hello Time: 對一個想變成橋或者是根橋的橋在固定的時間發送的配置BPDU信息(默認2s) Forward Delay: 從Listening到Learning和從Learning到Forwarding花費的時間(默認15s) Bridge Max Age: 橋作為根或想成為根橋的橋標識的它作為根橋的最大年齡 Bridge Hello Time: 非根橋向根橋發送TCN BPDU的時間間隔 Bridge Forward Delay: 和上面介紹的Forward Delay值一樣 Topology Change Detected: 用來標識網絡拓撲結構改變的布爾變量 Topology Change: 參數設置表示拓撲改變 Topology Change Time: 根橋產生配置消息指明拓撲改變的時間間隔 Hold Time: 在這個時間間隔至少應該收到一個配置BPDU信息
2.3.2 網橋時間參數
Hello Timer: 根橋定時傳輸配置BPDU信息的時間間隔
Topology Change Notification Timer: 如果沒收到拓撲改變回應消息便會定時發送拓撲改變通知
Topology Change Time: 定時發送拓撲改變通知來通知橋進行重配
2.3.3 端口參數
Port Identifier: 橋的端口ID, 由優先級和端口號組成
Port State: 端口狀態
Path Cost: 本端口的路徑開銷(see tips below)
Designated Root: 根橋的橋ID
Designated Cost: 從這個端口到根橋的路徑開銷
Designated Bridge: 指定橋的橋ID
Designated Port: 指定橋中和它交互的端口ID
Topology Change Acknowledge: 設置來回應拓撲改變通知
Configuration Pending: 設置表示在Holdtimer超時后應該傳輸配置BPDU信息
Change Detection Enabled: 表示是否理會拓撲改變通知
2.3.4 端口時間參數
Message Age Timer: 表示超時丟棄協議信息的時間
Forward Delay Timer: 和端口狀態改變的時間值有關
Hold Timer: 在這個時間間隔至少應該收到一個配置BPDU信息
tips: 關於路徑開銷,可參考<IEEE802.1t路徑開銷列表>
通常根據端口的帶寬不同,Path Cost值也不同:
• 10 Mb/s: 100
• 100 Mb/s: 19
• 1000 Mb/s: 4
• 10000 Mb/s: 2
2.4 BPDU
2.4.1 介紹
STP的數據單元稱為BPDU(Bridge Protocol Data Unit)
BPDU是網橋間信息交流的報文,目標MAC地址為:01-80-C2-00-00-00
網橋之間定期發送BPDU包,交換STP配置信息,以便能夠對網絡的拓撲、花費或優先級的變化做出及時的響應
BPDU報文在直連的兩個網橋或多個網橋內交換,不能被轉發
沒有運行STP協議的網橋將把BPDU報文當作普通業務報文轉發
BPDU被封裝在LLC的Payload中
下面是一個C BPDU
2.4.2 類型
BPDU有三種類型
- Configuration BPDU: 即C BPDU, 包含配置信息的BPDU包
- Topology Change Notification BPDU: 即TCN BPDU, 當檢測到網絡拓撲結構變化時發送拓撲變化通知BPDU
- Topology Change Notification Acknowledgment: 即TCA BPDU(TCA flag置1的C BPDU)
BPDU報文格式如下
2.4.3 發送與接收
C BPDU:
只有RB發送C BPDU,它有一個Hello Timer,當這個定時器expire的時候產生一個BPDU的包
而其他的non-RB在他們的RP中收到這些包之后進行中繼,中繼的過程中對BPDU中的各項值根據自身的情況進行更新
TCN BPDU:
當某個non-RB檢測到拓撲變化后,該br會通過RP向RB方向發送TCN BPDU,其他br從它的DP接收到TCN BPDU時,首先從該DP上回應一個TCA BPDU,然后將該TCN BPDU從RP向RB方向發送, 這樣TCN BPDU最后會到達RB
RB則會發出一個C BPDU(topology change置1),表示發現拓撲變化,這個包會被所有br轉發,傳遍全網,這樣所有br都得知拓撲變化,然后做出相應的動作(修改ageing timer: 5m->15s)
該過程如下圖所示:

TIP: C BPDU是從RB往樹的各個方向傳送;TCN BPDU則是從樹的某個br上行直至RB
2.5 選舉
2.5.1 根橋的選舉
網橋初始化以后,都認為自己是Root Bridge,在它認為的指定端口上,周期性的發送C BPDU直到收到BridgeID更小的網橋信息,才直到自己不是根網橋
或者由於配置管理(端口使能,設置網橋優先級等),端口狀態改變,也會引起Root Bridge的重新選舉
2.5.2 根端口的選擇
非根橋的其他網橋將各自選擇一條到Root Bridge代價最小的路徑,相應端口的角色就成為根端口
2.5.3 指定端口的選擇
根端口選擇以后,選擇到根橋路徑開銷最小的端口作為指定端口
2.5.4 可選端口的選擇
端口enable,參與選舉的拓撲計算,既不是根端口,也不是指定端口,那就是可選端口了
2.5.5 選擇過程
選舉過程如下所示
選舉根橋 -> 根端口 -> 指定端口 -> 其余為阻塞端口
生成樹經過一段時間(默認值是30秒左右)穩定之后,所有端口要么進入轉發狀態,要么進入阻塞狀態
STP BPDU仍然會定時從各個網橋的指定端口發出,以維護鏈路的狀態
如果網絡拓撲發生變化,生成樹就會重新計算,端口狀態也會隨之改變
TIP:
網橋接受BPDU時開銷值增加,發送BPDU時開銷值不變
只有根橋是沒有根端口的,根橋的所有端口都是指定端口
選舉根端口,比較接收的BPDU;
選舉指定端口,比較發送的BPDU.
3. 實現
3.1 數據結構
net_bridge: 網橋STP相關參數
struct net_bridge { ... /* STP */ bridge_id designated_root; /* RB ID */ bridge_id bridge_id; /* ID */ u32 root_path_cost; /* 路徑開銷 */ unsigned long max_age; /* 配置信息保留時間 */ unsigned long hello_time; unsigned long forward_delay; unsigned long bridge_max_age; unsigned long ageing_time; unsigned long bridge_hello_time; unsigned long bridge_forward_delay; u8 group_addr[ETH_ALEN]; u16 root_port; /* 本網橋的根端口 */ enum { BR_NO_STP, /* no spanning tree */ BR_KERNEL_STP, /* old STP in kernel */ BR_USER_STP, /* new RSTP in userspace */ } stp_enabled; unsigned char topology_change; unsigned char topology_change_detected; struct timer_list hello_timer; struct timer_list tcn_timer; struct timer_list topology_change_timer; struct timer_list gc_timer; };
net_bridge_port: 網橋端口STP相關參數
struct net_bridge_port { /* STP */ u8 priority; u8 state; u16 port_no; unsigned char topology_change_ack; unsigned char config_pending; port_id port_id; /* 本端口的端口ID */ port_id designated_port; /* 發送當前BPDU包的端口ID */ bridge_id designated_root; /* 根橋的網橋ID */ bridge_id designated_bridge; /* 發送當前BPDU包的網橋ID */ u32 path_cost; /* 本端口的路徑開銷 */ u32 designated_cost; /* 到根橋的路徑開銷 */ unsigned long designated_age; struct timer_list forward_delay_timer; struct timer_list hold_timer; struct timer_list message_age_timer; };
br_config_bpdu: BPDU包
struct br_config_bpdu { unsigned int topology_change:1; unsigned int topology_change_ack:1; bridge_id root; int root_path_cost; bridge_id bridge_id; port_id port_id; int message_age; int max_age; int hello_time; int forward_delay; };
可以發現,br_config_bpdu和C BPDU報文格式對照可以發現,缺少 Protocol ID、Version、Message Type和Flags
因為Message Type基本在使用的時候可以確定,而Flags則可由topology_change和topology_change_ack來確定
具體可參考函數br_send_config_bpdu()的實現
3.2 STP模塊
3.2.1 基本使用
/* * 新增網橋 * br_dev_setup() */ # brctl addbr br0 /* * 向網橋新增端口 * br_add_if() -> new_nbp() -> br_init_port() */ # brctl addif br0 eth0 /* * 啟用和禁用STP * on -> br_stp_start() -> br_port_state_selection() * off -> br_stp_stop() */ # brctl stp br0 <on|off>
3.2.2 數據通路
static const struct stp_proto br_stp_proto = { .rcv = br_stp_rcv, }; static int __init br_init(void) { stp_proto_register(&br_stp_proto); ...... }
參考:
<Linux Bridge STP HOWTO>
<Spanning Tree Protocol (STP)>
<Linux的網橋中的STP的實現分析初步>
