// 传输配置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);//回复本网桥使用的配置信息 } }