網絡子係統35_BPDU的發送與接收
// 傳輸配置BPDU
// 函數主要任務:
// 1.檢查速率限製
// 2.填充bpdu報文
// 3.發送配置bpdu
// 注:每個端口對bpdu的發送速率具有限製,通過net_bridge_port->hold_timer進行限製。
1.1 void br_transmit_config(struct net_bridge_port *p)
{
struct br_config_bpdu bpdu;//配置bpdu
struct net_bridge *br;
if (timer_pending(&p->hold_timer)) {//如果在速率限製區間內,則設置config_pending,然後返回
p->config_pending = 1;
return;
}
br = p->br;//網橋的設備
bpdu.topology_change = br->topology_change;//初始化配置bpdu協議相關項
bpdu.topology_change_ack = p->topology_change_ack;
bpdu.root = br->designated_root;
bpdu.root_path_cost = br->root_path_cost;
bpdu.bridge_id = br->bridge_id;
bpdu.port_id = p->port_id;
if (br_is_root_bridge(br))//根網橋
bpdu.message_age = 0;//自從根網橋生成該BPDU中包含的信息後已經過的時間
else {
struct net_bridge_port *root
= br_get_port(br, br->root_port);
bpdu.message_age = br->max_age
- (root->message_age_timer.expires - jiffies)
+ MESSAGE_AGE_INCR;
}
bpdu.max_age = br->max_age;//配置BPDU的最大生存期
bpdu.hello_time = br->hello_time;
bpdu.forward_delay = br->forward_delay;
if (bpdu.message_age < br->max_age) {
br_send_config_bpdu(p, &bpdu);//發送配置BPDU
p->topology_change_ack = 0;
p->config_pending = 0;
mod_timer(&p->hold_timer, jiffies + BR_HOLD_TIME);
}
}
// 處理入口bpdu幀
// 調用路徑netif_receive_skb->...->br_stp_handle_bpdu
// 函數主要任務:
// 1.確保sk_buff->data足夠bpdu協議格式
// 2.通過報文內容,區別配置bpdu,tc-bpdu,分別處理
2.1 int br_stp_handle_bpdu(struct sk_buff *skb)
{
struct net_bridge_port *p = skb->dev->br_port;
struct net_bridge *br = p->br;
unsigned char *buf;
if (!pskb_may_pull(skb, sizeof(header)+1) ||//使skb->data到skb->tail的數據量足夠 802 和 STP 協議最小的協議頭
memcmp(skb->data, header, sizeof(header)))//拷貝協議頭到header中
goto err;
buf = skb_pull(skb, sizeof(header));//skb->data向下移動n個字節,skb->len-=n
spin_lock_bh(&br->lock);//獲取網橋的鎖
if (p->state == BR_STATE_DISABLED //skb的接收端口被關閉
|| !(br->dev->flags & IFF_UP)//網橋被關閉
|| !br->stp_enabled)//網橋沒有運行stp協議
goto out;
if (buf[0] == BPDU_TYPE_CONFIG) {//配置BPDU
struct br_config_bpdu bpdu;
//按照配置BPDU的報文格式填充結構
if (!pskb_may_pull(skb, 32))
goto out;
buf = skb->data;
bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;//拓撲是否改變
bpdu.topology_change_ack = (buf[1] & 0x80) ? 1 : 0;//拓撲改變BPDU的ack
//優先級向量<根網橋id,根網橋開銷,發送網橋id,發送網橋端口id>
bpdu.root.prio[0] = buf[2];
bpdu.root.prio[1] = buf[3];
bpdu.root.addr[0] = buf[4];
bpdu.root.addr[1] = buf[5];
bpdu.root.addr[2] = buf[6];
bpdu.root.addr[3] = buf[7];
bpdu.root.addr[4] = buf[8];
bpdu.root.addr[5] = buf[9];
bpdu.root_path_cost =
(buf[10] << 24) |
(buf[11] << 16) |
(buf[12] << 8) |
buf[13];
bpdu.bridge_id.prio[0] = buf[14];
bpdu.bridge_id.prio[1] = buf[15];
bpdu.bridge_id.addr[0] = buf[16];
bpdu.bridge_id.addr[1] = buf[17];
bpdu.bridge_id.addr[2] = buf[18];
bpdu.bridge_id.addr[3] = buf[19];
bpdu.bridge_id.addr[4] = buf[20];
bpdu.bridge_id.addr[5] = buf[21];
bpdu.port_id = (buf[22] << 8) | buf[23];
bpdu.message_age = br_get_ticks(buf+24);//自從根網橋生成該bpdu以來,已經過去的時間
bpdu.max_age = br_get_ticks(buf+26);//配置bpdu的最大生存期
bpdu.hello_time = br_get_ticks(buf+28);//hello定時器所用的時限
bpdu.forward_delay = br_get_ticks(buf+30);//forward delay所用的時限
br_received_config_bpdu(p, &bpdu);//協議處理函數
}
else if (buf[0] == BPDU_TYPE_TCN) {//拓撲改變bpdu
br_received_tcn_bpdu(p);
}
out:
spin_unlock_bh(&br->lock);
err:
kfree_skb(skb);
return 0;
}
// 處理配置BPDU
// 調用路徑br_stp_handle_bpdu->br_received_config_bpdu
// 函數主要任務:
// 1.配置bpdu的優先級向量高於當前網橋的優先級向量
// 1.1 使用配置bpdu的配置信息
// 1.2 更新網橋配置信息, 重新選擇根端口,指定端口
// 1.3 網橋端口的狀態選擇
// 1.4 如果網橋由非根網橋變為根網橋
// 1.4.1 使用tcn_timer,周期性向網絡傳輸設置有tc標誌的配置bpdu
// 2.配置bpdu的優先級向量低於當前網橋的優先級向量
// 2.1 如果接收端口是指定端口,則向指定端口發送本網橋使用的配置信息。
2.2 void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
{
struct net_bridge *br;
int was_root;
br = p->br;
was_root = br_is_root_bridge(br);//當前網橋是否為根網橋
if (br_supersedes_port_info(p, bpdu)) {//配置bpdu中的優先級向量高於當前網橋的優先級向量
br_record_config_information(p, bpdu);//更新接收到的端口優先級向量,修改message_age定時器的到期時間
br_configuration_update(br);//更新網橋的配置信息,選擇根端口,指定端口
br_port_state_selection(br);//開啟網橋端口狀態的選擇
if (!br_is_root_bridge(br) && was_root) {//由根網橋變成了非根網橋
del_timer(&br->hello_timer);//非根網橋不需要使用hello定時器
if (br->topology_change_detected) {//檢測到拓撲的變化設置該字段
del_timer(&br->topology_change_timer);//根網橋檢測到拓撲變化後,使用該定時器,通知其他網橋拓撲的變化
br_transmit_tcn(br);//從根端口發送tcn
mod_timer(&br->tcn_timer,
jiffies + br->bridge_hello_time);//普通網橋檢測到拓撲變化後,使用的定時器,等待接收到tca
}
}
if (p->port_no == br->root_port) {//當前端口為根端口
br_record_config_timeout_values(br, bpdu);//更新br->max_age,hello_time,forward delay,topology_change。當根網橋檢測的拓撲變化後,會啟動topology_change_timer,並且在發送的配置bpdu中設置tc標誌,當根網橋的topology_change_timer到期後,之後發送的配置bpdu,都不會再有tc標誌。其他非根網橋,在每次從根端口收到配置bpdu時,都會將其中的tc標誌保存在br->topology_change字段中,然後從指定端口發送的配置bpdu時,根據該字段,決定是否設置tc標誌。
br_config_bpdu_generation(br);//在每個使能的指定端口,發送配置BPDU
if (bpdu->topology_change_ack)//此bpdu時對本機之前發送的TCN的應答,TCN從根端口發送,然後從根端口接收到TCA的配置BPDU
br_topology_change_acknowledged(br);//收到TCN應答
}
} else if (br_is_designated_port(p)) {//收到的bpdu優先級向量低於本網橋使用的優先級向量
br_reply(p);//回複本網橋使用的配置信息
}
}
最後更新:2017-04-03 14:53:45